我有一个包含多个自定义控件的程序。其中一个自定义控件是文本输入控件。由于单击窗口时窗口不会自动接收键盘焦点,因此我在程序中创建了一个鼠标钩子,当用户在该窗口中单击时,该钩子会在窗口上调用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,但我显然是将一个有效的窗口句柄传递给该函数,为什么它会失败?
答案 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);