在vs2013下手把手创建/调用dll
参考了大佬的文章
首先,体会一下静态编译:
创建Win32Project,选DLL,添加一个.h和.cpp文件
点击生成解决方案,然后去debug目录下拷贝.lib和.h文件。
新建一个控制台程序,添加一个main.cpp,效果如下:
(也可以在vs工具栏里添加,不过写代码更快更易懂方便)
静态编译部分到此结束。
以下是动态编译:
创建win32动态dll项目,添加.h和.cpp文件
在同一个工作环境下添加另一个win控制台应用程序,添加main文件如下
这样以后就可以调用了,DLL内的函数分为两种:
(1)DLL导出函数,可供应用程序调用;
(2) DLL内部函数,只能在DLL程序使用,应用程序无法调用它们。
以上就是动态链接库内容,编写dll时还可以添加一个.def文件,专门用于描述导出什么函数作为接口,如下:
在之前dll的.h和.cpp基础上作改动:(头文件里可以不用声明deal函数)
以上全部内容,手把手跟着做就可以,优缺点设计原因自己解释。
DLL的静态调用:
(与动态调用的不同点:编译过程中就通过lib文件向exe文件中导入函数的信息,所以就不存在运行时再LoadLibrary)
(与普通函数的不同点:普通函数调用查函数表找地址运行就好,DLL函数查表得dll内存中位置及该函数序号,然后找dll调用该函数。ps:会有DllMain函数的调用,在该进程/线程开始及结束时)
总而言之,静态调用相对方便,windows帮了不少忙,动态调用很麻烦,但灵活性高(运行效率高)
(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的输出函数,就象调用程序内部的其他函数一样。
关于DllMain函数我觉得百度百科的这一篇很详尽
我在win32 dll项目.cpp文件里声明DllMain函数,在vs项目属性-->C/C++预编译器去掉_USRDLL一项,再自己写DllMain如下
DllMain的官方声明如下:
BOOL WINAPI DllMain( _In_ HINSTANCE hinstDLL, // 指向自身的句柄 _In_ DWORD fdwReason, // 调用原因 _In_ LPVOID lpvReserved // 隐式加载和显式加载 ); // 以上内容来自MSDN
关于其他语言调用DLL
1 #ifndef LIB_H 2 #define LIB_H 3 int /*__stdcall*/ add(int x, int y); 4 5 #endif // LIB
关于调用DLL中的全局变量
/*DLL项目部分*/ //DLL.h #ifndef LIB_H #define LIB_H extern "C" int /*__stdcall*/ add(int x, int y); extern "C" int dllGlobalVar; #endif // LIB //DLL.cpp #include "WinDll.h" #include <Windows.h> #include <stdio.h> int dllGlobalVar = 1997; int deal(int x, int y){ return 100 - x - y; } int /*__stdcall*/ add(int x, int y) { return deal(x, y); } BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: printf("procss attach"); dllGlobalVar = 1157; break; case DLL_PROCESS_DETACH: printf("procss detach, global var is%d\n", dllGlobalVar); dllGlobalVar = 0; break; case DLL_THREAD_ATTACH: printf("thread attach"); break; case DLL_THREAD_DETACH: printf("thread detach"); break; default: printf("can't default"); } return TRUE; } /* .def文件 LIBRARY WinDll EXPORTS add @ 1 dllGlobalVar DATA */ /*控制台程序部分*/ //main.cpp #include <stdio.h> #include <Windows.h> #pragma comment(lib, "..\\Debug\\WinDll.lib") extern "C" int __declspec(dllimport) dllGlobalVar; extern "C" int __declspec(dllimport) add(int, int); int main(int argc, char **argv) { printf("\ndllGLobalVar:%d\n", dllGlobalVar); dllGlobalVar = 2018; return 0; }
以上即C代码导出dll的全部内容,下面进入到C++导出类:
懒得写了,C++类的导出和C差不多,类的声明如下
#ifndef POINT_H #define POINT_H #ifdef DLL_FILE class __declspec(dllexport) point //导出类point #else class __declspec(dllimport) point //导入类point #endif { public: float y; float x; point(); point(float x_coordinate, float y_coordinate); }; #endif
这样的话生成lib和dll文件以后拷到需要的工程目录下,
#pragma comment(lib, "path")
一句就可以了。
例如OpenCV库,就需要在QT的pro文件里写上
INCLUDEPATH += D:/opencv/build/include CONFIG(debug, debug|release): { LIBS += -LD:/opencv/build/x64/vc12/lib \ -lopencv_core2413d \ -lopencv_imgproc2413d \ -lopencv_highgui2413d \ -lopencv_ml2413d \ -lopencv_video2413d \ -lopencv_features2d2413d \ -lopencv_calib3d2413d \ -lopencv_objdetect2413d \ -lopencv_contrib2413d \ -lopencv_legacy2413d \ -lopencv_flann2413d } else:CONFIG(release, debug|release): { LIBS += -LD:/opencv/build/x64/vc12/lib \ -lopencv_core2413 \ -lopencv_imgproc2413 \ -lopencv_highgui2413 \ -lopencv_ml2413 \ -lopencv_video2413 \ -lopencv_features2d2413 \ -lopencv_calib3d2413 \ -lopencv_objdetect2413 \ -lopencv_contrib2413 \ -lopencv_legacy2413 \ -lopencv_flann2413 }
目的就是导入库文件来进行静态链接,可见我几乎每天都在用但是今天才发现。
类的动态编译也一样,略。
关于DLL的方面告一小结,接下来要去整MFC了。
-------------2018.01.11----------------