我正在编写一个测试自动化程序,它必须与遗留 MFC 应用程序中的网格控件交互。我的程序必须连接到旧版应用程序,并从网格中读取数据。
我知道包含网格的窗口的 HWND,所以现在我需要找到与该 HWND 关联的 CWnd 派生类指针。CWnd::FromHandlePermanent似乎是我的朋友,但是是的,我知道,您不能CWnd::FromHandlePermanent从应用程序外部调用,因为 AFX 窗口映射 ( afxMapHWND()) 仅包含在目标应用程序中。
因此,我将一个 DLL(使用CreateRemoteThread/ LoadLibrary)注入到目标应用程序中,并让它调用FromHandlePermanent. 但即使这样也不够好,因为我不在HWND的线程中,并且afxMapHWND()正在查看错误的本地线程存储。
因此,在我注入的 DLL 中,我还(暂时)将 HWND 的 WndProc (SetWindowLong等)子类化,然后调用SendMessage. 现在我在正确的线程(目标应用程序的主线程)中,我尝试CWnd::FromHandlePermanent用我的 HWND 调用,但它返回 NULL!如果我查看afxMapHWND()->m_permanentMap->m_nCount,我们看到它是 0。因此,permanentMap 中没有附加类,这对我来说似乎是错误的。
那么我怎样才能得到派生的 CWnd 指针呢?
其他一些信息:
- Target 应用程序静态链接到 MFC
- 这是 MFC 的不同版本,windowclass 是 AfxWnd70s
- 我正在使用VS2010编译注入的DLL
- 注入的 DLL 还静态链接到 (VS2010) MFC 库。
这是注入的DLL中的代码:
// this is how we pass the HWND to the target DLL 
// (this shared segment is also loaded in calling app)
#pragma data_seg (".shared")
__declspec(dllexport) HWND  g_hWnd = 0;
#pragma data_seg ()
#pragma comment(linker,"/SECTION:.shared,RWS") 
BEGIN_MESSAGE_MAP(CLibSpyMFCDllApp, CWinApp)
END_MESSAGE_MAP()
CLibSpyMFCDllApp::CLibSpyMFCDllApp()
{
}
CLibSpyMFCDllApp theApp;
extern CHandleMap* PASCAL afxMapHWND(BOOL bCreate = FALSE);
UINT g_WM_GETGRIDDATA = 0;
WNDPROC wpOrigEditProc; 
LRESULT CALLBACK SpySubProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (g_WM_GETGRIDDATA != 0 && message == g_WM_GETGRIDDATA) {
        CWnd * target1 = CWnd::FromHandlePermanent(hWnd);
                // fails - target1 is null
        return 0;
    }
    return CallWindowProc((WNDPROC ) wpOrigEditProc, hWnd, message, wParam, lParam);
}
BOOL CLibSpyMFCDllApp::InitInstance()
{
    CoInitializeEx(NULL,  COINIT_APARTMENTTHREADED);
    CWinApp::InitInstance();
    if (g_hWnd) {
        g_WM_GETGRIDDATA = RegisterWindowMessage(L"GetGridData");
        wpOrigEditProc = (WNDPROC) SetWindowLong(g_hWnd, GWL_WNDPROC, (LONG) SpySubProc);
        ::SendMessage(g_hWnd, g_WM_GETGRIDDATA, 0, 0);
        SetWindowLong(g_hWnd, GWL_WNDPROC, (LONG) wpOrigEditProc); 
    }
    return TRUE;
}