进行dll库封装时的自我笔记,以便自己时常回顾。
一、dll库的封装
右击工程,点击属性,打开属性界面,在常规一栏中将配置类型从.exe改为.dll
1.建立一个dllmain源文件,加上定义dll应用程序的入口点的程序,如下:
//dllmain.cpp:定义DLL应用程序的入口点
#include "stdafx.h"
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
dllmain.cpp作为定义DLL应用程序的入口点,它的作用跟exe文件有个main或者WinMain入口函数是一样的,它就是作为DLL的一个入口函数,实际上它是个可选的文件。它是在静态链接时或动态链接时调用LoadLibrary和FreeLibrary时都会被调用。
2.在主程序相对应的头文件中添加:
#ifdef VISIONDll
#define VISIONAPI extern"C" _declspec(dllexport)
#else
#define VISIONAPI extern"C" _declspec(dllimport)
#endif
extern “C” _declspec(dllexport)与project2.h中的#ifdef…endif是将C++函数导出,才会生成lib文件
3.添加targetver.h头文件
// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。
// 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并将
// 将 _WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。
#include <SDKDDKVer.h>#pragma once
包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。
如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并将
将 _WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。
4.添加stdafx.h头文件
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 从 Windows 头中排除极少使用的资料
// Windows 头文件:
#include <windows.h>
// TODO: 在此处引用程序需要的其他头文件
stdafx.h : 标准系统包含文件的包含文件,
或是经常使用但不常更改的
特定于项目的包含文件
5 . 在“.cpp”文件中对要导出的函数名前加上“VISIONAPI”这个宏;此方法为将函数直接封装为C接口,
VISIONAPI bool trigger( std::string&str_imageFilePath);
6 . 将这些设置好后,编译链接即可在工程目录下的Debug目录下生存“.dll”(动态连接库)和“.lib”(编译库此文件不同于静态库),这两个文件再加上“*.h”文件即为我们刚封装好的有效文件;
二、动态库的调用方式
(一)静态调用
1、添加动态库函数声明头文件 #include“*.h”
2.在.h文件中,在需要调接口函数的地方添加如下语句:
#pragma comment(lib, ".\.lib")
.\.lib为动态库的绝对路径。
就可以调用动态库中的函数了。
(二)动态调用
dll显示调用
对于显示连接,即动态加载我们需要调用LoadLibrary
在MSDN中:HMODULE WINAPI LoadLibrary( __in LPCTSTR lpFileName);
它的功能是映射一个可执行模块到调用进程的地址空间。由此我们知道显示调用就是函数指针来调用函数。
1.声明头文件<windows.h>,说明我想用windows32方法来加载和卸载DLL
#include <windows.h>
2、定义一个与动态库函数接受参数类型和返回值均相同的函数指针类型。
即用typedef定义一个指针函数类型。这个指针类型,要和调用的函数类型和参数保持一致
typedef int (* lpAddFun)(int ,int);
lpAddFun addFun;
typedef bool(*pTrigger)(std::string str_imagePath);
pTrigger trigger;
3、定一个句柄实例,用来取DLL的实例地址。HINSTANCE hDll;
格式为:hDll=LoadLibrary(“DLL地址”)//动态加载DLL模块句柄
这里字符串类型是LPSTR,当是unicode字符集的时候会不行,
因此要在配置-属性-常规里面把默认字符集“unicode”改成支持多字符扩展即可。
HINSTANCE hInst = LoadLibrary(L"OCRDetect.dll");
4、取的地址要判断,返回的句柄是否为空,如果为无效句柄,那么要释放加载DLL所占用的内存。
if (hInst == NULL)
{
FreeLibrary(hInst);
}
5、定义一个函数指针,用来获取所加载DLL模块中函数的地址。
然后通过GetProcAdress来获取函数的地址,参数是DLL的句柄和想要调用的函数名:比如:addFun=(lpAddFun) GetProcAddress(hDll,“Add_new”);
这里也要判断要函数指针是否为空,如果没取到要求的函数,那么要释放句柄。
trigger = (pTrigger)::GetProcAddress(hInst, "trigger");
6、然后通过函数指针来调用函数。
7、调用结束后,就释放句柄FreeLibrary(hdll);
FreeLibrary(hInst);
提示:若动态库程序和调用动态库的程序在一个解决方案中可以利用项目依赖项,定义项目生成顺序。也可以在调用动态库程序右键属性 -> c/c++ ->常规的附加包含目录中填入动态库目录。
然后用形如
#ifdef _DEBUG
#pragma comment(lib,"…\Debug\TestDll.lib")
#else
#pragma comment(lib,"…\Release\TestDll.lib")
#endif
这种方式静态加载动态库
调用动态库例程
#include <iostream>
#include <string>
#include <windows.h>
#include <WinBase.h>
typedef bool(*pTrigger)(std::string str_imagePath);
pTrigger trigger;
int main()
{
HINSTANCE hInst = LoadLibrary("OCRDetect.dll"); //动态加载模块句柄
if (hInst == NULL)
{
FreeLibrary(hInst);
std::cout << "DLL加载失败!" << std::endl;
return 0;
}
trigger = (pTrigger)::GetProcAddress(hInst, "trigger");
if (hInst == NULL)
{
FreeLibrary(hInst);
}
std::string str_imageFile = "bottle2.png";
trigger(str_imageFile);
FreeLibrary(hInst);
return 0;
}