内容目录
背景
从底层角度来分析,借鉴reactos 系统(一款兼容window软件的系统 ),从代码找到他们里面的实现,我猜测是对话框无非是从配置创建对应的窗口。
验证
搜索创建对话框的代码 DialogBoxParamW,然后找到实现代码
/***********************************************************************
* DIALOG_CreateIndirect
* Creates a dialog box window
*
* modal = TRUE if we are called from a modal dialog box.
* (it's more compatible to do it here, as under Windows the owner
* is never disabled if the dialog fails because of an invalid template)
*/
static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
HWND owner, DLGPROC dlgProc, LPARAM param,
BOOL unicode, HWND *modal_owner )
{
HWND hwnd;
RECT rect;
POINT pos;
SIZE size;
DLG_TEMPLATE template;
DIALOGINFO * dlgInfo = NULL;
DWORD units = GetDialogBaseUnits();
HWND disabled_owner = NULL;
HMENU hMenu = 0;
HFONT hUserFont = 0;
UINT flags = 0;
UINT xBaseUnit = LOWORD(units);
UINT yBaseUnit = HIWORD(units);
/* Parse dialog template */
if (!dlgTemplate) return 0;
dlgTemplate = DIALOG_ParseTemplate32( dlgTemplate, &template );
/* Load menu */
if (template.menuName) hMenu = LoadMenuW( hInst, template.menuName );
/* Create custom font if needed */
if (template.style & DS_SETFONT)
{
HDC dc = GetDC(0);
if (template.pointSize == 0x7fff)
{
/* We get the message font from the non-client metrics */
NONCLIENTMETRICSW ncMetrics;
ncMetrics.cbSize = sizeof(NONCLIENTMETRICSW);
if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
sizeof(NONCLIENTMETRICSW), &ncMetrics, 0))
{
hUserFont = CreateFontIndirectW( &ncMetrics.lfMessageFont );
}
}
else
{
/* We convert the size to pixels and then make it -ve. This works
* for both +ve and -ve template.pointSize */
int pixels = MulDiv(template.pointSize, GetDeviceCaps(dc , LOGPIXELSY), 72);
hUserFont = CreateFontW( -pixels, 0, 0, 0, template.weight,
template.italic, FALSE, FALSE, DEFAULT_CHARSET, 0, 0,
PROOF_QUALITY, FF_DONTCARE,
template.faceName );
}
if (hUserFont)
{
SIZE charSize;
HFONT hOldFont = SelectObject( dc, hUserFont );
charSize.cx = GdiGetCharDimensions( dc, NULL, &charSize.cy );
if (charSize.cx)
{
xBaseUnit = charSize.cx;
yBaseUnit = charSize.cy;
}
SelectObject( dc, hOldFont );
}
ReleaseDC(0, dc);
TRACE("units = %d,%d\n", xBaseUnit, yBaseUnit );
}
/* Create dialog main window */
SetRect(&rect, 0, 0, MulDiv(template.cx, xBaseUnit, 4), MulDiv(template.cy, yBaseUnit, 8));
if (template.style & DS_CONTROL)
template.style &= ~(WS_CAPTION|WS_SYSMENU);
template.style |= DS_3DLOOK;
if (template.style & DS_MODALFRAME)
template.exStyle |= WS_EX_DLGMODALFRAME;
if ((template.style & DS_CONTROL) || !(template.style & WS_CHILD))
template.exStyle |= WS_EX_CONTROLPARENT;
AdjustWindowRectEx( &rect, template.style, (hMenu != 0), template.exStyle );
pos.x = rect.left;
pos.y = rect.top;
size.cx = rect.right - rect.left;
size.cy = rect.bottom - rect.top;
if (template.x == CW_USEDEFAULT16)
{
pos.x = pos.y = CW_USEDEFAULT;
}
else
{
HMONITOR monitor = 0;
MONITORINFO mon_info;
mon_info.cbSize = sizeof(mon_info);
if (template.style & DS_CENTER)
{
monitor = MonitorFromWindow( owner ? owner : GetActiveWindow(), MONITOR_DEFAULTTOPRIMARY );
GetMonitorInfoW( monitor, &mon_info );
pos.x = (mon_info.rcWork.left + mon_info.rcWork.right - size.cx) / 2;
pos.y = (mon_info.rcWork.top + mon_info.rcWork.bottom - size.cy) / 2;
}
else if (template.style & DS_CENTERMOUSE)
{
GetCursorPos( &pos );
monitor = MonitorFromPoint( pos, MONITOR_DEFAULTTOPRIMARY );
GetMonitorInfoW( monitor, &mon_info );
}
else
{
pos.x += MulDiv(template.x, xBaseUnit, 4);
pos.y += MulDiv(template.y, yBaseUnit, 8);
//
// REACTOS : Need an owner to be passed!!!
//
if (!(template.style & (WS_CHILD|DS_ABSALIGN)) && owner ) ClientToScreen( owner, &pos );
}
if ( !(template.style & WS_CHILD) )
{
INT dX, dY;
/* try to fit it into the desktop */
if (!monitor)
{
SetRect( &rect, pos.x, pos.y, pos.x + size.cx, pos.y + size.cy );
monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
GetMonitorInfoW( monitor, &mon_info );
}
if ((dX = pos.x + size.cx + GetSystemMetrics(SM_CXDLGFRAME) - mon_info.rcWork.right) > 0)
pos.x -= dX;
if ((dY = pos.y + size.cy + GetSystemMetrics(SM_CYDLGFRAME) - mon_info.rcWork.bottom) > 0)
pos.y -= dY;
if( pos.x < mon_info.rcWork.left ) pos.x = mon_info.rcWork.left;
if( pos.y < mon_info.rcWork.top ) pos.y = mon_info.rcWork.top;
}
}
if (modal_owner && owner)
{
HWND parent = NULL;
/*
* Owner needs to be top level window. We need to duplicate the logic from server,
* because we need to disable it before creating dialog window. Note that we do that
* even if dialog has WS_CHILD, but only for modal dialogs, which matched what
* Windows does.
*/
while ((GetWindowLongW( owner, GWL_STYLE ) & (WS_POPUP|WS_CHILD)) == WS_CHILD)
{
parent = GetParent( owner );
if (!parent || parent == GetDesktopWindow()) break;
owner = parent;
}
////// Wine'ie babies need to fix your code!!!! CORE-11633
if (!parent) parent = GetAncestor( owner, GA_ROOT );
if (parent)
{
owner = parent;
if (IsWindowEnabled( owner ))
{
HWND captured = NULL;
disabled_owner = owner;
EnableWindow( disabled_owner, FALSE );
captured = GetCapture();
if (captured)
SendMessageW(captured, WM_CANCELMODE, 0, 0);
}
}
*modal_owner = owner;
}
if (unicode)
{
hwnd = CreateWindowExW(template.exStyle, template.className, template.caption,
template.style & ~WS_VISIBLE, pos.x, pos.y, size.cx, size.cy,
owner, hMenu, hInst, NULL );
}
else
{
LPCSTR class = (LPCSTR)template.className;
LPCSTR caption = (LPCSTR)template.caption;
LPSTR class_tmp = NULL;
LPSTR caption_tmp = NULL;
if (!IS_INTRESOURCE(class))
{
DWORD len = WideCharToMultiByte( CP_ACP, 0, template.className, -1, NULL, 0, NULL, NULL );
class_tmp = HeapAlloc( GetProcessHeap(), 0, len );
WideCharToMultiByte( CP_ACP, 0, template.className, -1, class_tmp, len, NULL, NULL );
class = class_tmp;
}
if (!IS_INTRESOURCE(caption))
{
DWORD len = WideCharToMultiByte( CP_ACP, 0, template.caption, -1, NULL, 0, NULL, NULL );
caption_tmp = HeapAlloc( GetProcessHeap(), 0, len );
WideCharToMultiByte( CP_ACP, 0, template.caption, -1, caption_tmp, len, NULL, NULL );
caption = caption_tmp;
}
hwnd = CreateWindowExA(template.exStyle, class, caption,
template.style & ~WS_VISIBLE, pos.x, pos.y, size.cx, size.cy,
owner, hMenu, hInst, NULL );
HeapFree( GetProcessHeap(), 0, class_tmp );
HeapFree( GetProcessHeap(), 0, caption_tmp );
}
if (!hwnd)
{
if (hUserFont) DeleteObject( hUserFont );
if (hMenu) DestroyMenu( hMenu );
if (disabled_owner) EnableWindow( disabled_owner, TRUE );
return 0;
}
/* moved this from the top of the method to here as DIALOGINFO structure
will be valid only after WM_CREATE message has been handled in DefDlgProc
All the members of the structure get filled here using temp variables */
dlgInfo = DIALOG_get_info( hwnd, TRUE );
// ReactOS
if (dlgInfo == NULL)
{
if (hUserFont) DeleteObject( hUserFont );
if (hMenu) DestroyMenu( hMenu );
if (disabled_owner) EnableWindow( disabled_owner, TRUE );
return 0;
}
//
dlgInfo->hwndFocus = 0;
dlgInfo->hUserFont = hUserFont;
dlgInfo->hMenu = hMenu;
dlgInfo->xBaseUnit = xBaseUnit;
dlgInfo->yBaseUnit = yBaseUnit;
dlgInfo->flags = flags;
if (template.helpId) SetWindowContextHelpId( hwnd, template.helpId );
if (unicode) SetWindowLongPtrW( hwnd, DWLP_DLGPROC, (ULONG_PTR)dlgProc );
else SetWindowLongPtrA( hwnd, DWLP_DLGPROC, (ULONG_PTR)dlgProc );
if (dlgProc && dlgInfo->hUserFont)
SendMessageW( hwnd, WM_SETFONT, (WPARAM)dlgInfo->hUserFont, 0 );
/* Create controls */
if (DIALOG_CreateControls32( hwnd, dlgTemplate, &template, hInst, unicode ))
{
/* Send initialisation messages and set focus */
if (dlgProc)
{
HWND focus = GetNextDlgTabItem( hwnd, 0, FALSE );
if (!focus) focus = GetNextDlgGroupItem( hwnd, 0, FALSE );
if (SendMessageW( hwnd, WM_INITDIALOG, (WPARAM)focus, param ) && IsWindow( hwnd ) &&
((~template.style & DS_CONTROL) || (template.style & WS_VISIBLE)))
{
/* By returning TRUE, app has requested a default focus assignment.
* WM_INITDIALOG may have changed the tab order, so find the first
* tabstop control again. */
focus = GetNextDlgTabItem( hwnd, 0, FALSE );
if (!focus) focus = GetNextDlgGroupItem( hwnd, 0, FALSE );
if (focus)
{
if (SendMessageW( focus, WM_GETDLGCODE, 0, 0 ) & DLGC_HASSETSEL)
SendMessageW( focus, EM_SETSEL, 0, MAXLONG );
SetFocus( focus );
}
else
{
if (!(template.style & WS_CHILD))
SetFocus( hwnd );
}
}
//// ReactOS see 43396, Fixes setting focus on Open and Close dialogs to the FileName edit control in OpenOffice.
//// This now breaks test_SaveRestoreFocus.
//DEFDLG_SaveFocus( hwnd );
////
}
//// ReactOS Rev 30613 & 30644
if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_CHILD))
SendMessageW( hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0);
////
if (template.style & WS_VISIBLE && !(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_VISIBLE))
{
ShowWindow( hwnd, SW_SHOWNORMAL ); /* SW_SHOW doesn't always work */
UpdateWindow( hwnd );
IntNotifyWinEvent(EVENT_SYSTEM_DIALOGSTART, hwnd, OBJID_WINDOW, CHILDID_SELF, 0);
}
return hwnd;
}
if (disabled_owner) EnableWindow( disabled_owner, TRUE );
IntNotifyWinEvent(EVENT_SYSTEM_DIALOGEND, hwnd, OBJID_WINDOW, CHILDID_SELF, 0);
if( IsWindow(hwnd) )
{
DestroyWindow( hwnd );
//// ReactOS
if (owner)
{ ERR("DIALOG_CreateIndirect 1\n");
if ( NtUserGetThreadState(THREADSTATE_FOREGROUNDTHREAD) && // Rule #1.
!NtUserQueryWindow(owner, QUERY_WINDOW_FOREGROUND) )
{ ERR("DIALOG_CreateIndirect SFW\n");
SetForegroundWindow(owner);
}
}
////
}
return 0;
}
创建对话框,直接通过创建window
hwnd = CreateWindowExW(template.exStyle, template.className, template.caption,
template.style & ~WS_VISIBLE, pos.x, pos.y, size.cx, size.cy,
owner, hMenu, hInst, NULL );
对话框业务层回调与创建窗口绑定,这样子处理消息才能丢给业务层回调,这样子就建立关系
if (unicode) SetWindowLongPtrW( hwnd, DWLP_DLGPROC, (ULONG_PTR)dlgProc );
else SetWindowLongPtrA( hwnd, DWLP_DLGPROC, (ULONG_PTR)dlgProc );
创建控件,通过资源然后动态创建子控件
***********************************************************************
* DIALOG_CreateControls32
*
* Create the control windows for a dialog.
*/
static BOOL DIALOG_CreateControls32( HWND hwnd, LPCSTR template, const DLG_TEMPLATE *dlgTemplate,
HINSTANCE hInst, BOOL unicode )
{
DIALOGINFO * dlgInfo;
DLG_CONTROL_INFO info;
HWND hwndCtrl, hwndDefButton = 0;
INT items = dlgTemplate->nbItems;
if (!(dlgInfo = GETDLGINFO(hwnd))) return FALSE;
TRACE(" BEGIN\n" );
while (items--)
{
template = (LPCSTR)DIALOG_GetControl32( (const WORD *)template, &info,
dlgTemplate->dialogEx );
info.style &= ~WS_POPUP;
info.style |= WS_CHILD;
if (info.style & WS_BORDER)
{
info.style &= ~WS_BORDER;
info.exStyle |= WS_EX_CLIENTEDGE;
}
if (unicode)
{
hwndCtrl = CreateWindowExW( info.exStyle | WS_EX_NOPARENTNOTIFY,
info.className, info.windowName,
info.style | WS_CHILD,
MulDiv(info.x, dlgInfo->xBaseUnit, 4),
MulDiv(info.y, dlgInfo->yBaseUnit, 8),
MulDiv(info.cx, dlgInfo->xBaseUnit, 4),
MulDiv(info.cy, dlgInfo->yBaseUnit, 8),
hwnd, (HMENU)(ULONG_PTR)info.id,
hInst, (LPVOID)info.data );
}
else
{
LPSTR class = (LPSTR)info.className;
LPSTR caption = (LPSTR)info.windowName;
if (!IS_INTRESOURCE(class))
{
DWORD len = WideCharToMultiByte( CP_ACP, 0, info.className, -1, NULL, 0, NULL, NULL );
class = HeapAlloc( GetProcessHeap(), 0, len );
if (class != NULL)
WideCharToMultiByte( CP_ACP, 0, info.className, -1, class, len, NULL, NULL );
}
if (!IS_INTRESOURCE(caption))
{
DWORD len = WideCharToMultiByte( CP_ACP, 0, info.windowName, -1, NULL, 0, NULL, NULL );
caption = HeapAlloc( GetProcessHeap(), 0, len );
if (caption != NULL)
WideCharToMultiByte( CP_ACP, 0, info.windowName, -1, caption, len, NULL, NULL );
}
if (class != NULL && caption != NULL)
{
hwndCtrl = CreateWindowExA( info.exStyle | WS_EX_NOPARENTNOTIFY,
class, caption, info.style | WS_CHILD,
MulDiv(info.x, dlgInfo->xBaseUnit, 4),
MulDiv(info.y, dlgInfo->yBaseUnit, 8),
MulDiv(info.cx, dlgInfo->xBaseUnit, 4),
MulDiv(info.cy, dlgInfo->yBaseUnit, 8),
hwnd, (HMENU)(ULONG_PTR)info.id,
hInst, (LPVOID)info.data );
}
else
hwndCtrl = NULL;
if (!IS_INTRESOURCE(class)) HeapFree( GetProcessHeap(), 0, class );
if (!IS_INTRESOURCE(caption)) HeapFree( GetProcessHeap(), 0, caption );
}
if (info.windowNameFree)
{
HeapFree( GetProcessHeap(), 0, (LPVOID)info.windowName );
}
if (!hwndCtrl)
{
WARN("control %s %s creation failed\n", debugstr_w(info.className),
debugstr_w(info.windowName));
if (dlgTemplate->style & DS_NOFAILCREATE) continue;
return FALSE;
}
/* Send initialisation messages to the control */
if (dlgInfo->hUserFont) SendMessageW( hwndCtrl, WM_SETFONT,
(WPARAM)dlgInfo->hUserFont, 0 );
if (SendMessageW(hwndCtrl, WM_GETDLGCODE, 0, 0) & DLGC_DEFPUSHBUTTON)
{
/* If there's already a default push-button, set it back */
/* to normal and use this one instead. */
if (hwndDefButton)
SendMessageW( hwndDefButton, BM_SETSTYLE, BS_PUSHBUTTON, FALSE );
hwndDefButton = hwndCtrl;
dlgInfo->idResult = GetWindowLongPtrA( hwndCtrl, GWLP_ID );
}
}
TRACE(" END\n" );
return TRUE;
}
其他
我用sublime text 查看代码还是比较方便,确实比较轻便,用vscode无法定义,他回跳到系统头文件里面去。