<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>不敢流泪 &#187; DLL</title>
	<atom:link href="http://www.boluor.com/tag/dll/feed" rel="self" type="application/rss+xml" />
	<link>http://www.boluor.com</link>
	<description></description>
	<lastBuildDate>Fri, 07 Oct 2011 07:28:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>一天纪要</title>
		<link>http://www.boluor.com/summary-today.html</link>
		<comments>http://www.boluor.com/summary-today.html#comments</comments>
		<pubDate>Tue, 16 Jun 2009 17:50:00 +0000</pubDate>
		<dc:creator>boluor</dc:creator>
				<category><![CDATA[日记]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[DLL]]></category>
		<category><![CDATA[FS]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://www.boluor.com/?p=135</guid>
		<description><![CDATA[<p>今天搞定了好多事情，感觉相当不错^.^</p>
<p>凌晨和Sandy在601聊天到4点半多，把很多的事情都好好地讨论了下，感觉下一步做什么怎么做清晰了很多。</p>
<p>上午睡过去的&#8230;</p>
<p>下午到实验室和KouPeng师兄，猜想加搜索后，终于发现ecryptfs的源码原来已经集成到了linux的源码包中，不过不知道为什么ecryptfs的官网都不提示下的。单单搜源码就耗了好长的时间。官网提供的utils包只是如何方便的使用ecryptfs的工具包而已。</p>
<p>回来又把windows核心编程中windows内存结构看了下。DLL到底加载到了什么位置搞明白了。我之前的《detours笔记》中写的DLL在一定程度上说也是正确的，&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>今天搞定了好多事情，感觉相当不错^.^</p>
<p>凌晨和Sandy在601聊天到4点半多，把很多的事情都好好地讨论了下，感觉下一步做什么怎么做清晰了很多。</p>
<p>上午睡过去的&#8230;</p>
<p>下午到实验室和KouPeng师兄，猜想加搜索后，终于发现ecryptfs的源码原来已经集成到了linux的源码包中，不过不知道为什么ecryptfs的官网都不提示下的。单单搜源码就耗了好长的时间。官网提供的utils包只是如何方便的使用ecryptfs的工具包而已。</p>
<p>回来又把windows核心编程中windows内存结构看了下。DLL到底加载到了什么位置搞明白了。我之前的《detours笔记》中写的DLL在一定程度上说也是正确的，那就是在系统分配的用户地址空间为3G的时候。在XP下的boot.ini中启动项加入/3G开关，讲会使用户空间程序的地址空间为3G，内核被压缩到剩下的1G空间中。这样会导致系统能创建线程，堆栈和其它资源数量的减少，此外，系统最多只能使用16GB的RAM，而通常情况下最多可以使用64GB的RAM，因为内存方式中没有足够的虚拟地址空间来管理更多的RAM。XP默认不开3G开关，这种情况下，DLL等加载的地址是在2G一下的。这也就解释了我之前的疑惑，为什么用IceSword看DLL的base地址时，都是0&#215;80000000下的。<br />
<span id="more-135"></span><br />
刚才和Sandy聊天，说到《c缺陷与陷阱》中提到的(*(void(*)())0)()怎么通过剥皮的方式逐步搞清楚他的真面目。比较复杂的式子在c++中也有，也需要花点功夫去搞明白。之后说，c和c++对NULL的定义是不一样的&#8230;我想了想没有想法，只知道c中的定义是 ((void *)0)，c++中对NULL直接定义为0。这样子问题来了，例如在c++中重载时，两个函数:</p>
<p>int test(int a);</p>
<p>int test( char *b);</p>
<p>这种情况下调用test(NULL)本意是传指针但却被当做是一个普通的整数0了。</p>
<p>还有一点，为什么一个程序的返回值如果是BOOL型的时候为什么一般不判断是否为TRUE，而可以判断是否为FALSE，更一般的情况下市判断0或者非0。因为通常情况下，FALSE只有一个值就是0，但是其他所有的都为真。这样如果有个程序可以返回3，4，5等整型值来表示不同的返回信息，用TRUE就很难判断函数是否运行失败。恩，还有函数的返回值如果是PVOID类型的话表示是数据块的内存地址，在API中经常见到。</p>
<p>最觉得有收获的，是Sandy推荐的牛人的文章，也就是pongba的博客。不论是《why c++》还是《为什么你应该（从现在开始就）写博客》，还是《书写是为了更好的思考》，篇篇经典，相当值得收藏。</p>
<p>最后吧，Sandy推荐说没事就看下《c++标准程序库》吧，我确实也应该看了。c++相当的值得学习。另，微软俱乐部的申请写的太简略了，Sandy说别人一看还以为你在BS人家&#8230;真没那意思，本来写的比较多，考虑到怕人家看着烦，才总结成两句话的&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.boluor.com/summary-today.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>detours 笔记</title>
		<link>http://www.boluor.com/detours-notes.html</link>
		<comments>http://www.boluor.com/detours-notes.html#comments</comments>
		<pubDate>Fri, 12 Jun 2009 18:15:26 +0000</pubDate>
		<dc:creator>boluor</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[detours]]></category>
		<category><![CDATA[DLL]]></category>
		<category><![CDATA[hook]]></category>
		<category><![CDATA[VC]]></category>

		<guid isPermaLink="false">http://www.boluor.com/?p=125</guid>
		<description><![CDATA[<p>　　花了一周的时间来了解detours，现在终于有了进展。一周前下载了detours的时候，茫然了好一阵子才找到了头绪。现在总结下方便像我一样的初学者了。</p>
<p>　　我不再介绍Detours到底是什么了，因为已经有很好的帮助文档了，从detours官网上可以下载detours包。我下载的版本是detours2.1 express版本。包中有帮助文档，有detours的源码，还有很多例子。编译的时候，CMD下在detours的目录输入nmake，就可以编译了。需要注意的是，detours2.1平台要求是NT内核的，所以98下是不能用的。</p>
<p>　　编译好后，下面就说使用之前几点需要知道的，看帮助文档是可以获得这些&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>　　花了一周的时间来了解detours，现在终于有了进展。一周前下载了detours的时候，茫然了好一阵子才找到了头绪。现在总结下方便像我一样的初学者了。</p>
<p>　　我不再介绍Detours到底是什么了，因为已经有很好的帮助文档了，从detours官网上可以下载detours包。我下载的版本是detours2.1 express版本。包中有帮助文档，有detours的源码，还有很多例子。编译的时候，CMD下在detours的目录输入nmake，就可以编译了。需要注意的是，detours2.1平台要求是NT内核的，所以98下是不能用的。</p>
<p>　　编译好后，下面就说使用之前几点需要知道的，看帮助文档是可以获得这些信息的。了解了这几点，使用detours会更加明了。detours是通过在目标进程中”打桩“来完成拦截目标函数的(语出Sandy)。这个概念需要理清。介绍文档中有示意图，可以仔细看。detours修改的是目标进程在内存中的二进制映像，并不修改硬盘上的文件，所以可以恢复。</p>
<p>　　detours编译好后，有几个文件需要注意。detours.h包含了detours提供的API的声明，如果需要使用这些函数，需要包含此头文件。detours.lib就是这些API的实现，是静态链接库，跟DLL生成的lib文件是不同的，如果不是很清楚的话可以看我之前的一篇”Dll的两种链接方式与LIB”。当调用了detours的API的时候，需要在项目中声明，在程序连接时包含此lib，这样编译器就会将detours.lib链接到目标程序中生成最后的可执行程序，这个程序运行时就不再需要detours.lib了。还有一个是detoured.dll，使用了detours的话是总是需要在项目文件夹下添加此文件的，按照文档说明，detoured.dll 是一个标志，帮助微软的开发人员和工具判断某个进程是否已经被detours拦截。</p>
<p>　　下面创建一个非常简单的例子，VC2005拦截Sleep API。detours包中的例子中的simple也是拦截的Sleep API。</p>
<p><strong>1.</strong>	首先创建一个Non-MFC DLL工程HookSleepDLL。将detours.h，detoured.dll，detours.lib三个文件拷贝这个工程目录中。在HookSleepDLL.cpp中添加三行声明：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:Courier new,verdana;"><span style="color: #339900;">#include &quot;detours.h&quot;</span>
<span style="color: #339900;">#pragma comment(lib,&quot;detoured.lib&quot;)</span>
<span style="color: #339900;">#pragma comment(lib,&quot;detours.lib&quot;)</span></pre></div></div>

<p><strong>2.	</strong>接着需要定义Sleep函数的指针：<br />
static VOID (WINAPI * TrueSleep)(DWORD dwMilliseconds) = Sleep;<br />
detours通过这个函数指针来找到Sleep在内存中的映像，当然作为常识，参数和返回值和Sleep需要一致，不然怎么叫这个函数的指针^.^。<br />
<strong>3.</strong>定义替换Sleep函数的函数，这个函数的参数类型和返回值需要和Sleep函数一摸一样。<br />
VOID WINAPI DelaySleep(DWORD dwMilliseconds){<br />
	TrueSleep(dwMilliseconds+15000);<br />
}<br />
我这里为了简单，就在原来Sleep的时间上加15秒。这就是说，如果Sleep被Hook住了，那么Sleep(1000)也就是睡一秒会变为睡16秒。</p>
<p><strong>4.</strong>下面说明在DLL被加载的时候开始拦截Sleep API。</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:Courier new,verdana;">BOOL APIENTRY DllMain<span style="color: #008000;">&#40;</span> HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 <span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> error<span style="color: #008080;">;</span>
	<span style="color: #0000ff;">switch</span><span style="color: #008000;">&#40;</span>ul_reason_for_call<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
		<span style="color: #0000ff;">case</span> DLL_PROCESS_ATTACH<span style="color: #008080;">:</span>
			DetourTransactionBegin<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourUpdateThread<span style="color: #008000;">&#40;</span><span style="color: #008080;">::</span><span style="color: #007788;">GetCurrentThread</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourAttach<span style="color: #008000;">&#40;</span><span style="color: #000040;">&amp;</span><span style="color: #008000;">&#40;</span>PVOID<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span>TrueSleep, DelaySleep<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			error <span style="color: #000080;">=</span> DetourTransactionCommit<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span>NO_ERROR<span style="color: #000040;">!</span><span style="color: #000080;">=</span>error<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
				<span style="color: #008080;">::</span><span style="color: #007788;">MessageBox</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;Error!&quot;</span>,<span style="color: #FF0000;">&quot;Error in Detours!&quot;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #008000;">&#125;</span>
			<span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
		<span style="color: #0000ff;">case</span> DLL_PROCESS_DETACH<span style="color: #008080;">:</span>
			DetourTransactionBegin<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourUpdateThread<span style="color: #008000;">&#40;</span>GetCurrentThread<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourDetach<span style="color: #008000;">&#40;</span><span style="color: #000040;">&amp;</span><span style="color: #008000;">&#40;</span>PVOID<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span>TrueSleep, DelaySleep<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			error <span style="color: #000080;">=</span> DetourTransactionCommit<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #008080;">::</span><span style="color: #007788;">MessageBox</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;Detour ends&quot;</span>,<span style="color: #FF0000;">&quot;Prompt!&quot;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">return</span> TRUE<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>　　用到的几个API简要说明下：<br />
DetourTransactionBegin：	开始一个新的detours事务。<br />
DetourUpdateThread：讲一个线程入队等待更新。（不知道理解的有没有偏差）<br />
DetourAttach：将目标进程(Sleep)和替换它的函数DelaySleep绑定。第一个参数是一个指针，这个指针指向目标函数的指针(有点绕&#8230;)。<br />
DetourTransactionCommit:	事务提交，如果不提交之前的更改是不会生效的。这个函数会返回一个值，如果是NO_ERROR 表明是成功，如果是其他的表示失败。<br />
DetourDetach：	解除目标进程和替换函数的绑定，参数和DetourDetach一摸一样。</p>
<p><strong>5.</strong>生成DLL。如果提示诸如无法将const char *转换到LPCSTR之类的错误，这是字符集的问题，将项目的属性设置为多字节的。或者使用_T，L之类的标记，具体可google。<br />
测试程序。创建一个MFC对话框程序。将前一步生成的HookSleepDLL.dll和detoured.dll拷贝到当前项目目录下。在界面上画两个Button控件BtnTest和BtnLoadLibrary。在BtnTest的事件中写：<br />
Sleep(1000);<br />
MessageBox(NULL,”hello,test”,”test”,MB_OK);<br />
说明在停一秒后弹出对话框。<br />
然后在BtnLoadLibrary的事件中写：<br />
HINSTANCE g_dll;<br />
g_dll=LoadLibrary(“SleepHOOKDll.dll”);</p>
<p><strong>6.</strong>编译运行，首先点击BtnLoadLibrary，将DLL加载到当前进程空间中。这个时候可以用IceSword查看模块信息，是否已经被加载。然后点击BtnTest，如果没有问题的话，效果会如预期，在16秒后才弹出对话框。</p>
<p>　　至此，一个简单的利用detours拦截API的例子已经完成。想要完成更复杂的工程是需要更多的知识储备和耐心的。在了解detours的时候，我顺便加深了对DLL的了解程度。需要特别注意的是，假如现在有一个DLL工程并没有输出任何的符号，在编译后有lib文件，当然这个DLL可以在被加载的时候干点事情。然后使用这个DLL的工程如果采用了静态链接的方式，那么编译器会对其进行优化，在进程运行时，并不会加载这个DLL。或者是你有输出函数，但是工程并没有调用任何函数，那么也会被优化掉。所以不错的选择是选择DLL的动态链接方式。</p>
<p>　　还有一点，作为DLL的补充吧，因为之后要完成一个进程保护程序，提前说下准备的知识吧，关于DLL的。windows为DLL等系统资源预留了2g-4g的虚拟内存空间地址，所以加载DLL的时候，系统会将DLL加载到这个位置，这样多个进程可以共享。所以虽然DLL可以被多个进程加载多次，但是系统只会保留一份在内存，也就是说系统检测到内存中已经有这个DLL，就不会再加载了，而是简单的映射下就可以了。这种共享在大家都只是读DLL的时候是没问题的，但是如果DLL中的某个变量可写，那么就会很危险。所以系统采取了COW（copy on write）机制。在某个进程需要写的时候，就单独申请空间，然后将这个新的页面映射到进程的写数据页面。这样，某个进程写数据并不会干扰其他的进程，因为改的只是一个拷贝。回到我们今天的主题detours上，拿上面的例子，拦截Sleep，也只是拦截本进程中调用Sleep的操作，并不会拦截其他进程调用Sleep操作，因为拦截时发生了写操作，所以修改的时候修改的是一份针对本进程的拷贝，并不是所有的进程。如果想达到拦截所有进程调用Sleep的操作，我目前想到的方法就是对所有的进程注入DLL实现拦截了。</p>
<p>　　终于写完了，两点多了，睡哦。　　</p>
]]></content:encoded>
			<wfw:commentRss>http://www.boluor.com/detours-notes.html/feed</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Dll的两种链接方式与LIB</title>
		<link>http://www.boluor.com/two-link-mode-of-dll-and-lib.html</link>
		<comments>http://www.boluor.com/two-link-mode-of-dll-and-lib.html#comments</comments>
		<pubDate>Thu, 11 Jun 2009 06:54:00 +0000</pubDate>
		<dc:creator>boluor</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[DLL]]></category>

		<guid isPermaLink="false">http://www.boluor.com/?p=123</guid>
		<description><![CDATA[<p>&#160;&#160;&#160;&#160;&#160; 终于搞清楚DLL的两种加载方式以及一个特别容易混淆的概念，DLL的静态链接方式和静态连接库lib。</p>
<p><b>&#160;&#160;&#160;&#160;&#160;&#160; 静态链接库</b>LIB文件包含了函数等的具体实现，是编译好的二进制代码，可以看做.obj文件。当某个程序中声明连接LIB文件时，程序在链接阶段将LIB文件中的二进制代码一并生成到目标程序中。所以，目标程序不需要额外的依赖就可以运行。比如C中的printf等函数，通常是以这种方式链接到目标程序中去的。</p>
<p>VS2005中，如果需要连接某个静态链接库，可以在工程中设置LINKER，或&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>&#160;&#160;&#160;&#160;&#160; 终于搞清楚DLL的两种加载方式以及一个特别容易混淆的概念，DLL的静态链接方式和静态连接库lib。</p>
<p><b>&#160;&#160;&#160;&#160;&#160;&#160; 静态链接库</b>LIB文件包含了函数等的具体实现，是编译好的二进制代码，可以看做.obj文件。当某个程序中声明连接LIB文件时，程序在链接阶段将LIB文件中的二进制代码一并生成到目标程序中。所以，目标程序不需要额外的依赖就可以运行。比如C中的printf等函数，通常是以这种方式链接到目标程序中去的。</p>
<p>VS2005中，如果需要连接某个静态链接库，可以在工程中设置LINKER，或者添加代码：</p>
<p> <span id="more-123"></span>
<p>#pragma comment(lib,”my.lib”)</p>
<p>&#160;&#160;&#160;&#160;&#160; 而<b>动态链接库</b>DLL是在运行时才被链接到目标程序中的。硬盘上的可执行程序中并没有在编译连接的时候就将DLL链接到一起，所以在运行时需要DLL文件，加入DLL文件找不到，那么程序就不能继续运行。</p>
<p>&#160;&#160;&#160;&#160;&#160; 程序调用DLL有两种方式，隐式链接和显式链接。在之前我已经总结过了<a href="http://www.boluor.com/?p=60">DLL基础</a> 。隐式链接的用法和静态链接库LIB的用法一样，不过特别注意的是其本质跟静态链接库完全不同。Dll的LIB只是包含了一些跳转指令之类的，在程序中需要调用DLL中的函数时，就跳转到具体的实现DLL中，它本身并不包含实现。</p>
<p>&#160;&#160;&#160;&#160;&#160; 不论是DLL的静态链接方式和动态链接方式，在程序加载DLL时，都会将DLL映射到进程的地址空间中去。用IceSword查看进程的模块信息中可以看到。但是有一点需要特别注意，如果采用了DLL的静态链接方式，但是代码中并没有调用DLL中的任何变量或者是函数，那么编译器会做一些优化，从而程序并不加载这个DLL，验证的话用IceSword查看时是看不到模块中有这个DLL的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.boluor.com/two-link-mode-of-dll-and-lib.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>DLL基础</title>
		<link>http://www.boluor.com/dll-base.html</link>
		<comments>http://www.boluor.com/dll-base.html#comments</comments>
		<pubDate>Mon, 11 May 2009 05:15:00 +0000</pubDate>
		<dc:creator>boluor</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[DLL]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://www.boluor.com/blog/?p=60</guid>
		<description><![CDATA[<p>&#160;&#160;&#160;&#160;&#160;&#160; 想写个程序,可以监控某个进程,然后对其进行某些操作…别怀疑我的动机，我是和谐社会的良民<img alt="" src="http://www.zu14.cn/coolemotion/emotions/hi_6.gif" /> </p>
<p>然后第一步开始，DLL到底是什么?听到DLLl这词的次数绝对比我的年龄大（本人年方22，嘿嘿！），不过却从来没去研究过，所以一直觉得DLL很神秘。不过，箭在弦上，不得不去了解了。想找本书看的，结果搜罗了半个小时没发现，烦了，就开始看<a href="http://hi.baidu.com/44997/blog/item/dcf47b59198b2d2a2934f0e1.html">某人的blog</a>，实现了后觉得豁然开朗啊。微软官网的<a href="http://support.microsoft.com/kb/815065/zh-cn">帮助</a>也不错。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 原文写的很不错。写下我的感受。</p>
<p>【<strong>概念</strong>】</p>
<p>&#160;&#160;&#038;&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>&#160;&#160;&#160;&#160;&#160;&#160; 想写个程序,可以监控某个进程,然后对其进行某些操作…别怀疑我的动机，我是和谐社会的良民<img alt="" src="http://www.zu14.cn/coolemotion/emotions/hi_6.gif" /> </p>
<p>然后第一步开始，DLL到底是什么?听到DLLl这词的次数绝对比我的年龄大（本人年方22，嘿嘿！），不过却从来没去研究过，所以一直觉得DLL很神秘。不过，箭在弦上，不得不去了解了。想找本书看的，结果搜罗了半个小时没发现，烦了，就开始看<a href="http://hi.baidu.com/44997/blog/item/dcf47b59198b2d2a2934f0e1.html">某人的blog</a>，实现了后觉得豁然开朗啊。微软官网的<a href="http://support.microsoft.com/kb/815065/zh-cn">帮助</a>也不错。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 原文写的很不错。写下我的感受。</p>
<p>【<strong>概念</strong>】</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; DLL是动态链接库的缩写，是一个包含有可使多个程序同时使用的数据和代码。使用促进代码的模块化、代码重用、内存的有效使用和减少所占用的磁盘空间。不过，由于使用DLL的程序需要实时的调用DLL中的数据或者代码，所以DLL必须和软件一起发放。</p>
<p> <span id="more-60"></span>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; DLL是建立在客户/服务器通信的概念上，包含若干函数、类或资源的库文件，函数和数据被存储在一个DLL（服务器）上并由一个或多个客户导出而使用，这些客户可以是应用程序或者是其它的DLL。DLL库不同于静态库，在静态库情况下，函数和数据被编译进一个二进制文件（通常扩展名为*.LIB），Visual C++的编译器在处理程序代码时将从静态库中恢复这些函数和数据并把他们和应用程序中的其他模块组合在一起生成可执行文件。这个过程称为&quot;静态链接&quot;，此时因为应用程序所需的全部内容都是从库中复制了出来，所以静态库本身并不需要与可执行文件一起发行。      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 在动态库的情况下，有两个文件，一个是引入库（.LIB）文件，一个是DLL文件，引入库文件包含被DLL导出的函数的名称和位置，DLL包含实际的函数和数据，应用程序使用LIB文件链接到所需要使用的DLL文件，库中的函数和数据并不复制到可执行文件中，因此在应用程序的可执行文件中，存放的不是被调用的函数代码，而是DLL中所要调用的函数的内存地址，这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来，从而节省了内存资源。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 微软的Visual C++支持三种DLL，它们分别是Non-MFC Dll（非MFC动态库）、Regular Dll（常规DLL）、Extension Dll（扩展DLL）。Non-MFC DLL指的是不用MFC的类库结构，直接用C语言写的DLL，其导出的函数是标准的C接口，能被非MFC或MFC编写的应用程序所调用。Regular DLL:和下述的Extension Dlls一样，是用MFC类库编写的，它的一个明显的特点是在源文件里有一个继承CWinApp的类（注意：此类DLL虽然从CWinApp派生，但没有消息循环）,被导出的函数是C函数、C++类或者C++成员函数（注意不要把术语C++类与MFC的微软基础C++类相混淆），调用常规DLL的应用程序不必是MFC应用程序，只要是能调用类C函数的应用程序就可以，它们可以是在Visual C++、Dephi、Visual Basic、Borland C等编译环境下利用DLL开发应用程序。</p>
<p>【<strong>编程基础</strong>】(以下多为引用)</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 由VS6或者VS2005时创建DLL工程只要按照提示一步步就行了。这里都是以VS2005，Non-MFC DLL。</p>
<p><strong>Win32 Dynamic-Link Library方式创建Non-MFC DLL动态链接库：</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 每一个DLL都必须有一个入口函数，默认是DllMain，这跟C里的main函数类似。用这个确省的入口函数就可以对DLL正确的初始化。典型的格式如下：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:Courier new,verdana;">BOOL APIENTRY DllMain<span style="color: #008000;">&#40;</span> HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 <span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">switch</span> <span style="color: #008000;">&#40;</span>ul_reason_for_call<span style="color: #008000;">&#41;</span>
	<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">case</span> DLL_PROCESS_ATTACH<span style="color: #008080;">:</span>
	<span style="color: #0000ff;">case</span> DLL_THREAD_ATTACH<span style="color: #008080;">:</span>
	<span style="color: #0000ff;">case</span> DLL_THREAD_DETACH<span style="color: #008080;">:</span>
	<span style="color: #0000ff;">case</span> DLL_PROCESS_DETACH<span style="color: #008080;">:</span>
		<span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">return</span> TRUE<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 对于进程线程的创建和释放时,需要进行的操作在case时调用就可以了。</p>
<p><strong>导出DLL中的Symbols</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; DLL是包含若干个函数的库文件，应用程序使用DLL中的函数之前，应该先导出这些函数，以便供给应用程序使用。要导出这些函数有两种方法，一是在定义函数时使用导出关键字__declspec(dllexport)，另外一种方法是在创建DLL文件时使用模块定义文件.Def。需要注意的是在使用第一种方法的时候，不能使用DEF文件。例子看附件。</p>
<ol>
<li>&#160;&#160;&#160;&#160;&#160; 使用__declspec(dllexport)来创建mydll.dll。该动态链接库中有两个函数，分别用来实现得到两个数的最大和最小数。在MyDll.h和MyDLL.cpp文件中分别输入如下原代码：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:Courier new,verdana;"><span style="color: #666666;">//MyDll.h</span>
<span style="color: #0000ff;">extern</span> <span style="color: #FF0000;">&quot;C&quot;</span> __declspec<span style="color: #008000;">&#40;</span>dllexport<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">int</span> Max<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> a,<span style="color: #0000ff;">int</span> b<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">extern</span> <span style="color: #FF0000;">&quot;C&quot;</span> __declspec<span style="color: #008000;">&#40;</span>dllexport<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">int</span> Min<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> a,<span style="color: #0000ff;">int</span> b<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #666666;">//MyDll.cpp</span>
<span style="color: #0000ff;">int</span> Max<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> a,<span style="color: #0000ff;">int</span> b<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">return</span> a<span style="color: #000080;">&gt;</span>b<span style="color: #008080;">?</span>a<span style="color: #008080;">:</span>b<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
<span style="color: #0000ff;">int</span> Min<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> a,<span style="color: #0000ff;">int</span> b<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">return</span> a<span style="color: #000080;">&gt;</span>b<span style="color: #008080;">?</span>b<span style="color: #008080;">:</span>a<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>  &#160;&#160;&#160;&#160;&#160; 该动态链接库编译成功后，打开MyDll工程中的debug目录，可以看到MyDll.dll、MyDll.lib两个文件。LIB文件中包含DLL文件名和DLL文件中的函数名等，该LIB文件只是对应该DLL文件的&quot;映像文件&quot;，与DLL文件中，LIB文件的长度要小的多，在进行隐式链接DLL时要用到它。读者可能已经注意到在MyDll.h中有关键字&quot;extern C&quot;，它可以使其他编程语言访问你编写的DLL中的函数。 </li>
<li>&#160;&#160;&#160;&#160; 用.def文件来创建DLL。        <br />&#160;&#160; 为了用.def文件创建DLL，请先删除上个例子创建的工程中的MyDll.h文件，保留MyDll.cpp并在该文件头删除#include MyDll.h语句，同时往该工程中加入一个文本文件，命名为MyDll.def，再在该文件中加入如下代码：         <br />LIBRARY MyDll         <br />EXPORTS         <br />Max         <br />Min         <br />其中LIBRARY语句说明该def文件是属于相应DLL的，EXPORTS语句下列出要导出的函数名称。我们可以在.def文件中的导出函数后加 @n，如Max@1，Min@2，表示要导出的函数顺序号，在进行显式连时可以用到它。该DLL编译成功后，打开工程中的Debug目录，同样也会看到 MyDll.dll和MyDll.lib文件。 </li>
</ol>
<p>&#160;</p>
<p><strong>动态链接库DLL的链接 </strong>      <br />&#160;&#160;&#160;&#160;&#160; 应用程序使用DLL可以采用两种方式：一种是隐式链接，另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息。Visual C++安装目录下提供了一个名为Dumpbin.exe的小程序，用它可以查看DLL文件中的函数结构。另外，Windows系统将遵循下面的搜索顺序来定位DLL： 1．包含EXE文件的目录，2．进程的当前工作目录， 3．Windows系统目录， 4．Windows目录，5．列在Path环境变量中的一系列目录。&#160; <br />1．隐式链接       <br />&#160;&#160;&#160;&#160;&#160; 隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中。实现隐式链接很容易，只要将导入函数关键字 __declspec(dllimport)函数名等写到应用程序相应的头文件中就可以了。下面的例子通过隐式链接调用MyDll.dll库中的Min函数。首先生成一个项目为TestDll，在DllTest.h、DllTest.cpp文件中分别输入如下代码：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:Courier new,verdana;"><span style="color: #666666;">//DllTest.h</span>
<span style="color: #666666;">//dll1.dll生成时用的def文件，不是用头文件生成的。</span>
<span style="color: #339900;">#pragma comment(lib,&quot;dll1.lib&quot;)  </span>
<span style="color: #666666;">//如果生成DLL时使用了头文件，下面的声明和头文件的要相对应。</span>
<span style="color: #666666;">//比如在最前面加 extern &quot;C&quot;</span>
__declspec<span style="color: #008000;">&#40;</span>dllimport<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">int</span> Max<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> a,<span style="color: #0000ff;">int</span> b<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
__declspec<span style="color: #008000;">&#40;</span>dllimport<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">int</span> Min<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> a,<span style="color: #0000ff;">int</span> b<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>  
<span style="color: #666666;">//DllTest.cpp</span>
<span style="color: #339900;">#include &lt;iostream&gt;</span>
<span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span> std<span style="color: #008080;">;</span>
&nbsp;
<span style="color: #339900;">#include &quot;testdll.h&quot;</span>
&nbsp;
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
&nbsp;
	<span style="color: #0000ff;">int</span> a,b<span style="color: #008080;">;</span>
	<span style="color: #0000ff;">while</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">cin</span><span style="color: #000080;">&gt;&gt;</span>a<span style="color: #000080;">&gt;&gt;</span>b<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
		<span style="color: #0000dd;">cout</span><span style="color: #000080;">&lt;</span> <span style="color: #000080;">&lt;</span><span style="color: #FF0000;">&quot;较大数:&quot;</span> <span style="color: #000080;">&lt;&lt;</span> Max<span style="color: #008000;">&#40;</span>a,b<span style="color: #008000;">&#41;</span><span style="color: #000080;">&lt;&lt;</span>endl<span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span>
	<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>    在创建DllTest.exe文件之前，要先将MyDll.dll和MyDll.lib拷贝到当前工程所在的目录下面，也可以拷贝到windows的 System目录下。如果DLL使用的是def文件，要删除TestDll.h文件中关键字extern &quot;C&quot;。TestDll.h文件中的关键字Progam commit是要Visual C+的编译器在link时，链接到MyDll.lib文件，当然，开发人员也可以不使用#pragma comment(lib,”dll1.lib”)语句，而直接在工程的Setting-&gt;Link页的Object/Moduls栏填入 MyDll.lib既可。 </p>
<p>2．显式链接&#160; <br />&#160;&#160;&#160;&#160;&#160; 显式链接是应用程序在执行过程中随时可以加载DLL文件，也可以随时卸载DLL文件，这是隐式链接所无法作到的，所以显式链接具有更好的灵活性，对于解释性语言更为合适。不过实现显式链接要麻烦一些。在应用程序中用LoadLibrary或MFC提供的AfxLoadLibrary显式的将自己所做的动态链接库调进来，动态链接库的文件名即是上述两个函数的参数，此后再用GetProcAddress()获取想要引入的函数。自此，你就可以象使用如同在应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前，应该用FreeLibrary或MFC提供的AfxFreeLibrary释放动态链接库。下面是通过显式链接调用DLL中的Max函数的例子。</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:Courier new,verdana;"><span style="color: #0000ff;">void</span> main<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">typedef</span> <span style="color: #0000ff;">int</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>pMax<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> a,<span style="color: #0000ff;">int</span> b<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">typedef</span> <span style="color: #0000ff;">int</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>pMin<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> a,<span style="color: #0000ff;">int</span> b<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	HINSTANCE hDLL<span style="color: #008080;">;</span>
	PMax Max<span style="color: #008080;">;</span>
	HDLL<span style="color: #000080;">=</span>LoadLibrary<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;MyDll.dll&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><span style="color: #666666;">//加载动态链接库MyDll.dll文件；</span>
	Max<span style="color: #000080;">=</span><span style="color: #008000;">&#40;</span>pMax<span style="color: #008000;">&#41;</span>GetProcAddress<span style="color: #008000;">&#40;</span>hDLL,<span style="color: #FF0000;">&quot;Max&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	A<span style="color: #000080;">=</span>Max<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">5</span>,<span style="color: #0000dd;">8</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	Printf<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;比较的结果为%d&quot;</span>,a<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	FreeLibrary<span style="color: #008000;">&#40;</span>hDLL<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><span style="color: #666666;">//卸载MyDll.dll文件；</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>&#160;&#160;&#160;&#160;&#160;&#160; 在上例中使用类型定义关键字typedef，定义指向和DLL中相同的函数原型指针，然后通过LoadLibray()将DLL加载到当前的应用程序中并返回当前DLL文件的句柄，然后通过GetProcAddress()函数获取导入到应用程序中的函数指针，函数调用完毕后，使用 FreeLibrary()卸载DLL文件。在编译程序之前，首先要将DLL文件拷贝到工程所在的目录或Windows系统目录下。       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; 使用显式链接应用程序编译时不需要使用相应的Lib文件。另外，使用GetProcAddress()函数时，可以利用 MAKEINTRESOURCE()函数直接使用DLL中函数出现的顺序号，如将GetProcAddress(hDLL,&quot;Min&quot;)改为 GetProcAddress(hDLL, MAKEINTRESOURCE(2))（函数Min()在DLL中的顺序号是2），这样调用DLL中的函数速度很快，但是要记住函数的使用序号，否则会发生错误。</iostream></p>
]]></content:encoded>
			<wfw:commentRss>http://www.boluor.com/dll-base.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

