您的位置:首页 > 其它

[VC]MFC程序动态调用plugin DLL的方式

2012-07-05 11:26 609 查看
首先我们知道有几种VC可以创建的DLL:

第一种 非MFC的DLL,这是通过DLL形式的win32 project来创建的,这种DLL的入口函数形如:

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;
}


这种DLL可以被MFC或者非MFC的程序使用,可以认为是系统级的win32 DLL。当然可以在这种DLL加入MFC的头文件在内部使用MFC的类。

第二种 MFC规则DLL,通过MFC DLL模板里选中regular DLL来创建,这种DLL会定义一个派生自CWinApp的类,由这个类的InitInstance()完成DLL的初始化,内部提供DllMain函数,比如:

CCalcModuleRgApp theApp;

// CCalcModuleRgApp initialization

BOOL CCalcModuleRgApp::InitInstance()
{
CWinApp::InitInstance();

return TRUE;
}


这种DLL可以是动态链接也可以是静态链接到主程序,其输出函数可以被所有win32的程序使用。

第三种 MFC扩展DLL,通过MFC DLL模板里选中extension DLL来创建,主要用于输出可以被MFC程序可以使用的类,它没有一个从CWinApp继承的类,入口函数形如:

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// Remove this if you use lpReserved
UNREFERENCED_PARAMETER(lpReserved);

if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("CalcModule.DLL Initializing!\n");

// Extension DLL one-time initialization
if (!AfxInitExtensionModule(CalcModuleDLL, hInstance))
return 0;

new CDynLinkLibrary(CalcModuleDLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("CalcModule.DLL Terminating!\n");

// Terminate the library before destructors are called
AfxTermExtensionModule(CalcModuleDLL);
}
return 1;   // ok
}


这种DLL只被用MFC类库编写的程序调用,应用程序必须有一个从CWinApp派生的类。我们知道在使用常规DLL的资源时,必须使用 AFX_MANAGE_STATE(AfxGetStaticModuleState( )) 来切换资源句柄到DLL模块,但是这个调用在扩展DLL是不能使用的,否则编译器会提示

error LNK2005: _DllMain@12 already defined in dllmain.obj

扩展dll在DllMain初始化时会将资源链入主程序,一般不会出现找不到资源的情况。

回到正题,我们的plugin DLL要求是能够被动态装载的,通过检查DLL是否输出预定义格式的函数来检查DLL是否是我们的plugin,下面使用规则DLL来实现一个简单的例子:

HWND hWndCaller=NULL;
CCalcEventLog* pCalcEventLogCaller=NULL;
extern "C" __declspec(dllexport) void QueryModule(__out LPTSTR szDescription,__in   int nMaxCount)
{
TCHAR szModuleDesc[]=_T("算法1");
int n=_tcslen(szModuleDesc);
n=n>nMaxCount-1?nMaxCount-1:n;
_tcsncpy(szDescription,szModuleDesc,n);
szDescription
='\0';
}

extern "C" __declspec(dllexport) void ConfigModule(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CDllDialog dllDialog;
dllDialog.DoModal();
}

extern "C" __declspec(dllexport) void InitModule(HWND hWnd,CCalcEventLog* pCalcEventLog)
{
hWndCaller=hWnd;
pCalcEventLogCaller=pCalcEventLog;
}

extern "C" __declspec(dllexport) void DoCalc()
{
CString strMsg;
for(int i=1;i<100;i++)
{
strMsg.Format(_T("%d"),i);
if(pCalcEventLogCaller!=NULL) pCalcEventLogCaller->LogMessge(strMsg);
Sleep(100);
}

}


在主程序中这样来调用:

HINSTANCE hDll = AfxLoadLibrary(_T("CalcModuleRg.dll"));
if(NULL == hDll)
{
AfxMessageBox(_T("DLL动态加载失败"));
return;
}

//查找querymodule函数得到算法描述
QueryModule QueryModuleFunc=(QueryModule) GetProcAddress(hDll,"QueryModule");
if(QueryModuleFunc!=NULL)
{
TCHAR szModuleDesc[255];
QueryModuleFunc(szModuleDesc,255);
AfxMessageBox(szModuleDesc);

//调用配置函数
ConfigModule ConfigModuleFunc=(ConfigModule) GetProcAddress(hDll,"ConfigModule");
if(ConfigModuleFunc!=NULL)
ConfigModuleFunc();

//初始化算法模块
CMyCalcEventLog myCalcEventLog;
InitModule InitModuleFunc=(InitModule) GetProcAddress(hDll,"InitModule");
if(InitModuleFunc!=NULL) InitModuleFunc(NULL,&myCalcEventLog);

//调用计算方法
DoCalc DoCalcFunc=(DoCalc) GetProcAddress(hDll,"DoCalc");
if(DoCalcFunc!=NULL) DoCalcFunc();
}

AfxFreeLibrary(hDll);


那么使用MFC扩展DLL是否也可以呢?首先我们在扩展DLL中输出一个函数来返回我们运算类的一个对象指针:

extern "C" BOOL AFX_EXT_API GetCalcObj(void** calcObj)
{
*calcObj=new MyCalcObj();
return TRUE;
}


在主程序中我们试图动态装载这个扩展DLL来获取这个MyCalcObj对象:

typedef   BOOL  (*GETCALCOBJ)(void**);
void LoadDllAndRunExt()
{
HINSTANCE hDll = AfxLoadLibrary(_T("CalcModule.dll"));//其实已经在装载EXE时调入
if(NULL == hDll)
{
AfxMessageBox(_T("MFC扩展DLL动态加载失败"));
return;
}

MyCalcObj* pCalcObj=NULL;
GETCALCOBJ GetCalcObjFunc=(GETCALCOBJ) GetProcAddress(hDll,"GetCalcObj");
if(GetCalcObjFunc!=NULL)
{
GetCalcObjFunc((void**) &pCalcObj);
if(pCalcObj!=NULL)
{
pCalcObj->Do();
delete pCalcObj;
}
}

AfxFreeLibrary(hDll);

}


这样程序是能正常运行的,但是如果我们把这个扩展DLL从EXE的运行目录中移除,程序在启动时就会提示找不到动态链接库,而移除掉规则DLL主程序EXE仍然是能够正常启动的,这意味着扩展DLL是被exe启动时隐式加载的,这和我们要求plugin DLL能够被动态检测到是不一致的。实际上如果我们删除掉扩展DLL的lib文件,主程序是不能编译通过的,想想其实原因很简单,链接程序是要去搜索pCalcObj->Do()这样函数的地址的,既然不是通过GeProdAddress来动态获得的,编译器就必须在链接时通过lib来查找了,这就是为什么扩展DLL会被隐式加载的原因。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: