zoukankan      html  css  js  c++  java
  • [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会被隐式加载的原因。

  • 相关阅读:
    hdu1425
    iOS 纯代码跳转到Xib界面和Storyboard界面
    iOS 获取当前app的 App Store 版本号
    iOS 延时方法,定时器。等待一段时间在执行
    iOS获取当前app的名称和版本号及其他信息(持续维护)
    iOS 截取字符串(持续更新)截取、匹配、分隔
    iOS 屏幕大小尺寸,分辨率,代码获取屏幕大小(持续维护添加)
    iOS NSString只保留字符串中的数字
    iOS NSlog打印数据不完全,xcode处理办法
    iOS Xcode 10.3 xib:-1:未能找到或创建描述的执行上下文“<IBCocoaTouchPlatformToolDescript
  • 原文地址:https://www.cnblogs.com/duanshuiliu/p/2577402.html
Copyright © 2011-2022 走看看