当前位置:首页 » 编程软件 » 多个dll编译成一个

多个dll编译成一个

发布时间: 2022-07-23 15:12:44

㈠ vbnet编译后有很多的dll文件如何才能编译成只有一个exe文件

exe程序文件与dll文件是两个独立的文件,功能不同。exe是调用dll文件的代码的。想通过编译把n多dll文件与exe文件合为一个exe文件,目前没见过。可试试捆绑来实现。

㈡ 有没有将多个dll合并成为一个dll的工具或方法

在做.Net底层编码过程中,为了功能独立,有可能会生成多个DLL,引用时非常不便。这方面微软提供了一个ILMerge工具
原版DOS工具
,可以将多个DLL合并成一个。下载完成后需要安装一下,然后通过DOS命令进入。具体用法如下:D:\ProgramFiles\Microsoft\ILMergeilmerge/log:log
.txt/targetplatform:v4/out:merge
.dllAnalysisLibrary.dllUSBClassLibrary.dll
说明:此操作的目的是:将当前目录下的AnalysisLibrary.dll和USBClassLibrary.dll类库,按照.NetFramework 4.0形式(V4)合并成merge.dll。
其中:/log:log
.txt命令是将生成过程中的日志,写入到log.txt文件中。
.dll:是输出文件。

㈢ 如何将多个类制作成一个dll,但只导出其中一个类

自从Windows的开始阶段动态链接库(DLL)就是Windows平台的一个组成部分。动态链接库允许在一个独立的模块中封装一系列的功能函数然后以一个显式的C函数列表提供外部使用者使用。在上个世纪80年代,当Windows DLLs面世时,对于广大开发者而言只有C语言是切实可行的开发手段。所以, Windows DLLs很自然地以C函数和数据的形式向外部暴露功能。从本质来说,一个DLL可以由任何语言实现,但是为了使DLL用于其它的语言和环境之下,一个DLL接口必须后退到最低要求的母体——C语言。使用C接口并不自动意味一个开发者应该应该放弃面向对象的开发方式。甚至C接口也能用于真正的面向对象编程,尽管它有可能被认为是一种单调乏味的实现方式。很显然世界上使用人数排第二的编程语言是C++,但它却不得不被DLL所诱惑。然而,和C语言相反,在调用者和被调用者之间的二进制接口被很好的定义并被广泛接受,但是在C++的世界里却没有可识别的应用程序二进制接口。实际上,由一个C++编译器产生的二进制代码并不能被其它C++编译器兼容。再者,在同一个编译器但不同版本的二进制代码也是互不兼容的。所有这些导致从一个DLL中一个C++类简直就是一个冒险。这篇文章就是演示几种从一个DLL模块中导出C++类的方法。源码演示了导出虚构的Xyz对象的不同技巧。Xyz对象非常简单,只有一个函数:Foo。下面是Xyz对象的图解:Xyzint Foo(int)Xyz对象在一个DLL里实现,这个DLL能作为一个分布式系统供范围很广的客户端使用。一个用户能以下面三种方式调用Xyz的功能:使用一个规则的C++类使用一个抽象的C++接口源码(译注:文章附带的源码)包含两个工程:XyzLibrary– 一个DLL工程XyzExecutable– 一个Win32 使用"XyzLibrary.dll"的控制台程序XyzLibrary工程使用下列方便的宏导出它的代码:#if defined(XYZLIBRARY_EXPORT) // inside DLL# define XYZAPI __declspec(dllexport)#else // outside DLL# define XYZAPI __declspec(dllimport)#endif // XYZLIBRARY_EXPORTXYZLIBRARY_EXPORT标识符仅仅在XyzLibrary工程定义,因此在XYZAPI宏在DLL生成时被扩展为__declspec(dllexport)而在客户程序生成时被扩展为__declspec(dllimport)。C语言方式 经典的C语言方式进行面向对象编程的一种方式就是使用晦涩的指针,比如句柄。一个用户能够使用一个函数创建一个对象。实际上这个函数返回的是这个对象的一个句柄。接着用户能够调用这个对象相关的各种操作函数只要这个函数能够接受这个句柄作为它的一个参数。一个很好的例子就是在Win32窗口相关的API中句柄的习惯是使用一个HWND句柄来代表一个窗口。虚构的Xyz对象通过下面这样一种方式导出一个C接口:typedef tagXYZHANDLE {} * XYZHANDLE;// 创建一个Xyz对象实例的函数XYZAPI XYZHANDLE APIENTRY GetXyz(VOID);// 调用Xyz.Foo函数XYZAPI INT APIENTRY XyzFoo(XYZHANDLE handle, INT n);// 释放Xyz实例和占用的资源XYZAPI VOID APIENTRY XyzRelease(XYZHANDLE handle);// APIENTRY is defined as __stdcall in WinDef.h header.下面是一个客户端调用的C代码:#include "XyzLibrary.h"/* 创建Xyz实例*/XYZHANDLE hXyz = GetXyz();if(hXyz) /* 调用 Xyz.Foo函数*/ XyzFoo(hXyz, 42); /*析构 Xyz实例并释放已取得的资源. */ XyzRelease(hXyz); hXyz = NULL;使用这种方式,一个DLL必须提供显式的对象构建和删除函数。 对于所有的导出函数记住它们调用协定是重要的。对于很多初学者来说忘记添加调用协定是非常普遍的错误。只要客户端的调用协定和DLL的调用协定匹配,一切都能运行。但是,一旦客户端改变了它的调用协定,开发者将会产生一个难以察觉的直到运行时才发生的错误。XyzLibrary工程使用一个APIENTRY宏,这个宏在"WinDef.h"这个头文件里被定义为__stdcall。异常安全性 在DLL范围内不允许发生C++异常。在一段时间内,C语言不识别C++的异常,并且不能正确处理它们。假如一个对象的方法需要报告一个错误,这时一个返回码需要用到。l 一个DLL能被最广泛的合适的开发者所使用。几乎每一种现代编程语言都支持纯C函数的互用性。l 一个DLL的C运行时库和它的客户端是互相独立的。因为资源的获取和释放完全发生在DLL模块的内部,所以一个客户端不受一个DLL的C运行时库选择的影响。l 获取正确对象的合适的方法的责任落在DLL的使用者的肩上。比如在下面的代码片断,编译器不能捕捉到其中发生的错误:l 获取正确对象的合适的方法的责任落在DLL的使用者的肩上。比如在下面的代码片断,编译器不能捕捉到其中发生的错误:/* void* GetSomeOtherObject(void)是别的地方定义的一个函数 */XYZHANDLE h = GetSomeOtherObject();/* 啊! 错误: 在错误的对象实例上调用Xyz.Foo函数*/XyzFoo(h, 42); l 显式要求创建和摧毁一个对象的实例。其中特别烦人的是对象实例的删除。客户端必须极仔细地在一个函数的退出点调用XyzRelease函数。假如开发者忘记调用XyzRelease函数,那时资源就会泄露,因为编译器不能跟踪一个对象实例的生命周期。那些支持析构函数或垃圾收集器的语言通过在C接口上作一层封装有助于降低这个问题发生的概率。l 假如一个对象的函数返回或接受其它对象作为参数,那时DLL作者也就不得不为这些对象提供一个正确的C接口。假如退回到最大限度的复用,也就是C语言,那么只有以字节创建的类型(如int, double, char*等等)可以作为返回类型和函数参数 C++天然的方式:导出一个类在Windows平台上几乎每一个现代的编译器都支持从一个DLL中导出一个类。导出一个类和导出一个C函数非常相似。用那种方法一个开发者被要求做就是在类名之前使用__declspec(dllexport/dllimport)关键字来指定假如整个类都需要被导出,或者在指定的函数声明前指定假如只是特定的类函数需要被导出。这儿有一个代码片断:// 整个CXyz类被导出,包括它的函数和成员class XYZAPI CXyzpublic: int Foo(int n);// 只有 CXyz::Foo函数被导出class CXyzpublic: XYZAPI int Foo(int n); 在导出整个类或者它们的方法没有必要显式指定一个调用协定。根据预设,C++编译器使用__thiscall作为类成员函数的调用协定。然而,由于不同的编译器具有不同的命名修饰法则,导出的C++类只能用于同一类型的同一版本的编译器。这儿有一个MS Visual C++编译器的命名修饰法则的应用实例: 注意这里修饰名是怎样不同于C++原来的名字。下面是屏幕截图显示的是通过使用Dependency Walker 工具对同一个DLL的修饰名进行破译得到的: 只有MS Visual C++编译器能使用这个DLL.DLL和客户端代码只有在同一版本的MS Visual C++编译器才能确保在调用者和被调用者修饰名匹配。这儿有一个客户端代码使用Xyz对象的例子:#include "XyzLibrary.h"// 客户端使用Xyz对象作为一个规则C++类.CXyz xyz;xyz.Foo(42);正如你所看到的,导出的C++类的用法和其它任何C++类的用法几乎是一样的。没什么特别的。重要事项:使用一个导出C++类的DLL和使用一个静态库没有什么不同。所有应用于有C++代码编译出来的静态库的规则完全适用于导出C++类的DLL。所见即所得一个细心的读者必然已经注意到Dependency Walker工具显示了额外的导出成员,那就是CXyz& CXyz::operator =(const CXyz&)赋值操作符。在工作你所看到的正是C++的收入(译注:我估计这是原文作者幽默的说法,意思是你没有定义一个=赋值操作符,而编译器帮你自动定义一个,不是收入是什么?)。根据C++标准,每一个类有四个指定的成员函数:默认构造函数拷贝构造函数赋值操作符 (operator =)假如类的作者没有声明同时没有提供这些成员的实现,那么C++编译器会声明它们,并产生一个隐式的默认的实现。在CXyz类,编译器断定它的默认构造函数,拷贝构造函数和析构函数都毫无意义,经过优化后把它们排除掉了。而赋值运算符在优化之后还存活并从DLL中导出。重要事项:使用__declspec(dllexport)来指定类导出来告诉编译器来尝试导出任何和类相关的东西。它包括所有类的数据成员,所有类的成员函数(或者显式声明,或者由编译器隐式生成),所有类的基类和所有它们的成员。考虑:class Baseclass Data// MS Visual C++ 中国piler 会发出C4275 warning ,因为没有导出基类class __declspec(dllexport) Derived : public Baseprivate: Data m_data; // C4251 warning,因为没有导出数据成员. 在上面的代码片断,编译器会警告你没有导出基类和类的数据成员。所以,为了成功导出一个类,一个开发者被要求导出所有相关基类和所有类的已定义的数据成员。这个滚雪球般的导出要求是一个重大缺点。这也是为什么,比如,导出派生自STL模板类或者使用STL模板类对象作为数据成员是非常困难和令人生厌的。比如一个STL容器比如std::map实例可能要求导出数十个额外的内部类。异常安全性一个导出的C++类可能会在没有任何错误发生的情况下抛出异常。因为一个DLL和它的客户端使用同一版本的同一类型的编译器的事实,C++异常将在超出DLL的范围进行捕捉和抛出好像DLL没有分界线一样。记住,使用一个带有导出C++代码和使用带有相同代码的静态库是完全一样的。优点l 一个导出的C++类和其它任何C++类的用法是一样的l 客户端能毫不费力地捕捉在DLL发生的异常l 当在一个DLL模块内有一些小的代码改动时,其它模块也不用重新生成。这对于有着许多复杂难懂代码的大工程是非常有用的。l 在一个大工程中按照业务逻辑分成不同的DLL实现可以被认为真正的模块划分的第一步。总的来说,它是使工程达到模块化值得去做的事缺点l 从一个DLL中导出C++类在它的对象和使用者需要保持紧密的联系。DLL应该被视作一个带有考虑到代码依赖的静态库。l 客户端代码和DLL都必须和同一版本的CRT(译注:C运行时库)动态连接在一起。为了能够在模块之间修正CRT资源的纪录,这一步是必需的。假如一个客户端和DLL连接到不同版本的CRT,或者静态连接到CRT,那么在一个CRT实例申请的资源有可能在另一个CRT实例中释放。它将损坏CRT实例的内在状态并企图操作外部资源,并很可能导致运行失败。l 客户端代码和DLL必须在异常处理和产生达成一致,同时在编译器的异常设置也必须一致l 导出C++类要求同时导出这个类的所有相关的东西,包括:所有它的基类、所有类定义的用到的数据成员等等。C++成熟的方法:使用抽象接口一个C++抽象接口(比如一个拥有纯虚函数和没有数据成员的C++类)设法做到两全其美:对对象而言独立于编译器的规则的接口以及方便的面向对象方式的函数调用。为达到这些要求去做的就是提供一个接口声明的头文件,同时实现一个能返回最新创建的对象实例的工厂函数。只有这个工厂函数需要使用__declspec(dllexport/dllimport)指定。接口不需要任何额外的指定。// Xyz object的抽象接口// 不要求作额外的指定struct IXyz virtual int Foo(int n) = 0; virtual void Release() = 0;// 创建Xyz对象实例的工厂函数extern "C" XYZAPI IXyz* APIENTRY GetXyz();在上面的代码片断中,工厂函数GetXyz被声明为extern XYZAPI。这样做是为了防止函数名被修饰(译注:如上面提到的导出一个C++类,其成员函数名导出后会被修饰)。这样,这个函数在外部表现为一个规则的C函数,并且很容易被和C兼容的编译器所识别。这就是当使用一个抽象接口时客户端代码看起来和下面一样:#include "XyzLibrary.h"IXyz* pXyz = ::GetXyz();if(pXyz) pXyz->Foo(42); pXyz->Release(); pXyz = NULL;C++不用为接口提供一个特定的标记以便其它编程语言使用(比如C#或Java)。但这并不意味C++不能声明和实现接口。设计一个C++的接口的一般方法是去声明一个没有任何数据成员的抽象类。这样,派生类可以继承这个接口并实现这个接口,但这个实现对客户端是不可见的。接口的客户端不用知道和关注接口是如何实现的。它只需知道函数是可用的和它们做什么。内部机制在这种方法背后的思想是非常简单的。一个由纯虚函数组成的成员很少的类只不过是一个虚函数表——一个函数指针数组。在DLL范围内这个函数指针数组被它的作者填充任何他认为必需的东西。这样这个指针数组在DLL外部使用就是调用接口的实际上的实现。下面是IXyz接口的用法说明图表。 上面的图表演示了IXyz接口被DLL和EXE模块二者都用到。在DLL模块内部,XyzImpl类派生自IXyz接口并实现它的方法。在EXE的函数调用引用DLL模块经过一个虚表的实际实现。这种DLL为什么能和其它的编译器一起运行简短的解释是:因为COM技术和其它的编译器一起运行。现在作一个详细解释,实际上,在模块之间使用一个成员很少的虚基类作为接口准确来说是COM对外暴露了一个COM接口。如我们所知的虚表的概念,能很精确地添加COM标准的标记。这不是一个巧合。C++语言,作为一个至少跨越了十年的主流开发语言,已经广泛地应用在COM编程。因为C++天生地支持面向对象的特性。微软将它作为产业COM开发的重量级的工具是毫不奇怪的。作为COM技术的所有者,微软已经确保COM的二进制标准和它们拥有的在Visual C++编译器实现的C++对象模型能以最小的成本实现匹配。难怪其它的编译器厂商都和微软采用相同的方式实现虚表的布局。毕竟,每个人都想支持COM技术,并做到和微软已存在的解决方法兼容。假设某个C++编译器不能有效支持COM,那么它注定会被Windows市场所抛弃。这就是为什么时至今日,通过一个抽象接口从一个DLL导出一个C++类能和Windows平台上过得去的编译器能可靠地运行在一起。使用一个智能指针为了确保正确的资源释放,一个虚接口提供了一个额外的函数来清除对象实例。手动调用这个函数令人厌烦并容易导致错误发生。我们都知道这个错误在C世界里这是一个很普遍的错误,因为在那儿开发者不得不记得释放显式函数调用获取的资源。这就是为什么典型的C++代码借助于智能指针使用RAII(资源获取即初始化)的习惯。XyzExecutable工程提供了一个例子,使用了AutoClosePtr模板。AutoClosePtr模板是一个最简单的智能指针,这个智能指针调用了一个类消灭一个实例的主观方法来代替delete操作符。这儿有一段演示带有IXyz接口的一个智能指针的用法的代码片断:#include "XyzLibrary.h"#include "AutoClosePtr.h"typedef AutoClosePtr IXyzPtr;IXyzPtr ptrXyz(::GetXyz());if(ptrXyz) ptrXyz->Foo(42);// 不需要调用ptrXyz->Release(). 智能指针将在析构函数里自动调用这个函数不管怎样,使用智能指针将确保Xyz对象能正当地适当资源。因为一个错误或者一个内部异常的发生,函数会过早地退出,但是C++语言保证所有局部对象的析构函数能在函数退出之前被调用。异常安全性和COM接口一样不再允许因为任何内部异常的发生而导致资源泄露,抽象类接口不会让任何内部异常突破DLL范围。函数调用将会使用一个返回码来明确指示发生的错误。对于特定的编译器,C++异常的处理都是特定的,不能够分享。所以,在这个意义上,一个抽象类接口表现得十足像一个C函数。优点:l 一个导出的C++类能够通过一个抽象接口,被用于任何C++编译器l 一个DLL的C运行库和DLL的客户端是互相独立的。因为资源的初始化和释放都完全发生在DLL内部,所以客户端不受DLL的C运行库选择的影响。l 真正的模块分离能高度完美实现。结果模块可以重新设计和重新生成而不受工程的剩余模块的影响。l 如果需要,一个DLL模块能很方便地转化为真正的COM模块。缺点:l 一个显式的函数调用需要创建一个新的对象实例并删除它。尽管一个智能指针能免去开发者之后的调用l 一个抽象接口函数不能返回或者接受一个规则的C++对象作为一个参数。它只能以内置类型(如int、double、char*等)或者另一个虚接口作为参数类型。它和COM接口有着相同的限制。STL模板类是怎样做的C++标准模板库的容器(如vector,list或map)和其它模板并没有设计为DLL模块(以抽象类接口方式)。有关DLL的C++标准是没有的因为DLL是一种平台特定技术。C++标准不需要出现在没有用到C++语言的其它平台上。当前,微软的Visual C++编译器能够导出和导入开发者显式以__declspec(dllexport/dllimport)关键字标识的STL类实例。编译器会发出几个令人讨厌的警告,但是还能运行。然而,你必须记住,导出STL模板实例和导出规则C++类是完全一样的,有着一样的限制。所以,在那方面STL是没什么特别的。总结这篇文章讨论了几种从一个DLL模块中导出一个C++对象的不同方法。对每种方法的优点和缺点的详细论述也已给出。下面是得出的几个结论:l 以一个完全的C函数导出一个对象有着最广泛的开发环境和开发语言的兼容性。然而,为了使用现代编程范式一个DLL使用者被要求使用过时的C技巧对C接口作一层额外的封装。l 导出一个规则的C++类和以C++代码提供一个单独的静态库没什么区别。用法非常简单和熟悉,然而DLL和客户端有着非常紧密的连接。DLL和它的客户端必须使用相同版本和相同类型的编译器。l 定义一个无数据成员的抽象类并在DLL内部实现是导出C++对象的最好方法。到目前为止,这种方法在DLL和它的客户端提供了一个清晰的,明确界定的面向对象接口。这样一种DLL能在Windows平台上被任何现代C++编译器所使用。接口和智能指针一起结合使用的用法几乎和一个导出的C++类的用法一样方便

㈣ c#如何合并多个dll文件

ILMerge可以满足合并dll的需求:
下载连接 http://download.microsoft.com/download/1/3/4/1347C99E-9DFB-4252-8F6D-A3129A069F79/ILMerge.msi

安装完成以后,可以将安装目录加入PATH下,
在命令行用如下命令进行merge

ILMerge /ndebug /target:dll /out:D:\output\KCService.dll /log D:\project\xxx-1.dll D:\project\xxx-2.dll
参数说明:
(1) "/out:D:\output\KCService.dll" 为合并后的DLL目录及最终的DLL文件
(2) “D:\project\xxx-1.dll D:\project\xxx-2.dll” 为要合并的源头DLL文件

如果要发布exe文件则可以将dll文件打入exe文件中
ILMerge /ndebug /target:winexe /out:D:\output\runKCService.exe /log D:\project\kcservice.exe D:\project\xxx-1.dll D:\project\xxx-2.dll

㈤ vs2013怎么把多个dll文件打包成一个dll,使用的时候不再引用其他dll

项目--属性--配置属性--常规
MFC的使用,修改为:在静态库中使用MFC
就会把用到的DLL编译器来。

㈥ 如何编译一个 dll文件

创建DLL工程
这里,我们为了简要说明DLL的原理,我们决定使用最简单的编译环境VC6.0,如下图,我们先建立一个新的Win32 Dynamic-Link Library工程,名称为“MyDLL”,在Visual Studio中,你也可以通过建立Win32控制台程序,然后在“应用程序类型”中选择“DLL”选项,

点击确定,选择“一个空的DLL工程”,确定,完成即可。

一个简单的dll
在第一步我们建立的工程中建立一个源码文件”dllmain.cpp“,在“dllmain.cpp”中,键入如下代码

[cpp] view plain
#include <Windows.h>
#include <stdio.h>

BOOL APIENTRY DllMain(HMODULE hMole, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
printf("DLL_PROCESS_ATTACH\n");
break;
case DLL_THREAD_ATTACH:
printf("DLL_THREAD_ATTACH\n");
break;
case DLL_THREAD_DETACH:
printf("DLL_THREAD_DETACH\n");
break;
case DLL_PROCESS_DETACH:
printf("DLL_PROCESS_DETACH\n");
break;
}
return TRUE;
}
之后,我们直接编译,即可以在Debug文件夹下,找到我们生成的dll文件,“MyDLL.dll”,注意,代码里面的printf语句,并不是必须的,只是我们用于测试程序时使用。而DllMain函数,是dll的进入/退出函数。

实际上,让线程调用DLL的方式有两种,分别是隐式链接和显式链接,其目的均是将DLL的文件映像映射进线程的进程的地址空间。我们这里只大概提一下,不做深入研究,如果感兴趣,可以去看《Window高级编程指南》的第12章内容。
隐式链接调用
隐士地链接是将DLL的文件影响映射到进程的地址空间中最常用的方法。当链接一个应用程序时,必须制定要链接的一组LIB文件。每个LIB文件中包含了DLL文件允许应用程序(或另一个DLL)调用的函数的列表。当链接器看到应用程序调用了某个DLL的LIB文件中给出的函数时,它就在生成的EXE文件映像中加入了信息,指出了包含函数的DLL文件的名称。当操作系统加载EXE文件时,系统查看EXE文件映像的内容来看要装入哪些DLL,而后试图将需要的DLL文件映像映射到进程的地址空间中。当寻找DLL时,系统在系列位置查找文件映像。

1.包含EXE映像文件的目录
2.进程的当前目录
3.Windows系统的目录
4.Windows目录
5.列在PATH环境变量中的目录

这种方法,一般都是在程序链接时控制,反映在链接器的配置上,网上大多数讲的各种库的配置,比如OPENGL或者OPENCV等,都是用的这种方法

显式链接调用
这里我们只提到两种函数,一种是加载函数
[cpp] view plain
HINSTANCE LoadLibrary(LPCTSTR lpszLibFile);

HINSTANCE LoadLibraryEx(LPCSTR lpszLibFile,HANDLE hFile,DWORD dwFlags);
返回值HINSTANCE值指出了文件映像映射的虚拟内存地址。如果DLL不能被映进程的地址空间,函数就返回NULL。你可以使用类似于

[cpp] view plain
LoadLibrary("MyDLL")
或者

[cpp] view plain
LoadLibrary("MyDLL.dll")
的方式进行调用,不带后缀和带后缀在搜索策略上有区别,这里不再详解。

显式释放DLL

在显式加载DLL后,在任意时刻可以调用FreeLibrary函数来显式地从进程的地址空间中解除该文件的映像。
[cpp] view plain
BOOL FreeLibrary(HINSTANCE hinstDll);
这里,在同一个进程中调用同一个DLL时,实际上还牵涉到一个计数的问题。这里也不在详解。
线程可以调用GetMoleHandle函数:

[cpp] view plain
GetMoleHandle(LPCTSTR lpszMoleName);
来判断一个DLL是否被映射进进程的地址空间。例如,下面的代码判断MyDLL.dll是否已被映射到进程的地址空间,如果没有,则装入它:

[cpp] view plain
HINSTANCE hinstDll;
hinstDll = GetMoleHandle("MyDLL");
if (hinstDll == NULL){
hinstDll = LoadLibrary("MyDLL");
}
实际上,还有一些函数,比如 GetMoleFileName用来获取DLL的全路径名称,FreeLibraryAndExitThread来减少DLL的使用计数并退出线程。具体内容还是参见《Window高级编程指南》的第12章内容,此文中不适合讲太多的内容以至于读者不能一下子接受。

DLL的进入与退出函数

说到这里,实际上只是讲了几个常用的函数,这一个小节才是重点。
在上面,我们看到的MyDLL的例子中,有一个DllMain函数,这就是所谓的进入/退出函数。系统在不同的时候调用此函数。这些调用主要提供信息,常常被DLL用来执行进程级或线程级的初始化和清理工作。如果你的DLL不需要这些通知,就不必再你的DLL源代码中实现此函数,例如,如果你创建的DLL只含有资源,就不必实现该函数。但如果有,则必须像我们上面的格式。
DllMain函数中的ul_reason_for_call参数指出了为什么调用该函数。该参数有4个可能值: DLL_PROCESS_ATTACH、DLL_THREAD_ATTACH、DLL_THREAD_DETACH、DLL_PROCESS_DETACH。
其中,DLL_PROCESS_ATTACH是在一个DLL首次被映射到进程的地址空间时,系统调用它的DllMain函数,传递的ul_reason_for_call参数为DLL_PROCESS_ATTACH。这只有在首次映射时发生。如果一个线程后来为已经映射进来的DLL调用LoadLibrary或LoadLibraryEx,操作系统只会增加DLL的计数,它不会再用DLL_PROCESS_ATTACH调用DLL的DllMain函数。
而DLL_PROCESS_DETACH是在DLL被从进程的地址空间解除映射时,系统调用它的DllMain函数,传递的ul_reason_for_call值为DLL_PROCESS_DETACH。我们需要注意的是,当用DLL_PROCESS_ATTACH调用DLL的DllMain函数时,如果返回FALSE,说明初始化不成功,系统仍会用DLL_PROCESS_DETACH调用DLL的DllMain。因此,必须确保没有清理那些没有成功初始化的东西。
DLL_THREAD_ATTACH:当进程中创建一个线程时,系统察看当前映射到进程的地址空间中的所有DLL文件映像,并用值DLL_THREAD_ATTACH调用所有的这些DLL的DllMain函数。该通知告诉所有的DLL去执行线程级的初始化。注意,当映射一个新的DLL时,进程中已有的几个线程在运行,系统不会为已经运行的线程用值DLL_THREAD_ATTACH调用DLL的DllMain函数。
而DLL_THREAD_DETACH,如果线程调用ExitThread来终结(如果让线程函数返回而不是调用ExitThread,系统会自动调用ExitThread),系统察看当前映射到进程空间的所有DLL文件映像,并用值DLL_THREAD_DETACH来调用所有的DLL的DllMain函数。该通知告诉所有的DLL去执行线程级的清理工作。
这里,我们需要注意的是,如果线程的终结是因为系统中的一个线程调用了TerminateThread,系统就不会再使用DLL_THREAD_DETACH来调用DLL和DllMain函数。这与TerminateProcess一样,不再万不得已时,不要使用。
下面,我们贴出《Window高级编程指南》中的两个图来说明上述四种参数的调用情况。

好的,介绍了以上的情况,下面,我们来继续实践,这次,建立一个新的空的win32控制台工程TestDLL,不再多说,代码如下:

[cpp] view plain
#include <iostream>
#include <Windows.h>
using namespace std;

DWORD WINAPI someFunction(LPVOID lpParam)
{
cout << "enter someFunction!" << endl;
Sleep(1000);
cout << "This is someFunction!" << endl;
Sleep(1000);
cout << "exit someFunction!" << endl;
return 0;
}

int main()
{
HINSTANCE hinstance = LoadLibrary("MyDLL");
if(hinstance!=NULL)
{
cout << "Load successfully!" << endl;
}else {
cout << "Load failed" << endl;
}
HANDLE hThread;
DWORD dwThreadId;

cout << "createThread before " << endl;
hThread = CreateThread(NULL,0,someFunction,NULL,0,&dwThreadId);
cout << "createThread after " << endl;
cout << endl;

Sleep(3000);

cout << "waitForSingleObject before " << endl;
WaitForSingleObject(hThread,INFINITE);
cout << "WaitForSingleObject after " << endl;
cout << endl;

FreeLibrary(hinstance);
return 0;
}

代码很好理解,但是前提是,你必须对线程有一定的概念。另外,注意,我们上面编译的获得的“MyDLL.dll"必须拷贝到能够让我们这个工程找到的地方,也就是上面我们提到的搜索路径中的一个地方。
这里,我们先贴结果,当然,这只是在我机器上其中某次运行结果。

有了上面我们介绍的知识,这个就不是很难理解,主进程在调用LoadLibrary时,用DLL_PROCESS_ATTACH调用了DllMain函数,而线程创建时,用DLL_THREAD_ATTACH调用了DllMain函数,而由于主线程和子线程并行的原因,可能输出的时候会有打断。但是,这样反而能让我们更清楚的理解程序。

㈦ vc中如何将dll件和exe编译成一个exe文件

在项目属性中设置就可以,以VS2005为例,如下图选择第二项,就会把MFC的DLL编译到EXE文件中,文件长度增加大约400多K

㈧ 一个应用程序里有很多DLL是怎么编译出来的

DLL就是功能独立出来的软件包,主要就是为了实现程序功能模块的分割,一个DLL可以同时在系统中被多个EXE进程映射,而时间上在内存中的拷贝只有一份。
DLL和EXE一样都是PE文件。

比如你的控制台程序(EXE的)要引用第三方的软件包,你就 可以将他们提供的lib文件加入到你的程序中,使用“#pragram comment(lib,"xxx.lib") ”——如果对方提供有.h文件,也要将其在包含在cpp的前面,或者延迟加载“LoadLibrary / GetProcAddress / FreeLibray ”,

自己也可以做个dll,导出函数、类、变量,等

任何一个程序都默认包含了系统的几个dll库文件。

㈨ 如何将两个(多个)DLL合并成一个DLL

使用ILMerge工具,在DOS下用命令行合并

ILMerge工具和合并方法详见:http://download.csdn.net/detail/h_h_h_123/5883277

热点内容
安卓如何设置桌面返回键 发布:2025-02-06 13:58:15 浏览:48
bi可视化php 发布:2025-02-06 13:50:15 浏览:931
shell写脚本文件 发布:2025-02-06 13:47:32 浏览:231
健身器材脚本 发布:2025-02-06 13:46:36 浏览:856
怎么从手机里卸载存储卡 发布:2025-02-06 13:35:04 浏览:644
诛仙青云志2ftp 发布:2025-02-06 13:34:48 浏览:34
mill91编程 发布:2025-02-06 13:10:27 浏览:294
华为平板怎么储存服务器文件 发布:2025-02-06 12:49:21 浏览:482
php查询结果数组 发布:2025-02-06 12:31:05 浏览:717
怎样把照片压缩打包 发布:2025-02-06 12:15:19 浏览:498