重要:VC DLL编程
VC DLL编程
静态链接:每个应用程序使用函数库,必须拥有一份库的备份。多个应用程序运行时,内存中就有多份函数库代码的备份。
动态连接库:多个应用程序可以共享一份函数库的备份。
DLL的调用方式:即DLL的使用者在调用库中输出函数时,函数参数的压栈和出栈顺序和方法。
VC++支持四种方式:
<1>_cdecl调用方式: 也叫C调用方式,函数参数的压栈顺序是从右至左,参数的出栈方式由调用者完成,在调用DLL的函数的地方都应包含清空堆栈的代码,它是C/C++缺省的调用方式。
<2>_stdcall标准调用方式:函数参数压栈顺序是从右至左,参数出栈工作由被调用者负责完成。系统将加在函数原型定义前的”WINAPI”宏翻译为适当的调用方式,对于Win32是_stdcall调用方式。
<3>_fastcall:主要特点是调用速度快,被调用的函数参数传递不依靠堆栈,而是通过寄存器,但并不是对所有的参数传递均使用寄存器,往往只是用ECX和EDX传送前两个双字或比较小的参数,其余的参数传递仍然采用从右至左压栈方式,出栈工作由被调用的函数完成。
<4>thiscall:前三种是关键字,可以加到函数前作修饰,thiscall不是关键字,因此程序中不能显式写入,这种方式仅应用于C++成员函数,this指C++中指向对象的指针,this存放在ECX寄存器中,参数从右至左压栈,出栈由被调用者完成。
DLL的入口函数
DllMain()函数负责完成DLL的初始化和解说DLL调用后的清理工作。当加载DLL时,如存在DllMain()函数则调用它。
MFC DLL
MFC DLL可以让我们的程序使用MFC库,它分为3类:
<1>Regular Dll with MFC Statically linked (正规)
静态链接MFC库,在DLL工程中将包含工程中所需的MFC库代码的拷贝,因此,程序可以脱离MFC库使用。
<2>Regular Dll using shared MFC Dll (正规)
动态共享MFC库,工程必须在装有MFC库的机器上才能运行。
MFC正规DLL编写注意问题:
应在输出函数的函数体内首先加入:
AFX_MANAGE_STATE(AfxGetStaticModuleState());
<3>MFC Extension Dll(using shared MFC Dll) (扩展)
它不但能输出函数,还能输出类,用户可以直接使用、继承这个输出类,但它不是正规DLL,如果要让非VC++程序调用,必须使用正规DLL。
MFC扩展DLL编写注意问题:
<1>在要输出的类定义中加入:AFC_EXT_CLASS
如:class AFX_EXT_CLASS cls{};
<2>在要输出的函数定义前加入:AFC_EXT_CLASS
如:AFX_EXT_CLASS int func(){}
自行编写DLL的方法
<1>在DLL中编写的函数前加上”__declspec(dllexport)”即可导出该函数。
<2>从DLL中导出类,是在class和类名之间加入”__declspec(dllexport)”,如果只想导出类中指定的函数,可只在该函数前加上”__declspec(dllexport)”。
<3>C++为支持函数重载,采用了名称压轧,因此,DLL文件在编译时,函数名会发生改变,为保证对DLL的正确访问,可在”__declspec(dllexport)”声明之前加入”extern “c””,编译时就不会发生名称改变,但extern “c”只能用于导出全局函数,不能导出类的成员,如果在函数名前加入了调用约定(如:_stdcall),编译时还是会发生名称改变。
也可通过模块定义文件的方式解决名称改变的问题,模块定义文件的后缀为”.def”,步骤如下:
1新建一个文本文档,改名为”x.def”;
2将x.def加入到工程;
3编辑x.def。EXPORTS下所写函数名如与DLL文件中函数名相同,则以所写名称导出该函数。
<4>DLL通过GetForegroundWindow()获得正在使用它的前景窗口的句柄。
<5>GetModuleHandle()得到一个DLL的句柄。
GetSystemMetrics()获取系统信息。
系统对DLL中可改变的数据,在进程写访问时会拷贝到一个新的数据页面,如果多个进程要共享该数据,可设置节,创建节后,将数据放到节中且必须初始化:
#pragma data_seg(“name”) //开头
//数据
#pragma data_seg() //结尾
#pragma comment(linker,”/section:name,RWS”)
//设为读、写、共享,也可写在.def文件中。
使用DLL
要使用DLL,首先要将DLL文件映像到用户进程的地址空间中,并声明被调用的函数,然后才能进行函数调用,调用方法与一般函数相同。
将DLL映像到进程地址空间的方法:
<1>隐式的加载时链接
DLL工程经编译后,产生一个.dll文件,一个.lib文件及一个包含DLL输出函数声明的.h头文件,隐式调用DLL就是将这个.lib文件链接到工程中。
lib文件中包含了DLL允许调用的所有函数列表,链接器发现程序调用了lib文件中列出的某个函数时,会在程序的可执行文件的文件映像中加入包含这个函数的DLL文件的名字信息,当程序运行时,可执行文件被操作系统产生映像文件,系统会查看这个映像文件中关于DLL的信息,然后将这个DLL文件映像到进程的地址空间。
链接lib文件的方法:
1加入到文件列表
2在Link项下加入
3#pragma comment(lib,”mydll.lib”)
<2>显式的运行时链接步骤如下:
1用LoadLibrary()或AfxLoadLibrary()加载DLL或可运行模块;
2用GetProcAddress()得到要调用的DLL中函数的指针,然后使用该函数;
3使用完DLL以后,用FreeLibrary()将DLL在进程的映射解除,减少加载DLL的记数。
被调用的函数声明的方法有三种:
<1>用”extern”声明被调用函数。
<2>使用”__declspec(dllimport)”声明,即告诉编译器,所引用的函数或文件是从DLL中输入的,编译器能生成运行效率更高的代码。
<3>也可将声明放在DLL编写的头文件中,在使用的文件中包含该头文件即可。
要使用DLL中导出的类,必须在使用的文件中包含该类所在的头文件!
尽量导出方法(做接口)少导出类。
标准DLL中导出函数的写法:
extern "C" BOOL __declspec(dllexport) EXPORT ShowDlg() ///标准导出函数格式
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());//些宏不可少!
// 此处为普通函数体
}
1.AfxGetStaticModuleState()指向当前模块状态;
2.当前函数调用结束后原模块的状态自动被恢复;
3.用于DLL中所调用MFC函数、类、资源时的模块状态切换
并在def文件中定义导出函数的序号:ShowDlg @1
在调用处写如下代码:
typedef void (*dllfun)(); //定义函数指针类型
HINSTANCE hlib= NULL;
hlib=LoadLibrary("std_dll.dll"); //加载库
dllfun ShowDlg = NULL;//定义函数指针
ShowDlg=(dllfun)GetProcAddress(hlib,"ShowDlg");//获取库中函数地址
ShowDlg(); //调用函数
扩展DLL导出类的写法:
class AFX_EXT_CLASS clsName{};
扩展DLL中的资源使用
简单的说:每个DLL有自己特有的资源。在使用时,明确的告诉系统要使用哪个DLL的资源。现在的问题就是如何告诉系统使用哪个DLL的资源。函数:AfxSetResourceHandle() 可以完成这个功能。参数是资源的句柄。
那怎么得到某个DLL的资源句柄呢?如下:
在扩展DLL的入口函数
extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
…
if (!AfxInitExtensionModule(ShpSymbolDLL, hInstance))
return 0;
…
}
其中ShpSymbolDll 可能会因工程名不同而不同,这里就以这个名称代替来说明了,DLL的资源句柄就可以在此得到。
ShpSymbolDLL 定义:
AFX_EXTENSION_MODULE ShpSymbolDLL = { NULL, NULL };
ShpSymbolDLL.hResource 这个就是我们要的了。其它参数请看说明。现在我们在使用某DLL的资源时只要先加入以下两行就可以正确执行了:
HINSTANCE hOld = AfxGetResourceHandle();
AfxSetResourceHandle( ShpSymbolDLL.hResource );
注意在用完之后再恢复:
AfxSetResourceHandle( hOld );
另外一个不得不提起的东西,在入口函数中有一行
new CDynLinkLibrary(ShpSymbolDLL);
旁边有一说明:将此 DLL 插入到资源链中。
的确如此。言下之意,上面所说的没什么用了?其实不然,假如DLL中有一个Dialog。 ID为120,在你调用此DLL的应用程序资源中,如果没有ID的值为120。那么,上面的都是白做了,你会得到预料中的结果。但如果应用程序中有一相同ID的对话框资源呢?请大家一试。结果就不一样了。其中的原因与new CDynLinkLibrary(ShpSymbolDLL) 相关联。
具体请看MFC中 的代码 DoModal() 就会得到解答。或看MSDN中带的例子 dllhusk,系统自动会查找相应的资源,但不会判断哪个是正确的。以找到的第一个资源为准。
另:为了编写方便,可以写一个类 ,写成全局的。
class CModuleInfo
{public:
HMODULE m_hModule;
HMODULE m_hResource;
public:
CModuleInfo(void){}
~CModuleInfo(void){}
};
class AFX_EXT_CLASS CModuleStateMana
{
HINSTANCE m_hInstOld;
public:
CModuleStateMana();
~CModuleStateMana();
};
实现如下:
CModuleInfo s_mi;
CModuleStateMana::CModuleStateMana()
{
m_hInstOld = AfxGetResourceHandle();
AfxSetResourceHandle( s_mi.m_hModule );
}
CModuleStateMana::~CModuleStateMana()
{
AfxSetResourceHandle( m_hInstOld );
}
然后在入口函数之前加入
extern CModuleInfo s_mi;
函数中加入:
s_mi.m_hModule = ShpSymbolDLL.hModule;
s_mi.m_hResource= ShpSymbolDLL.hResource;
在调用的时候只要先加入:
CModuleStateMana msm;
就可以正确调用了。
重要:VC DLL编程的更多相关文章
- VC++动态链接库(DLL)编程深入浅出(zz)
VC++动态链接库(DLL)编程深入浅出(zz) 1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用 ...
- VC++动态链接库(DLL)编程深入浅出
1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静 ...
- VC++动态链接库(DLL)编程深入浅出(四)
这是<VC++动态链接库(DLL)编程深入浅出>的第四部分,阅读本文前,请先阅读前三部分:(一).(二).(三). MFC扩展DLL的内涵为MFC的扩展,用户使用MFC扩展DLL就像使用M ...
- VC++动态链接库(DLL)编程深入浅出(一)
1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静 ...
- VC++动态链接库(DLL)编程深入浅出(转帖:基础班)
1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静 ...
- VC-基础:VC++动态链接库(DLL)编程深入浅出
1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静 ...
- 基于Visual C++6.0的DLL编程实现
整理自基于Visual C++6.0的DLL编程实现 本文通过通俗易懂的方式,全面介绍了动态链接库的概念.动态链接库的创建和动态链接库的链接,并给出个简单明了的例子,相信读者看了本文后,能够创建自己的 ...
- MFC下DLL编程(图解)
MFC下DLL编程(图解) DLL(Dynamic Link Library,动态链接库)是微软公司为Windows和OS/2操作系统设计一种供应用程序在运行时调用的共享函数库.DLL是应用程序的一种 ...
- 在DLL编程中,导出函数为什么需要extern "C"
转自:http://blog.csdn.net/zhongjling/article/details/8088664 一般来讲,在DLL编程过程中,对于导出的函数前 都需要加入 extern “C”, ...
随机推荐
- DIOCP
DIOCP GITHUB: https://github.com/ymofen/diocp-v5.git diocp5====== ## 快速开始 从那里得到: git更新(推荐同步更新) 1.htt ...
- 【spring cloud】【IDEA】【maven】spring cloud多模块在idea上使用maven插件打包报错:程序包XXX不存在
>>>>spring cloud 多模块 >>>>在idea上使用maven插件打包,欲打包成jar包后 进行部署 >>>> 报 ...
- Windows Embedded Compact 7初体验
Windows Embedded Compact 7初体验 Windows Embedded Compact 7已经出来半年多了,一直没时间搞.最近它又出了Refresh的版本,电脑也换了个1T的硬盘 ...
- Android中XML解析-PULL解析
前面写了两篇XML解析的Dom和SAX方式,Dom比较符合思维方式,SAX事件驱动注重效率,除了这两种方式以外也可以使用Android内置的Pull解析器解析XML文件. Pull解析器的运行方式与 ...
- SQL2005,错误 0xc00470fe 数据流任务 产品级别对于 组件“源 - 2009_txt”(1) 而言不足
今天在将txt文件导入MSSQL2005时,出了这个错误,到网上查了一下资料,说是因为没有安装SQL 2005 SP1的原因,所以我就下载了个. 安装后,再次导入数据,OK 没问题了.http://w ...
- 图解vue中 v-for 的 :key 的作用,虚拟dom Diff算法
其实不只是vue,react中在执行列表渲染时也会要求给每个组件添加上key这个属性. 要解释key的作用,不得不先介绍一下虚拟DOM的Diff算法了. 我们知道,vue和react都实现了一套虚拟D ...
- Android -- Fragment动画异常Unknown animation name: objectAnimator
异常 Caused by: java.lang.RuntimeException: Unknown animation name: objectAnimator 异常代码 FragmentTransa ...
- 本地主机DNS劫持演示及防范
劫持演示 如果要进行DNS劫持 ...
- POJ 1265:Area
Area Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 4725 Accepted: 2135 Description ...
- 防止excel数字变成科学计数法
在网上查了很多资料知道解决办法大概有两个:一是在身份证字段前加个英文单引号,二是设置Excel的格式为文本格式. 我试用过第一种确实可以显示,但是有个“'”号在那里感觉确实不是很好,虽然听说不影响,但 ...