这是WINDOWS程序设计里第6章鼠标测试里的一段代码,我想测试一下WM_SETFOCUS消息的触发条件,就在父窗口的消息处理函数里的WM_SETFOCUS后面加了几行代码,结果确在弹出一个窗口后点确定又会弹出,如此一共会弹出16个,这是什么意思啊,难道父窗口里产生了16个WM_SETFOCUS消息?或者是我对窗口的某些操作导致它又产生了WM_SETFOCUS消息?还有就是为什么子窗口创建后再点击子窗口,父窗口没有接收到WM_SETFOCUS消息?
我记得programming  windows上的原话是"实际上当你在子窗口上单击的时候,情况有些复杂,这是是父窗口而不是自窗口获取焦点"难道说这句话有问题?#include <windows.h>#define DIVISIONS 5
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;LRESULT CALLBACK ChildWndProc (HWND, UINT, WPARAM, LPARAM) ;int idFocus = 0 ;TCHAR szChildClass[] = TEXT ("Checker4_Child") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static TCHAR szAppName[] = TEXT ("Checker4") ;HWND hwnd ;MSG msg ;WNDCLASS wndclass ;wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = WndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;wndclass.lpszMenuName = NULL ;wndclass.lpszClassName = szAppName ;if (!RegisterClass (&wndclass)){MessageBox (NULL, TEXT ("Program requires Windows NT!"),szAppName, MB_ICONERROR) ;return 0 ;}wndclass.lpfnWndProc = ChildWndProc ;wndclass.cbWndExtra = sizeof (long) ;wndclass.hIcon = NULL ;wndclass.lpszClassName = szChildClass ;RegisterClass (&wndclass) ;hwnd = CreateWindow (szAppName, TEXT ("Checker4 Mouse Hit-Test Demo"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam){static HWND hwndChild[DIVISIONS][DIVISIONS] ;int cxBlock, cyBlock, x, y ;HWND hwnd1;switch (message){case WM_CREATE :for (x = 0 ; x < DIVISIONS ; x++)for (y = 0 ; y < DIVISIONS ; y++)hwndChild[x][y] = CreateWindow (szChildClass, NULL,WS_CHILDWINDOW | WS_VISIBLE,0, 0, 0, 0,hwnd, (HMENU) (y << 8 | x),HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ;return 0 ;case WM_SIZE :cxBlock = LOWORD (lParam) / DIVISIONS ;cyBlock = HIWORD (lParam) / DIVISIONS ;for (x = 0 ; x < DIVISIONS ; x++)for (y = 0 ; y < DIVISIONS ; y++)MoveWindow ( hwndChild[x][y],x * cxBlock, y * cyBlock,cxBlock, cyBlock, TRUE) ;return 0 ;case WM_LBUTTONDOWN :MessageBeep (0) ;return 0 ;
// On set-focus message, set focus to child windowcase WM_SETFOCUS:hwnd1=SetFocus (GetDlgItem (hwnd, idFocus)) ;
wsprintf((LPWSTR)buffer,L"%d",hwnd1)
MessageBox(hwnd,(LPCWSTR)buffer,(LPCWSTR)"",NULL);
return 0 ;
// On key-down message, possibly change the focus window
case WM_KEYDOWN:x = idFocus & 0xFF ;y = idFocus >> 8 ;
switch (wParam){case VK_UP: y-- ; break ;case VK_DOWN: y++ ; break ;case VK_LEFT: x-- ; break ;case VK_RIGHT: x++ ; break ;case VK_HOME: x = y = 0 ; break ;case VK_END: x = y = DIVISIONS - 1 ; break ;default: return 0 ;}
x = (x + DIVISIONS) % DIVISIONS ;y = (y + DIVISIONS) % DIVISIONS ;
idFocus = y << 8 | x ;
SetFocus (GetDlgItem (hwnd, idFocus)) ;return 0 ;
case WM_DESTROY :PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}
LRESULT CALLBACK ChildWndProc (HWND hwnd, UINT message,WPARAM wParam, LPARAM lParam){HDC hdc ;PAINTSTRUCT ps ;RECT rect ;switch (message){case WM_CREATE :SetWindowLong (hwnd, 0, 0) ; // on/off flagreturn 0 ;
case WM_KEYDOWN:// Send most key presses to the parent windowif (wParam != VK_RETURN && wParam != VK_SPACE){SendMessage (GetParent (hwnd), message, wParam, lParam) ;return 0 ;}// For Return and Space, fall through to toggle the squarecase WM_LBUTTONDOWN :SetWindowLong (hwnd, 0, 1 ^ GetWindowLong (hwnd, 0)) ;SetFocus (hwnd) ;InvalidateRect (hwnd, NULL, FALSE) ;return 0 ;
// For focus messages, invalidate the window for repaintcase WM_SETFOCUS:idFocus = GetWindowLong (hwnd, GWL_ID) ;
// Fall through
case WM_KILLFOCUS:InvalidateRect (hwnd, NULL, TRUE) ;return 0 ;case WM_PAINT :hdc = BeginPaint (hwnd, &ps) ;GetClientRect (hwnd, &rect) ;Rectangle (hdc, 0, 0, rect.right, rect.bottom) ;
// Draw the "x" if (GetWindowLong (hwnd, 0)){MoveToEx (hdc, 0, 0, NULL) ;LineTo (hdc, rect.right, rect.bottom) ;MoveToEx (hdc, 0, rect.bottom, NULL) ;LineTo (hdc, rect.right, 0) ;}// Draw the "focus" rectangleif (hwnd == GetFocus ()){rect.left += rect.right / 10 ;rect.right -= rect.left ;rect.top += rect.bottom / 10 ;rect.bottom -= rect.top ;
SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;SelectObject (hdc, CreatePen (PS_DASH, 0, 0)) ;Rectangle (hdc, rect.left, rect.top, rect.right, rect.bottom) ;DeleteObject (SelectObject (hdc, GetStockObject (BLACK_PEN))) ;}
EndPaint (hwnd, &ps) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}

解决方案 »

  1.   

    我没有测试你的代码,但我看了一下,你在窗口的某个控件获得焦点时就会弹出一个信息框。这样做会使原来的对话框上的控件失去焦点,当你点信息框的确定关闭信息框后,焦点又重回到了原来的那个控件,那样应该再次触发WM_SETFOCUS才对,以此推理,在弹出这个信息框后应该进入一个死循环而不是16个才对。我建一个win32工程试一下你的代码。
      

  2.   

    我没有测试你的代码,但我看了一下,你在窗口的某个控件获得焦点时就会弹出一个信息框。这样做会使原来的对话框上的控件失去焦点,当你点信息框的确定关闭信息框后,焦点又重回到了原来的那个控件,那样应该再次触发WM_SETFOCUS才对,以此推理,在弹出这个信息框后应该进入一个死循环而不是16个才对。 我建一个win32工程试一下你的代码。 
    ------------------
    同意  我刚想这么说呢。
      

  3.   

    不好意思,看来还是要测试一下才行。我错了!
    上面的代码(当然你的buffer要增加定义)我运行后,出现了12次信息框后就不再出了。但是如果把MessageBox()的第一个参数改为NULL就是死循环,看来这个MessageBox()作为一个子窗口运行时还真有点奇怪呢。后来我定义了一个全局变量int n;,然后将MessageBox处改为
    n++;
    sprintf(buffer, "%d:%d", hwnd, n);
    SetWindowText(hwnd, buffer);
    结果窗口标题就如期显示了自己的句柄和一个1,一切正常。
    至此,为何MessageBox()的第一个参数使用主窗口句柄后没有成为死循环,也就是弹出一定量的信息框后为何就中再触发WM_SETFOCUS,没搞清楚。而且我这里在这个时间窗口标题都消失了。如果切换至其它程序再切回该程序,则WM_SETFOCUS又重新恢复到以前的情况。最后,看来这个奇怪的现象是MessageBox这个函数造成的,原来如何我不知,看有哪个喜欢刨根问底的人来解开这个疑问吧。
      

  4.   


    MessageBox 不是什么地方都能用的。比如这个问题中,楼主就不该用 MessageBox 函数输出数据;可以改用其它方式,如:写文件、输出调试信息等。===========================================================不过,换句话说,能通过这个问题了解WINDOWS的底层处理机制也不错。不过,这个问题我没研究过。
      

  5.   

    收藏了,WINDOWS真的很令人郁闷,难道就这样结贴了?可是上面各位的回答很难令我满意啊。还有没有高手能给我详细解答下?
      

  6.   

    在VC6.0里有一个小工具叫SPY++,你用他看窗口收到了多少SETFOCUS不就行了吗!