窗体两大组成部分,非客户区与客户区.
大部分时候,我们编程都是针对客户区的,那么非客户区包括那几个部分呢?就我知道的列举如下:1.标题.2,标题图标,3,标题上按钮,4,边框,5,滚动条.6.菜单栏.其实工具栏以及状态栏等.所以窗口对象本身也是有很多小对象组成的.
下面看下窗体对象结构:
typedef struct _WINDOW_OBJECT
{
/* NOTE: Do *NOT* Move this pointer anywhere in this structure! This
is a pointer to the WINDOW structure that eventually replaces
the WINDOW_OBJECT structure! USER32 expects this pointer to
be here until WINDOW_OBJECT has completely been superseded! */
PWINDOW Wnd; 窗口基本信息,昨天在"编辑框"里已经分析过
/* Pointer to the thread information */
PW32THREADINFO ti; //WIN32线程
/* Pointer to the desktop */
PDESKTOPINFO Desktop; //桌面对象
/* system menu handle. */
HMENU SystemMenu; //系统菜单对象
/* Entry in the thread's list of windows. */
LIST_ENTRY ListEntry; //线程中的列表位置
/* Handle for the window. */
HWND hSelf; //Handle
/* Window flags. */
ULONG Flags; //
/* Handle of region of the window to be updated. */
HANDLE UpdateRegion; //更新区域
/* Handle of the window region. */
HANDLE WindowRegion; //整个区域
/* Pointer to the owning thread's message queue. */
PUSER_MESSAGE_QUEUE MessageQueue; //消息队列
struct _WINDOW_OBJECT* FirstChild; //窗口树
struct _WINDOW_OBJECT* LastChild;
struct _WINDOW_OBJECT* NextSibling;
struct _WINDOW_OBJECT* PrevSibling;
/* Entry in the list of thread windows. */
LIST_ENTRY ThreadListEntry; //线程列表头
/* Handle to the parent window. */
struct _WINDOW_OBJECT* Parent; //父窗口
/* Handle to the owner window. */
HWND hOwner; //拥有者窗口
/* DC Entries (DCE) */
PDCE Dce; //设备描述符相关信息入口
/* Scrollbar info */
PWINDOW_SCROLLINFO Scroll; //滚动条信息
PETHREAD OwnerThread; //当前线程
HWND hWndLastPopup; /* handle to last active popup window (wine doesn't use pointer, for unk. reason)*/ 最后一次的弹出窗口
ULONG Status;
/* counter for tiled child windows */
ULONG TiledCounter;
/* WNDOBJ list */
LIST_ENTRY WndObjListHead; //线程窗口列表头
} WINDOW_OBJECT; /* PWINDOW_OBJECT already declared at top of file */
其实创建一个窗口过程是蛮复杂的,不管是CreateWindow亦或是CreateWindowEx最终都是调用系统服务:NtUserCreateWindowEx,很显然也是win32k里一导出函数.然后它再调用co_IntCreateWindowEx ---> UserCreateObject创建窗体对象.其实这其中还做了非常多的事情,比如创建SystemMenu,设置窗口菜单,设置HOOK等东西,还有对于非客户区的初始化只是co_IntSendMessage(Window->hSelf, M_NCCREATE, 0, (LPARAM) &Cs)而已.
系统默认的窗口回调函数(这里请注意,除了公共的默认处理函数以外,WINDOWS通用控件也有自己的默认函数):
LRESULT WINAPI
User32DefWindowProc(HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam,
BOOL bUnicode)
{
switch (Msg)
{
case WM_NCPAINT:
{
return DefWndNCPaint(hWnd, (HRGN)wParam, -1); //非客户区画图消息,看下面分解
}
case WM_NCCALCSIZE: //返回需要画的一些状态
{
return DefWndNCCalcSize(hWnd, (BOOL)wParam, (RECT*)lParam);
}
case WM_POPUPSYSTEMMENU: //系统弹出菜单
{
/* This is an undocumented message used by the windows taskbar to //注意,这是一个winodows未公开的消息,主要是工具栏使用
display the system menu of windows that belong to other processes. */
HMENU menu = GetSystemMenu(hWnd, FALSE);
if (menu)
TrackPopupMenu(menu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON, //这个函数在菜单那里已经说得很明白了
LOWORD(lParam), HIWORD(lParam), 0, hWnd, NULL);
return 0;
}
case WM_NCACTIVATE:
return DefWndNCActivate(hWnd, wParam); //只简单的调用下 DefWndNCPaint(hWnd, (HRGN)1, wParam)后return True;
}
case WM_NCHITTEST:
{
POINT Point;
Point.x = GET_X_LPARAM(lParam);
Point.y = GET_Y_LPARAM(lParam);
return (DefWndNCHitTest(hWnd, Point)); //只是简单的判断参数点位置,并返回
}
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
iF10Key = iMenuSysKey = 0; //只是简单判断是否是菜单键
break;
case WM_NCLBUTTONDOWN:
{
return (DefWndNCLButtonDown(hWnd, wParam, lParam)); //看下面分解
}
case WM_LBUTTONDBLCLK:
return (DefWndNCLButtonDblClk(hWnd, HTCLIENT, lParam)); //看下面分解
case WM_NCLBUTTONDBLCLK:
{
return (DefWndNCLButtonDblClk(hWnd, wParam, lParam)); //
}
case WM_WINDOWPOSCHANGING:
{
return (DefWndHandleWindowPosChanging(hWnd, (WINDOWPOS*)lParam)); //看下面分解
}
case WM_WINDOWPOSCHANGED:
{
return (DefWndHandleWindowPosChanged(hWnd, (WINDOWPOS*)lParam)); //看下面分解
}
case WM_NCRBUTTONDOWN: //非客户区按下
{
/* in Windows, capture is taken when right-clicking on the caption bar */
if (wParam == HTCAPTION)
{
SetCapture(hWnd); //此时直接设置鼠标
}
break;
}
case WM_RBUTTONUP:
{
POINT Pt;
if (hWnd == GetCapture())
{
ReleaseCapture();
}
Pt.x = GET_X_LPARAM(lParam);
Pt.y = GET_Y_LPARAM(lParam);
ClientToScreen(hWnd, &Pt);
lParam = MAKELPARAM(Pt.x, Pt.y);
if (bUnicode)
{
SendMessageW(hWnd, WM_CONTEXTMENU, (WPARAM)hWnd, lParam); //右键处理,负责WM_CONTEXTMENU支持
}
else
{
SendMessageA(hWnd, WM_CONTEXTMENU, (WPARAM)hWnd, lParam);
}
break;
}
case WM_NCRBUTTONUP:
/*
* FIXME : we must NOT send WM_CONTEXTMENU on a WM_NCRBUTTONUP (checked
* in Windows), but what _should_ we do? According to MSDN :
* "If it is appropriate to do so, the system sends the WM_SYSCOMMAND
* message to the window". When is it appropriate?
*/
break;
case WM_CONTEXTMENU:
{
if (GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_CHILD)
{
if (bUnicode)
{
SendMessageW(GetParent(hWnd), Msg, wParam, lParam); //由此可见,WM_CONTEXTMENU消息是要往上传递的,本身不是处理,注意windows许多消息是要上传的.
}
else
{
SendMessageA(GetParent(hWnd), WM_CONTEXTMENU, wParam, lParam);
}
}
else
{
POINT Pt;
DWORD Style;
LONG HitCode;
Style = GetWindowLongPtrW(hWnd, GWL_STYLE);
Pt.x = GET_X_LPARAM(lParam);
Pt.y = GET_Y_LPARAM(lParam);
if (Style & WS_CHILD)
{
ScreenToClient(GetParent(hWnd), &Pt);
}
HitCode = DefWndNCHitTest(hWnd, Pt);
if (HitCode == HTCAPTION || HitCode == HTSYSMENU) //系统菜单处理
{
HMENU SystemMenu;
UINT Flags;
if((SystemMenu = GetSystemMenu(hWnd, FALSE)))
{
MenuInitSysMenuPopup(SystemMenu, GetWindowLongPtrW(hWnd, GWL_STYLE),
GetClassLongPtrW(hWnd, GCL_STYLE), HitCode);
if(HitCode == HTCAPTION)
Flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON;
else
Flags = TPM_LEFTBUTTON;
TrackPopupMenu(SystemMenu, Flags,
Pt.x, Pt.y, 0, hWnd, NULL);
}
}
}
break;
}
case WM_PRINT:
{
DefWndPrint(hWnd, (HDC)wParam, lParam); //系统默认打印消息
return (0);
}
case WM_SYSCOLORCHANGE: //系统颜色改变消息
{
/* force to redraw non-client area */
DefWndNCPaint(hWnd, (HRGN)1, -1);
/* Use InvalidateRect to redraw client area, enable
* erase to redraw all subcontrols otherwise send the
* WM_SYSCOLORCHANGE to child windows/controls is required
*/
InvalidateRect(hWnd,NULL,TRUE);
return (0);
}
case WM_PAINTICON:
case WM_PAINT: //处理画系统图标消息
{
PAINTSTRUCT Ps;
HDC hDC = BeginPaint(hWnd, &Ps);
if (hDC)
{
HICON hIcon;
if (GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_MINIMIZE &&
(hIcon = (HICON)GetClassLongPtrW(hWnd, GCL_HICON)) != NULL)
{
RECT ClientRect;
INT x, y;
GetClientRect(hWnd, &ClientRect);
x = (ClientRect.right - ClientRect.left -
GetSystemMetrics(SM_CXICON)) / 2;
y = (ClientRect.bottom - ClientRect.top -
GetSystemMetrics(SM_CYICON)) / 2;
DrawIcon(hDC, x, y, hIcon); //画系统图标对象
}
EndPaint(hWnd, &Ps);
}
return (0);
}
case WM_SYNCPAINT: //??
{
HRGN hRgn;
hRgn = CreateRectRgn(0, 0, 0, 0);
if (GetUpdateRgn(hWnd, hRgn, FALSE) != NULLREGION)
{
RedrawWindow(hWnd, NULL, hRgn,
RDW_ERASENOW | RDW_ERASE | RDW_FRAME |
RDW_ALLCHILDREN);
}
DeleteObject(hRgn);
return (0);
}
case WM_SETREDRAW: //重画消息
{
DefWndSetRedraw(hWnd, wParam);
return (0);
}
case WM_CLOSE: //关闭窗口,注意这里直接执行的是DestroyWindow
{
DestroyWindow(hWnd);
return (0);
}
case WM_MOUSEACTIVATE: //鼠标活动窗口
{
if (GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_CHILD) //又是一个要上传的消息
{
LONG Ret;
if (bUnicode)
{
Ret = SendMessageW(GetParent(hWnd), WM_MOUSEACTIVATE,
wParam, lParam);
}
else
{
Ret = SendMessageA(GetParent(hWnd), WM_MOUSEACTIVATE,
wParam, lParam);
}
if (Ret)
{
return (Ret);
}
}
return ((LOWORD(lParam) >= HTCLIENT) ? MA_ACTIVATE : MA_NOACTIVATE);
}
case WM_ACTIVATE: //激活窗口
{
/* Check if the window is minimized. */
if (LOWORD(wParam) != WA_INACTIVE &&
!(GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_MINIMIZE))
{
SetFocus(hWnd);
}
break;
}
case WM_MOUSEWHEEL: //又是一个要上传的消息
{
if (GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_CHILD)
{
if (bUnicode)
{
return (SendMessageW(GetParent(hWnd), WM_MOUSEWHEEL,
wParam, lParam));
}
else
{
return (SendMessageA(GetParent(hWnd), WM_MOUSEWHEEL,
wParam, lParam));
}
}
break;
}
case WM_ERASEBKGND:
case WM_ICONERASEBKGND:
{
RECT Rect;
HBRUSH hBrush = (HBRUSH)GetClassLongPtrW(hWnd, GCL_HBRBACKGROUND);
if (NULL == hBrush)
{
return 0;
}
if (GetClassLongPtrW(hWnd, GCL_STYLE) & CS_PARENTDC) //CS_PARENTDC决定是否使用父窗口设备描述符
{
/* can't use GetClipBox with a parent DC or we fill the whole parent */
GetClientRect(hWnd, &Rect);
DPtoLP((HDC)wParam, (LPPOINT)&Rect, 2);
}
else
{
GetClipBox((HDC)wParam, &Rect);
}
FillRect((HDC)wParam, &Rect, hBrush);
return (1);
}
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORBTN:
case WM_CTLCOLORDLG:
case WM_CTLCOLORSTATIC:
case WM_CTLCOLORSCROLLBAR: //控件获取颜色消息
return (LRESULT) DefWndControlColor((HDC)wParam, Msg - WM_CTLCOLORMSGBOX);
case WM_CTLCOLOR: //取得控件颜色
return (LRESULT) DefWndControlColor((HDC)wParam, HIWORD(lParam));
case WM_SETCURSOR: //设置鼠标
{
ULONG Style = GetWindowLongPtrW(hWnd, GWL_STYLE);
if (Style & WS_CHILD) //又是一个要上传的消息
{
if (LOWORD(lParam) < HTLEFT || LOWORD(lParam) > HTBOTTOMRIGHT)
{
BOOL bResult;
if (bUnicode)
{
bResult = SendMessageW(GetParent(hWnd), WM_SETCURSOR,
wParam, lParam);
}
else
{
bResult = SendMessageA(GetParent(hWnd), WM_SETCURSOR,
wParam, lParam);
}
if (bResult)
{
return(TRUE);
}
}
}
return (DefWndHandleSetCursor(hWnd, wParam, lParam, Style));
}
case WM_SYSCOMMAND:
return (DefWndHandleSysCommand(hWnd, wParam, lParam)); //Ok
case WM_KEYDOWN:
if(wParam == VK_F10) iF10Key = VK_F10;
break;
/* FIXME: This is also incomplete. */
case WM_SYSKEYDOWN:
{
if (HIWORD(lParam) & KEYDATA_ALT)
{
HWND top = GetAncestor(hWnd, GA_ROOT);
/* if( HIWORD(lParam) & ~KEYDATA_PREVSTATE ) */
if ( (wParam == VK_MENU || wParam == VK_LMENU
|| wParam == VK_RMENU) && !iMenuSysKey )
{
iMenuSysKey = 1;
/* mimic behaviour of XP, sending a WM_SYSCOMMAND when pressing <alt> */
SendMessageW( top, WM_SYSCOMMAND, SC_KEYMENU, 0L );
}
else
iMenuSysKey = 0;
iF10Key = 0;
if (wParam == VK_F4) /* Try to close the window */ //ALT+F4处理函数
{
if (!(GetClassLongPtrW(top, GCL_STYLE) & CS_NOCLOSE))
{
if (bUnicode)
PostMessageW(top, WM_SYSCOMMAND, SC_CLOSE, 0);
else
PostMessageA(top, WM_SYSCOMMAND, SC_CLOSE, 0);
}
}
else if (wParam == VK_SNAPSHOT) //当前窗口截屏消息处理
{
HWND hwnd = hWnd;
while (GetParent(hwnd) != NULL)
{
hwnd = GetParent(hwnd);
}
DefWndScreenshot(hwnd);
}
}
else if( wParam == VK_F10 )
iF10Key = 1;
else if( wParam == VK_ESCAPE && (GetKeyState(VK_SHIFT) & 0x8000))
SendMessageW( hWnd, WM_SYSCOMMAND, SC_KEYMENU, ' ' );
break;
}
case WM_KEYUP:
case WM_SYSKEYUP:
{
/* Press and release F10 or ALT */
if (((wParam == VK_MENU || wParam == VK_LMENU || wParam == VK_RMENU)
&& iMenuSysKey) || ((wParam == VK_F10) && iF10Key))
SendMessageW( GetAncestor( hWnd, GA_ROOT ), WM_SYSCOMMAND, SC_KEYMENU, 0L ); //向祖先窗口传送消息
iMenuSysKey = iF10Key = 0;
break;
}
case WM_SYSCHAR:
{
iMenuSysKey = 0;
if (wParam == '\r' && IsIconic(hWnd))
{
PostMessageW( hWnd, WM_SYSCOMMAND, SC_RESTORE, 0L );
break;
}
if ((HIWORD(lParam) & KEYDATA_ALT) && wParam)
{
if (wParam == '\t' || wParam == '\x1b') break;
if (wParam == ' ' && (GetWindowLongPtrW( hWnd, GWL_STYLE ) & WS_CHILD))
SendMessageW( GetParent(hWnd), Msg, wParam, lParam );
else
SendMessageW( hWnd, WM_SYSCOMMAND, SC_KEYMENU, wParam );
}
else /* check for Ctrl-Esc */
if (wParam != '\x1b') MessageBeep(0);
break;
}
case WM_SHOWWINDOW:
{
if (lParam) // Call when it is necessary.
NtUserMessageCall( hWnd, Msg, wParam, lParam, 0, FNID_DEFWINDOWPROC, FALSE);
break;
}
case WM_CANCELMODE: //前几天在弹出菜单见到过此消息
{
iMenuSysKey = 0;
/* FIXME: Check for a desktop. */
if (!(GetWindowLongPtrW( hWnd, GWL_STYLE ) & WS_CHILD)) EndMenu();
if (GetCapture() == hWnd)
{
ReleaseCapture(); //释放鼠标
}
break;
}
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
return (-1);
/*
case WM_DROPOBJECT:
return DRAG_FILE;
*/
case WM_QUERYDROPOBJECT:
{
if (GetWindowLongPtrW(hWnd, GWL_EXSTYLE) & WS_EX_ACCEPTFILES)
{
return(1);
}
break;
}
case WM_QUERYDRAGICON:
{
UINT Len;
HICON hIcon;
hIcon = (HICON)GetClassLongPtrW(hWnd, GCL_HICON);
if (hIcon)
{
return ((LRESULT)hIcon);
}
for (Len = 1; Len < 64; Len++)
{
if ((hIcon = LoadIconW(NULL, MAKEINTRESOURCEW(Len))) != NULL)
{
return((LRESULT)hIcon);
}
}
return ((LRESULT)LoadIconW(0, IDI_APPLICATION));
}
/* FIXME: WM_ISACTIVEICON */
case WM_NOTIFYFORMAT:
{
if (lParam == NF_QUERY)
return IsWindowUnicode(hWnd) ? NFR_UNICODE : NFR_ANSI;
break;
}
case WM_SETICON: //设置图标
{
INT Index = (wParam != 0) ? GCL_HICON : GCL_HICONSM;
HICON hOldIcon = (HICON)GetClassLongPtrW(hWnd, Index);
SetClassLongPtrW(hWnd, Index, lParam);
SetWindowPos(hWnd, 0, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
SWP_NOACTIVATE | SWP_NOZORDER);
return ((LRESULT)hOldIcon);
}
case WM_GETICON: //获取图标
{
INT Index = (wParam == ICON_BIG) ? GCL_HICON : GCL_HICONSM;
return (GetClassLongPtrW(hWnd, Index));
}
case WM_HELP: //帮助,上传消息
{
if (bUnicode)
{
SendMessageW(GetParent(hWnd), Msg, wParam, lParam);
}
else
{
SendMessageA(GetParent(hWnd), Msg, wParam, lParam);
}
break;
}
case WM_SYSTIMER: //系统时钟,处理插入符的,昨天在编辑框时候就提到过
{
THRDCARETINFO CaretInfo;
switch(wParam)
{
case 0xffff: /* Caret timer */ //插入符TIMER的ID
/* switch showing byte in win32k and get information about the caret */
if(NtUserSwitchCaretShowing(&CaretInfo) && (CaretInfo.hWnd == hWnd))
{
DrawCaret(hWnd, &CaretInfo);
}
break;
}
break;
}
case WM_QUERYOPEN:
case WM_QUERYENDSESSION:
{
return (1);
}
case WM_INPUTLANGCHANGEREQUEST://????
{
HKL NewHkl;
if(wParam & INPUTLANGCHANGE_BACKWARD
&& wParam & INPUTLANGCHANGE_FORWARD)
{
return FALSE;
}
//FIXME: What to do with INPUTLANGCHANGE_SYSCHARSET ?
if(wParam & INPUTLANGCHANGE_BACKWARD) NewHkl = (HKL) HKL_PREV;
else if(wParam & INPUTLANGCHANGE_FORWARD) NewHkl = (HKL) HKL_NEXT;
else NewHkl = (HKL) lParam;
NtUserActivateKeyboardLayout(NewHkl, 0);
return TRUE;
}
case WM_INPUTLANGCHANGE: //???
{
int count = 0;
HWND *win_array = WIN_ListChildren( hWnd );
if (!win_array)
break;
while (win_array[count])
SendMessageW( win_array[count++], WM_INPUTLANGCHANGE, wParam, lParam);
HeapFree(GetProcessHeap(),0,win_array);
break;
}
case WM_QUERYUISTATE: //查询UI状态
{
LRESULT Ret = 0;
PWINDOW Wnd = ValidateHwnd(hWnd);
if (Wnd != NULL)
{
if (Wnd->HideFocus)
Ret |= UISF_HIDEFOCUS;
if (Wnd->HideAccel)
Ret |= UISF_HIDEACCEL;
}
return Ret;
}
case WM_CHANGEUISTATE: //UI状态改变消息处理
{
BOOL AlwaysShowCues = FALSE;
WORD Action = LOWORD(wParam);
WORD Flags = HIWORD(wParam);
PWINDOW Wnd;
SystemParametersInfoW(SPI_GETKEYBOARDCUES, 0, &AlwaysShowCues, 0);
if (AlwaysShowCues)
break;
Wnd= ValidateHwnd(hWnd);
if (!Wnd || lParam != 0)
break;
if (Flags & ~(UISF_HIDEFOCUS | UISF_HIDEACCEL | UISF_ACTIVE))
break;
if (Flags & UISF_ACTIVE)
{
WARN("WM_CHANGEUISTATE does not yet support UISF_ACTIVE!\n");
}
if (Action == UIS_INITIALIZE)
{
PDESKTOPINFO Desk = GetThreadDesktopInfo();
if (Desk == NULL)
break;
Action = Desk->LastInputWasKbd ? UIS_CLEAR : UIS_SET;
Flags = UISF_HIDEFOCUS | UISF_HIDEACCEL;
/* We need to update wParam in case we need to send out messages */
wParam = MAKEWPARAM(Action, Flags);
}
switch (Action)
{
case UIS_SET:
/* See if we actually need to change something */
if ((Flags & UISF_HIDEFOCUS) && !Wnd->HideFocus)
break;
if ((Flags & UISF_HIDEACCEL) && !Wnd->HideAccel)
break;
/* Don't need to do anything... */
return 0;
case UIS_CLEAR:
/* See if we actually need to change something */
if ((Flags & UISF_HIDEFOCUS) && Wnd->HideFocus)
break;
if ((Flags & UISF_HIDEACCEL) && Wnd->HideAccel)
break;
/* Don't need to do anything... */
return 0;
default:
WARN("WM_CHANGEUISTATE: Unsupported Action 0x%x\n", Action);
break;
}
if ((Wnd->Style & WS_CHILD) && Wnd->Parent != NULL)
{
/* We're a child window and we need to pass this message down until
we reach the root */
hWnd = UserHMGetHandle((PWINDOW)DesktopPtrToUser(Wnd->Parent));
}
else
{
/* We're a top level window, we need to change the UI state */
Msg = WM_UPDATEUISTATE;
}
if (bUnicode)
return SendMessageW(hWnd, Msg, wParam, lParam);
else
return SendMessageA(hWnd, Msg, wParam, lParam);
}
case WM_UPDATEUISTATE: //更新UI状态
{
BOOL Change = TRUE;
BOOL AlwaysShowCues = FALSE;
WORD Action = LOWORD(wParam);
WORD Flags = HIWORD(wParam);
PWINDOW Wnd;
SystemParametersInfoW(SPI_GETKEYBOARDCUES, 0, &AlwaysShowCues, 0);
if (AlwaysShowCues)
break;
Wnd = ValidateHwnd(hWnd);
if (!Wnd || lParam != 0)
break;
if (Flags & ~(UISF_HIDEFOCUS | UISF_HIDEACCEL | UISF_ACTIVE))
break;
if (Flags & UISF_ACTIVE)
{
WARN("WM_UPDATEUISTATE does not yet support UISF_ACTIVE!\n");
}
if (Action == UIS_INITIALIZE)
{
PDESKTOPINFO Desk = GetThreadDesktopInfo();
if (Desk == NULL)
break;
Action = Desk->LastInputWasKbd ? UIS_CLEAR : UIS_SET;
Flags = UISF_HIDEFOCUS | UISF_HIDEACCEL;
/* We need to update wParam for broadcasting the update */
wParam = MAKEWPARAM(Action, Flags);
}
switch (Action)
{
case UIS_SET:
/* See if we actually need to change something */
if ((Flags & UISF_HIDEFOCUS) && !Wnd->HideFocus)
break;
if ((Flags & UISF_HIDEACCEL) && !Wnd->HideAccel)
break;
/* Don't need to do anything... */
Change = FALSE;
break;
case UIS_CLEAR:
/* See if we actually need to change something */
if ((Flags & UISF_HIDEFOCUS) && Wnd->HideFocus)
break;
if ((Flags & UISF_HIDEACCEL) && Wnd->HideAccel)
break;
/* Don't need to do anything... */
Change = FALSE;
break;
default:
WARN("WM_UPDATEUISTATE: Unsupported Action 0x%x\n", Action);
return 0;
}
/* Pack the information and call win32k */
if (Change)
{
if (!NtUserCallTwoParam((DWORD)hWnd, (DWORD)Flags | ((DWORD)Action << 3), TWOPARAM_ROUTINE_ROS_UPDATEUISTATE))
break;
}
/* Always broadcast the update to all children */
EnumChildWindows(hWnd, //向所有窗口发送UI更新消息
UserSendUiUpdateMsg,
(LPARAM)wParam);
break;
}
}
return 0;
}
windows是消息驱动的,上面的默认函数可以说是消息机制最重要的部分之一,消息来源:1.大部分消息来源于键盘和鼠标,或其引起的. 2.来源于本身的消息处理函数. 3.来源于系统. 4.来源于其他的应用程序,当然也可以把这归结于来源于系统.废话不多说.具体看默认窗口过程调用的函数吧.
LRESULT
DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active) //系统默认的非客户区画图函数
{
HDC hDC;
DWORD Style, ExStyle;
HWND Parent;
RECT ClientRect, WindowRect, CurrentRect, TempRect;
if (!IsWindowVisible(hWnd)) //通过判断当前窗口以父窗口的句柄(!(Wnd->Style & WS_VISIBLE))
return 0;
Style = GetWindowLongPtrW(hWnd, GWL_STYLE);
hDC = GetDCEx(hWnd, hRgn, DCX_WINDOW | DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN); //获得需要更新的DC,如果没有需要更新则直接退出
if (hDC == 0)
{
return 0;
}
Parent = GetParent(hWnd);
ExStyle = GetWindowLongPtrW(hWnd, GWL_EXSTYLE);
if (Active == -1)
{
if (ExStyle & WS_EX_MDICHILD)
{
Active = IsChild(GetForegroundWindow(), hWnd);
if (Active)
Active = (hWnd == (HWND)SendMessageW(Parent, WM_MDIGETACTIVE, 0, 0));
}
else
{
Active = (GetForegroundWindow() == hWnd);
}
}
GetWindowRect(hWnd, &WindowRect); //取得整个窗口RECT
GetClientRect(hWnd, &ClientRect); //取得客户区RECT
CurrentRect.top = CurrentRect.left = 0; //计算非客户区RECT
CurrentRect.right = WindowRect.right - WindowRect.left;
CurrentRect.bottom = WindowRect.bottom - WindowRect.top; //以0为边界,取得整个RECT
/* Draw outer edge */
if (UserHasWindowEdge(Style, ExStyle)) //通过EXSTYLE类型,比如WS_THICKFRAME来判断
{
DrawEdge(hDC, &CurrentRect, EDGE_RAISED, BF_RECT | BF_ADJUST); //画连线,这里是直接调用API画线,代码很简单,这里不列
} else
if (ExStyle & WS_EX_STATICEDGE)
{
#if 0
DrawEdge(hDC, &CurrentRect, BDR_SUNKENINNER, BF_RECT | BF_ADJUST | BF_FLAT);
#else
SelectObject(hDC, GetSysColorBrush(COLOR_BTNSHADOW)); //画阴影
PatBlt(hDC, CurrentRect.left, CurrentRect.top, CurrentRect.right - CurrentRect.left, 1, PATCOPY); //上下阴影
PatBlt(hDC, CurrentRect.left, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY);
SelectObject(hDC, GetSysColorBrush(COLOR_BTNHIGHLIGHT)); //左右阴影颜色
PatBlt(hDC, CurrentRect.left, CurrentRect.bottom - 1, CurrentRect.right - CurrentRect.left, 1, PATCOPY);
PatBlt(hDC, CurrentRect.right - 1, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY);
InflateRect(&CurrentRect, -1, -1);
#endif
}
/* Firstly the "thick" frame */
if ((Style & WS_THICKFRAME) && !(Style & WS_MINIMIZE))
{
DWORD Width =
(GetSystemMetrics(SM_CXFRAME) - GetSystemMetrics(SM_CXDLGFRAME)) *
GetSystemMetrics(SM_CXBORDER);
DWORD Height =
(GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYDLGFRAME)) *
GetSystemMetrics(SM_CYBORDER);
SelectObject(hDC, GetSysColorBrush(Active ? COLOR_ACTIVEBORDER :
COLOR_INACTIVEBORDER));
/* Draw frame */ 画边框
PatBlt(hDC, CurrentRect.left, CurrentRect.top, CurrentRect.right - CurrentRect.left, Height, PATCOPY);
PatBlt(hDC, CurrentRect.left, CurrentRect.top, Width, CurrentRect.bottom - CurrentRect.top, PATCOPY);
#ifdef __REACTOS__
PatBlt(hDC, CurrentRect.left, CurrentRect.bottom - 1, CurrentRect.right - CurrentRect.left, -Height, PATCOPY);
PatBlt(hDC, CurrentRect.right - 1, CurrentRect.top, -Width, CurrentRect.bottom - CurrentRect.top, PATCOPY);
#else
PatBlt(hDC, CurrentRect.left, CurrentRect.bottom, CurrentRect.right - CurrentRect.left, -Height, PATCOPY);
PatBlt(hDC, CurrentRect.right, CurrentRect.top, -Width, CurrentRect.bottom - CurrentRect.top, PATCOPY);
#endif
InflateRect(&CurrentRect, -Width, -Height); //计算大小
}
/* Now the other bit of the frame */
if (Style & (WS_DLGFRAME | WS_BORDER) || ExStyle & WS_EX_DLGMODALFRAME)
{
DWORD Width = GetSystemMetrics(SM_CXBORDER);
DWORD Height = GetSystemMetrics(SM_CYBORDER);
SelectObject(hDC, GetSysColorBrush(
(ExStyle & (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE)) ? COLOR_3DFACE :
(ExStyle & WS_EX_STATICEDGE) ? COLOR_WINDOWFRAME :
(Style & (WS_DLGFRAME | WS_THICKFRAME)) ? COLOR_3DFACE :
COLOR_WINDOWFRAME));
/* Draw frame */
PatBlt(hDC, CurrentRect.left, CurrentRect.top, CurrentRect.right - CurrentRect.left, Height, PATCOPY);
PatBlt(hDC, CurrentRect.left, CurrentRect.top, Width, CurrentRect.bottom - CurrentRect.top, PATCOPY);
#ifdef __REACTOS__
PatBlt(hDC, CurrentRect.left, CurrentRect.bottom - 1, CurrentRect.right - CurrentRect.left, -Height, PATCOPY);
PatBlt(hDC, CurrentRect.right - 1, CurrentRect.top, -Width, CurrentRect.bottom - CurrentRect.top, PATCOPY);
#else
PatBlt(hDC, CurrentRect.left, CurrentRect.bottom, CurrentRect.right - CurrentRect.left, -Height, PATCOPY);
PatBlt(hDC, CurrentRect.right, CurrentRect.top, -Width, CurrentRect.bottom - CurrentRect.top, PATCOPY);
#endif
InflateRect(&CurrentRect, -Width, -Height);
}
/* Draw caption */ //画标题
if ((Style & WS_CAPTION) == WS_CAPTION) 判断是否存在标题
{
DWORD CaptionFlags = DC_ICON | DC_TEXT | DC_BUTTONS; //默认的存在图标,Caption以及按钮
HPEN PreviousPen;
BOOL Gradient = FALSE;
if(SystemParametersInfoW(SPI_GETGRADIENTCAPTIONS, 0, &Gradient, 0) && Gradient)
{
CaptionFlags |= DC_GRADIENT; //判断标题是否运行渐近色
}
TempRect = CurrentRect;
if (Active)
{
CaptionFlags |= DC_ACTIVE; //活动窗口
}
if (ExStyle & WS_EX_TOOLWINDOW)
{
CaptionFlags |= DC_SMALLCAP; //小图标
TempRect.bottom = TempRect.top + GetSystemMetrics(SM_CYSMCAPTION) - 1; 这里注意,如果是WS_EX_TOOLWINDOW高度大小不一样
CurrentRect.top += GetSystemMetrics(SM_CYSMCAPTION);
}
else
{
TempRect.bottom = TempRect.top + GetSystemMetrics(SM_CYCAPTION) - 1;
CurrentRect.top += GetSystemMetrics(SM_CYCAPTION);
}
NtUserDrawCaption(hWnd, hDC, &TempRect, CaptionFlags); //调用系统服务,来画标题,但是这里只画窗口图标以及TEXT
/* Draw buttons */
if (Style & WS_SYSMENU) //画系统按钮
{
UserDrawCaptionButton(&TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONCLOSE);
if ((Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(ExStyle & WS_EX_TOOLWINDOW)) //工具栏窗口是没有最小化按钮和最大化按钮
{
UserDrawCaptionButton(&TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMIN);
UserDrawCaptionButton(&TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMAX);
}
}
if(!(Style & WS_MINIMIZE))
{
/* Line under caption */ //画TEXT下方线条
PreviousPen = SelectObject(hDC, GetSysColorPen(
((ExStyle & (WS_EX_STATICEDGE | WS_EX_CLIENTEDGE |
WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE) ?
COLOR_WINDOWFRAME : COLOR_3DFACE));
MoveToEx(hDC, TempRect.left, TempRect.bottom, NULL);
LineTo(hDC, TempRect.right, TempRect.bottom);
SelectObject(hDC, PreviousPen);
}
}
if(!(Style & WS_MINIMIZE))
{
HMENU menu = GetMenu(hWnd);
/* Draw menu bar */ //画菜单栏
if (menu && !(Style & WS_CHILD))
{
TempRect = CurrentRect;
TempRect.bottom = TempRect.top + (UINT)NtUserSetMenuBarHeight(menu, 0);
CurrentRect.top += MenuDrawMenuBar(hDC, &TempRect, hWnd, FALSE);
}
if (ExStyle & WS_EX_CLIENTEDGE) //画客户区四周的边线
{
DrawEdge(hDC, &CurrentRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
}
/* Draw the scrollbars */ //画滚动条
if ((Style & WS_VSCROLL) && (Style & WS_HSCROLL) &&
IntIsScrollBarVisible(hWnd, OBJID_VSCROLL) && IntIsScrollBarVisible(hWnd, OBJID_HSCROLL))
{
RECT ParentClientRect;
TempRect = CurrentRect;
if (ExStyle & WS_EX_LEFTSCROLLBAR)
TempRect.right = TempRect.left + GetSystemMetrics(SM_CXVSCROLL);
else
TempRect.left = TempRect.right - GetSystemMetrics(SM_CXVSCROLL);
TempRect.top = TempRect.bottom - GetSystemMetrics(SM_CYHSCROLL);
FillRect(hDC, &TempRect, GetSysColorBrush(COLOR_BTNFACE));
/* FIXME: Correct drawing of size-box with WS_EX_LEFTSCROLLBAR */
if(Parent)
GetClientRect(Parent, &ParentClientRect);
if (HASSIZEGRIP(Style, ExStyle, GetWindowLongPtrW(Parent, GWL_STYLE), WindowRect, ParentClientRect))
{
DrawFrameControl(hDC, &TempRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); //这里和画标题按钮调用的是一个函数,只是参数不同,滚动条的LINEBUTTON
}
IntDrawScrollBar(hWnd, hDC, SB_VERT); //画滚动条具体调用
IntDrawScrollBar(hWnd, hDC, SB_HORZ);
}
else
{ //这里可能有点问题,因为没有计算CurrentRect
if (Style & WS_VSCROLL && IntIsScrollBarVisible(hWnd, OBJID_VSCROLL))
IntDrawScrollBar(hWnd, hDC, SB_VERT);
else if (Style & WS_HSCROLL && IntIsScrollBarVisible(hWnd, OBJID_HSCROLL))
IntDrawScrollBar(hWnd, hDC, SB_HORZ);
}
}
ReleaseDC(hWnd, hDC);
return 0;
}
处理系统按钮函数
LRESULT
DefWndNCLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case HTCAPTION:
{
HWND hTopWnd = GetAncestor(hWnd, GA_ROOT);
if (SetActiveWindow(hTopWnd) || GetActiveWindow() == hTopWnd)
{
SendMessageW(hWnd, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, lParam);
}
break;
}
case HTSYSMENU:
{
if (GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_SYSMENU)
{
SendMessageW(hWnd, WM_SYSCOMMAND, SC_MOUSEMENU + HTSYSMENU,
lParam);
}
break;
}
case HTMENU:
{
SendMessageW(hWnd, WM_SYSCOMMAND, SC_MOUSEMENU + HTMENU, lParam);
break;
}
case HTHSCROLL:
{
SendMessageW(hWnd, WM_SYSCOMMAND, SC_HSCROLL + HTHSCROLL, lParam);
break;
}
case HTVSCROLL:
{
SendMessageW(hWnd, WM_SYSCOMMAND, SC_VSCROLL + HTVSCROLL, lParam);
break;
}
case HTMINBUTTON:
case HTMAXBUTTON:
case HTCLOSE:
{
DefWndDoButton(hWnd, wParam); //也是通过发送WM_SYSCOMMAND消息处理
break;
}
case HTLEFT:
case HTRIGHT:
case HTTOP:
case HTBOTTOM:
case HTTOPLEFT:
case HTTOPRIGHT:
case HTBOTTOMLEFT:
case HTBOTTOMRIGHT:
{
SendMessageW(hWnd, WM_SYSCOMMAND, SC_SIZE + wParam - (HTLEFT - WMSZ_LEFT), lParam);
break;
}
}
return(0);
}
以上函数可以看到,几乎所有处理都是通过WM_SYSCOMMAND,可见WM_SYSCOMMAND之重要程度
鼠标双击函数
LRESULT
DefWndNCLButtonDblClk(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
ULONG Style;
Style = GetWindowLongPtrW(hWnd, GWL_STYLE);
switch(wParam)
{
case HTCAPTION:
{
/* Maximize/Restore the window */
if((Style & WS_CAPTION) == WS_CAPTION && (Style & WS_MAXIMIZEBOX))
{
SendMessageW(hWnd, WM_SYSCOMMAND, ((Style & (WS_MINIMIZE | WS_MAXIMIZE)) ? SC_RESTORE : SC_MAXIMIZE), 0);
}
break;
}
case HTSYSMENU:
{
SendMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
break;
}
default:
return DefWndNCLButtonDown(hWnd, wParam, lParam);
}
return(0);
}
可见,双击函数也是通过WM_SYSCOMMAND处理的,其实这也说明了一个问题,那就是系统默认的本身只处理系统相关的命令,比如WM_SYSCOMMAND,至于应用程序可以自己处理.
正在移动处理消息
LRESULT
DefWndHandleWindowPosChanging(HWND hWnd, WINDOWPOS* Pos)
{
POINT maxTrack, minTrack;
LONG style = GetWindowLongPtrA(hWnd, GWL_STYLE);
if (Pos->flags & SWP_NOSIZE) return 0;
if ((style & WS_THICKFRAME) || ((style & (WS_POPUP | WS_CHILD)) == 0))
{
WinPosGetMinMaxInfo(hWnd, NULL, NULL, &minTrack, &maxTrack); //取得可以移动的最大位置,以下根据其处理
Pos->cx = min(Pos->cx, maxTrack.x);
Pos->cy = min(Pos->cy, maxTrack.y);
if (!(style & WS_MINIMIZE))
{
if (Pos->cx < minTrack.x) Pos->cx = minTrack.x;
if (Pos->cy < minTrack.y) Pos->cy = minTrack.y;
}
}
else
{
Pos->cx = max(Pos->cx, 0);
Pos->cy = max(Pos->cy, 0);
}
return 0;
}
移动改变完成处理消息
LRESULT
DefWndHandleWindowPosChanged(HWND hWnd, WINDOWPOS* Pos)
{
RECT Rect;
GetClientRect(hWnd, &Rect);
MapWindowPoints(hWnd, (GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_CHILD ?
GetParent(hWnd) : NULL), (LPPOINT) &Rect, 2);
if (! (Pos->flags & SWP_NOCLIENTMOVE)) //判断是否SWP_NOCLIENTMOVE,是就发送WM_MOVE消息
{
SendMessageW(hWnd, WM_MOVE, 0, MAKELONG(Rect.left, Rect.top)); //
}
if (! (Pos->flags & SWP_NOCLIENTSIZE))
{
WPARAM wp = SIZE_RESTORED;
if (IsZoomed(hWnd))
{
wp = SIZE_MAXIMIZED;
}
else if (IsIconic(hWnd))
{
wp = SIZE_MINIMIZED;
}
SendMessageW(hWnd, WM_SIZE, wp, //可以看到WM_SIZE也是此时发送的
MAKELONG(Rect.right - Rect.left, Rect.bottom - Rect.top));
}
return 0;
}
系统命令处理消息函数
LRESULT
DefWndHandleSysCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
WINDOWPLACEMENT wp;
POINT Pt;
#if 0
if (ISITHOOKED(WH_CBT))
{
if (NtUserMessageCall( hWnd, WM_SYSCOMMAND, wParam, lParam, 0, FNID_DEFWINDOWPROC, FALSE))
return 0;
}
#endif
switch (wParam & 0xfff0)
{
case SC_MOVE:
case SC_SIZE: //移动窗口
DefWndDoSizeMove(hWnd, wParam);
break;
case SC_MINIMIZE: //最小化窗口
wp.length = sizeof(WINDOWPLACEMENT);
if(GetWindowPlacement(hWnd, &wp))
{
wp.showCmd = SW_MINIMIZE;
SetWindowPlacement(hWnd, &wp);
}
break;
case SC_MAXIMIZE: //最大化窗口
wp.length = sizeof(WINDOWPLACEMENT);
if(GetWindowPlacement(hWnd, &wp))
{
wp.showCmd = SW_MAXIMIZE;
SetWindowPlacement(hWnd, &wp);
}
break;
case SC_RESTORE: //还原窗口
wp.length = sizeof(WINDOWPLACEMENT);
if(GetWindowPlacement(hWnd, &wp))
{
wp.showCmd = SW_RESTORE;
SetWindowPlacement(hWnd, &wp);
}
break;
case SC_CLOSE: //关闭窗口
SendMessageA(hWnd, WM_CLOSE, 0, 0);
break;
case SC_MOUSEMENU: //弹出系统菜单
{
Pt.x = (short)LOWORD(lParam);
Pt.y = (short)HIWORD(lParam);
MenuTrackMouseMenuBar(hWnd, wParam & 0x000f, Pt);
}
break;
case SC_KEYMENU: //键盘弹出系统菜单
MenuTrackKbdMenuBar(hWnd, wParam, (WCHAR)lParam);
break;
case SC_VSCROLL:
case SC_HSCROLL: //滚动条处理
{
Pt.x = (short)LOWORD(lParam);
Pt.y = (short)HIWORD(lParam);
DefWndTrackScrollBar(hWnd, wParam, Pt);
}
break;
default:
/* FIXME: Implement */
UNIMPLEMENTED;
break;
}
return(0);
}
通过以上学习,可以实现:
1.如何通过WM_SYSCOMMAND弹出系统菜单:PostMessage(Handle,WM_SYSCOMMAND,SC_KEYMENU + HTSYSMENU,ord(' ')); 此时KEY = ' '.类似于alt + ' '
2.如何通过WM_SYSCOMMAND改变大小:SendMessage(Handle, WM_SYSCOMMAND, SC_SIZE + WMSZ_BOTTOM, 0);此时WMSZ_BOTTOM可以是WMSZ_TOP,WMSZ_LEFT,WMSZ_RIGHT任意一个.也用+号可以合并使用.
----------Henry 20100710