MFC中DLL的创建
可创建的MFC DLL有三种:
① Regular DLLusing shared MFC DLL: MFC动态链接常规DLL。需要MFC动态链接库。该类型DLL既可以被MFC程序使用也可以被一般的Win32程序使用。
② Regular DLLwith MFC statically linked:MFC静态链接常规DLL。这种DLL将要使用的MFC的类代码直接加入到DLL中,从而使得DLL增大。但这样可以使得程序无需依赖MFC环境,不需要具有MFC动态链接库。该类型DLL既可以被MFC程序使用也可以被一般的Win32程序使用。
③ MFCextension DLL:MFC扩展DLL。该种类型的DLL要求可以链接到MFC动态链接库。故MFC扩展DLL是在MFC动态链接库基础上创建的。也就是说,要使用该DLL,首先要有MFC动态链接库,并且只能被MFC程序使用。
第三种MFC DLL工程创建后,会有一个DllMain函数,但前两种DLL工程没有:
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
……
}
一般来说,不用对该函数做改动。
封装自定义代码
常用的封装有两种:纯函数封装与类封装。
无论是哪一种封装,编译后,都会在Debug或Release文件夹下生成许多文件。其中需要用到的只有.DLL文件与.lib文件,除此之外还有相关的.h文件。
使用自定义DLL需要三个文件:.h、.DLL与.lib。
① 将这三个文件复制到新工程的根目录下。
② 在新工程中将.h文件导入工程并在需要的地方#include。
③ 在需要调用DLL中自定义类的地方
#pragma comment(lib,"MyDll.lib")
然后就可以正常使用DLL中封装的类了。
lib文件是DLL文件的一个代理,记录了DLL中的函数名,但不含实现代码。exe程序会与lib文件进行链接,从而调用DLL中的具体函数。
一.要将一个纯函数封装到DLL中,则:
1. 纯函数的声明与实现一定要分开。声明在.h文件中,实现在.cpp文件中。
2. 将.h与.cpp正常加入到工程中,然后修改.h中的类声明:
将
bool MyFunction();
修改为:
extern "C" _declspec(dllexport) bool MyFunction();
编译即可。
二.要将一个自定义类封装到DLL中,则:
1. 将该类的.h与.cpp正常加入到工程中,然后修改.h中的类声明:
将
class CYuEdit :public CEdit
修改为:
class AFX_EXT_CLASS CYuEdit : public CEdit
AFX_EXT_CLASS关键字用于导出DLL中的函数。只有这样才能使exe程序调用DLL中的函数。
编译即可。
三.要将一个资源封装到MFC DLL中,则:
1. 若将一个对话框封装在DLL里,首先添加一个对话框资源,然后为该对话框添加类。该类中会绑定该对话框:
enum { IDD = IDD_DIALOG_TEST};
注意该对话框资源是存在于DLL工程中的,所以在exe中无法识别该资源。可以在DLL中写一个纯函数用于显示对话框,而不要将对话框类的.h直接导入引用工程中。
2. 若需要将.BMP与Icon等资源封装到DLL中,那么直接将相应资源添加进DLL工程,编译即可。比如添加了一个BMP,在DLL的Resource.h该BMP定义为:
#define IDB_BITMAP_TEST 2001
使用资源时,将DLL与lib导入后,新工程依然不能识别上面的BMP的ID。为了可以正确识别,首先要在新工程的Resource.h中定义需要使用的资源,也就是将
#define IDB_BITMAP_TEST 2001
添加到新工程的Resource.h中。
然后就可以引用了。
特别注意两个Resource.h中的BMP资源定义必须相同。
HMODULE等同于HINSTANCE 。
HINSTANCE hDllModule; //DLL句柄
hDllModule = LoadLibrary(_T("DLL_TEST.dll"));
if (hDllModule)
{
//执行某些操作
FreeLibrary(hDllModule);
}
上面代码用于获取DLL的句柄以及释放DLL。注意LoadLibrary ()与FreeLibrary()是对应的。
根据DLL句柄,可以用两种方法获取DLL中的资源(推荐使用第二种):
① 使用FindResource()与LoadResource()配合。该方法可以获取所有类型资源。注意FindResource()要求传入一个HMODULE,而HMODULE与HINSTANCE 是相同的,直接传入HINSTANCE 即可。
② 使用各种资源相应的Load函数。如BITMAP的函数为LoadBitmap()。
示例代码:
① 使用FindResource()与LoadResource()配合。
HRSRC hRsrc = FindResource(hDllModule, MAKEINTRESOURCE(IDB_BITMAP_TEST), RT_BITMAP);
if (NULL == hRsrc)
{
return FALSE;
}
//获取资源的大小
DWORD dwSize = SizeofResource(NULL, hRsrc);
if (0 == dwSize)
{
return FALSE;
}
//加载资源
HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
if (NULL == hGlobal)
{
return FALSE;
}
//锁定资源
LPVOID pBuffer = LockResource(hGlobal);
if (NULL == pBuffer)
{
return FALSE;
}
//执行某些操作
//释放资源
FreeResource(hGlobal);
注意LockResource()与FreeResource()是对应的。
② 使用各种资源相应的Load函数。
HBITMAP hBitmap=LoadBitmap(hDllModule,MAKEINTRESOURCE(IDB_BITMAP_TEST)); //从DLL中读取BITMAP资源
四.纯资源DLL
若要将所有的资源封装进一个DLL中,然后为所有程序共享,那么就需要编写纯资源DLL。纯资源DLL内部只含纯资源。资源DLL使用的是Win32 DLL。
创建项目时,选择Win32Project,在后续的Application Setting页面的Application Type中选择DLL。
然后直接将相应资源添加进DLL工程。
为了避免编译出现错误“: error LNK2001:unresolved external symbol [email protected]”,在project右键选则properties,Linker->Input->AdditionalDependencies填入:
Debug:加入 msvcrtd.lib
Release:加入 msvcrt.lib
其中Debug与Release的选择在左上角。也可直接选择All Configurations。
编译即可。
纯资源DLL用法跟上面三中2一样。但纯资源DLL没有lib文件,只有DLL文件。
对于上面的三与四来说,要引用DLL中的资源就必须保证DLL中的Resource.h与新工程中的Resource.h有相同的资源定义。
但这样是很不方便的,使用者是无法知道DLL中Resource.h的定义的。所以,一般都使用另一种方法:
① 创建DLL工程并添加一个资源。比如:
#define IDB_BITMAP1 1001
② 找到该资源在Resource.h中的定义,也就是上面的代码。然后为该资源修改一个别名:
#define IDB_BITMAP_TEST 1001
③ 新建一个.h文件。该文件用于定义所有共享函数与共享资源。然后将上面的代码复制到该.h文件中。
④ 将DLL及lib加入新工程后,也要将.h加入新工程。这样,当需要使用DLL中的资源时,就可以通过.h文件得知资源名称与ID。
但该方法依然需要在新工程的Resource.h中再次定义,或者在新工程使用函数前定义。