首页 > 解决方案 > c++ 活动窗口句柄与我的基于 MFC 对话框的应用程序的 m_hwnd 不匹配

问题描述

有人可以知道使用哪个句柄(而不是 m_hWnd)来匹配钩子回调报告的活动窗口句柄吗?

我认为基于 MFC 对话框的应用程序的 m_hWnd 会匹配钩子回调报告的活动句柄窗口。

下面是一个小示例(基于 MFC 对话框的应用程序),它在标题栏中显示其主窗口句柄 (m_hWnd) 和 CEdit 控件中的活动窗口句柄。

在此处输入图像描述

我设置了一个钩子来检测活动窗口何时更改

h_event_hook = SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, NULL, 
                               &window_change_hook, 0, 0, WINEVENT_OUTOFCONTEXT);
void CALLBACK window_change_hook(HWINEVENTHOOK hWinEventHook, DWORD event, 
                                 HWND hwnd, LONG idObject, LONG idChild, 
                                 DWORD dwEventThread, DWORD dwmsEventTime)
{
    ::PostMessage(h_this_app_wnd, WM_UPDATE_ACTIVE_WINDOW_CEDIT, (WPARAM)0, (LPARAM)hwnd);
}

对话框标题显示其句柄如下:

CString hwnd_text;
hwnd_text.Format("MonitorActiveWindow (%p)", m_hWnd);
SetWindowText(hwnd_text);

在重现问题的 MWE(最小工作示例)下方:

#include "stdafx.h"
#include "MonitorActiveWindow.h"
#include "MonitorActiveWindowDlg.h"
#include "afxdialogex.h"
#include <string>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define WM_UPDATE_ACTIVE_WINDOW_CEDIT           WM_APP + 0x1001

static HWND h_this_app_wnd;
static HWINEVENTHOOK h_event_hook;

void CALLBACK window_change_hook(HWINEVENTHOOK hWinEventHook, DWORD event, 
                                 HWND hwnd, LONG idObject, LONG idChild, 
                                 DWORD dwEventThread, DWORD dwmsEventTime)
{
    ::PostMessage(h_this_app_wnd, WM_UPDATE_ACTIVE_WINDOW_CEDIT, (WPARAM)0, (LPARAM)hwnd);
}

LRESULT CMonitorActiveWindowDlg::OnUpdateActiveWindowCedit(WPARAM w_param, LPARAM l_param)
{
    const auto h_wnd = reinterpret_cast<HWND>(l_param);
    CString text;
    text.Format("%p", h_wnd);
    GetDlgItem(IDC_EDIT1)->SetWindowText(text);
    return 0;
}

BOOL CMonitorActiveWindowDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    // TODO: Add extra initialization here

    // Display the main window handle
    CString hwnd_text;
    hwnd_text.Format("MonitorActiveWindow (%p)", m_hWnd);
    SetWindowText(hwnd_text);

    // Keep a copy of m_hWnd just so the callback can call PostMessage
    h_this_app_wnd = m_hWnd;

    // Set a hook to detect when the active window changes
    h_event_hook = SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, NULL, 
                                   &window_change_hook, 0, 0, WINEVENT_OUTOFCONTEXT);

    return TRUE;  // return TRUE  unless you set the focus to a control
}

void CMonitorActiveWindowDlg::OnDestroy()
{
    CDialogEx::OnDestroy();
    UnhookWinEvent(h_event_hook);
}

CMonitorActiveWindowDlg::CMonitorActiveWindowDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_MONITORACTIVEWINDOW_DIALOG, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMonitorActiveWindowDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_EDIT1, m_output_cedit);
}

BEGIN_MESSAGE_MAP(CMonitorActiveWindowDlg, CDialogEx)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_WM_DESTROY()
    ON_MESSAGE(WM_UPDATE_ACTIVE_WINDOW_CEDIT, OnUpdateActiveWindowCedit)
END_MESSAGE_MAP()

void CMonitorActiveWindowDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // Center icon in client rectangle
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Draw the icon
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialogEx::OnPaint();
    }
}

HCURSOR CMonitorActiveWindowDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

标签: c++mfc

解决方案


编辑控件似乎是只读的编辑控件。此控件获得默认焦点,并报告此编辑控件的窗口句柄。您可以将编辑控件样式更改为“禁用”,这样它就不会从父对话框中窃取焦点。

如果您对焦点窗口不感兴趣,您可以寻找前景窗口EVENT_SYSTEM_FOREGROUND,例如

SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, 
        NULL, &window_change_hook, 0, 0, WINEVENT_OUTOFCONTEXT);
...

void CALLBACK window_change_hook(HWINEVENTHOOK, DWORD, HWND hwnd, LONG, LONG, DWORD, DWORD)
{
    CString str;
    GetWindowText(GetAncestor(hwnd, GA_ROOT), str.GetBuffer(100), 100);
    str.ReleaseBuffer();
    ::SetDlgItemText(h_this_app_wnd, IDC_EDIT1, str);
}

推荐阅读