📄 dlgdyn.dpr
字号:
program DlgDyn;
{$R DlgDyn.res}
uses Windows, Messages, DlgTmplt in 'DlgTmplt.pas', DynDlg in 'DynDlg.pas';
const
IDD_DLGDYN = 101; // 主窗体对话框模板资源ID
IDI_DLGDYN = 102; // 主窗体对话框图标资源ID
IDC_MODAL = 1000; // 主窗体'建立模态对话框'按钮ID
IDC_MODELESS = 1001; // 主窗体'建立/删除非模态对话框'按钮ID
var
// 在本程序中, 主窗体是一个模态对话框, 当再产生一个非模态的对话框, 它的'键盘接口'
// 将不起作用. 因为主窗体的消息循环不会为后来的非模态对话框调用IsDialogMessage(),
// 为了使非模态对话框响应特殊按键(如切换焦点的Tab), 作者安装了一个消息过滤器钩子,
// 下面这个变量保存非模态对话框的句柄, 消息过滤器钩子会为他调用IsDialogMessage()
g_hWndModeless: HWND = 0;
// 消息过滤器钩子句柄, 此钩子为非模态对话框g_hWndModeless添加'键盘接口'
g_hHookMsgFilter: HHOOK = 0;
// 主窗体WM_INITDIALOG消息处理
function DlgDyn_OnInitDialog(hWnd, hWndFocus: HWND; lParam: LPARAM): BOOL;
begin
// 设置主窗体对话框图标
SendMessage(hWnd, WM_SETICON, ICON_BIG, LoadIcon(HInstance, MakeIntResource(IDI_DLGDYN)));
// 允许系统设置默认焦点
Result := TRUE;
end;
// 主窗体WM_COMMAND消息处理
procedure DlgDyn_OnCommand(hWnd: HWND; id: Integer; hWndCtl: HWND; codeNotify: UINT);
var
DlgTemplatePt: PDlgTemplateEx;
begin
case (id) of
IDOK, IDCANCEL:
begin
// 删除窗体, 停止循环
EndDialog(hWnd, id);
end;
IDC_MODAL:
begin
// 产生对话框内存结构
DlgTemplatePt := BuildDynamicDlgBox(FALSE);
if (DlgTemplatePt = nil) then Exit;
// 根据结构建立对话框
DialogBoxIndirect(HInstance, PDlgTemplate(DlgTemplatePt)^, hWnd, @DynDlg_DlgProc);
// 释放对话框结构内存
DlgTemplate_Free(DlgTemplatePt);
end;
IDC_MODELESS:
begin
// 如果非模态对话框已存在, 则删除, 否则建立
if IsWindow(g_hWndModeless) then
begin
// 删除窗体, 句柄清零
DestroyWindow(g_hWndModeless);
g_hWndModeless := 0;
end else
begin
// 产生对话框内存结构
DlgTemplatePt := BuildDynamicDlgBox(TRUE);
if (DlgTemplatePt = nil) then Exit;
// 根据结构建立对话框
g_hWndModeless := CreateDialogIndirect(HInstance,
PDlgTemplate(DlgTemplatePt)^, hWnd, @DynDlg_DlgProc);
// 释放对话框结构内存
DlgTemplate_Free(DlgTemplatePt);
end;
// 根据非模态对话框状态设置按钮文字
if IsWindow(g_hWndModeless) then
SetWindowText(hWndCtl, 'Destroy dynamic modeless dialog box')
else
SetWindowText(hWndCtl, 'Create dynamic modeless dialog box');
end;
end; // END: case (id) of ..
end;
// 主窗体对话框过程
function DlgDyn_DlgProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;
begin
case (uMsg) of
WM_INITDIALOG:
begin
Result := BOOL(SetWindowLong(hWnd, DWL_MSGRESULT,
Longint(DlgDyn_OnInitDialog(hWnd, wParam, lParam))));
end;
WM_COMMAND:
begin
DlgDyn_OnCommand(hWnd, LOWORD(wParam), lParam, HIWORD(wParam));
Result := TRUE;
end;
else Result := FALSE;
end;
end;
// 消息过滤钩子回调函数, 为非模态对话框调用IsDialogMessage(),
// 从而可以对其键盘消息作'对话框式'的处理, 比如Tab键转移焦点
function DlgDyn_MsgFilterProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
const
{$J+}
// 这个静态变量用于防止下面的IsDialogMessage()调用引起无限的递归,
// 因为IsDialogMessage()会在第一时间调用所有已经安装的消息过滤钩子
fRecurse: BOOL = FALSE;
{$J-}
begin
// 如果本次被调用是由于之前调用的IsDialogMessage(), 则直接返回
if (fRecurse) then
begin
// 返回0表示没有处理消息, 让系统作正常处理, 例如让IsDialogMessage()继续处理,
// 注意: 我们没有调用CallNextHookEx(), 因为不希望同一个消息多次经过钩子链 ..
Result := 0;
Exit;
end;
// 这里, 移除钩子然后重新挂钩, 使得我们的钩子总是处于系统钩子链的最前面,
// 这是必要的, 因为, 处于我们钩子之前的钩子可能会因为一个消息被调用两次,
// 把我们的钩子放到链表的最前面, 可以保证我们的钩子第一个被调用, 当遇到
// 因我们调用IsDialogMessage()引起的钩子调用, 可阻止信息通过后面的钩子链
UnhookWindowsHookEx(g_hHookMsgFilter);
g_hHookMsgFilter := SetWindowsHookEx(WH_MSGFILTER, @DlgDyn_MsgFilterProc, 0, GetCurrentThreadId());
// 将钩子信息传递给钩子链中的下一个钩子函数, 并得到其返回值
// (麻子注: 如果把这句移到重新挂钩的前面会不会更合适一些呢)
Result := CallNextHookEx(g_hHookMsgFilter, nCode, wParam, lParam);
// 如果后面的钩子允许作其他的处理, 并且非模态对话框句柄有效
if (Result = 0) and IsWindow(g_hWndModeless) then
begin
// 设置fRecurse为TRUE防止无限递归, 再调用IsDialogMessage()分发消息到窗体过程,
// IsDialogMessage()的返回值表示消息是否被处理了, 此处作为钩子返回值正合适 ..
fRecurse := TRUE;
Result := LRESULT(IsDialogMessage(g_hWndModeless, PMsg(lParam)^));
fRecurse := FALSE;
end;
end;
// 程序'主线程'入口
begin
// 安装消息过滤钩子,
// 使得有机会为非模态对话框调用IsDialogMessage(),
// 从而可以对其键盘消息作'对话框式'的'特殊'的处理
g_hHookMsgFilter := SetWindowsHookEx(WH_MSGFILTER, @DlgDyn_MsgFilterProc, 0, GetCurrentThreadId());
// 建立主窗体对话框
DialogBox(HInstance, MakeIntResource(IDD_DLGDYN), 0, @DlgDyn_DlgProc);
// 移除消息过滤钩子
UnhookWindowsHookEx(g_hHookMsgFilter);
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -