WIN32动态链接库设计与使用
WINDOWS动态链接库技术能很好地实现代码的分模块,综合来说,windows动态链接库分为三种WIN32动态链接库,使用WINDOWS api函数调用设计,贴近底层,体积小,是最初Windows程序员最喜欢的技术之一,后来微软推出了MFC类库,于是动态链接库进行了升级,多了两种,第一种是非规则MFC类库,这种类库能够使用MFC的API进行程序设计,相对而言,比WIN32动态链接库设计的时候简便很多,同时还能实现资源与逻辑的分离,本地化等等特性,但是缺点就是往往需要带有系统MFC库编译,效率降低了一些,不过这些年编程的趋势就是如此,用速度换取开发效率嘛,第三种动态链接库叫做MFC扩展类库,这一类库的主要功能是扩展MFC的控件,大家知道,MFC推出时附带了一系列的控件,但是,这些控件相对来说,比较简陋,我们可以在这些控件的基础上扩展,实现一个我们自己想要的控件,比如一个richtext,一个可以编辑的list等,这种类库控件收集多了,对程序员而言是宝贵的财富啊.
另外,动态链接库的使用也包括两种,一种叫做显式调用,就是在程序中手动的用API装载类库,导入方法,这种用法又叫做动态调用,在这种调用中,只需要dll文件和程序员知道DLL文件中的方法名变量名就可以使用,极为方便和隐蔽.另一种叫做隐式调用,编译时就将整个目标dll装入,不需要动态装在,函数变量随时可以用,这种办法编译出来的程序体积略大,不过写程序操心比较少,各有优劣.这种方法又叫做静态调用,使用这种办法,需要类库开发者给出DLL对应的lib文件和对应的导出函数头文件.
现在先上代码,看看如何编写一个WIN32类库
C文件如下
#include "DinkWin32DllExtend.h"
#include <iostream> HANDLE hDllHandleLocal;
DWORD lastDllCallReason; //第一个参数为系统给的DLL的基地址
bool APIENTRY DllMain( HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved )
{
hDllHandleLocal = hDllHandle;
lastDllCallReason = dwReason;
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
MessageBox(NULL,TEXT("dll process attach\r\n"),TEXT("message"),MB_OK);
//printf("dll process attach\r\n");
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
} HANDLE _stdcall GetDllHandle(void)
{
return hDllHandleLocal;
} int DinkMath::Add(int x,int y)
{
return x+y;
}
#ifndef __DINK_WIN32_DLL_EXTEND_H_
#define __DINK_WIN32_DLL_EXTEND_H_
#include <windows.h> #define DINK_WIN32_LIB_NAME "Win32Dll.dll" /************************************************************************/
/* WIN32DLL_EXPORTS是WIN32_DLL工程预定义的 */
/* 宏定义,便于文件被定义和引用 */
/* 主要用于静态链接 */
/************************************************************************/
#ifndef WIN32DLL_EXPORTS
#define DINK_WIN32_DLL_FUNC __declspec(dllimport)
#define DINK_WIN32_DLL_VAR __declspec(dllimport)
#define DINK_WIN32_DLL_CLASS __declspec(dllimport)
#else
#define DINK_WIN32_DLL_FUNC __declspec(dllexport)
#define DINK_WIN32_DLL_VAR __declspec(dllexport)
#define DINK_WIN32_DLL_CLASS __declspec(dllexport)
#endif /************************************************************************/
/* 导出变量 */
/************************************************************************/
EXTERN_C DWORD DINK_WIN32_DLL_VAR lastDllCallReason; /************************************************************************/
/* 导出方法 */
/************************************************************************/
EXTERN_C HANDLE DINK_WIN32_DLL_FUNC GetDllHandle(void); /************************************************************************/
/* 注意,类不能动态加载,想要动态加载,使用COM */
/************************************************************************/
class DINK_WIN32_DLL_CLASS DinkMath//导出类
{
public:
int Add(int x,int y);
int myVar;
protected: private: }; /************************************************************************/
/*方法函数名:定义规则 DINK_DLL_FUNC_NAME_+函数名大写 */
/*方法导入规则:定义一个方法类型,直接可以供外部程序使用 */
/*typedef 返回值 (*函数名+DllCall)(参数列表) */
/************************************************************************/
#define DINK_DLL_FUNC_NAME_GETDLLHANDLE "GetDllHandle"
typedef HANDLE (*GetDllHandleDllCall)(void); /*******************************************************************************/
/* 变量名 定义规则 DINK_DLL_VAR_NAME_+变量名大写 */
/*定义一个宏转换规则,直接将空指针转换为制定变量的操作空指针转换为对应指针的操作*/
/*定义一个规则,直接将获取到的指针转换为 */
/*******************************************************************************/
#define DINK_DLL_VAR_NAME_LASTDLLCALLREASON "lastDllCallReason"
#define DINK_MAKE_LASTDLLCALLREASON(ptr) (*((DWORD*)ptr))
#define DINK_MAKE_LASTDLLCALLREASON_PTR(ptr) ((DWORD*)ptr) #endif
上面的代码有几点需要讲解.
1.bool DllMain()函数是windows调用DLL时候的入口函数,DLL被装载的时候自动调用该函数,函数的第一个参数是WINDOW调用这个DLL的时候给这个DLL的虚拟内存地址,通过这个地址可以找到类库,第二个参数为系统调用DLL的原因,包括是个选项,分别代表当一个进程的主线程调用和主线程卸载dll,一个进程中的非主线程调用或卸载DLL,宏定义的英文含义已经很明确,就不用多说了.
2.__declspec(dllexport) 关键字,表示导出一个变量,方法或者类
3.__declspec(dllimport)关键字,表示在一个项目中导入一个变量,方法,类
4.APIENTRY实际上表示_stdcall,这是windows函数调用的命名约定,表示调用这个函数的必须是windows API,与之对应的是_cdcel,表示调用这个函数的是一个C标准运行时.
5.在头文件中通过宏定义的方法来指明导出和导入,这样便于将这个头文件同时用作dll编译时的导出文件和外部调用时的导入文件,方便快捷,这是一个窍门.
6.在动态装载库的时候,我们得到的都是一个内存地址,无类型的,无论是变量还是函数,所以最好是定义一套将空的地址转换为dll预定义的模块的宏,别人使用起来还会很方便.
然后我们先说静态调用,静态调用的代码如下
#include <windows.h>
#include <iostream> //静态链接
#include "..\\Win32Dll\\DinkWin32DllExtend.h"
#pragma comment(lib,"Win32Dll.lib") int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
MessageBox(NULL,TEXT("hello dll test"),TEXT("message"),MB_OK);
DWORD hdll = (DWORD)GetDllHandle();
wchar_t* str = (wchar_t*)malloc();
swprintf_s (str,,TEXT("hello dll address = %d"),hdll);
MessageBox(NULL,str,TEXT("message"),MB_OK);
swprintf_s (str,,TEXT("last call reason = %d"),lastDllCallReason);
MessageBox(NULL,str,TEXT("message"),MB_OK);
DinkMath math;
swprintf_s (str,,TEXT("dinkmath Add %d + %d = %d"),,,math.Add(,));
MessageBox(NULL,str,TEXT("message"),MB_OK);
free((void*)str);
return -;
}
这里面有几个要点,
1.包含文件的时候指明文件的相对路径
2.要将编译dll的时候对应的lib文件添加到我们的工程中
3.#pragma关键字是静态调用的时候很重要的关键字
4.因为我们在dll中创建了类,调试模式下,当库被释放的时候,对应的内存释放回引起CRT运行时错误,是堆栈不一致造成的,因为windows会为每一个DLL创建一个独立的堆栈,这不用管,因为当你使用release编译就不会报错了,也可以在调试中关掉CRT,不过不建议,不然以后出别的原因引起的错误因为被关掉了无法发现就划不来了.
5.使用unicode编程的时候,注意使用安全字符串函数.
静态链接就说这么说,大家看代码领会精神,注意这个文件要和dll的.h文件一起看哦,这样容易记住要点.
接下来说说动态调用,其实也简单,代码如下
//动态链接
//获取库用loadlibrary
//释放库用freeLibrary
//获取函数和变量用GetProcProcess
//dll句柄 HMODULE
#include "..\\Win32Dll\DinkWin32DllExtend.h" int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd )
{
HMODULE dinkDllLib;
GetDllHandleDllCall GetDllHandle;
DWORD* reason;
dinkDllLib = LoadLibrary(TEXT(DINK_WIN32_LIB_NAME));
wchar_t* str = (wchar_t*)malloc();
if (dinkDllLib != NULL)
{
GetDllHandle = (GetDllHandleDllCall)GetProcAddress(dinkDllLib,DINK_DLL_FUNC_NAME_GETDLLHANDLE);
if(GetDllHandle != NULL)
{
swprintf_s(str,,TEXT("GetDllHandle result is %d"),GetDllHandle());
MessageBox(NULL,str,TEXT("message"),MB_OK);
reason = DINK_MAKE_LASTDLLCALLREASON_PTR(GetProcAddress(dinkDllLib,DINK_DLL_VAR_NAME_LASTDLLCALLREASON));
if(reason != NULL)
{
swprintf_s(str,,TEXT("last call reason is %d"),*reason);
MessageBox(NULL,str,TEXT("message"),MB_OK);
}
else
{
MessageBox(NULL,TEXT("var load failed"),TEXT("message"),MB_OK );
return -;
}
}
else
{
MessageBox(NULL,TEXT("function load failed"),TEXT("message"),MB_OK);
return -;
}
FreeLibrary(dinkDllLib);
free(str);
}
else
{
MessageBox(NULL,TEXT("lib load failed"),TEXT("message"),MB_ICONERROR);
return -;
}
return ;
}
同样有几个要点
1.loadlibrary获得的就是dll模块被程序装载到内存中的地址,实际上可以用一个三十二位的dword来表示,其window句柄类型为hmodule,这个值在后面资源切换的时候其实蛮重要的,大家可以当成这就是模块地址吧.
2.GetProcAddress函数通过给定字符串参数获取到一个指针,返回的指针是一个VOID类型的指针,所以需要我们的强制转换,不能直接访问,切记切记.
3.调用dll完成以后如果不用dll了使用freeLibrary函数释放DLL内存.
4.动态调用不能调用类,要是的类能被动态调用,使用静态链接.
5.动态调用的时候要将DLL放在EXE问价能找到的地方,包括环境变量path路径制定的区域,系统system区域,exe文件运行路径这几个.
以上就是WIN32动态链接库的实际使用了,下面是一些补充,算是我看书记得笔记.
1.如果不想使用#pragma指令,那么也可以在vs工程的属性中设置库文件路径可和库文件名,那样就可以自动连接,记得OPENCV就是这么干的.
2.DllMain函数可以做一些DLL内部的初始化工作,还可以做一些资源的初始化,资源文件的读取,释放等工作.
3.DllMain不属于导出函数,他属于内部函数,不能导出,另外,当一个DLL源文件不包含DllMain时候,系统会自动为DLL创建一个默认的空DllMain,类似于构造方法.
4.一个进程中调用的每一个DDLL都有一个全局唯一的32字节的hmodule句柄,该句柄只能在特殊的函数内部使用,代表了Dll在进程虚拟空间中的起始地址,在WIN32总,HINSTANCE与HMODULE相同,两者可以替换使用.
5.为了更好地符合windows调用规则,在定义函数和导出函数的时候使用_stdcall是一个好办法.
WIN32动态链接库设计与使用的更多相关文章
- VC++:创建,调用Win32动态链接库
VC++:创建,调用Win32动态链接库 概述 DLL(Dynamic Linkable Library)动态链接库,Dll可以看作一种仓库,仓库中包含了可以直接使用的变量,函数或类.仓库的发展史经历 ...
- Win32动态链接库和MFC 动态链接库
通过使用 DLL,程序可以实现模块化,由相对独立的组件组成.例如,一个计帐程序可以按模块来销售.可以在运行时将各个模块加载到主程序中(如果安装了相应模块).因为模块是彼此独立的,所以程序的加载速度 ...
- 深入浅出Win32多线程设计之MFC的多线程-线程与消息队列(经典)
1.创建和终止线程 在MFC程序中创建一个线程,宜调用AfxBeginThread函数.该函数因参数不同而具有两种重载版本,分别对应工作者线程和用户接口(UI)线程. 工作者线程 CWinThread ...
- Go win32 - 1
上次说到,我们的GO可以执行系统调用,嘿嘿 不假,但如果你认为你已经掌握了,哈哈,那么不然 网上的例子,总是不深入,不彻底,除非是官网上的demo,也就是说只有设计者才知道告诉你什么才是它设计的正真意 ...
- WINDOWS动态链接库--MFC规则动态链接库
第一代window程序员使用windows api进行编程,到了后来,微软推出MFC类库,于是,动态链接库进行了升级,可以在动态连接库中使用MFC的API,这就叫做MFC动态链接库, 其中MFC动态链 ...
- 动态链接库(Dynamic Link Library)学习笔记(附PE文件分析)
转载:http://www.cnblogs.com/yxin1322/archive/2008/03/08/donamiclinklibrary.html 作者:EricYou 转载请注明出处 注 ...
- C# .NET 基本概念
1. private. protected. public. internal 修饰符的访问权限. private : 私有成员, 在类的内部才可以访问. protected : 保护成员, ...
- 微软软件开发技术二十年回顾-COM、OLE、ActiveX及COM+篇
本文摘自:http://www.job168.com/info/read_100394.html 微软的许多技术,如OLE.ActiveX.以及DirectX等都是基于COM技术而建立起来的.微软本身 ...
- COM组件
COM组件 COM component(COM组件)是微软公司为了计算机工业的软件生产更加符合人类的行为方式开发的一种新的软件开发技术.在COM构架下,人们可以开发出各种各样的功能专一的组件,然后 ...
随机推荐
- 【HDU 5833】Zhu and 772002(异或方程组高斯消元讲解)
题目大意:给出n个数字a[],将a[]分解为质因子(保证分解所得的质因子不大于2000),任选一个或多个质因子,使其乘积为完全平方数.求其方法数. 学长学姐们比赛时做的,当时我一脸懵逼的不会搞……所以 ...
- 最小点集覆盖=最大匹配<二分图>/证明
来源 最小点集覆盖==最大匹配. 首先,最小点集覆盖一定>=最大匹配,因为假设最大匹配为n,那么我们就得到了n条互不相邻的边,光覆盖这些边就要用到n个点. 现在我们来思考为什么最小点击覆盖一定& ...
- List、Set、Map集合存放null解析及HashMap、Hashtable异同点解析
1.List.Set.Map集合存放null解析: @Test public void CollectionTest() { // 测试List List<Object> list = n ...
- SwiftDate 浅析
SwiftDate是Github上开源的,使用Swift语言编写的NSDate封装库,可以很方便的在Swift中处理日期,比如日期创建,比较,输出等. 特性 支持数学运算符进行日期计算(比如myDat ...
- SuperSocket+unity 网络笔记
学习SuperSocket 必须要注意的 代码是 static void Main(string[] args) { WebSocketServer appServer = new WebSocket ...
- HDU 4685 Prince and Princess(二分匹配+强联通分量)
题意:婚配问题,但是题目并不要求输出最大匹配值,而是让我们输出,一个王子可以与哪些王妃婚配而不影响最大匹配值. 解决办法:先求一次最大匹配,如果有两个已经匹配的王妃,喜欢她们两个的有两个或者以上相同的 ...
- FZU 2086 餐厅点餐(枚举)
///个人感觉vector好看一点 #include<iostream> #include<cstdio> #include<cstring> #include&l ...
- 【从汉字中提取数字】不用公式,不用VBA,如此简单的方法你是否用过?
转自:http://huaban.com/pins/19664410 具体操作过程请看附图动画:
- Cannot call sendRedirect() after the response has been committed
AJAX+JSP时,out.write('content')之后,如果后面还有代码,无法被执行到,会报 错,java.lang.IllegalStateException: Cannot call s ...
- UVA699 dfs and map
和书上的方法不一样... 因为我不知道节点,所以就直接用map来存左右了. #include<bits/stdc++.h> using namespace std; map <int ...