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

首先我们知道有几种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[n]='\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会被隐式加载的原因。

原文地址:https://www.cnblogs.com/duanshuiliu/p/2577402.html