内容列表
lib的创建和使用最简单的DLL 显式加载DLL 用def文件定义输出函数 显式调用DLL中的函数 使用__declspec(dllexport)定义DLL输出的函数使用extern "c"隐式创建和使用DLL__declspec(dllexport)和__declspec(dllimport)配对使用 DLL中导出全局变量和对象 VC中编写和调用DLL
参考
DLL编写教程
http://www.blogjava.net/wxb_nudt/archive/2007/09/11/144371.html
比较及创建例子:静态链接库和动态链接库
http://www.cnblogs.com/Winston/archive/2008/07/05/1236273.html
--------------------------------------------------------
LIB相关
lib的创建 建立Win32 Project , 选择lib,支持MFC;之后直接在工程中添加类;编译,生成lib提供给用户头文件和.lib文件注:lib文件输出路径设置Linker-Advanced-Import Library下,例如: ..\lib2007\$(TargetName).liblib的使用 包含头文件在工程设置中添加.lib文件,使用类时添加头文件即可。或者在要使用类的cpp文件头部加入下述语句: #pragma comment(lib,"**.lib")-------------------------------------------------------
最简单的DLL
程序#include <objbase.h> #include <iostream.h> BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) { HANDLE g_hModule; switch(dwReason) { case DLL_PROCESS_ATTACH: cout<<"Dll is attached!"<<endl; g_hModule = (HINSTANCE)hModule; break; case DLL_PROCESS_DETACH: cout<<"Dll is detached!"<<endl; g_hModule=NULL; break; } return true; }参数说明 hModule:表示本dll的实例句柄dwReason:dll当前所处的状态DLL_PROCESS_ATTACH表示dll刚刚被加载到一个进程中DLL_PROCESS_DETACH表示dll刚刚从一个进程中卸载-------------------------------------------------------
用LoadLibrary显式加载DLL
程序#include <windows.h> #include <iostream.h> int main(void) { //加载我们的dll HINSTANCE hinst=::LoadLibrary("dll_nolib.dll"); if (NULL != hinst) { cout<<"dll loaded!"<<endl; } return 0; }DLL的位置 将DLL放到sln项目所在的当前目录,可以加载;问题 如何放到其他目录,然后通过包含目录的方式加载?解决:在linker-General选项卡下有lib目录的设置-------------------------------------------------------
用LoadLibraryEx显式加载DLL
原型HMODULE WINAPI LoadLibraryEx(
_In_ LPCTSTR lpFileName,
_Reserved_ HANDLE hFile,
_In_ DWORD dwFlags
);
例子HMODULE m_hModule;
m_hModule = NULL;
CString strPath = teamworkEnv();
strPath += _T("\\Bin\\Win32\\CooBaseInterface.dll");
m_hModule = LoadLibraryEx( strPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
-------------------------------------------------------
使用Def文件定义输出函数
Def文件格式 首先是LIBRARY关键字,指定dll的名字然后一个可选的关键字DESCRIPTION,后面写上版权等信息(不写也可以);最后是EXPORTS关键字,后面写上dll中所有要输出的函数名或变量名,然后接上@以及依次编号的数字(从1到N),最后接上修饰符 eg:FuncInDll @1 PRIVATE注意@前的空格!-------------------------------------------------------
显式调用DLL里的函数
定义函数指针 typedef void (* DLLWITHLIB )(void); 定义一个函数指针变量 DLLWITHLIB pfFuncInDll = NULL; 加载DLL HINSTANCE hinst=::LoadLibrary("dll_def.dll"); 之后判断hinst是否为NULL使用GetPorcAddress找到dll中导出的函数,赋值给函数指针变量 pfFuncInDll = (DLLWITHLIB)GetProcAddress(hinst, "FuncInDll"); 之后判断pfFuncInDll是否为空注意函数 GetPorcAddress 用途:这个API是用来查找dll中的函数地址的第一个参数是DLL的句柄,即LoadLibrary返回的句柄第二个参数是dll中的函数名称,即dumpbin中输出的函数名(注意,这里的函数名称指的是编译后的函数名,不一定等于dll源代码中的函数名)。调用dll里的函数 (*pfFuncInDll)();-------------------------------------------------------
使用__declspec(dllexport)定义dll的输出函数
方法: 去掉def文件,并在每个要输出的函数前面加上声明__declspec(dllexport)容易产生的问题 编译后的函数名为?FuncInDll@@YAXXZ,而并不是FuncInDll;这是因为c++编译器基于函数重载的考虑,会更改函数名解决方法 使用extern“C”指令来命令c++编译器以c编译器的方式来命名该函数修改后的函数声明:extern "C" __declspec(dllexport) void FuncInDll (void)-------------------------------------------------------
隐式调用DLL
DLL的创建 .h文件:包含函数声明.cpp文件:包含DllMain函数及函数实现例子:代码如下: dll_withlibAndH.h extern "C" __declspec(dllexport) void FuncInDll (void); dll_withlibAndH.cpp #include <objbase.h> #include <iostream.h> #include "dll_withLibAndH.h"//看到没有,这就是我们增加的头文件 extern "C" __declspec(dllexport) void FuncInDll (void) { cout<<"FuncInDll is called!"<<endl; } BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) { HANDLE g_hModule; switch(dwReason) { case DLL_PROCESS_ATTACH: g_hModule = (HINSTANCE)hModule; break; case DLL_PROCESS_DETACH: g_hModule=NULL; break; } return TRUE; }DLL的使用 步骤 包含头文件 dll_withLibAndH.h .h的路径可以通过C++-常规选项卡下设置加载lib #pragma comment(lib,"dll_withLibAndH.lib")也可以通过linker选项卡设置lib的路径可以通过linker-常规选项卡下设置将DLL文件拷贝到当前项目目录下 如果不拷贝DLL文件,运行时会报错!第三步也是使用DLL方式和使用LIB方式操作上的主要区别!两种方式前两步的设置是一样的!客户端代码:#include "dll_withLibAndH.h" //注意路径,加载 dll的另一种方法是 Project | setting | link 设置里 #pragma comment(lib,"dll_withLibAndH.lib") int main(void) { FuncInDll();//只要这样我们就可以调用dll里的函数了 return 0; }-----------------------------------------------------------
问题:
(显式调用) 如何将DLL放到其他位置?然后通过设置路径调用?使用LoadLibraryEx, 第一个参数传入DLL路径;
例如:
m_hModule = LoadLibraryEx( strPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
说明:DLL生成后直接放置在Debug下,同一个Solution的project之间的引用不用设置也不会出问题;不同的Solution需要将DLL拷贝至使用者的Debug下
-------------------------------------------------------
配对使用__declspec(dllexport)和__declspec(dllimport)
这时要考虑一个情况:若DLL1.CPP是源,DLL2.CPP使用了DLL1中的函数,但同时DLL2也是一个DLL,也要输出一些函数供Client.CPP使用。那么在DLL2中如何声明所有的函数,其中包含了从DLL1中引入的函数,还包括自己要输出的函数。这个时候就需要同时使用__declspec(dllexport)和__declspec(dllimport)了。前者用来修饰本dll中的输出函数,后者用来修饰从其它dll中引入的函数。
DLL1,DLL2,client的创建和使用过程
创建DLL1 包括.h和.cpp文件,h文件中包含函数声明,cpp文件中包含函数实现 h文件中要有如下语句: #ifdef DLL_DLL1_EXPORTS #define DLL_DLL1_API __declspec(dllexport) #else #define DLL_DLL1_API __declspec(dllimport) #endif 说明: 在头文件中以这种方式定义宏DLL_DLL2_EXPORTS和DLL_DLL2_API,可以确保DLL端的函数用__declspec(dllexport)修饰,而客户端的函数用__declspec(dllimport)修饰。当然,记得在编译dll时加上参数/D “DLL_DLL2_EXPORTS”,或者干脆就在dll的cpp文件第一行加上#define DLL_DLL2_EXPORTS h文件中的函数声明写为: DLL_DLL1_API void FuncInDll1(void); DLL_DLL1_API void FuncInDll1(int); cpp文件中要定义宏: #define DLL_DLL1_EXPORTS cpp文件中的函数实现,开头添加: DLL_DLL1_API 创建DLL2 .h和.cpp文件的写法与DLL1中类似不同1:.h中包含DLL1.h 需要设置DLL1.h的路径(可以从C++-常规下设置) 不同2:.cpp中包含语句 #pragma comment(lib,"dll1.lib")也可以从linker-input下设置需要设置dll1.lib的路径(可以从linker-常规下设置) 创建client 需要包含DLL2.h(里边已包含DLL1.h) 需要包含lib #pragma comment(lib,"dll2.lib") #pragma comment(lib,"dll1.lib") 自己的Solution中采用的方法 将.h文件全放到Solution目录下lib文件夹中 .lib的路径设置为Solution的Debug目录 需要设置project之间的依赖关系(dll2依赖dll1,client依赖上述两个),不然编译时顺序会有问题 DLL生成后直接放置在Debug下,同一个Solution的project之间的引用不用设置也不会出问题;不同的Solution需要将DLL拷贝至使用者的Debug下。
-------------------------------------------------------
导出全局变量
语法 头文件中:extern DLL_OBJECT_API int g_nDll;cpp文件中:DLL_OBJECT_API int g_nDll = 9;
-------------------------------------------------------
小结
显式调用和隐式调用的使用时机 只有一个时候使用显式调用是合理的,就是当客户端不是C/C++的时候。这时是无法隐式调用的。Def的使用 其实def的功能相当于extern “C” __declspec(dllexport),所以它也仅能处理C函数,而不能处理重载函数。而__declspec(dllexport)和__declspec(dllimport)配合使用能够适应任何情况,因此__declspec(dllexport)是更为先进的方法。C语言调用DLL 若使用extern “C”,则函数名称保持不变,调用较方便,但是不支持函数重载等一系列c++功能若不使用extern “C”,则调用前要查看编译后的符号,非常不方便这两个问题DLL都不能很好的解决,只能说凑合着用。但是在COM中,都得到了完美的解决。所以,要在Windows平台实现语言无关性,还是只有使用COM中间件。-------------------------------------------------------
转载于:https://www.cnblogs.com/huan7550/archive/2013/03/05/2944137.html
