﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-thisliy-随笔分类-C语言</title><link>http://www.blogjava.net/thisliy/category/42849.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 30 Nov 2009 12:12:42 GMT</lastBuildDate><pubDate>Mon, 30 Nov 2009 12:12:42 GMT</pubDate><ttl>60</ttl><item><title>DllMain详解</title><link>http://www.blogjava.net/thisliy/archive/2009/11/30/304261.html</link><dc:creator>liyang</dc:creator><author>liyang</author><pubDate>Mon, 30 Nov 2009 07:25:00 GMT</pubDate><guid>http://www.blogjava.net/thisliy/archive/2009/11/30/304261.html</guid><wfw:comment>http://www.blogjava.net/thisliy/comments/304261.html</wfw:comment><comments>http://www.blogjava.net/thisliy/archive/2009/11/30/304261.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/thisliy/comments/commentRss/304261.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/thisliy/services/trackbacks/304261.html</trackback:ping><description><![CDATA[<span style="font-size: 12pt;"><span style="font-size: 10pt;"><span style="font-size: 10pt;"><span style="font-size: 12pt;"><span style="font-family: 幼圆;">
<p g_t_wrap="" g_t_left="" g_t_20="" g_c_pdin="" c07="" id="blogtitle_fks_083066093087081074086081080095081095087069086094080071" style="margin: 20px auto 10px;"><span style="font-family: '微软雅黑','黑体',Arial,Helvetica,Sans-Serif;">DllMain详解</span></p>
<p style="text-indent: 2em;">1&nbsp;&nbsp; DLL的进入/退出函数</p>
<p style="text-indent: 2em;">1.1 DllMain简介</p>
<p style="text-indent: 2em;">跟exe有个main或者WinMain入口函数一样，DLL也有一个入口函数，就是DllMain。以&#8220;DllMain&#8221;为关键字，来看看MSDN帮助文档怎么介绍这个函数的。</p>
<p style="text-indent: 2em;">The DllMain function is an optional method
of entry into a dynamic-link library
(DLL)。（简要翻译：对于动态链接库，DllMain是一个可选的入口函数。）这句话很重要，很多初学者可能都认为一个动态链接库肯定要有
DllMain函数。其实不然，像很多仅仅包含资源信息的DLL是没有DllMain函数的。</p>
<p style="text-indent: 2em;">1.2 何时调用DllMain</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 系统是在什么时候调用DllMain函数的呢？静态链接时，或动态链接时调用LoadLibrary和FreeLibrary都会调用DllMain函数。DllMain的第三个参数fdwReason指明了系统调用Dll的原因，它可能是:</p>
<p style="text-indent: 2em;"><font color="#ff6600">DLL_PROCESS_ATTACH、</font></p>
<p style="text-indent: 2em;"><font color="#333399">DLL_PROCESS_DETACH、</font></p>
<p style="text-indent: 2em;"><font color="#800080">DLL_THREAD_ATTACH</font></p>
<p style="text-indent: 2em;"><font color="#008000">DLL_THREAD_DETACH。</font></p>
<p style="text-indent: 2em;">以下从这四种情况来分析系统何时调用了DllMain。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p style="text-indent: 2em;">1.2.1 DLL_PROCESS_ATTACH</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 大家都知道，一个程序要调用Dll里的函数，首先要先把DLL文件映射到进程的地址空间。要把一个DLL文件映射到进程的地址空间，有两种方法：静态链接和动态链接的LoadLibrary或者LoadLibraryEx。</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
当一个DLL文件被映射到进程的地址空间时，系统调用该DLL的DllMain函数，传递的fdwReason参数为
DLL_PROCESS_ATTACH。这种调用只会发生在第一次映射时。如果同一个进程后来为已经映射进来的DLL再次调用LoadLibrary或者
LoadLibraryEx，操作系统只会增加DLL的使用次数，它不会再用DLL_PROCESS_ATTACH调用DLL的DllMain函数。不同
进程用LoadLibrary同一个DLL时，每个进程的第一次映射都会用DLL_PROCESS_ATTACH调用DLL的DllMain函数。</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可参考DllMainTest的DLL_PROCESS_ATTACH_Test函数。</p>
<p style="text-indent: 2em;">1.2.2 DLL_PROCESS_DETACH</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当DLL被从进程的地址空间解除映射时，系统调用了它的DllMain，传递的fdwReason值是DLL_PROCESS_DETACH。当DLL处理该值时，它应该执行进程相关的清理工作。</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么什么时候DLL被从进程的地址空间解除映射呢？两种情况：</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◆FreeLibrary解除DLL映射（<font color="#ff0000">有几个LoadLibrary，就要有几个FreeLibrary</font>）</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
◆进程结束而解除DLL映射，在进程结束前还没有解除DLL的映射，进程结束后会解除DLL映射。（如果进程的终结是因为调用了
TerminateProcess，系统就不会用DLL_PROCESS_DETACH来调用DLL的DllMain函数。这就意味着DLL在进程结束前
没有机会执行任何清理工作。）</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#0000ff">注意：当用DLL_PROCESS_ATTACH调用DLL的DllMain函数时，如果返回FALSE，说明没有初始化成功，系统仍会用DLL_PROCESS_DETACH调用DLL的DllMain函数。因此，必须确保清理那些没有成功初始化的东西。</font></p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可参考DllMainTest的DLL_PROCESS_DETACH_Test函数。</p>
<p style="text-indent: 2em;">1.2.3 DLL_THREAD_ATTACH</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当进程创建一线程时，系统查看当前映射到进程地址空间中的所有DLL文件映像，并用值DLL_THREAD_ATTACH调用DLL的DllMain函数。</p>
<p style="text-indent: 2em;">新创建的线程负责执行这次的DLL的DllMain函数，只有当所有的DLL都处理完这一通知后，系统才允许进程开始执行它的线程函数。</p>
<p style="text-indent: 2em;">注意跟DLL_PROCESS_ATTACH的区别，我们在前面说过，第
n(n&gt;=2)次以后地把DLL映像文件映射到进程的地址空间时，是不再用DLL_PROCESS_ATTACH调用DllMain的。而
DLL_THREAD_ATTACH不同，进程中的每次建立线程，都会用值DLL_THREAD_ATTACH调用DllMain函数，哪怕是线程中建立
线程也一样。</p>
<p style="text-indent: 2em;">1.2.4 DLL_THREAD_DETACH</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
如果线程调用了ExitThread来结束线程（线程函数返回时，系统也会自动调用ExitThread），系统查看当前映射到进程空间中的所有DLL文
件映像，并用DLL_THREAD_DETACH来调用DllMain函数，通知所有的DLL去执行线程级的清理工作。</p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意：如果线程的结束是因为系统中的一个线程调用了TerminateThread，系统就不会用值DLL_THREAD_DETACH来调用所有DLL的DllMain函数。</p>
<p style="text-indent: 2em;">1.3 为DllMain换名</p>
<p style="text-indent: 2em;">在早期的SDK版本中，DllMain是叫做DllEntryPoint。其实有一件鲜为人
知的事：一个Dll的入口函数名是可以自己定义的。下面我将以VC++6.0为例来演示如何更改。首先要说明一点，虽然DllMain可以换成其他函数
名，但函数的参数和返回值必须和DllMain一样。而且这个函数要为__stdcall类型（DllMain本身也是__stdcall类型）。</p>
<p style="text-indent: 2em;">打开VC++菜单Project"Settings"Link tab" Output
in the Category box，如下图，在Entry-point
symbol中输入要替换DllMain的函数名（当然这个函数名是你程序中已经实现的函数）。Entry-point
symbol是干么的呢？可以以关键字&#8220;Entry-point symbol&#8221;搜索MSDN帮助文档查看，搜索时，打钩&#8220;仅搜索标题&#8221;会更快定位。</p>
<p style="text-indent: 2em;"><img title="DllMain详解 - 生活 - 无敌" alt="DllMain详解 - 生活 - 无敌" src="http://p.blog.csdn.net/images/p_blog_csdn_net/benkaoya/1.jpg" border="0" /></p>
<p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 按OK后，如果马上编译的话会出现如下错误：</p>
<p style="text-indent: 2em;">LIBCMTD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main</p>
<p style="text-indent: 2em;">Debug/Dll.dll : fatal error LNK1120: 1 unresolved externals</p>
<p style="text-indent: 2em;">打开VC++菜单Project"Settings"C/C++选项卡，如下图，在
Project
Options：末尾的地方添加&#8221;/D&#8221;（图中蓝色高亮的地方），要注意位置，我试了，要把/D放到/GZ后面也会链接错误，我也不懂为什么，^_^。按
OK，再次编译，成功。大家可以自己测试下到底有没有更改成功，什么，如果测试？打出调式信息啊。</p>
<p style="text-indent: 2em;"><img title="DllMain详解 - 生活 - 无敌" alt="DllMain详解 - 生活 - 无敌" src="http://p.blog.csdn.net/images/p_blog_csdn_net/benkaoya/2.jpg" border="0" /></p>
<p style="text-indent: 2em;">1.4 DisableThreadLibraryCalls</p>
<p style="text-indent: 2em;">看帮助就知道它是干么用的：</p>
<p style="text-indent: 2em;">The DisableThreadLibraryCalls function
disables the DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications for
the dynamic-link library (DLL) specified by hLibModule. This can reduce
the size of the working co<wbr>de set for some applications.</p>
<p style="text-indent: 2em;">转自:http://blog.csdn.net/benkaoya/archive/2008/06/02/2504781.aspx</p>
</span></span></span></span></span>
<img src ="http://www.blogjava.net/thisliy/aggbug/304261.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/thisliy/" target="_blank">liyang</a> 2009-11-30 15:25 <a href="http://www.blogjava.net/thisliy/archive/2009/11/30/304261.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>typedef的四个用途和两个陷阱</title><link>http://www.blogjava.net/thisliy/archive/2009/11/25/303564.html</link><dc:creator>liyang</dc:creator><author>liyang</author><pubDate>Wed, 25 Nov 2009 02:08:00 GMT</pubDate><guid>http://www.blogjava.net/thisliy/archive/2009/11/25/303564.html</guid><wfw:comment>http://www.blogjava.net/thisliy/comments/303564.html</wfw:comment><comments>http://www.blogjava.net/thisliy/archive/2009/11/25/303564.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/thisliy/comments/commentRss/303564.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/thisliy/services/trackbacks/303564.html</trackback:ping><description><![CDATA[<span style="font-family: 微软雅黑;"><span style="font-family: 方正姚体;">用途一：
<br />
定义一种类型的别名，而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如：
<br />
char*&nbsp;pa,&nbsp;pb;&nbsp;&nbsp;//&nbsp;这多数不符合我们的意图，它只声明了一个指向字符变量的指针，&nbsp;
<br />
//&nbsp;和一个字符变量；
<br />
以下则可行：
<br />
typedef&nbsp;char*&nbsp;PCHAR;&nbsp;&nbsp;//&nbsp;一般用大写
<br />
PCHAR&nbsp;pa,&nbsp;pb;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;可行，同时声明了两个指向字符变量的指针
<br />
虽然：
<br />
char&nbsp;*pa,&nbsp;*pb;
<br />
也可行，但相对来说没有用typedef的形式直观，尤其在需要大量指针的地方，typedef的方式更省事。
<br />
<br />
用途二：
<br />
用在旧的C代码中（具体多旧没有查），帮助struct。以前的代码中，声明struct新对象时，必须要带上struct，即形式为：&nbsp;struct&nbsp;结构名&nbsp;对象名，如：
<br />
struct&nbsp;tagPOINT1
<br />
{
<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;x;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;y;
<br />
};
<br />
struct&nbsp;tagPOINT1&nbsp;p1;&nbsp;
<br />
<br />
而在C++中，则可以直接写：结构名&nbsp;对象名，即：
<br />
tagPOINT1&nbsp;p1;
<br />
<br />
估计某人觉得经常多写一个struct太麻烦了，于是就发明了：
<br />
typedef&nbsp;struct&nbsp;tagPOINT
<br />
{
<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;x;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;y;
<br />
}POINT;
<br />
<br />
POINT&nbsp;p1;&nbsp;//&nbsp;这样就比原来的方式少写了一个struct，比较省事，尤其在大量使用的时候
<br />
<br />
或许，在C++中，typedef的这种用途二不是很大，但是理解了它，对掌握以前的旧代码还是有帮助的，毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。
<br />
<br />
用途三：
<br />
用typedef来定义与平台无关的类型。
<br />
比如定义一个叫&nbsp;REAL&nbsp;的浮点类型，在目标平台一上，让它表示最高精度的类型为：
<br />
typedef&nbsp;long&nbsp;double&nbsp;REAL;&nbsp;
<br />
在不支持&nbsp;long&nbsp;double&nbsp;的平台二上，改为：
<br />
typedef&nbsp;double&nbsp;REAL;&nbsp;
<br />
在连&nbsp;double&nbsp;都不支持的平台三上，改为：
<br />
typedef&nbsp;float&nbsp;REAL;&nbsp;
<br />
也就是说，当跨平台时，只要改下&nbsp;typedef&nbsp;本身就行，不用对其他源码做任何修改。
<br />
标准库就广泛使用了这个技巧，比如size_t。
<br />
另外，因为typedef是定义了一种类型的新别名，不是简单的字符串替换，所以它比宏来得稳健（虽然用宏有时也可以完成以上的用途）。
<br />
<br />
用途四：
<br />
为复杂的声明定义一个新的简单的别名。方法是：在原来的声明里逐步用别名替换一部分复杂声明，如此循环，把带变量名的部分留到最后替换，得到的就是原声明的最简化版。举例：
<br />
<br />
1.&nbsp;原声明：int&nbsp;*(*a[5])(int,&nbsp;char*);
<br />
变量名为a，直接用一个新别名pFun替换a就可以了：
<br />
typedef&nbsp;int&nbsp;*(*pFun)(int,&nbsp;char*);&nbsp;
<br />
原声明的最简化版：
<br />
pFun&nbsp;a[5];&nbsp;
<br />
<br />
2.&nbsp;原声明：void&nbsp;(*b[10])&nbsp;(void&nbsp;(*)());
<br />
变量名为b，先替换右边部分括号里的，pFunParam为别名一：
<br />
typedef&nbsp;void&nbsp;(*pFunParam)();
<br />
再替换左边的变量b，pFunx为别名二：
<br />
typedef&nbsp;void&nbsp;(*pFunx)(pFunParam);
<br />
原声明的最简化版：
<br />
pFunx&nbsp;b[10];
<br />
<br />
3.&nbsp;原声明：doube(*)()&nbsp;(*e)[9];&nbsp;
<br />
变量名为e，先替换左边部分，pFuny为别名一：
<br />
typedef&nbsp;double(*pFuny)();
<br />
再替换右边的变量e，pFunParamy为别名二
<br />
typedef&nbsp;pFuny&nbsp;(*pFunParamy)[9];
<br />
原声明的最简化版：
<br />
pFunParamy&nbsp;e;&nbsp;
<br />
<br />
理解复杂声明可用的&#8220;右左法则&#8221;：从变量名看起，先往右，再往左，碰到一个圆括号就调转阅读的方向；括号内分析完就跳出括号，还是按先右后左的顺序，如此循环，直到整个声明分析完。举例：
<br />
int&nbsp;(*func)(int&nbsp;*p);
<br />
首先找到变量名func，外面有一对圆括号，而且左边是一个*号，这说明func是一个指针；然后跳出这个圆括号，先看右边，又遇到圆括号，这说明
(*func)是一个函数，所以func是一个指向这类函数的指针，即函数指针，这类函数具有int*类型的形参，返回值类型是int。
<br />
int&nbsp;(*func[5])(int&nbsp;*);
<br />
func右边是一个[]运算符，说明func是具有5个元素的数组；func的左边有一个*，说明func的元素是指针（注意这里的*不是修饰
func，而是修饰func[5]的，原因是[]运算符优先级比*高，func先跟[]结合）。跳出这个括号，看右边，又遇到圆括号，说明func数组的
元素是函数类型的指针，它指向的函数具有int*类型的形参，返回值类型为int。
<br />
<br />
也可以记住2个模式：
<br />
type&nbsp;(*)(....)函数指针&nbsp;
<br />
type&nbsp;(*)[]数组指针&nbsp;
<br />
－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－
<br />
<br />
陷阱一：
<br />
记住，typedef是定义了一种类型的新别名，不同于宏，它不是简单的字符串替换。比如：
<br />
先定义：
<br />
typedef&nbsp;char*&nbsp;PSTR;
<br />
然后：
<br />
int&nbsp;mystrcmp(const&nbsp;PSTR,&nbsp;const&nbsp;PSTR);
<br />
<br />
const&nbsp;PSTR实际上相当于const&nbsp;char*吗？不是的，它实际上相当于char*&nbsp;const。
<br />
原因在于const给予了整个指针本身以常量性，也就是形成了常量指针char*&nbsp;const。
<br />
简单来说，记住当const和typedef一起出现时，typedef不会是简单的字符串替换就行。
<br />
<br />
陷阱二：
<br />
typedef在语法上是一个存储类的关键字（如auto、extern、mutable、static、register等一样），虽然它并不真正影响对象的存储特性，如：
<br />
typedef&nbsp;static&nbsp;int&nbsp;INT2;&nbsp;//不可行
<br />
编译将失败，会提示&#8220;指定了一个以上的存储类&#8221;。
</span></span>
<img src ="http://www.blogjava.net/thisliy/aggbug/303564.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/thisliy/" target="_blank">liyang</a> 2009-11-25 10:08 <a href="http://www.blogjava.net/thisliy/archive/2009/11/25/303564.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>