2012年11月30日星期五

Ubuntu下安装tomcat 6


Ubuntu下安装tomcat 6  

2011-04-04 09:24:14|  分类: WEB技术|字号 订阅
Tomcat设置JSP开发环境,有两种,或是用源安装,或是自己下载配置 Tomcat,推荐后者。

源方式(不推荐)

安装Tomcat
Tomcat是由Apache Foundation研发用于支持JSP(Java Server Page)的网络服务软件。
sudo apt-get install tomcat6 
稍等片刻,Tomcat6 即会被自动安装在/usr/share/tomcat6的目录中

设置Tomcat运行的JAVA环境

首先请确保Sun的Java Development Toolkit已经安装。如果尚未安装,参照Java

启动和停止Tomcat

要启动Tomcat,运行
qii@ubuntu:~$ sudo /etc/init.d/tomcat6 start
 * Starting Tomcat servlet engine tomcat6                                               [ OK ] 
此时打开浏览器,在地址栏内输入http://localhost:8080

要停止Tomcat,运行
qii@ubuntu:~$ sudo /etc/init.d/tomcat6 stop
 * Stopping Tomcat servlet engine tomcat6                                               [ OK ] 

Tomcat 配置文件路径

Tomcat home directory : /usr/share/tomcat6
Tomcat base directory : /var/lib/tomcat6
要停止Tomcat,运行
qii@ubuntu:~$ sudo /etc/init.d/tomcat6 stop * Stopping Tomcat servlet engine tomcat6 [ OK ]

Tomcat 配置文件路径

Tomcat home directory : /usr/share/tomcat6 Tomcat base directory : /var/lib/tomcat6

设置Tomcat管理员帐号

Tomcat的用户帐号信息都保存在tomcat-users.xml的文件中,运行
sudo nano /var/lib/tomcat6/conf/tomcat-users.xml
在</tomcat-users>的标签前添加一行
<user username="用户名" password="密码" roles="admin,manager"/>
保存并关闭。重新运行tomcat即可输入该用户名和密码,登录Tomcat的管理页面。

弊端

Ubuntu 默认把 Tomcat 分到2个目录,在之后配置 Eclipse Server 时会把人逼疯的,配置哪个路径都不对,Eclipse 无法识别安装的 Tomcat。 解决方案: sudo ln -s /var/lib/tomcat6/conf /usr/share/tomcat6/conf sudo ln -s /etc/tomcat6/policy.d/03catalina.policy /usr/share/tomcat6/conf/catalina.policy sudo ln -s /var/log/tomcat6 /usr/share/tomcat6/log sudo chmod -R 777 /usr/share/tomcat6/conf 

下载安装最新版本的Tomcat

这种方法绕过 Ubuntu 自身的包管理器,直接从Apache Tomcat的主页上下载并使用Tomcat软件,该方法方便快捷的安装旧版或新测试版 Tomcat 而并不影响系统的稳定性,轻松自在。
从tomcat的官方下载页面
http://tomcat.apache.org/download-70.cgi
下载Binary->Core分类中的zip或者tar.gz包后,本地解压缩并将新生成的目录重命名为tomcat,以方便使用。将这个文件夹移动至某路径PATH/。 参考以上设置环境变量和端口设置等步骤,大功告成! 直接运行
PATH/tomcat/bin/./startup.sh
Tomcat 便即刻在后台服务了。 (请将PATH替换成适合您的路径) 

配置开发环境

Eclipse

以下载的 Eclipse for Java EE 为例,右击底部面板分页 Servers - New - Server,选择你要的 Tomcat 版本,添加进路径。 
Ubuntu下安装tomcat 6 - philn - IT基础知识
 
Preferences - Server - Runtime Environments
Ubuntu下安装tomcat 6 - philn - IT基础知识

ubuntu 安装 eclipse




There are two ways of installing eclipse IDE in ubuntu.
1.Using Ubuntu Software Center Or
2.Download  Eclipse IDE package and then install manually


1.If you use Ubuntu Software Center for installing eclipse,new version of eclipse is not available.

 The above version is 3.7.2-1.However newer version 4.2 juno is available here.


NOTE:Before installing eclipse IDE you need to check few things.

First, whether you have Java installed or not.For that you need to run following command in your terminal...
                                    # java -version
If you get the following output that means Java is not installed on your Ubuntu12.04.You need to  install Sun JDK or Open JDK using Ubuntu Software Center.

And if you get the following output that means you have java installed in your OS.


Second, if you are installing Eclipse IDE for C/C++,you need to check whether g++ is installed or not.
To check whether g++ is installed or not you need to run the following command in the terminal...
                                                 # g++ --version

If you get the following output that means g++ is already installed else you need to install it using Ubuntu Software Center.




2.By manually downloading the eclipse IDE package.
         
         a.First download the eclipse tar.gz package from here.

      
         b.Then righ-click the eclipse tar.gz and choose the extract here option to extract the tar.gz package.You can also use the command line to extract the tar.gz package.
       
                               # tar xzf eclipse-cpp-juno-linux-gtk.tar.gz
            
        
          c.Move the extracted eclipse in the  /opt/ folder.Before running the below commmand be sure that you are in the directory which contains extracted eclipse folder.

                               # mv eclipse /opt/
           
            Use sudo if the above command gives permission denied message.
                                 
                              # sudo mv eclipse /opt/


       d.Create a desktop file and place it into /usr/share/applications
              
                            # sudo gedit /usr/share/applications/eclipse.desktop            
       
           and copy the following to the eclipse.desktop file
  1. [Desktop Entry]
    Name=Eclipse 
    Type=Application
    Exec=/opt/eclipse/eclipse
    Terminal=false
    Icon=/opt/eclipse/icon.xpm
    Comment=Integrated Development Environment
    NoDisplay=false
    Categories=Development;IDE
    Name[en]=eclipse.desktop
 
       e. Create a symlink in /usr/local/bin using
  1.  # cd /usr/local/bin
     # sudo ln -s /opt/eclipse/eclipse

   
       f. Now its the time to launch eclipse.
       
             # /opt/eclipse/eclipse  -clean  &    

2012年11月29日星期四

Ubuntu 12.04中文输入法的安装


Ubuntu 12.04中文输入法的安装
 
Ubuntu上的输入法主要有小小输入平台(支持拼音/二笔/五笔等),Fcitx,Ibus,Scim等。其中Scim和Ibus是输入法框架。
在Ubuntu的中文系统中自带了中文输入法,通过Ctrl+Space可切换中英文输入法。这里我们主要说下Ubuntu英文系统中,中文输入法的安装。
安装输入法的第一步,是安装语言包。我们选择System Settings-->Language Support-->Install/Remove Languages,将弹出以下窗口:  www.2cto.com  
 

 
输入密码后,系统会安装简体中文语言包。
第二步,安装IBus框架,在终端输入以下命令:
sudo apt-get install ibus ibus-clutter ibus-gtk ibus-gtk3 ibus-qt4
启动IBus框架,在终端输入:
im-switch -s ibus
安装完IBus框架后注销系统,保证更改立即生效。
第三步:安装拼音引擎
 
有下面几种常用选择:
IBus拼音:sudo apt-get install ibus-pinyin
IBUS五笔:sudo apt-get install ibus-table-wubi
谷歌拼音输入法:sudo apt-get install ibus-googlepinyin
Sun拼音输入法:sudo apt-get install ibus-sunpinyin
第四步:设置IBus框架  www.2cto.com  
ibus-setup
此时,IBus Preference设置被打开。我们在Input Method选项卡中,选择自己喜欢的输入方式,并配置自己喜欢的快捷键即可。如下图所示:
 

 
第五步:通常情况下,IBus图标(一个小键盘)会出现在桌面右上角的任务栏中。有时候这个图标会自行消失,可使用以下命令,找回消失的IBus图标:
ibus-daemon -drx

2012年9月28日星期五

DOS下设置永久环境变量

如果想设置JAVA_HOME,则需执行如下命令即可:setx JAVA_HOME "C:\Program Files\LightenBSM Server\jdk1.6.0_16"
在path中加入JAVA执行如下:setx Path "%Path%;%JAVA_HOME%\bin"
 

2012年9月19日星期三

LaTex 中在插图figure 的\caption 中使用 \cite 报错

LaTex 中在插图figure 的\caption 中使用 \cite 报错时, 在\cite 左边加上\protect 就可以解决 例如:
\begin{figure}[htbp]
\includegraphics[width=0.45\textwidth]{./pic/Hello}
\caption{Hello World!  \protect \cite{Wiki}}
\label{fig:Hello}
\end{figure}

2012年9月11日星期二

常用求导公式

sin(x)   cos(x)
cos(x)  -sin(x)
tan(x)  1/((cos(x))^2)
asin(x) 1/sqrt(1-x^2))            -1 <=x<=1
acos(x)  -1/sqrt(1-x^2)          -1<=x<=1
atan(x)  1/(1+x^2)
sinh(x)  cosh(x)                                            sinh(x)=(exp(x) - exp(-x)) / 2
cosh(x)  sinh(x)                                            cosh(x)=(exp(x) + exp(-x)) /2
tanh(x)   1/(cosh(x))^2                                  tanh(x)=sinh(x) / cosh(x)
asinh(x)  1/sqrt(1+x^2)
acosh(x)   1/sqrt(x^2-1)
atanh(x)   1/(1-x^2)

ln(x)   1/x
log10(x)  ln(10)/x
exp(x)  exp(x)
sqrt(x)   0.5/sqrt(x)
atan2(x, y)    (y*dx - x*dy) / (x^2 + y^2)
hypot(x,y)   (x*dx + y*dy) / sqrt(x^2 + y^2)              hypot(x,y)=sqrt(x^2+y^2)
pow(x,y)  pow(x,y-1) * (y*dx + x*ln(x)*dy)

2012年8月17日星期五

LaTeX技巧421:LaTeX表格tabular背景色添加技巧

LaTeX技巧421:LaTeX表格tabular背景色添加技巧

 

zz:http://blog.sina.com.cn/s/blog_5e16f1770100mvrx.html

(2010-11-15 12:17:54)
TeX/LaTeX社区QQ群:80300084
常见数学排版问题集下载
我们所用的宏包为colortbl,这个宏包可以设置表格中数据、文本、行、列、单元格前景和背景以及边框的颜色,从而得到彩色表格。同时需要 array 和 color 两个宏包的支持。 宏包提供了一组着色命令,经常用到是列着色命令,其格式为:
\columncolor[色系]{色名}[左伸出][右伸出]。
常用色系有三原色 rgb 灰度 gray 
和四色cmyk三种;被预定义的色名有68个,详见 color 宏包介绍中所附的色标;左右伸出的长度单位可用 pt。
colortbl 的主要命令
命令
作用
\columncolor
让整个字段着色
\rowcolor
整个横列着色
\arrayrulecolor{颜色}
指定线条的颜色
\doublerulesepcolor{颜色}
指定双线内间隔的颜色
在这里,\columncolor\rowcolor 的参数是一样的,他们的共同语法是:

                  \columncolor[颜色模型]{颜色}[左伸出长度][右伸出长度]
我们现在就来看个实例,这里头有些例子,包括:灰阶横条、部份字段着色、整个表格在着色背景及单一个表格内方框着色:
LaTeX技巧421:LaTeX表格tabular背景色添加技巧

LaTeX技巧421:LaTeX表格tabular背景色添加技巧

LaTeX技巧421:LaTeX表格tabular背景色添加技巧

LaTeX技巧421:LaTeX表格tabular背景色添加技巧

LaTeX技巧421:LaTeX表格tabular背景色添加技巧

LaTeX技巧421:LaTeX表格tabular背景色添加技巧

示例代码如下:
\documentclass[12pt]{article}
\usepackage{textcomp,booktabs}
\usepackage[usenames,dvipsnames]{color}
\usepackage{colortbl}
\definecolor{mygray}{gray}{.9}
\definecolor{mypink}{rgb}{.99,.91,.95}
\definecolor{mycyan}{cmyk}{.3,0,0,0}
\parindent=0pt
\parskip=3ex
\begin{document}
\centering

\section*{SPECIFIC HEATS (20 \textcelsius\ AND 1 ATM)}
\begin{tabular}{>{\sf }lll}

  %
\toprule

& \multicolumn{2}{c}{\bf Specific Heats} \\
\cmidrule{2-3}

& $c$ (J/kg$\cdot$K) & $C$ (J/mol$\cdot$K) \\
\midrule
Aluminum 
    & 900  & 24.3 \\
\rowcolor{mygray}
Copper 
      & 385  & 24.4 \\
Gold 
        & 130  & 25.6 \\
\rowcolor{mygray}
Steel/Iron 
  & 450  & 25.0 \\
Lead 
        & 130  & 26.8 \\
\rowcolor{mygray}
Mercury 
     & 140  & 28.0 \\
Water 
       & 4190 & 75.4 \\
\rowcolor{mygray}
Ice ($-$10 \textcelsius) & 2100 & 38 \\
\bottomrule
\end{tabular}

\vspace{8ex} %
\section*{SPECIFIC HEATS (20 \textcelsius\ AND 1 ATM)}
\begin{tabular}{>{\columncolor{mypink}\sf }lll@{}}
\toprule
\rowcolor{white}

& \multicolumn{2}{c}{\bf Specific Heats} \\
\cmidrule{2-3}
\rowcolor{white}

& $c$ (J/kg$\cdot$K) & $C$ (J/mol$\cdot$K) \\
\midrule
Aluminum 
    & 900  & 24.3 \\
Copper 
      & 385  & 24.4 \\
Gold 
        & 130  & 25.6 \\
Steel/Iron 
  & 450  & 25.0 \\
Lead 
        & 130  & 26.8 \\
Mercury 
     & 140  & 28.0 \\
Water 
       & 4190 & 75.4 \\
Ice ($-$10 \textcelsius) & 2100 & 38 \\
\bottomrule
\end{tabular}


\section*{SPECIFIC HEATS (20 \textcelsius\ AND 1 ATM)}
\fboxsep=12pt
\colorbox{mypink}{
\begin{tabular}{@{}>{\sf }lll@{}}
\toprule

& \multicolumn{2}{c}{\bf Specific Heats} \\
\cmidrule{2-3}

& $c$ (J/kg$\cdot$K) & $C$ (J/mol$\cdot$K) \\
\midrule
Aluminum 
    & 900  & 24.3 \\
Copper 
      & 385  & 24.4 \\
Gold 
        & 130  & 25.6 \\
Steel/Iron 
  & 450  & 25.0 \\
Lead 
        & 130  & 26.8 \\
Mercury 
     & 140  & 28.0 \\
Water 
       & 4190 & 75.4 \\
Ice ($-$10 \textcelsius) & 2100 & 38 \\
\bottomrule
\end{tabular}}

\vspace{8ex} %
\section*{SPECIFIC HEATS (20 \textcelsius\ AND 1 ATM)}
\begin{tabular}{@{}>{\sf }lll@{}}
\toprule

& \multicolumn{2}{c}{\bf Specific Heats} \\
\cmidrule{2-3}

& $c$ (J/kg$\cdot$K) & $C$ (J/mol$\cdot$K) \\
\midrule
Aluminum 
    & 900  & 24.3 \\
Copper 
      & 385  & 24.4 \\
Gold 
        & 130  & 25.6 \\
Steel/Iron 
  & 450  & 25.0 \\
Lead 
        & 130  & 26.8 \\
Mercury 
     & 140  & 28.0 \\
Water 
       & \multicolumn{1}{>{\columncolor{mycyan}}l}{4190} & 75.4 \\
Ice ($-$10 \textcelsius) & 2100 & 38 \\
\bottomrule
\end{tabular}


\setlength{\extrarowheight}{2mm}
\begin{tabular}{|l|c|c|c|c|c|c|c|}
\hline
Sydney & OG4G &Thu Oct 10 &Mon Oct 21 or 28 &11 or 18 days &999\\
\rowcolor[gray]{0.5}
& &Thu Oct 17 &Mon Oct 21 or 28 & 4 or 11 days &999\\
&OG7A &Sun Oct 13 &Mon Oct 21 or 28 & 8 or 15 days &999\\
\rowcolor[gray]{0.5}
& &Sun Oct 20 &Mon Oct 28 & 8 days &999\\
\hline
\end{tabular}


\setlength{\extrarowheight}{2mm}
\setlength{\tabcolsep}{2mm}
\begin{tabular}{|l|%
>{\columncolor{yellow}}c|c|>{\columncolor{yellow}}c|c|%
>{\columncolor{red}\bfseries}c<{\textsc{GBP}}|}
\hline
\multicolumn{3}{>{\columncolor{red}}l}{\color{white}\textsf{LONDON}}
&\multicolumn{3}{>{\columncolor{red}}r}{\color{white}\textsf{Price}}
\\[1pt]
\hline
Sydney & OG4G &Thu Oct 10 &Mon Oct 21 or 28 &11 or 18 days &999\\
& &Thu Oct 17 &Mon Oct 21 or 28 & 4 or 11 days &999\\
& OG7A &Sun Oct 13 &Mon Oct 21 or 28 & 8 or 15 days &999\\
& &Sun Oct 20 &Mon Oct 28 & 8 days &999\\
\hline
\end{tabular}


\setlength{\arrayrulewidth}{2pt}
\arrayrulecolor{green}
\begin{tabular}{|l|c|r|}
\arrayrulecolor{black}\hline
United Kingdom & London & Thames\\
\arrayrulecolor{blue}\hline
France & Paris & Seine \\
\arrayrulecolor{black}\cline{1-1}
\arrayrulecolor{red}\cline{2-3}
Russia & Moscow & Moskva \\ \hline
\end{tabular}
\end{document}

2012年7月18日星期三

谈机器学习(Machine Learning)大家

转自:http://blog.sina.com.cn/s/blog_4e66cb5f0100cdh1.html
谈机器学习(Machine Learning)大家 (full version)
闲着无事,想写点一些我所了解的machine learning大家。由于学识浅薄,见识有限,并且仅局限于某些领域,一些在NLP及最近很热的生物信息领域活跃的学者我就浅陋无知,所以不对的地方大家仅当一笑。
  在我的眼里,M Jordan无疑是武林中的泰山北斗。他师出MIT,现在在berkeley坐镇一方,在附近的两所名校(加stanford)中都可以说无出其右者,stanford的Daphne Koller虽然也声名遐迩,但是和Jordan比还是有一段距离。
Jordan身兼stat和cs两个系的教授,从他身上可以看出Stat和ML的融合。
Jordan最先专注于mixtures of experts,并迅速奠定了自己的地位,我们哈尔滨工业大学的校友徐雷跟他做博后期间,也在这个方向上沾光不少。Jordan和他的弟子在很多方面作出了开创性的成果,如spectral clustering, Graphical model和nonparametric Bayesian。现在后两者在ML领域是非常炙手可热的两个方向,可以说很大程度上是Jordan的lab一手推动的。
更难能可贵的是,Jordan不仅自己武艺高强,并且揽钱有法,教育有方,手下门徒众多且很多人成了大器,隐然成为江湖大帮派。他的弟子中有10多人任教授,个人认为他现在的弟子中最出色的是stanford的Andrew Ng,不过由于资历原因,现在还是assistant professor,不过成为大教授指日可待;另外Tommi Jaakkola和David Blei也非常厉害,其中Tommi Jaakkola在mit任教而David Blei在cmu做博后,数次获得NIPS最佳论文奖,把SVM的最大间隔方法和Markov network的structure结构结合起来,赫赫有名。还有一个博后是来自于toronto的Yee Whye Teh,非常不错,有幸跟他打过几次交道,人非常nice。另外还有一个博后居然在做生物信息方面的东西,看来jordan在这方面也捞了钱。这方面他有一个中国学生Eric P. Xing(清华大学校友),现在在cmu做assistant professor。
总的说来,我觉得Jordan现在做的主要还是graphical model和Bayesian learning,他去年写了一本关于graphical model的书,今年由mit press出版,应该是这个领域里程碑式的著作。3月份曾经有人答应给我一本打印本看看,因为Jordan不让他传播电子版,但后来好像没放在心上(可见美国人也不是很守信的),人不熟我也不好意思问着要,可以说是一大遗憾. 另外发现一个有趣的现象就是Jordan对hierarchical情有独钟,相当多的文章都是关于hierarchical的,所以能hierarchical大家赶快hierarchical,否则就让他给抢了。
用我朋友话说看jordan牛不牛,看他主页下面的Past students and postdocs就知道了。
D. Koller是1999年美国青年科学家总统奖(PECASE)得主,IJCAI 2001 Computers and Thought Award(IJCAI计算机与思维奖,这是国际人工智能界35岁以下青年学者的最高奖)得主,2004 World Technology Award得主。
最先知道D koller是因为她得了一个大奖,2001年IJCAI计算机与思维奖。Koller因她在概率推理的理论和实践、机器学习、计算博弈论等领域的重要贡献,成为继Terry Winograd、David Marr、Tom Mitchell、Rodney Brooks等人之后的第18位获奖者。说起这个奖挺有意思的,IJCAI终身成就奖(IJCAI Award for Research Excellence),是国际人工智能界的最高荣誉; IJCAI计算机与思维奖是国际人工智能界35岁以下青年学者的最高荣誉。早期AI研究将推理置于至高无上的地位; 但是1991年牛人Rodney Brooks对推理全面否定,指出机器只能独立学习而得到了IJCAI计算机与思维奖; 但是koller却因提出了Probabilistic Relational Models 而证明机器可以推理论知而又得到了这个奖,可见世事无绝对,科学有轮回。
D koller的Probabilistic Relational Models在nips和icml等各种牛会上活跃了相当长的一段时间,并且至少在实验室里证明了它在信息搜索上的价值,这也导致了她的很多学生进入了 google。虽然进入google可能没有在牛校当faculty名声响亮,但要知道google的很多员工现在可都是百万富翁,在全美大肆买房买车的 主。
Koller的研究主要都集中在probabilistic graphical model,如Bayesian网络,但这玩意我没有接触过,我只看过几篇他们的markov network的文章,但看了也就看了,一点想法都没有,这滩水有点深,不是我这种非科班出身的能趟的,并且感觉难以应用到我现在这个领域中。
Koller才从教10年,所以学生还没有涌现出太多的牛人,这也是她不能跟Jordan比拟的地方,并且由于在stanford的关系,很多学生直接去 硅谷赚大钱去了,而没有在学术界开江湖大帮派的影响,但在stanford这可能太难以办到,因为金钱的诱惑实在太大了。不过Koller的一个学生我非 常崇拜,叫Ben Taskar,就是我在(1)中所提到的Jordan的博后,是好几个牛会的最佳论文奖,他把SVM的最大间隔方法和Markov network结合起来,可以说是对structure data处理的一种标准工具,也把最大间隔方法带入了一个新的热潮,近几年很多牛会都有这样的workshop。 我最开始上Ben Taskar的在stanford的个人网页时,正赶上他刚毕业,他的顶上有这么一句话:流言变成了现实,我终于毕业了!可见Koller是很变态的,把 自己的学生关得这么郁闷,这恐怕也是大多数女faculty的通病吧,并且估计还非常的push!
  • Machine learning 大家(3): J. D. Lafferty
大家都知道NIPS和ICML向来都是由大大小小的山头所割据,而John Lafferty无疑是里面相当高的一座高山,这一点可从他的publication list里的NIPS和ICML数目得到明证。虽然江湖传说计算机重镇CMU现在在走向衰落,但这无碍Lafferty拥有越来越大的影响力,翻开AI兵器谱排名第一的journal of machine learning research的很多文章,我们都能发现author或者editor中赫然有Lafferty的名字。
Lafferty给人留下的最大的印象似乎是他2001年的conditional random fields,这篇文章后来被疯狂引用,广泛地应用在语言和图像处理,并随之出现了很多的变体,如Kumar的discriminative random fields等。虽然大家都知道discriminative learning好,但很久没有找到好的discriminative方法去处理这些具有丰富的contextual inxxxxation的数据,直到Lafferty的出现。
而现在Lafferty做的东西好像很杂,semi-supervised learning, kernel learning,graphical models甚至manifold learning都有涉及,可能就是像武侠里一样只要学会了九阳神功,那么其它的武功就可以一窥而知其精髓了。这里面我最喜欢的是semi-supervised learning,因为随着要处理的数据越来越多,进行全部label过于困难,而完全unsupervised的方法又让人不太放心,在这种情况下semi-supervised learning就成了最好的。这没有一个比较清晰的认识,不过这也给了江湖后辈成名的可乘之机。到现在为止,我觉得cmu的semi-supervised是做得最好的,以前是KAMAL NIGAM做了开创性的工作,而现在Lafferty和他的弟子作出了很多总结和创新。
Lafferty的弟子好像不是很多,并且好像都不是很有名。不过今年毕业了一个中国人,Xiaojin Zhu(上海交通大学校友),就是做semi-supervised的那个人,现在在wisconsin-madison做assistant professor。他做了迄今为止最全面的Semi-supervised learning literature survey,大家可以从他的个人主页中找到。这人看着很憨厚,估计是很好的陶瓷对象。另外我在(1)中所说的Jordan的牛弟子D Blei今年也投奔Lafferty做博后,就足见Lafferty的牛了。
Lafferty做NLP是很好的,著名的Link Grammar Parser还有很多别的应用。其中language model在IR中应用,这方面他的另一个中国学生ChengXiang Zhai(南京大学校友,2004年美国青年科学家总统奖(PECASE)得主),现在在uiuc做assistant professor。
  • Machine learning 大家(4): Peter L. Bartlett
鄙人浅薄之见,Jordan比起同在berkeley的Peter Bartlett还是要差一个层次。Bartlett主要的成就都是在learning theory方面,也就是ML最本质的东西。他的几篇开创性理论分析的论文,当然还有他的书Neural Network Learning: Theoretical Foundations。
UC Berkeley的统计系在强手如林的北美高校中一直是top3,这就足以证明其肯定是群星荟萃,而其中,Peter L. Bartlett是相当亮的一颗星。关于他的研究,我想可以从他的一本书里得到答案:Neural Network Learning: Theoretical Foundations。也就是说,他主要做的是Theoretical Foundations。基础理论虽然没有一些直接可面向应用的算法那样引人注目,但对科学的发展实际上起着更大的作用。试想vapnik要不是在VC维 的理论上辛苦了这么多年,怎么可能有SVM的问世。不过阳春白雪固是高雅,但大多数人只能听懂下里巴人,所以Bartlett的文章大多只能在做理论的那 个圈子里产生影响,而不能为大多数人所广泛引用。
Bartlett在最近两年做了大量的Large margin classifiers方面的工作,如其convergence rate和generalization bound等。并且很多是与jordan合作,足见两人的工作有很多相通之处。不过我发现Bartlett的大多数文章都是自己为第一作者,估计是在教育上存在问题吧,没带出特别牛的学生出来。
Bartlett的个人主页的talk里有很多值得一看的slides,如Large Margin Classifiers: Convexity and Classification;Large Margin Methods for Structured Classification: Exponentiated Gradient Algorithms。大家有兴趣的话可以去下来看看。
  • Machine learning 大家(5):   Michael Collins
自然语言处理(NLP)江湖的第一高人。出身Upenn,靠一身叫做Collins Parser的武功在江湖上展露头脚。当然除了资质好之外,其出身也帮了不少忙。早年一个叫做Mitchell P. Marcus的师傅传授了他一本葵花宝典-Penn Treebank。从此,Collins整日沉迷于此,终于练成盖世神功。
学成之后,Collins告别师傅开始闯荡江湖,投入了一个叫AT&T Labs Research的帮会,并有幸结识了Robert Schapire、Yoram Singer等众多高手。大家不要小瞧这个叫AT&T Labs Research的帮会,如果谁没有听过它的大名总该知道它的同父异母的兄弟Bell Labs吧。
言归正传,话说Collins在这里度过了3年快乐的时光。其间也奠定了其NLP江湖老大的地位。并且练就了Discriminative Reranking, Convolution Kernels,Discriminative Training Methods for Hidden Markov Models等多种绝技。然而,世事难料,怎奈由于帮会经营不善,这帮大牛又不会为帮会拼杀,终于被一脚踢开,大家如鸟兽散了。Schapire去了Princeton, Singer 也回老家以色列了。Collins来到了MIT,成为了武林第一大帮的六袋长老,并教授一门叫做Machine Learning Approaches for NLP (http://www.ai.mit.edu/courses/6.891-nlp/) 的功夫。虽然这一地位与其功力极不相符,但是这并没有打消Collins的积极性,通过其刻苦打拼,终于得到了一个叫Sloan Research Fellow的头衔,并于今年7月,光荣的升任7袋Associate Professor。
在其下山短短7年时间内,Collins共获得了4次世界级武道大会冠军(EMNLP2002, 2004, UAI2004, 2005)。相信年轻的他,总有一天会一统丐帮,甚至整个江湖。
看过Collins和别人合作的一篇文章,用conditional random fields 做object recogntion。还这么年轻,admire to death

2012年7月13日星期五

zz 美国国家学术出版社所有PDF图书开放免费下载

美国国家学术出版社所有PDF图书开放免费下载


美国的国家学术出版社(National Academies Press,NAP)于2011年6月2日宣布,将其出版的所有PDF版图书对所有读者免费开放下载,并且将这些图书去除DRM保护。这其中不仅包括超过4000种最新出版的图书,还包括已经提交报告将于未来一段时间出版的图书。

       国家学术出版社负责美国国家科学院(National Academy of Sciences)、美国国家工程学院(National Academy of Engineering)、美国国家医学院(The Institute of Medicine)和美国国家研究委员会(National Research Council)相关研究成果的出版,其目标是在维持收支平衡的同时尽可能广泛地传播这些研究机构的研究成果。为了实现这一目的,NAP从1994年就开 始提供免费的在线内容。在6月2日的声明之前,这些所有的PDF版图书对发展中国家都是免费的,65%的内容对所有国家用户免费。

        网址: http://www.nap.edu/



附:全球部分免费开放的电子图书馆

1.澳大利亚国立大学ANU电子出版库:http://dspace.anu.edu.au/

2.阿德雷德大学电子文本收藏中心,包括古典文学,哲学,科学和医学著作:http://ebooks.adelaide.edu.au/

3.澳大利亚数字化人文门户(澳大利亚人文学界的数字化资源门户) http://www.ehum.edu.au/

4.科廷大学技术文献库(科廷技术大学科研人员和研究生的科研成果)http://espace.library.curtin.edu.au/R

5.墨尔本大学电子出版物收藏网 
http://www.lib.unimelb.edu.au/eprints/

6.昆士兰大学数字文库 
http://espace.library.uq.edu.au/

7.SETIS悉尼大学学术电子文本及图像服务 http://setis.library.usyd.edu.au/

8.新西兰数字文献收集网 
http://nzdl.sadl.uleth.ca/cgi-bin/library

9.古腾堡数字化图书馆 
http://www.gutenberg.org/wiki/Main_Page

10.Infomotions 西方文学/哲学网 (包括自美国/英国的文学和西方哲学公开著作) 
http://infomotions.com/

11.康奈尔大学Arxiv (收藏了物理,数学,非线性科学和计算机科学方面的数字化 "预印本" 出版物) 
http://arxiv.org/

12.Bartleby.com (包含世界历史百科全书,以及哈佛经典著作,提供免费的电子文本) 
http://www.bartleby.com/

13.Bibliomania (提供超过2000部免费电子文献,以及研究成果) http://www.bibliomania.com/

14.Cogprints(有各类心理学,神经科学,语言学,哲学,生物学,人类学和计算机科学电子文献, 部分区域需要注册) 
http://cogprints.org/

15.印第安纳大学国际文献档案库(这是一个服务公众的全文数字图书馆,作者可以提交著作,并被连入参考文献) 
http://dlc.dlib.indiana.edu/dlc/

16.DLESE地球系统教育数字图书馆( 涵盖了环境,地理,地质,海洋以及其他物理科学;空间科学与技术;教育方法和科学哲学内容) http://www.dlese.org/library/

17.Elfwood(拥有超过两万部文学和艺术作品,来自超过一千五百名幻想/科幻艺术家和作家) 
http://www.elfwood.com/

18.Eserver.org(收藏了大量在线智慧文学和资源,由华盛顿大学创立)http://eserver.org/

19.IPL互联网公共图书馆 (密歇根大学信息学院的学习和教学环境) http://www.ipl.org/

20.库尔特·斯塔博的在线图书馆(收藏了古代和现代的大量生物学著作,其中很多珍本, 可在线阅读) http://www.zum.de/

21.麻省理工学院的开放文献网站 http://ocw.mit.edu/OcwWeb/web/home/home/index.htm

22.美国国家科学院在线数据据库(超过3000部科学,工程和健康卫生方面的著作,可以在线阅读,这些文献代表了美国在这些领域的研究精华) 
http://www.nap.edu/

23.Ndltd.org 
(搜集了来自澳大利亚,加拿大,许多欧洲国家,香港,台湾和美国的论文)http://www.ndltd.org/

24.宾夕法尼亚大学网站 
(有超过 16000 部在线电子书 ,值得一读) :http://digital.library.upenn.edu/books/

25.牛津大学档案馆 
(建于1976年,这里有用于研究和教学的大量高品质文献 资料公共区域可以免费在线检索目录,下载):http://ota.ahds.ac.uk/

26.弗吉尼亚大学电子文献中心 
(超过10000 部可以公开或取的著作(以及超过 164000 幅图像):http://www2.lib.virginia.edu/etext/index.html

27.Gallica.bnf.fr 
(法兰西国家图书馆资助的网站,法文):http://gallica.bnf.fr/

28.世界图书馆(世界图书馆,法语) http://abu.cnam.fr/

29.意大利电子书网站(包括小说,诗歌,古典文学,戏剧,传记,恐怖和幻想小说,新经济学等)http://www.ebookgratis.it/

30.日本文学著作( 格式包括 HTML , ZIP(下载)和日文电子书格式)http://www.aozora.gr.jp/

31.今日美国开放图书计划 
(一家报纸网站的独立部门,一些有名的小说家开放了他们的版权,供所有的访客阅)http://www.usatoday.com/life/boo ... unding-gutter_x.htm

32.英语文学网站 
(超过一千位学生为这个巨大的网站捐助成果,焦点是英语文学) http://www.litencyc.com/

33.计算机程序设计电子书 
(包括:Abap, Java, Linux, Php, Oracle & Vb.net 。 PDF 格式。注意,在下载之前,需先建立一个账户)
http://www.downloadfreepdf.com/

34.数学世界 
(为学生,教育家,数学爱好者和研究者准备的全面地数学百科全书)http://mathworld.wolfram.com/

35.在线医学百科全书 
(超过1500主题的在线医学百科全书,包括康复,疾病,伤害,营养,手术,症状,试验)http://www.healthopedia.com/

36.医生的免费电子书(免费使用的医学电子书) http://freebooks4doctors.com/

37.奥地利文献 
(超过12000 部奥地利文献,甚至包括明信片,可以在因特网上访问)http://www.literature.at/default.alo;jsessionid=453DD0DC127BBBB02C863B1887F76E28 

38.GPO Access(美国政府文献):http://www.access.gpo.gov/

39.世界最大的社会科学文献网站(ICPSR) 
(Inter-university Consortium for Political and Social Research):http://www.icpsr.umich.edu/icpsrweb/ICPSR/

40.National Academy Press 
(美国国家科学院、国家工程院、医学协会等机构的论文/报告/PPT,内容几乎涵盖所有学科) 
http://www.nationalacademies.org/publications/

41.UNESCO 
(联合国教科文组织提供的文档,包含自然科学与社会科学,有多种语言,包括中文) 
http://www.unesco.org/new/en/unesco/

2012年6月11日星期一

Adobe Reader 出现 "Deskewing image";"Rotating Image";"Decomposing Page";"Recognising Text"

问题:打开一个文件,有时会出现:"Deskewing image";"Rotating Image";"Decomposing Page";"Recognising Text"必须重启才行;
解决:打开设置中
* Edit > Preferences... > Reading
* Reading Order: "Use reading order in raw print stream".
* Page vs Document: "Only read the currently visible pages".
设置后,即可!

Ref:http://forums.adobe.com/message/1988985

2012年6月8日星期五

zz ctrl+shift+del 清理火狐缓存,解决页面显示错乱问题

ctrl+shift+del 清理火狐缓存,解决页面显示错乱问题

3
 
zz:http://mozilla.com.cn/post/32465/
 
阿_天, @coolwdp, +8003
3人支持,来自 Hawkeyes WindrachelFelicia
有些用户在使用火狐时,偶然会发现本来正常显示的页面出现各种诡异的显示问题和排版问题,例如样式表加载不上,页面全向左排列等等。

出现这些问题主要是由于火狐缓存混乱造成的,而造成缓存错乱的原因就是用了第三方工具清理了火狐的缓存,可能是第三方工具的临时bug造成火狐缓存没删干净。目前360等已经升级解决了这个bug,请360用户升级即可。

出 现问题后,请按ctrl+shift+del(Mac下 command+shift+delete),调出火狐内置的清除最近的历史记录工具(或者按 alt键弹出菜单,工具-》清空最近历史记录),然后选择清除全部的缓存和cookie(cookie保存的是你的登陆网站数据等等,如果清理后自动登陆 的网站需要重新登陆,可以先尝试清理缓存,还有问题再尝试清理cookie),出现问题的网站基本都可以恢复。


另一个技巧是,ctrl+F5跳过缓存刷新,可以立即修复好当前的网页,少量网站出现问题可以尝试ctrl+F5。(Mac下是 command+shift+R,windows也可以用ctrl+shift+R,单手比ctrl+F5好按。)

所以:
1.尽量不要使用的第三方的清理和优化工具,大部分软件都自带缓存和管理软件的。
2.各种辅助工具,最好使用软件自带的,比如就火狐的清理历史和缓存,ctrl+shift+del很好很强大。

3.网页各种显示混乱和问题,ctrl+shift+del和ctrl+F5 治百病,前提是网站本身就没问题支持火狐,并且网络畅通。

2012年4月18日星期三

(转载)Qt 下 OpenGL glut 安装

(http://www.qtcn.org/bbs/simple/?t46022.html)

我的做法是把glut32.dll,glut.dll放入到C:\WINDOWS\SYSTEM32里面
然后把 glut32.lib,glut.lib复制到QT安装路径下面的mingw\lib文件夹里(我的是:D:\Qt\2010.04\mingw \lib),把glut.h放到mingw\include\GL文件夹里(我的是:D:\Qt\2010.04\mingw\include\GL), 然后建一个空项目OpenGL.pro,并且向本项目里添加main.cpp,main.cpp的内容如下(这是一个测试项目):



我使用的是vs2008,相信vs2005应该跟这个步骤差不多,运行的时候总是提示:
fatal error C1083: 无法打开包括文件:“GL/glut.h”: No such file or directory
这个时候就要进行相应的如下操作:
下载GLUT工具包,然后如下:
1、将下载的压缩包解开,将得到5个文件
2、把解压得到的glut.h放到C:/Program Files/Microsoft Visual Studio 9.0/VC/include这个文件夹。
3、把解压得到的glut.lib和glut32.lib放到静态函数库所在C:/Program Files/Microsoft Visual Studio 9.0/VC/lib文件夹。
4、把解压得到的glut.dll和glut32.dll放到操作系统目录下面的system32文件夹内。
5、再次运行,如果还是出错,那么就更改头文件原有的名称#include <GL/glut.h>为#include <glut.h>



#include <windows.h>
#include <GL/glut.h>
#include <math.h>
const GLfloat Pi = 3.1415926536f;
void myDisplay(void)
{
GLfloat a = 1 / (2-2*cos(72*Pi/180));
GLfloat bx = a * cos(18 * Pi/180);
GLfloat by = a * sin(18 * Pi/180);
GLfloat cy = -a * cos(18 * Pi/180);
GLfloat
PointA[2] = { 0, a },
PointB[2] = { bx, by },
PointC[2] = { 0.5, cy },
PointD[2] = { -0.5, cy },
PointE[2] = { -bx, by };
glClear(GL_COLOR_BUFFER_BIT);
// 按照A->C->E->B->D->A的顺序,可以一笔将五角星画出
glBegin(GL_LINE_LOOP);
glVertex2fv(PointA);
glVertex2fv(PointC);
glVertex2fv(PointE);
glVertex2fv(PointB);
glVertex2fv(PointD);
glEnd();
glFlush();
}
int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(400, 400);
    glutCreateWindow("第一个OpenGL程序");
    glutDisplayFunc(&myDisplay);
    glutMainLoop();
    return 0;
}

最后再在OpenGL.pro里添加:
LIBS=-l opengl32 -l glut32 -l glut
再编译就可以看到一个黑色的窗口。是用GLUT库编译的。

2012年3月20日星期二

dll 的调用

编译时,关键是要能找到.lib或.a文件,所以要将存放.lib或.a文件的路径加入到.pro文件中,比如 LIBS += -L"C:\path1\for\lib",同时将库名(也可全称)加到LIBS中,比如LIBS += -llibname.lib
运 行时,关键是能找到对应的.dll或.so文件,在windows下,此时要么将.dll文件放到可执行文件目录下,要么在环境变量PATH中将.dll 文件所在路径加进去。在linux下,则要将.so文件所在的目录加入到环境变量LD_LIBRARY_PATH路径中去(或放到那些公用库目录下,比如 /usr/lib/

2012年3月16日星期五

VC++ 问题 cannot convert parameter 1 from 'const char [21]' to 'LPCWSTR'

 选择 项目属性--> Character Set --> 选择Use Multi-Byte Character Set
 
 
This error message means that you are trying to pass a multi-byte string (const char [12]) to a function which expects a unicode string (LPCTSTR). The LPCTSTR type extends to const TCHAR*, where TCHAR ischarwhen you compile for multi-byte andwchar_tfor unicode. Since the compiler doesn't accept the char array, we can safely assume that the actual type of TCHAR, in this compilation, is wchar_t.
 
Resolution
You will have to do one of two things:
Change your project configuration to use multibyte strings. Press ALT+F7 to open the properties, and navigate to Configuration Properties > General. Switch Character Set to "Use Multi-Byte Character Set".
Indicate that the string literal, in this case "Hello world!" is of a specific encoding. This can be done through either prefixing it withL, such asL"Hello world!", or surrounding it with the generic_T("Hello world!")macro. The latter will expand to theLprefix if you are compiling for unicode (see #1), and nothing (indicating multi-byte) otherwise.
Variations
Another error message, indicating the same problem, would be:
cannot convert parameter 1 from 'const char [12]' to 'LPCWSTR'
Where LPCWSTR maps to a wchar_t pointer, regardless of your build configuration. This problem can be resolved primarily by using solution #2, but in some cases also #1. A lot of the Microsoft provided libraries, such as the Platform SDK, have got two variations of each function which takes strings as parameters. In case of a unicode build, the actual functions are postfixed W, such as the MessageBoxW seen above. In case of multi-byte, the function would be MessageBoxA (ASCII). Which of these functions is actually used when you compile your application, depends on the setting described in resolution #1 above.

2012年3月15日星期四

转载 VC++动态链接库(DLL)编程深入浅出

1.概论
 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。
 静态链接库与动态链接库都是共享代 码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终 EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包 含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
对动态链接库,我们还需建立如下概念:
(1)DLL 的编制与具体的编程语言及编译器无关
只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。
(2)动态链接库随处可见
 我 们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多 数API都包含在这些DLL中。kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。
 一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态链接库中。由此可见DLL对我们来说其实并不陌生。
(3)VC动态链接库的分类
Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。
非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。
 由于本文篇幅较长,内容较多,势必需要先对阅读本文的有关事项进行说明,下面以问答形式给出。
问:本文主要讲解什么内容?
答:本文详细介绍了DLL编程的方方面面,努力学完本文应可以对DLL有较全面的掌握,并能编写大多数DLL程序。
问:如何看本文?
答:本文每一个主题的讲解都附带了源代码例程,可以随文下载(每个工程都经WINRAR压缩)。所有这些例程都由笔者编写并在VC++6.0中调试通过。
 当然看懂本文不是读者的最终目的,读者应亲自动手实践才能真正掌握DLL的奥妙。
 问:学习本文需要什么样的基础知识?
答:如果你掌握了C,并大致掌握了C++,了解一点MFC的知识,就可以轻松地看懂本文。
2.静态链接库
对静态链接库的讲解不是本文的重点,但是在具体讲解DLL之前,通过一个静态链接库的例子可以快速地帮助我们建立“库”的概念。

图1 建立一个静态链接库
如图1,在VC++6.0中new一个名称为libTest的static library工程(单击此处下载本工程附件 ),并新建lib.h和lib.cpp两个文件,lib.h和lib.cpp的源代码如下:


 编译这个工程就得到了一个.lib文件,这个文件就是一个函数库,它提供了add的功能。将头文件和.lib文件提交给用户后,用户就可以直接使用其中的add函数了。
标准Turbo C2.0中的C库函数(我们用来的scanf、printf、memcpy、strcpy等)就来自这种静态库。
 下面来看看怎么使用这个库,在libTest工程所在的工作区内new一个libCall工程。libCall工程仅包含一个main.cpp文件,它演示了静态链接库的调用方法,其源代码如下:


  静态链接库的调用就是这么简单,或许我们每天都在用,可是我们没有明白这个概念。代码中#pragma comment( lib , "..debuglibTest.lib" )的意思是指本文件生成的.obj文件应与libTest.lib一起连接。
如果不用#pragma comment指定,则可以直接在VC++中设置,如图2,依次选择tools、options、directories、library files菜单或选项,填入库文件路径。图2中加红圈的部分为我们添加的libTest.lib文件的路径。

图2 在VC中设置库文件路径
这个静态链接库的例子至少让我们明白了库函数是怎么回事,它们是哪来的。我们现在有下列模糊认识了:
 (1)库不是个怪物,编写库的程序和编写一般的程序区别不大,只是库不能单独执行;
(2)库提供一些可以给别的程序调用的东东,别的程序要调用它必须以某种方式指明它要调用之。
 以上从静态链接库分析而得到的对库的懵懂概念可以直接引申到动态链接库中,动态链接库与静态链接库在编写和调用上的不同体现在库的外部接口定义及调用方式略有差异。
3.库的调试与查看
 在具体进入各类DLL的详细阐述之前,有必要对库文件的调试与查看方法进行一下介绍,因为从下一节开始我们将面对大量的例子工程。
 由 于库文件不能单独执行,因而在按下F5(开始debug模式执行)或CTRL+F5(运行)执行时,其弹出如图3所示的对话框,要求用户输入可执行文件的 路径来启动库函数的执行。这个时候我们输入要调用该库的EXE文件的路径就可以对库进行调试了,其调试技巧与一般应用工程的调试一样。

图3 库的调试与“运行”
 通常有比上述做法更好的调试途径,那就是将库工程和应用工程(调用库的工程)放置在同一VC工作区,只对应用工程进行调试,在应用工程调用库中函 数的语句处设置断点,执行后按下F11,这样就单步进入了库中的函数。第2节中的libTest和libCall工程就放在了同一工作区,其工程结构如图 4所示。

图4 把库工程和调用库的工程放入同一工作区进行调试
上述调试方法对静态链接库和动态链接库而言是一致的。所以本文提供下载的所有源代码中都包含了库工程和调用库的工程,这二者都被包含在一个工作区内,这是笔者提供这种打包下载的用意所在。
动态链接库中的导出接口可以使用Visual C++的Depends工具进行查看,让我们用Depends打开系统目录中的user32.dll,看到了吧?红圈内的就是几个版本的MessageBox了!原来它真的在这里啊,原来它就在这里啊!

图5 用Depends查看DLL
当然Depends工具也可以显示DLL的层次结构,若用它打开一个可执行文件则可以看出这个可执行文件调用了哪些DLL。
 好,让我们正式进入动态链接库的世界,先来看看最一般的DLL,即非MFC DLL






上节给大家介绍了静态链接库与库的调试与查看(动态链接库(DLL)编程深入浅出(一) ),本节主要介绍非MFC DLL。
4.非MFC DLL
 4.1一个简单的DLL
 第2节给出了以静态链接库方式提供add函数接口的方法,接下来我们来看看怎样用动态链接库实现一个同样功能的add函数。
 如图6,在VC++中new一个Win32 Dynamic-Link Library工程dllTest(单击此处下载本工程附件 )。注意不要选择MFC AppWizard(dll),因为用MFC AppWizard(dll)建立的将是第5、6节要讲述的MFC 动态链接库。

图6 建立一个非MFC DLL
在建立的工程中添加lib.h及lib.cpp文件,源代码如下:

  1. /* 文件名:lib.h */  
  2. #ifndef LIB_H  
  3. #define LIB_H  
  4. extern "C" int __declspec(dllexport)add(int x, int y);  
  5. #endif  
  6. /* 文件名:lib.cpp */  
  7. #include "lib.h"  
  8. int add(int x, int y)  
  9. {  
  10. return x + y;  
  11. }  

  与第2节对静态链接库的调用相似,我们也建立一个与DLL工程处于同一工作区的应用工程dllCall,它调用DLL中的函数add,其源代码如下:

  1. #include <stdio.h>  
  2. #include <windows.h>  
  3. typedef int(*lpAddFun)(intint); //宏定义函数指针类型  
  4. int main(int argc, char *argv[])  
  5. {  
  6. HINSTANCE hDll; //DLL句柄  
  7. lpAddFun addFun; //函数指针  
  8. hDll = LoadLibrary("..DebugdllTest.dll");  
  9. if (hDll != NULL)  
  10. {  
  11. addFun = (lpAddFun)GetProcAddress(hDll, "add");  
  12. if (addFun != NULL)  
  13. {  
  14. int result = addFun(2, 3);  
  15. printf("%d", result);  
  16. }  
  17. FreeLibrary(hDll);  
  18. }  
  19. return 0;  
  20. }  

   分析上述代码,dllTest工程中的lib.cpp文件与第2节静态链接库版本完全相同,不同在于lib.h对函数add的声明前面添加了 __declspec(dllexport)语句。这个语句的含义是声明函数add为DLL的导出函数。DLL内的函数分为两种:
     (1)DLL导出函数,可供应用程序调用;
(2) DLL内部函数,只能在DLL程序使用,应用程序无法调用它们。
 而应用程序对本DLL的调用和对第2节静态链接库的调用却有较大差异,下面我们来逐一分析。
首先,语句typedef int ( * lpAddFun)(int,int)定义了一个与add函数接受参数类型和返回值均相同的函数指针类型。随后,在main函数中定义了lpAddFun的实例addFun;
其次,在函数main中定义了一个DLL HINSTANCE句柄实例hDll,通过Win32 Api函数LoadLibrary动态加载了DLL模块并将DLL模块句柄赋给了hDll;
再次,在函数main中通过Win32 Api函数GetProcAddress得到了所加载DLL模块中函数add的地址并赋给了addFun。经由函数指针addFun进行了对DLL中add函数的调用;
 最后,应用工程使用完DLL后,在函数main中通过Win32 Api函数FreeLibrary释放了已经加载的DLL模块。
 通过这个简单的例子,我们获知DLL定义和调用的一般概念:
(1)DLL中需以某种特定的方式声明导出函数(或变量、类);
(2)应用工程需以某种特定的方式调用DLL的导出函数(或变量、类)。
下面我们来对“特定的方式进行”阐述。
4.2 声明导出函数
DLL中导出函数的声明有两种方式:一种为4.1节例子中给出的在函数声明中加上__declspec(dllexport),这里不再举例说明;另外 一种方式是采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。
下面的代码演示了怎样同.def文件将函数add声明为DLL导出函数(需在dllTest工程中添加lib.def文件):
; lib.def : 导出DLL函数
LIBRARY dllTest
EXPORTS
add @ 1
  .def文件的规则为:
(1)LIBRARY语句说明.def文件相应的DLL;
 (2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
 (3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。
由此可以看出,例子中lib.def文件的含义为生成名为“dllTest”的动态链接库,导出其中的add函数,并指定add函数的序号为1。
4.3 DLL的调用方式
 在4.1节的例子中我们看到了由“LoadLibrary-GetProcAddress-FreeLibrary”系统Api提供的三位一体“DLL加载-DLL函数地址获取-DLL释放”方式,这种调用方式称为DLL的动态调用。
动态调用方式的特点是完全由编程者用 API 函数加载和卸载 DLL,程序员可以决定 DLL 文件何时加载或不加载,显式链接在运行时决定加载哪个 DLL 文件。
 与 动态调用方式相对应的就是静态调用方式,“有动必有静”,这来源于物质世界的对立统一。“动与静”,其对立与统一竟无数次在技术领域里得到验证,譬如静态 IP与DHCP、静态路由与动态路由等。从前文我们已经知道,库也分为静态库与动态库DLL,而想不到,深入到DLL内部,其调用方式也分为静态与动态。 “动与静”,无处不在。《周易》已认识到有动必有静的动静平衡观,《易.系辞》曰:“动静有常,刚柔断矣”。哲学意味着一种普遍的真理,因此,我们经常可以在枯燥的技术领域看到哲学的影子。
 静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时 DLL 的卸载。当调用某DLL的应用程序结束时,若系统中还有其它程序使用该 DLL,则Windows对DLL的应用记录减1,直到所有使用该DLL的程序都结束时才释放它。静态调用方式简单实用,但不如动态调用方式灵活。下面我 们来看看静态调用的例子(单击此处下载本工程附件 ),将编译dllTest工程所生成的.lib和.dll文件拷入dllCall工程所在的路径,dllCall执行下列代码:#pragma comment(lib,"dllTest.lib")
//.lib文件中仅仅是关于其对应DLL文件中函数的重定位信息

  1. extern "C" __declspec(dllimport) add(int x,int y);  
  2. int main(int argc, char* argv[])  
  3. {  
  4. int result = add(2,3);  
  5. printf("%d",result);  
  6. return 0;  
  7. }  

  由上述代码可以看出,静态调用方式的顺利进行需要完成两个动作:
 (1)告诉编译器与DLL相对应的.lib文件所在的路径及文件名,#pragma comment(lib,"dllTest.lib")就是起这个作用。
 程序员在建立一个DLL文件时,连接器会自动为其生成一个对应的.lib文件,该文件包含了DLL 导出函数的符号名及序号(并不含有实际的代码)。在应用程序里,.lib文件将作为DLL的替代文件参与编译。
(2)声明导入函数,extern "C" __declspec(dllimport) add(int x,int y)语句中的__declspec(dllimport)发挥这个作用。
 静 态调用方式不再需要使用系统API来加载、卸载DLL以及获取DLL中导出函数的地址。这是因为,当程序员通过静态链接方式编译生成应用程序时,应用程序 中调用的与.lib文件中导出符号相匹配的函数符号将进入到生成的EXE 文件中,.lib文件中所包含的与之对应的DLL文件的文件名也被编译器存储在 EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows将根据这些信息发现并加载DLL,然后通过符号名实现对DLL 函数的动态链接。这样,EXE将能直接通过函数名调用DLL的输出函数,就象调用程序内部的其他函数一样。
4.4 DllMain函数
 Windows在加载DLL的时候,需要一个入口函数,就如同控制台或DOS程序需要main函数、WIN32程序需要WinMain函数一样。 在前面的例子中,DLL并没有提供DllMain函数,应用工程也能成功引用DLL,这是因为Windows在找不到DllMain的时候,系统会从其它 运行库中引入一个不做任何操作的缺省DllMain函数版本,并不意味着DLL可以放弃DllMain函数。
根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数。这意味着不能直接在应用工程中引用DllMain函数,DllMain是自动被调用的。
我们来看一个DllMain函数的例子(单击此处下载本工程附件 )。
  1. BOOL APIENTRY DllMain( HANDLE hModule,  
  2. DWORD ul_reason_for_call,  
  3. LPVOID lpReserved  
  4. )  
  5. {  
  6. switch (ul_reason_for_call)  
  7. {  
  8. case DLL_PROCESS_ATTACH:  
  9. printf(" process attach of dll");  
  10. break;  
  11. case DLL_THREAD_ATTACH:  
  12. printf(" thread attach of dll");  
  13. break;  
  14. case DLL_THREAD_DETACH:  
  15. printf(" thread detach of dll");  
  16. break;  
  17. case DLL_PROCESS_DETACH:  
  18. printf(" process detach of dll");  
  19. break;  
  20. }  
  21. return TRUE;  
  22. }  

   DllMain函数在DLL被加载和卸载时被调用,在单个线程启动和终止时,DLLMain函数也被调用,ul_reason_for_call指明了 被调用的原因。原因共有4种,即PROCESS_ATTACH、PROCESS_DETACH、THREAD_ATTACH和 THREAD_DETACH,以switch语句列出。
来仔细解读一下DllMain的函数头BOOL APIENTRY DllMain( HANDLE hModule, WORD ul_reason_for_call, LPVOID lpReserved )。
APIENTRY被定义为__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;
进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识,只有在特定的进程内部有效,句柄代表了DLL模块在进程虚拟空间中的起始地 址。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用,这就是函数参数hModule的来历。
执行下列代码:
  1. hDll = LoadLibrary("..DebugdllTest.dll");  
  2. if (hDll != NULL)  
  3. {  
  4. addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1));  
  5. //MAKEINTRESOURCE直接使用导出文件中的序号  
  6. if (addFun != NULL)  
  7. {  
  8. int result = addFun(2, 3);  
  9. printf(" call add in dll:%d", result);  
  10. }  
  11. FreeLibrary(hDll);  
  12. }  

  我们看到输出顺序为:
process attach of dll
call add in dll:5
process detach of dll
这一输出顺序验证了DllMain被调用的时机。
代码中的GetProcAddress ( hDll, MAKEINTRESOURCE ( 1 ) )值得留意,它直接通过.def文件中为add函数指定的顺序号访问add函数,具体体现在MAKEINTRESOURCE ( 1 ),MAKEINTRESOURCE是一个通过序号获取函数名的宏,定义为(节选自winuser.h):
    #define MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i)))
    #define MAKEINTRESOURCEW(i) (LPWSTR)((DWORD)((WORD)(i)))
    #ifdef UNICODE
    #define MAKEINTRESOURCE MAKEINTRESOURCEW
    #else
    #define MAKEINTRESOURCE MAKEINTRESOURCEA
  4.5 __stdcall约定
 如果通过VC++编写的DLL欲被其他语言编 写的程序调用,应将函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却为__cdecl。 __stdcall方式与__cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而 __cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。
Windows编程中常见的几种函数类型声明宏都是与__stdcall和__cdecl有关的(节选自windef.h):
#define CALLBACK __stdcall //这就是传说中的回调函数
#define WINAPI __stdcall //这就是传说中的WINAPI
#define WINAPIV __cdecl
#define APIENTRY WINAPI //DllMain的入口就在这里
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
  在lib.h中,应这样声明add函数:
int __stdcall add(int x, int y);
 在应用工程中函数指针类型应定义为:
typedef int(__stdcall *lpAddFun)(int, int);
若在lib.h中将函数声明为__stdcall调用,而应用工程中仍使用typedef int (* lpAddFun)(int,int),运行时将发生错误(因为类型不匹配,在应用工程中仍然是缺省的__cdecl调用),弹出如图7所示的对话框。
  图7 调用约定不匹配时的运行错误
图8中的那段话实际上已经给出了错误的原因,即“This is usually a result of …”。
单击此处下载__stdcall调用例子工程源代码附件
4.6 DLL导出变量
DLL定义的全局变量可以被调用进程访问;DLL也可以访问调用进程的全局数据,我们来看看在应用工程中引用DLL中变量的例子(单击此处下载本工程附件 )。
  1. /* 文件名:lib.h */  
  2. #ifndef LIB_H  
  3. #define LIB_H  
  4. extern int dllGlobalVar;  
  5. #endif  
  6. /* 文件名:lib.cpp */  
  7. #include "lib.h"  
  8. #include <windows.h>  
  9. int dllGlobalVar;  
  10. BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)  
  11. {  
  12. switch (ul_reason_for_call)  
  13. {  
  14. case DLL_PROCESS_ATTACH:  
  15. dllGlobalVar = 100; //在dll被加载时,赋全局变量为100  
  16. break;  
  17. case DLL_THREAD_ATTACH:  
  18. case DLL_THREAD_DETACH:  
  19. case DLL_PROCESS_DETACH:  
  20. break;  
  21. }  
  22. return TRUE;  
  23. }  
  24. ;文件名:lib.def  
  25. ;在DLL中导出变量  
  26. LIBRARY "dllTest"  
  27. EXPORTS  
  28. dllGlobalVar CONSTANT  
  29. ;或dllGlobalVar DATA  
  30. GetGlobalVar  

 从lib.h和lib.cpp中可以看出,全局变量在DLL中的定义和使用方法与一般的程序设计是一样的。若要导出某全局变量,我们需要在.def文件的EXPORTS后添加:
变量名 CONSTANT   //过时的方法

变量名 DATA     //VC++提示的新方法
在主函数中引用DLL中定义的全局变量:
  1. #include <stdio.h>  
  2. #pragma comment(lib,"dllTest.lib")  
  3. extern int dllGlobalVar;  
  4. int main(int argc, char *argv[])  
  5. {  
  6. printf("%d ", *(int*)dllGlobalVar);  
  7. *(int*)dllGlobalVar = 1;  
  8. printf("%d ", *(int*)dllGlobalVar);  
  9. return 0;  
  10. }  

 特别要注意的是用extern int dllGlobalVar声明所导入的并不是DLL中全局变量本身,而是其地址,应用程序必须通过强制指针转换来使用DLL中的全局变量。这一点,从* (int*)dllGlobalVar可以看出。因此在采用这种方式引用DLL全局变量时,千万不要进行这样的赋值操作:
dllGlobalVar = 1;
 其结果是dllGlobalVar指针的内容发生变化,程序中以后再也引用不到DLL中的全局变量了。
 在应用工程中引用DLL中全局变量的一个更好方法是:
  1. #include <stdio.h>  
  2. #pragma comment(lib,"dllTest.lib")  
  3. extern int _declspec(dllimport) dllGlobalVar; //用_declspec(dllimport)导入  
  4. int main(int argc, char *argv[])  
  5. {  
  6. printf("%d ", dllGlobalVar);  
  7. dllGlobalVar = 1; //这里就可以直接使用, 无须进行强制指针转换  
  8. printf("%d ", dllGlobalVar);  
  9. return 0;  
  10. }  

  通过_declspec(dllimport)方式导入的就是DLL中全局变量本身而不再是其地址了,笔者建议在一切可能的情况下都使用这种方式。
4.7 DLL导出类
 DLL中定义的类可以在应用工程中使用。
下面的例子里,我们在DLL中定义了point和circle两个类,并在应用工程中引用了它们(单击此处下载本工程附件 )。
  1. //文件名:point.h,point类的声明  
  2. #ifndef POINT_H  
  3. #define POINT_H  
  4. #ifdef DLL_FILE  
  5. class _declspec(dllexport) point //导出类point  
  6. #else  
  7. class _declspec(dllimport) point //导入类point  
  8. #endif  
  9. {  
  10. public:  
  11. float y;  
  12. float x;  
  13. point();  
  14. point(float x_coordinate, float y_coordinate);  
  15. };  
  16. #endif  
  17. //文件名:point.cpp,point类的实现  
  18. #ifndef DLL_FILE  
  19. #define DLL_FILE  
  20. #endif  
  21. #include "point.h"  
  22. //类point的缺省构造函数  
  23. point::point()  
  24. {  
  25. x = 0.0;  
  26. y = 0.0;  
  27. }  
  28. //类point的构造函数  
  29. point::point(float x_coordinate, float y_coordinate)  
  30. {  
  31. x = x_coordinate;  
  32. y = y_coordinate;  
  33. }  
  34. //文件名:circle.h,circle类的声明  
  35. #ifndef CIRCLE_H  
  36. #define CIRCLE_H  
  37. #include "point.h"  
  38. #ifdef DLL_FILE  
  39. class _declspec(dllexport)circle //导出类circle  
  40. #else  
  41. class _declspec(dllimport)circle //导入类circle  
  42. #endif  
  43. {  
  44. public:  
  45. void SetCentre(const point ¢rePoint);  
  46. void SetRadius(float r);  
  47. float GetGirth();  
  48. float GetArea();  
  49. circle();  
  50. private:  
  51. float radius;  
  52. point centre;  
  53. };  
  54. #endif  
  55. //文件名:circle.cpp,circle类的实现  
  56. #ifndef DLL_FILE  
  57. #define DLL_FILE  
  58. #endif  
  59. #include "circle.h"  
  60. #define PI 3.1415926  
  61. //circle类的构造函数  
  62. circle::circle()  
  63. {  
  64. centre = point(0, 0);  
  65. radius = 0;  
  66. }  
  67. //得到圆的面积  
  68. float circle::GetArea()  
  69. {  
  70. return PI *radius * radius;  
  71. }  
  72. //得到圆的周长  
  73. float circle::GetGirth()  
  74. {  
  75. return 2 *PI * radius;  
  76. }  
  77. //设置圆心坐标  
  78. void circle::SetCentre(const point ¢rePoint)  
  79. {  
  80. centre = centrePoint;  
  81. }  
  82. //设置圆的半径  
  83. void circle::SetRadius(float r)  
  84. {  
  85. radius = r;  
  86. }  

  类的引用:

  1. #include "..circle.h"  //包含类声明头文件  
  2. #pragma comment(lib,"dllTest.lib");  
  3. int main(int argc, char *argv[])  
  4. {  
  5. circle c;  
  6. point p(2.0, 2.0);  
  7. c.SetCentre(p);  
  8. c.SetRadius(1.0);  
  9. printf("area:%f girth:%f", c.GetArea(), c.GetGirth());  
  10. return 0;  
  11. }  

  从上述源代码可以看出,由于在DLL的类实现代码中定义了宏DLL_FILE,故在DLL的实现中所包含的类声明实际上为:
class _declspec(dllexport) point //导出类point
{

}
  和
class _declspec(dllexport) circle //导出类circle
{

}
 而在应用工程中没有定义DLL_FILE,故其包含point.h和circle.h后引入的类声明为:
class _declspec(dllimport) point //导入类point
{

}
  和
class _declspec(dllimport) circle //导入类circle
{

}
  不错,正是通过DLL中的
class _declspec(dllexport) class_name //导出类circle 
{

}
  与应用程序中的
class _declspec(dllimport) class_name //导入类
{

}
  匹对来完成类的导出和导入的!
我们往往通过在类的声明头文件中用一个宏来决定使其编译为class _declspec(dllexport) class_name还是class _declspec(dllimport) class_name版本,这样就不再需要两个头文件。本程序中使用的是:
#ifdef DLL_FILE
class _declspec(dllexport) class_name //导出类
#else
class _declspec(dllimport) class_name //导入类
#endif
 实际上,在MFC DLL的讲解中,您将看到比这更简便的方法,而此处仅仅是为了说明_declspec(dllexport)与_declspec(dllimport)匹对的问题。
 由此可见,应用工程中几乎可以看到DLL中的一切,包括函数、变量以及类,这就是DLL所要提供的强大能力。只要DLL释放这些接口,应用程序使用它就将如同使用本工程中的程序一样!
本章虽以VC++为平台讲解非MFC DLL,但是这些普遍的概念在其它语言及开发环境中也是相同的,其思维方式可以直接过渡。
 接下来,我们将要研究MFC规则DLL



第4节我们对非MFC DLL进行了介绍,这一节将详细地讲述MFC规则DLL的创建与使用技巧。
 另外,自从本文开始连载后,收到了一些读者的e-mail。有的读者提出了一些问题,笔者将在本文的最后一次连载中选取其中的典型问题进行解答。 由于时间的关系,对于读者朋友的来信,笔者暂时不能一一回复,还望海涵!由于笔者的水平有限,文中难免有错误和纰漏,也热诚欢迎读者朋友不吝指正!
5. MFC规则DLL
5.1 概述
MFC规则DLL的概念体现在两方面:
(1) 它是MFC的
“是MFC的”意味着可以在这种DLL的内部使用MFC;
(2) 它是规则的
 “是规则的”意味着它不同于MFC扩展DLL,在MFC规则DLL的内部虽然可以使用MFC,但是其与应用程序的接口不能是MFC。而MFC扩展DLL与应用程序的接口可以是MFC,可以从MFC扩展DLL中导出一个MFC类的派生类。
 Regular DLL能够被所有支持DLL技术的语言所编写的应用程序调用,当然也包括使用MFC的应用程序。在这种动态连接库中,包含一个从CWinApp继承下来的类,DllMain函数则由MFC自动提供。
Regular DLL分为两类:
(1)静态链接到MFC 的规则DLL
 静态链接到MFC的规则DLL与MFC库(包括MFC扩展 DLL)静态链接,将MFC库的代码直接生成在.dll文件中。在调用这种DLL的接口时,MFC使用DLL的资源。因此,在静态链接到MFC 的规则DLL中不需要进行模块状态的切换。
使用这种方法生成的规则DLL其程序较大,也可能包含重复的代码。
(2)动态链接到MFC 的规则DLL
 动态链接到MFC 的规则DLL 可以和使用它的可执行文件同时动态链接到 MFC DLL 和任何MFC扩展 DLL。在使用了MFC共享库的时候,默认情况下,MFC使用主应用程序的资源句柄来加载资源模板。这样,当DLL和应用程序中存在相同ID的资源时(即 所谓的资源重复问题),系统可能不能获得正确的资源。因此,对于共享MFC DLL的规则DLL,我们必须进行模块切换以使得MFC能够找到正确的资源模板。
我们可以在Visual C++中设置MFC规则DLL是静态链接到MFC DLL还是动态链接到MFC DLL。如图8,依次选择Visual C++的project -> Settings -> General菜单或选项,在Microsoft Foundation Classes中进行设置。
图8 设置动态/静态链接MFC DLL
5.2 MFC规则DLL的创建
我们来一步步讲述使用MFC向导创建MFC规则DLL的过程,首先新建一个project,如图9,选择project的类型为MFC AppWizard(dll)。点击OK进入如图10所示的对话框。

图9 MFC DLL工程的创建
图10所示对话框中的1区选择MFC DLL的类别。
 2区选择是否支持automation(自动化)技术, automation 允许用户在一个应用程序中操纵另外一个应用程序或组件。例如,我们可以在应用程序中利用 Microsoft Word 或Microsoft Excel的工具,而这种使用对用户而言是透明的。自动化技术可以大大简化和加快应用程序的开发。
 3区选择是否支持Windows Sockets,当选择此项目时,应用程序能在 TCP/IP 网络上进行通信。 CWinApp派生类的InitInstance成员函数会初始化通讯端的支持,同时工程中的StdAfx.h文件会自动include <AfxSock.h>头文件。
添加socket通讯支持后的InitInstance成员函数如下:

  1. BOOL CRegularDllSocketApp::InitInstance()  
  2. {  
  3. if (!AfxSocketInit())  
  4. {  
  5. AfxMessageBox(IDP_SOCKETS_INIT_FAILED);  
  6. return FALSE;  
  7. }  
  8. return TRUE;  
  9. }  

  4区选择是否由MFC向导自动在源代码中添加注释,一般我们选择“Yes,please”。
  图10 MFC DLL的创建选项
 5.3 一个简单的MFC规则DLL
这个DLL的例子(属于静态链接到MFC 的规则DLL)中提供了一个如图11所示的对话框。
  图11 MFC规则DLL例子
 在DLL中添加对话框的方式与在MFC应用程序中是一样的。
在图11所示DLL中的对话框的Hello按钮上点击时将MessageBox一个“Hello,pconline的网友”对话框,下面是相关的文件及源代码,其中删除了MFC向导自动生成的绝大多数注释(下载本工程附件 ):
第一组文件:CWinApp继承类的声明与实现
  1. // RegularDll.h : main header file for the REGULARDLL DLL  
  2. #if !defined(AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_)  
  3. #define AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_  
  4. #if _MSC_VER > 1000  
  5. #pragma once  
  6. #endif // _MSC_VER > 1000  
  7. #ifndef __AFXWIN_H__  
  8. #error include 'stdafx.h' before including this file for PCH  
  9. #endif  
  10. #include "resource.h" // main symbols  
  11. class CRegularDllApp : public CWinApp  
  12. {  
  13. public:  
  14. CRegularDllApp();  
  15. DECLARE_MESSAGE_MAP()  
  16. };  
  17. #endif  
  18. // RegularDll.cpp : Defines the initialization routines for the DLL.  
  19. #include "stdafx.h"  
  20. #include "RegularDll.h"  
  21. #ifdef _DEBUG  
  22. #define new DEBUG_NEW  
  23. #undef THIS_FILE  
  24. static char THIS_FILE[] = __FILE__;  
  25. #endif  
  26. BEGIN_MESSAGE_MAP(CRegularDllApp, CWinApp)  
  27. END_MESSAGE_MAP()  
  28. /////////////////////////////////////////////////////////////////////////////  
  29. // CRegularDllApp construction  
  30. CRegularDllApp::CRegularDllApp()  
  31. {  
  32. }  
  33. /////////////////////////////////////////////////////////////////////////////  
  34. // The one and only CRegularDllApp object  
  35. CRegularDllApp theApp;  

  分析:
 在这一组文件中定义了一个继承自CWinApp的类CRegularDllApp,并同时定义了其的一个实例theApp。乍一看,您会以为它是 一个MFC应用程序,因为MFC应用程序也包含这样的在工程名后添加“App”组成类名的类(并继承自CWinApp类),也定义了这个类的一个全局实例 theApp。
 我们知道,在MFC应用程序中CWinApp取代了SDK程序中WinMain的地位,SDK程序WinMain所完成的工作由CWinApp的三个函数完成:
  1. virtual BOOL InitApplication( );  
  2. virtual BOOL InitInstance( );  
  3. virtual BOOL Run( ); //传说中MFC程序的“活水源头”  

 但是MFC规则DLL并不是MFC应用程序,它所继承自CWinApp的类不包含消息循环。这是因为,MFC规则DLL不包含 CWinApp::Run机制,主消息泵仍然由应用程序拥有。如果DLL 生成无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用从DLL 导出的函数来调用PreTranslateMessage成员函数。
另外,MFC规则DLL与MFC 应用程序中一样,需要将所有 DLL中元素的初始化放到InitInstance 成员函数中。
第二组文件 自定义对话框类声明及实现(点击查看附件 )
分析:
 这一部分的编程与一般的应用程序根本没有什么不同,我们照样可以利用MFC类向导来自动为对话框上的控件添加事件。MFC类向导照样会生成类似ON_BN_CLICKED(IDC_HELLO_BUTTON, OnHelloButton)的消息映射宏。
第三组文件 DLL中的资源文件
  1. //{{NO_DEPENDENCIES}}  
  2. // Microsoft Developer Studio generated include file.  
  3. // Used by RegularDll.rc  
  4. //  
  5. #define IDD_DLL_DIALOG 1000  
  6. #define IDC_HELLO_BUTTON 1000  

  分析:
 在MFC规则DLL中使用资源也与在MFC应用程序中使用资源没有什么不同,我们照样可以用Visual C++的资源编辑工具进行资源的添加、删除和属性的更改。
第四组文件 MFC规则DLL接口函数
  1. #include "StdAfx.h"  
  2. #include "DllDialog.h"  
  3. extern "C" __declspec(dllexportvoid ShowDlg(void)  
  4. {  
  5. CDllDialog dllDialog;  
  6. dllDialog.DoModal();  
  7. }  

  分析:
这个接口并不使用MFC,但是在其中却可以调用MFC扩展类CdllDialog的函数,这体现了“规则”的概类。
与非MFC DLL完全相同,我们可以使用__declspec(dllexport)声明或在.def中引出的方式导出MFC规则DLL中的接口。
5.4 MFC规则DLL的调用
笔者编写了如图12的对话框MFC程序(下载本工程附件 )来调用5.3节的MFC规则DLL,在这个程序的对话框上点击“调用DLL”按钮时弹出5.3节MFC规则DLL中的对话框。
  图12 MFC规则DLL的调用例子
 下面是“调用DLL”按钮单击事件的消息处理函数:
  1. void CRegularDllCallDlg::OnCalldllButton()  
  2. {  
  3. typedef void (*lpFun)(void);  
  4. HINSTANCE hDll; //DLL句柄  
  5. hDll = LoadLibrary("RegularDll.dll");  
  6. if (NULL==hDll)  
  7. {  
  8. MessageBox("DLL加载失败");  
  9. }  
  10. lpFun addFun; //函数指针  
  11. lpFun pShowDlg = (lpFun)GetProcAddress(hDll,"ShowDlg");  
  12. if (NULL==pShowDlg)  
  13. {  
  14. MessageBox("DLL中函数寻找失败");  
  15. }  
  16. pShowDlg();  
  17. }  

  上述例子中给出的是显示调用的方式,可以看出,其调用方式与第4节中非MFC DLL的调用方式没有什么不同。
 我们照样可以在EXE程序中隐式调用MFC规则DLL,只需要将DLL工程生成的.lib文件和.dll文件拷入当前工程所在的目录,并在RegularDllCallDlg.cpp文件(图12所示对话框类的实现文件)的顶部添加:
  1. #pragma comment(lib,"RegularDll.lib")  
  2. void ShowDlg(void);  
  3.   并将void CRegularDllCallDlg::OnCalldllButton() 改为:  
  4. void CRegularDllCallDlg::OnCalldllButton()  
  5. {  
  6. ShowDlg();  
  7. }  

  5.5 共享MFC DLL的规则DLL的模块切换
 应用程序进 程本身及其调用的每个DLL模块都具有一个全局唯一的HINSTANCE句柄,它们代表了DLL或EXE模块在进程虚拟空间中的起始地址。进程本身的模块 句柄一般为0x400000,而DLL模块的缺省句柄为0x10000000。如果程序同时加载了多个DLL,则每个DLL模块都会有不同的 HINSTANCE。应用程序在加载DLL时对其进行了重定位。
 共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE 句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。如果应用程 序需要来自于DLL的资源,就应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。
这次我们创建一个动态链接到MFC DLL的规则DLL(下载本工程附件 ),在其中包含如图13的对话框。
  图13 DLL中的对话框
另外,在与这个DLL相同的工作区中生成一个基于对话框的MFC程序,其对话框与图12完全一样。但是在此工程中我们另外添加了一个如图14的对话框。
  图14 EXE中的对话框
图13和图14中的对话框除了caption不同(以示区别)以外,其它的都相同。
 尤其值得特别注意,在DLL和EXE中我们对图13和图14的对话框使用了相同的资源ID=2000,在DLL和EXE工程的resource.h中分别有如下的宏:
  1. //DLL中对话框的ID  
  2. #define IDD_DLL_DIALOG 2000  
  3. //EXE中对话框的ID  
  4. #define IDD_EXE_DIALOG 2000  
  5.   与5.3节静态链接MFC DLL的规则DLL相同,我们还是在规则DLL中定义接口函数ShowDlg,原型如下:  
  6. #include "StdAfx.h"  
  7. #include "SharedDll.h"  
  8. void ShowDlg(void)  
  9. {  
  10. CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框  
  11. dlg.DoModal();  
  12. }  
  13.  而为应用工程主对话框的“调用DLL”的单击事件添加如下消息处理函数:  
  14. void CSharedDllCallDlg::OnCalldllButton()  
  15. {  
  16. ShowDlg();  
  17. }  

  我们以为单击“调用DLL”会弹出如图13所示DLL中的对话框,可是可怕的事情发生了,我们看到是图14所示EXE中的对话框!
惊讶?
 产生这个问题的根源在于应用程序与MFC规则DLL共享MFC DLL(或MFC扩展DLL)的程序总是默认使用EXE的资源,我们必须进行资源模块句柄的切换,其实现方法有三:
方法一 在DLL接口函数中使用:
AFX_MANAGE_STATE(AfxGetStaticModuleState());
我们将DLL中的接口函数ShowDlg改为:
  1. void ShowDlg(void)  
  2. {  
  3. //方法1:在函数开始处变更,在函数结束时恢复  
  4. //将AFX_MANAGE_STATE(AfxGetStaticModuleState());作为接口函数的第一//条语句进行模块状态切换  
  5. AFX_MANAGE_STATE(AfxGetStaticModuleState());  
  6. CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框  
  7. dlg.DoModal();  
  8. }  

 这次我们再点击EXE程序中的“调用DLL”按钮,弹出的是DLL中的如图13的对话框!嘿嘿,弹出了正确的对话框资源。 AfxGetStaticModuleState是一个函数,其原型为:
AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState( );
 该函数的功能是在栈上(这意味着其作用域是局部的)创建一个AFX_MODULE_STATE类(模块全局数据也就是模块状态)的实例,对其进行设置,并将其指针pModuleState返回。
AFX_MODULE_STATE类的原型如下:
  1. // AFX_MODULE_STATE (global data for a module)  
  2. class AFX_MODULE_STATE : public CNoTrackObject  
  3. {  
  4. public:  
  5. #ifdef _AFXDLL  
  6. AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion);  
  7. AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion,BOOL bSystem);  
  8. #else  
  9. AFX_MODULE_STATE(BOOL bDLL);  
  10. #endif  
  11. ~AFX_MODULE_STATE();  
  12. CWinApp* m_pCurrentWinApp;  
  13. HINSTANCE m_hCurrentInstanceHandle;  
  14. HINSTANCE m_hCurrentResourceHandle;  
  15. LPCTSTR m_lpszCurrentAppName;  
  16. … //省略后面的部分  
  17. }  

 AFX_MODULE_STATE类利用其构造函数和析构函数进行存储模块状态现场及恢复现场的工作,类似汇编中call指令对pc指针和sp寄存器的保存与恢复、中断服务程序的中断现场压栈与恢复以及操作系统线程调度的任务控制块保存与恢复。
许多看似不着边际的知识点居然有惊人的相似!
AFX_MANAGE_STATE是一个宏,其原型为:
AFX_MANAGE_STATE( AFX_MODULE_STATE* pModuleState )
该宏用于将pModuleState设置为当前的有效模块状态。当离开该宏的作用域时(也就离开了pModuleState所指向栈上对象的作用域),先前的模块状态将由AFX_MODULE_STATE的析构函数恢复。
 方法二 在DLL接口函数中使用:
AfxGetResourceHandle();
AfxSetResourceHandle(HINSTANCE xxx);
 AfxGetResourceHandle用于获取当前资源模块句柄,而AfxSetResourceHandle则用于设置程序目前要使用的资源模块句柄。
我们将DLL中的接口函数ShowDlg改为:
  1. void ShowDlg(void)  
  2. {  
  3. //方法2的状态变更  
  4. HINSTANCE save_hInstance = AfxGetResourceHandle();  
  5. AfxSetResourceHandle(theApp.m_hInstance);  
  6. CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框  
  7. dlg.DoModal();  
  8. //方法2的状态还原  
  9. AfxSetResourceHandle(save_hInstance);  
  10. }  

  通过AfxGetResourceHandle和AfxSetResourceHandle的合理变更,我们能够灵活地设置程序的资源模块句柄,而方法一则只能在DLL接口函数退出的时候才会恢复模块句柄。方法二则不同,如果将ShowDlg改为:
  1. extern CSharedDllApp theApp; //需要声明theApp外部全局变量  
  2. void ShowDlg(void)  
  3. {  
  4. //方法2的状态变更  
  5. HINSTANCE save_hInstance = AfxGetResourceHandle();  
  6. AfxSetResourceHandle(theApp.m_hInstance);  
  7. CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框  
  8. dlg.DoModal();  
  9. //方法2的状态还原  
  10. AfxSetResourceHandle(save_hInstance);  
  11. //使用方法2后在此处再进行操作针对的将是应用程序的资源  
  12. CDialog dlg1(IDD_DLL_DIALOG); //打开ID为2000的对话框  
  13. dlg1.DoModal();  
  14. }  

  在应用程序主对话框的“调用DLL”按钮上点击,将看到两个对话框,相继为DLL中的对话框(图13)和EXE中的对话框(图14)。
 方法三 由应用程序自身切换
 资源模块的切换除了可以由DLL接口函数完成以外,由应用程序自身也能完成(下载本工程附件 )。
现在我们把DLL中的接口函数改为最简单的:
  1. void ShowDlg(void)  
  2. {  
  3. CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框  
  4. dlg.DoModal();  
  5. }  
  6. // 而将应用程序的OnCalldllButton函数改为:  
  7. void CSharedDllCallDlg::OnCalldllButton()  
  8. {  
  9. //方法3:由应用程序本身进行状态切换  
  10. //获取EXE模块句柄  
  11. HINSTANCE exe_hInstance = GetModuleHandle(NULL);  
  12. //或者HINSTANCE exe_hInstance = AfxGetResourceHandle();  
  13. //获取DLL模块句柄  
  14. HINSTANCE dll_hInstance = GetModuleHandle("SharedDll.dll");  
  15. AfxSetResourceHandle(dll_hInstance); //切换状态  
  16. ShowDlg(); //此时显示的是DLL的对话框  
  17. AfxSetResourceHandle(exe_hInstance); //恢复状态  
  18. //资源模块恢复后再调用ShowDlg  
  19. ShowDlg(); //此时显示的是EXE的对话框  
  20. }  

  方法三中的Win32函数GetModuleHandle可以根据DLL的文件名获取DLL的模块句柄。如果需要得到EXE模块的句柄,则应调用带有Null参数的GetModuleHandle。
方法三与方法二的不同在于方法三是在应用程序中利用AfxGetResourceHandle和AfxSetResourceHandle进行资源模块 句柄切换的。同样地,在应用程序主对话框的“调用DLL”按钮上点击,也将看到两个对话框,相继为DLL中的对话框(图13)和EXE中的对话框(图 14)。





这是《VC++动态链接库(DLL)编程深入浅出》的第四部分,阅读本文前,请先阅读前三部分:(一)、(二)、(三)。
 MFC扩展DLL的内涵为MFC的扩展,用户使 用MFC扩展DLL就像使用MFC本身的DLL一样。除了可以在MFC扩展DLL的内部使用MFC以外,MFC扩展DLL与应用程序的接口部分也可以是 MFC。我们一般使用MFC扩展DLL来包含一些MFC的增强功能,譬如扩展MFC的CStatic、CButton等类使之具备更强大的能力。
 使用Visual C++向导生产MFC扩展DLL时,MFC向导会自动增加DLL的入口函数DllMain:

  1. extern "C" int APIENTRY  
  2. DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)  
  3. {  
  4. // Remove this if you use lpReserved  
  5. UNREFERENCED_PARAMETER(lpReserved);  
  6. if (dwReason == DLL_PROCESS_ATTACH)  
  7. {  
  8.   TRACE0("MFCEXPENDDLL.DLL Initializing! ");  
  9.   // Extension DLL one-time initialization  
  10.   if (!AfxInitExtensionModule(MfcexpenddllDLL, hInstance))  
  11.   return 0;  
  12.   // Insert this DLL into the resource chain  
  13.   // NOTE: If this Extension DLL is being implicitly linked to by  
  14.   // an MFC Regular DLL (such as an ActiveX Control)  
  15.   // instead of an MFC application, then you will want to  
  16.   // remove this line from DllMain and put it in a separate  
  17.   // function exported from this Extension DLL. The Regular DLL  
  18.   // that uses this Extension DLL should then explicitly call that  
  19.   // function to initialize this Extension DLL. Otherwise,  
  20.   // the CDynLinkLibrary object will not be attached to the  
  21.   // Regular DLL's resource chain, and serious problems will  
  22.   // result.  
  23.   new CDynLinkLibrary(MfcexpenddllDLL);  
  24. }  
  25. else if (dwReason == DLL_PROCESS_DETACH)  
  26. {  
  27.   TRACE0("MFCEXPENDDLL.DLL Terminating! ");  
  28.   // Terminate the library before destructors are called  
  29.   AfxTermExtensionModule(MfcexpenddllDLL);  
  30. }  
  31. return 1;  // ok  
  32. }  

  上述代码完成MFC扩展DLL的初始化和终止处理。
 由于MFC扩展DLL导出函数和变量的方式与其它DLL没有什么区别,我们不再细致讲解。下面直接给出一个MFC扩展DLL的创建及在应用程序中调用它的例子。
6.1 MFC扩展DLL的创建
 下 面我们将在MFC扩展DLL中导出一个按钮类CSXButton(扩展自MFC的CButton类),类CSXButton是一个用以取代 CButton的类,它使你能在同一个按钮上显示位图和文字,而MFC的按钮仅可显示二者之一。类CSXbutton的源代码在Internet上广泛流 传,有很好的“群众基础”,因此用这个类来讲解MFC扩展DLL有其特殊的功效。
MFC中包含一些宏,这些宏在DLL和调用DLL的应用程序中被以不同的方式展开,这使得在DLL和应用程序中,使用统一的一个宏就可以表示出输出和输入的不同意思:
  1. // for data  
  2. #ifndef AFX_DATA_EXPORT  
  3. #define AFX_DATA_EXPORT __declspec(dllexport)  
  4. #endif  
  5. #ifndef AFX_DATA_IMPORT  
  6. #define AFX_DATA_IMPORT __declspec(dllimport)  
  7. #endif  
  8. // for classes  
  9. #ifndef AFX_CLASS_EXPORT  
  10. #define AFX_CLASS_EXPORT __declspec(dllexport)  
  11. #endif  
  12. #ifndef AFX_CLASS_IMPORT  
  13. #define AFX_CLASS_IMPORT __declspec(dllimport)  
  14. #endif  
  15. // for global APIs  
  16. #ifndef AFX_API_EXPORT  
  17. #define AFX_API_EXPORT __declspec(dllexport)  
  18. #endif  
  19. #ifndef AFX_API_IMPORT  
  20. #define AFX_API_IMPORT __declspec(dllimport)  
  21. #endif  
  22. #ifndef AFX_EXT_DATA  
  23. #ifdef _AFXEXT  
  24.   #define AFX_EXT_CLASS    AFX_CLASS_EXPORT  
  25.   #define AFX_EXT_API     AFX_API_EXPORT  
  26.   #define AFX_EXT_DATA    AFX_DATA_EXPORT  
  27.   #define AFX_EXT_DATADEF  
  28. #else  
  29.   #define AFX_EXT_CLASS    AFX_CLASS_IMPORT  
  30.   #define AFX_EXT_API     AFX_API_IMPORT  
  31.   #define AFX_EXT_DATA    AFX_DATA_IMPORT  
  32.   #define AFX_EXT_DATADEF  
  33. #endif  
  34. #endif  

  导出一个类,直接在类声明头文件中使用AFX_EXT_CLASS即可,以下是导出CSXButton类的例子:
  1. #ifndef _SXBUTTON_H  
  2. #define _SXBUTTON_H  
  3. #defineSXBUTTON_CENTER-1  
  4. class AFX_EXT_CLASS CSXButton : public CButton  
  5. {  
  6. // Construction  
  7. public:  
  8. CSXButton();  
  9. // Attributes  
  10. private:  
  11. //Positioning  
  12. BOOL m_bUseOffset;    
  13. CPoint m_pointImage;  
  14. CPoint m_pointText;  
  15. int m_nImageOffsetFromBorder;  
  16. int m_nTextOffsetFromImage;  
  17. //Image  
  18. HICON m_hIcon;    
  19. HBITMAP m_hBitmap;  
  20. HBITMAP m_hBitmapDisabled;  
  21. int m_nImageWidth, m_nImageHeight;  
  22. //Color Tab  
  23. char m_bColorTab;    
  24. COLORREFm_crColorTab;  
  25. //State  
  26. BOOL m_bDefault;  
  27. UINT m_nOldAction;  
  28. UINT m_nOldState;  
  29. // Operations  
  30. public:  
  31. //Positioning  
  32. int SetImageOffset( int nPixels );  
  33. int SetTextOffset( int nPixels );  
  34. CPointSetImagePos( CPoint p );  
  35. CPointSetTextPos( CPoint p );  
  36. //Image  
  37. BOOLSetIcon( UINT nID, int nWidth, int nHeight );  
  38. BOOLSetBitmap( UINT nID, int nWidth, int nHeight );  
  39. BOOLSetMaskedBitmap( UINT nID, int nWidth, int nHeight, COLORREF crTransparentMask );  
  40. BOOLHasImage() { return (BOOL)( m_hIcon != 0 | m_hBitmap != 0 ); }  
  41. //Color Tab  
  42. voidSetColorTab(COLORREF crTab);  
  43. //State  
  44. BOOLSetDefaultButton( BOOL bState = TRUE );  
  45. private:  
  46. BOOLSetBitmapCommon( UINT nID, int nWidth, int nHeight, COLORREF crTransparentMask, BOOL bUseMask );  
  47. voidCheckPointForCentering( CPoint &p, int nWidth, int nHeight );  
  48. voidRedraw();  
  49. // Overrides  
  50. // ClassWizard generated virtual function overrides  
  51. //{{AFX_VIRTUAL(CSXButton)  
  52. public:  
  53. virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);  
  54. //}}AFX_VIRTUAL  
  55. // Implementation  
  56. public:  
  57. virtual ~CSXButton();  
  58. // Generated message map functions  
  59. protected:  
  60. //{{AFX_MSG(CSXButton)  
  61. afx_msg LRESULT OnGetText(WPARAM wParam, LPARAM lParam);  
  62. //}}AFX_MSG  
  63. DECLARE_MESSAGE_MAP()  
  64. };  
  65. #endif  

 把SXBUTTON.CPP文件直接添加到工程,编译工程,得到“mfcexpenddll.lib”和“mfcexpenddll.dll”两 个文件。我们用Visual Studio自带的Depends工具可以查看这个.dll,发现其导出了众多符号(见图15)。
 图15 导出类时导出的大量符号 (+放大该图片)
这些都是类的构造函数、析构函数及其它成员函数和变量经编译器处理过的符号,我们直接用__declspec(dllexport)语句声明类就导出了这些符号。
 如果我们想用.lib文件导出这些符号,是非常困难的,我们需要在工程中生成.map文件,查询.map文件的符号,然后将其一一导出。如图 16,打开DLL工程的settings选项,再选择Link,勾选其中的产生MAP文件(Generate mapfile)就可以产生.map文件了。
 打开mfcexpenddll工程生成的.map文件,我们发现其中包含了图15中所示的符号(symbol)
  1. 0001:00000380 ?HasImage@CSXButton@@QAEHXZ 10001380 f i SXBUTTON.OBJ  
  2. 0001:000003d0 ??0CSXButton@@QAE@XZ    100013d0 f  SXBUTTON.OBJ  
  3. 0001:00000500 ??_GCSXButton@@UAEPAXI@Z  10001500 f i SXBUTTON.OBJ  
  4. 0001:00000570 ??_ECSXButton@@UAEPAXI@Z  10001570 f i SXBUTTON.OBJ  
  5. 0001:00000630 ??1CSXButton@@UAE@XZ    10001630 f  SXBUTTON.OBJ  
  6. 0001:00000700 ?_GetBaseMessageMap@CSXButton@@KGPBUAFX_MSGMAP@@XZ 10001700 f  SXBUTTON.OBJ  
  7. 0001:00000730 ?GetMessageMap@CSXButton@@MBEPBUAFX_MSGMAP@@XZ 10001730 f  SXBUTTON.OBJ  
  8. 0001:00000770  ?Redraw@CSXButton@@AAEXXZ 10001770 f i SXBUTTON.OBJ  
  9. 0001:000007d0  ?SetIcon@CSXButton@@QAEHIHH@Z 100017d0 f  SXBUTTON.OBJ  
  10. ……………………………………………………………………..//省略  
  图16 产生.map文件 (+放大该图片)
所以,对于MFC扩展DLL,我们不宜以.lib文件导出类。