当前位置:编程学习 > VC++ >>

VC++动态链接库编程之MFC扩展 DLL

DLL类型 入口函数
非 MFC DLL 编程者提供DllMain函数
MFC规则 DLL CWinApp对象的InitInstance 和 ExitInstance
MFC扩展 DLL MFC DLL向导生成DllMain 函数

  对于MFC扩展DLL,系统会自动在工程中添加如下表所示的宏,这些宏为DLL和应用程序的编写提供了方便。像AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA这样的宏,在DLL和应用程序中将具有不同的定义,这取决于_AFXEXT宏是否被定义。这使得在DLL和应用程序中,使用统一的一个宏就可以表示出输出和输入的不同意思。在DLL中,表示输出(因为_AFXEXT被定义,通常是在编译器的标识参数中指定/D_AFXEXT);在应用程序中,则表示输入(_AFXEXT没有定义)。

定义
AFX_CLASS_IMPORT __declspec(dllexport)
AFX_API_IMPORT __declspec(dllexport)
AFX_DATA_IMPORT __declspec(dllexport)
AFX_CLASS_EXPORT __declspec(dllexport)
AFX_API_EXPORT __declspec(dllexport)
AFX_DATA_EXPORT __declspec(dllexport)
AFX_EXT_CLASS #ifdef _AFXEXT
 AFX_CLASS_EXPORT
#else
 AFX_CLASS_IMPORT
AFX_EXT_API #ifdef _AFXEXT
 AFX_API_EXPORT
#else
 AFX_API_IMPORT
AFX_EXT_DATA #ifdef _AFXEXT
 AFX_DATA_EXPORT
#else
 AFX_DATA_IMPORT

  6.2 MFC扩展DLL导出MFC派生类

  在这个例子中,我们将产生一个名为“ExtDll”的MFC扩展DLL工程,在这个DLL中导出一个对话框类,这个对话框类派生自MFC类CDialog。

  使用MFC向导生成MFC扩展DLL时,系统会自动添加如下代码:

static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };
extern "C" int APIENTRY

DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
{
 // Remove this if you use lpReserved

 UNREFERENCED_PARAMETER( lpReserved );

 //说明:lpReserved是一个被系统所保留的参数,对于隐式链接是一个非零值,对于显式链接值是零

 if (dwReason == DLL_PROCESS_ATTACH)
 {
  TRACE0( "EXTDLL.DLL Initializing! " );
  // Extension DLL one-time initialization
  if ( !AfxInitExtensionModule( ExtDllDLL, hInstance ))
   return 0;
   // Insert this DLL into the resource chain
  new CDynLinkLibrary( ExtDllDLL );
 }
 else if (dwReason == DLL_PROCESS_DETACH)
 {
  TRACE0( "EXTDLL.DLL Terminating! " );
  // Terminate the library before destructors are called
  AfxTermExtensionModule( ExtDllDLL );
 }
 return 1; // ok
}

  这一段代码含义晦涩,我们需要对其进行解读:

  (1)上述代码完成MFC扩展DLL的初始化和终止处理;

  (2)初始化期间所创建的 CDynLinkLibrary 对象使MFC扩展 DLL 可以将 DLL中的CRuntimeClass 对象或资源导出到应用程序;

  (3)AfxInitExtensionModule函数捕获模块的CRuntimeClass 结构和在创建 CDynLinkLibrary 对象时使用的对象工厂(COleObjectFactory 对象);

  (4)AfxTermExtensionModule函数使 MFC 得以在每个进程与扩展 DLL 分离时(进程退出或使用AfxFreeLibrary卸载DLL时)清除扩展 DLL;

  (5)第一条语句static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };定义了一个AFX_EXTENSION_MODULE类的静态全局对象,AFX_EXTENSION_MODULE的定义如下:

struct AFX_EXTENSION_MODULE
{
 BOOL bInitialized;
 HMODULE hModule;
 HMODULE hResource;
 CRuntimeClass* pFirstSharedClass;
 COleObjectFactory* pFirstSharedFactory;
};

  由AFX_EXTENSION_MODULE的定义我们可以更好的理解(2)、(3)、(4)点。

  在资源编辑器中添加一个如图15所示的对话框,并使用MFC类向导为其添加一个对应的类CExtDialog,系统自动添加了ExtDialog.h和ExtDialog.cpp两个头文件。


图15 MFC扩展DLL中的对话框

  修改ExtDialog.h中CExtDialog类的声明为:

class AFX_EXT_CLASS CExtDialog : public CDialog
{
 public:
  CExtDialog( CWnd* pParent = NULL );
  enum { IDD = IDD_DLL_DIALOG };
 protected:
  virtual void DoDataExchange( CDataExchange* pDX );
  DECLARE_MESSAGE_MAP()
};

  这其中最主要的改变是我们在class AFX_EXT_CLASS CExtDialog语句中添加了“AFX_EXT_CLASS”宏,则使得DLL中的CExtDialog类被导出。
 6.3 MFC扩展DLL的加载

  6.3.1 隐式加载

  我们在6.2工程所在的工作区中添加一个LoadExtDllDlg工程,用于演示MFC扩展DLL的加载。在LoadExtDllDlg工程中添加一个如图16所示的对话框,这个对话框上包括一个“调用DLL”按钮。


图16 MFC扩展DLL调用工程中的对话框

  在与图16对应对话框类实现文件的头部添加:

// LoadExtDllDlg.cpp : implementation file
//

#include "..ExtDialog.h"
#pragma comment( lib, "ExtDll.lib" )

而“调用DLL”按钮的单击事件的消息处理函数为:

void CLoadExtDllDlg::OnDllcallButton()
{
 CExtDialog extDialog;
 extDialog.DoModal();
}

  当我们单击“调用DLL”的时候,弹出了如图15的对话框。

  为提供给用户隐式加载(MFC扩展DLL一般使用隐式加载,具体原因见下节),MFC扩展DLL需要提供三个文件:

  (1)描述DLL中扩展类的头文件;

  (2)与动态链接库对应的.LIB文件;

  (3)动态链接库.DLL文件本身。

  有了这三个文件,应用程序的开发者才可充分利用MFC扩展DLL。

  6.3.2 显示加载

  显示加载MFC扩展DLL应使用MFC全局函数AfxLoadLibrary而不是WIN32 API中的LoadLibrary。AfxLoadLibrary 最终也调用了 LoadLibrary这个API,但是在调用之前进行了线程同步的处理。

  AfxLoadLibrary 的函数原型与 LoadLibrary完全相同,为:

HINSTANCE AFXAPI AfxLoadLibrary( LPCTSTR lpszModuleName );
  与之相对应的是,MFC 应用程序应使用AfxFreeLibrary 而非FreeLibrary 卸载MFC扩展DLL。AfxFreeLibrary的函数原型也与 FreeLibrary完全相同,为:

BOOL AFXAPI AfxFreeLibrary( HINSTANCE hInstLib );
  如果我们把上例中的“调用DLL”按钮单击事件的消息处理函数改为:

void CLoadExtDllDlg::OnDllcallButton()
{
 HINSTANCE hDll = AfxLoadLibrary( "ExtDll.dll" );
 if(NULL == hDll)
 {
  AfxMessageBox( "MFC扩展DLL动态加载失败" );
  return;
 }

 CExtDialog extDialog;
 extDialog.DoModal();
 AfxFreeLibrary(hDll);
}

  则工程会出现link错误:

LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual __thiscall CExtDialog::~CExtDialog(void)" (__imp_??1CExtDialogUAE@XZ)

LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall CExtDialog::CExtDialog(class CWnd *)" (__imp_??0CExtDialogQAE@PAVCWnd@Z)

  提示CExtDialog的构造函数和析构函数均无法找到!是的,对于派生MFC类的MFC扩展DLL,当我们要在应用程序中使用DLL中定义的派生类时,我们不宜使用动态加载DLL的方法。

  6.4 MFC扩展DLL加载MFC扩展DLL

  我们可以在MFC扩展DLL中再次使用MFC扩展DLL,但是,由于在两个DLL中对于AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA宏的定义都是输出,这会导致调用的时候出现问题。

  我们将会在调用MFC扩展DLL的DLL中看到link错误:

error LNK2001: unresolved external symbol ….......
  因此,在调用MFC扩展DLL的MFC扩展DLL中,在包含被调用DLL的头文件之前,需要临时重
补充:软件开发 , Vc ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,