SetFocus()失败并带有有效的窗口句柄

时间:2012-03-10 02:51:18

标签: c++ winapi

我有一个包含多个自定义控件的程序。其中一个自定义控件是文本输入控件。由于单击窗口时窗口不会自动接收键盘焦点,因此我在程序中创建了一个鼠标钩子,当用户在该窗口中单击时,该钩子会在窗口上调用SetFocus()。但是,有一个问题。

如果在单击程序窗口(或该窗口中的任何控件)时其他程序具有焦点,则SetFocus()会失败。然后我必须再次点击它才能成功。这是代码:

LRESULT CALLBACK kbfProc(int nCode, WPARAM wParam, LPARAM lParam) // Keyboard focus switching procedure
{
    switch(nCode)
    {
        case HC_ACTION:
        {
            if(wParam == WM_LBUTTONDOWN || wParam == WM_NCLBUTTONDOWN)
            {
                MOUSEHOOKSTRUCT * mhs = (MOUSEHOOKSTRUCT*) lParam;

                if(SetFocus(mhs->hwnd) == NULL)
                {
                    printf("SetFocus(Hwnd = %.8x) failed. Error code: %lu\n", mhs->hwnd, GetLastError());
                } else {

                    printf("SetFocus(Hwnd = %.8x) returned success.\n", mhs->hwnd);
                }
            }

        }
        break;
    }

    return CallNextHookEx(0, nCode, wParam, lParam);
}

这些printf调用的输出是这样的:

SetFocus(Hwnd = 00410c06) failed. Error code: 87
SetFocus(Hwnd = 00410c06) returned success.
SetFocus(Hwnd = 01740fc8) failed. Error code: 87
SetFocus(Hwnd = 01740fc8) returned success.

错误代码87是ERROR_INVALID_PARAMETER,但我显然是将一个有效的窗口句柄传递给该函数,为什么它会失败?

4 个答案:

答案 0 :(得分:3)

每当您调用SetFocus时,窗口必须附加到调用线程的消息队列,否则SetFocus将返回无效。要解决此问题,请在调用SetFocus之前先将鼠标移到窗口上时使用SetForegroundWindow

答案 1 :(得分:2)

我知道我要迟到几天,但由于我花了整整一天的时间来解决此问题,因此我也会在此处添加此修复程序,以防万一它对某人有所帮助。

基本上是上面在注释中提到的AttachThreadInput东西。 GetActiveWindow()也总是返回NULL。您需要附加到哪个窗口可能因情况而异,但是我需要根窗口,因此我使用了GetAncestor。如果知道所需的窗口句柄,则可以使用它。这是为我修复的代码:

AttachThreadInput(GetCurrentThreadId(), GetWindowThreadProcessId(GetAncestor(hWnd, GA_ROOT), NULL), TRUE);

答案 2 :(得分:1)

我找到了解决方案。我最终做了很多谷歌搜索和尝试了很多不同的事情,我最终遇到了this网页。这是一个冗长的阅读,它有点罗嗦,难以理解,但它有很多信息。我最后在我的主窗口的WM_ACTIVATE处理程序中添加了一些代码,用于搜索激活窗口时单击的子窗口。这是所有代码:

这是钩子程序:

LRESULT CALLBACK kbfProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    switch(nCode)
    {
        case HC_ACTION:
        {
            if(wParam == WM_LBUTTONDOWN || wParam == WM_NCLBUTTONDOWN)
            {
                MOUSEHOOKSTRUCT * mhs = (MOUSEHOOKSTRUCT*) lParam;

                BringWindowToTop(MainWindow->t_hwnd);
                SetFocus(mhs->hwnd);
            }

        }
        break;
    }

    return CallNextHookEx(0, nCode, wParam, lParam);
}

这是我放在WM_ACTIVATE处理程序中的代码:

    case WM_ACTIVATE:
    {
        unsigned long state = (unsigned long) wParam & 0x0000FFFF;
        unsigned long mState = (unsigned long) wParam & 0xFFFF0000;

        if(state != 0)
        {
            ...[some code here]...

            FocusChildWindow(hwnd);
        }

        ...[some code here]...
    }
    break;

这是FocusChildWindow()函数:

void FocusChildWindow(HWND hwnd)
{
    POINT mpos;
    GetCursorPos(&mpos);

    RECT wr;
    GetWindowRect(hwnd, &wr);

    mpos.x -= wr.left;
    mpos.y -= wr.top;

    HWND cw = ChildWindowFromPoint(hwnd, mpos);

    if(cw == NULL || cw == hwnd)
    {
        SetFocus(hwnd);
    } else {

        GetCursorPos(&mpos);
        HWND cw2;

        while(1)
        {
            POINT sc = mpos;
            MapWindowPoints(HWND_DESKTOP, cw, &sc, 1);

            cw2 = ChildWindowFromPoint(cw, sc);

            if(cw2 == NULL || cw2 == cw)
            {
                SetFocus(cw);
                break;
            } else {

                cw = cw2;
            }


        }

    }

}

它就像一个魅力。再次感谢大家对此问题的意见和建议。

答案 3 :(得分:0)

当SetFocus()无法将键盘焦点设置为属性表页面窗口上的子控件窗口时,以下内容对我有用:

::SendMessage(m_hPropPageWnd, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);