MFC DLL中如何響應PreTranslateMessage消息

最近項目中使用到MFC,由於工程是DLL的,在使用ToolTip時碰到非模態對話框無法顯示的問題。查了一番資料,發現原因是由於:雖然MFC Regular DLL派生了CWinApp類,並有一個theApp全局對象。但它不包含CWinApp::Run機制,主消息由exe負責接收、分發,導致DLL的PreTranslateMessage不生效。參考資料://www.cnblogs.com/hanford/p/6177904.html#_Toc425102281 第2.5 PreTranslateMessage小節。該文中提到使DLL調用PreTranslateMessage的方法,但對於NX二次開發來說無法實現,畢竟主exe是已經封裝好的。又經過一陣搜索,發現鉤子函數可以解決,具體方法如下:

第一步:在App類中定義鉤子和對話框變數

class CTestApp : public CWinApp
{
public:
    CTestApp();
    CTestDlg* m_dlg;
    HHOOK m_hHook;

第二步:構造函數中初始化成員變數

CTestApp::CTestApp():m_hHook(NULL),m_dlg(NULL)
{
    // TODO: 在此處添加構造程式碼,
    // 將所有重要的初始化放置在 InitInstance 中
}

第三步:定義鉤子函數

紅色標識的條件千萬不能省掉,否則可能導致介面卡死

class CTestApp : public CWinApp
{
public:
    CTestApp();
    CTestDlg* m_dlg;
    HHOOK m_hHook;
// 重寫
public:
    virtual BOOL InitInstance();

    DECLARE_MESSAGE_MAP()
    virtual int ExitInstance();
    static LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam);
};
LRESULT CALLBACK CTestApp::GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
    LPMSG lpMsg = (LPMSG)lParam;
    if( (AfxGetApp()->PreTranslateMessage(lpMsg)) && (lpMsg->hwnd == theApp.m_dlg->m_hWnd))
    {        
        theApp.m_dlg->PreTranslateMessage(lpMsg);    
    }
    return CallNextHookEx(theApp.m_hHook, nCode, wParam, lParam);
}

第四步:安裝鉤子

LRESULT CALLBACK CTestApp::GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
    LPMSG lpMsg = (LPMSG)lParam;
    if( (AfxGetApp()->PreTranslateMessage(lpMsg)) && (lpMsg->hwnd == theApp.m_dlg->m_hWnd))
    {        
        theApp.m_dlg->PreTranslateMessage(lpMsg);    
    }
    return CallNextHookEx(theApp.m_hHook, nCode, wParam, lParam);
}

第五步:卸載鉤子

int CTestApp::ExitInstance()
{
    // TODO: 在此添加專用程式碼和/或調用基類
    UnhookWindowsHookEx((HHOOK)m_hHook);
    return CWinApp::ExitInstance();
}

第六步:創建非模態對話框

static UF_MB_cb_status_t  test(
    UF_MB_widget_t             widget,
    UF_MB_data_t               client_data,
    UF_MB_activated_button_p_t call_button )
{
    if (UF_initialize() != 0) 
        return UF_MB_CB_ERROR;

    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    
    if (theApp.m_dlg == NULL)
    {
        theApp.m_dlg = new CTestDlg();
        theApp.m_dlg->Create(CTestDlg::IDD);
    }
    
    theApp.m_dlg->ShowWindow(SW_NORMAL);
    UF_terminate();   
    return UF_MB_CB_CANCEL;
}

大功告成!測試: