主程序是MDI风格的,在弹出很多子窗体并同时有个模态窗体的情况下,实现定时锁屏功能,锁屏后,要使得整个应用程序都最小化,当输入所凭密码后再恢复显示成最初的状态。
这个过程会有几个问题点需要解决:
1、主窗体包括子窗体可以用frmMain.Hide来隐藏
2、是否有弹出来模态窗体需要进行检测,如果有则需要抓出模态窗体的句柄Handle,通过对模态窗体的发消息,隐藏模态窗体
3、恢复主窗体用frmMain.Show来显示
4、如果模态窗体句柄存在,则恢复显示模态窗体,同样用对指定句柄发消息来实现
5、对应用程序进行restore,此步骤很关键,没有此步骤,则会导致锁屏多次后,主窗体的【最小化】按钮不起作用,这时窗体可以操作、可以最大化、可以关闭,却再也不能最小化了!这就是引用消息机制的副作用,即使你的wndproc里面含有inherited也没用,就是不最小化,原因是这样的:
在进程中,主窗体的WM_SYSCOMMAND 消息是被传递给Application 类处理的,当CmdType 为SC_MINIMIZE的时候,Application 会调用Minimize 方法:
procedure TApplication.Minimize;
begin
if not IsIconic(FHandle) then
begin
NormalizeTopMosts;
SetActiveWindow(FHandle);
if (MainForm <> nil) and (ShowMainForm or MainForm.Visible)
and IsWindowEnabled(MainForm.Handle) then
begin
SetWindowPos(FHandle, MainForm.Handle, MainForm.Left, MainForm.Top,
MainForm.Width, 0, SWP_SHOWWINDOW);
DefWindowProc(FHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
end else
ShowWinNoAnimate(FHandle, SW_MINIMIZE);
if Assigned(FOnMinimize) then FOnMinimize(Self);
end;
end;
注意这个IsIconic(FHandle),它就是问题原因的冰山一角。IsIconic 是用来检测窗体是否处于最小化状态的API。我发现,第二实例将前一实例的主窗体置前之后,这个窗体最小化调用这个方法时,每次IsIconic(FHandle) 都是True。也就是说,Application 一直认为自己是最小化的(所以不能怪wndproc里面的inherited,因为inherited起作用了)。
于是问题就比较清楚了:我们在将主窗体强行置到最前的时候,Application 并没有恢复原状态。于是在Minimize 方法中主窗体就得不到最小化的命令了。
难怪在VC 开发的程序中不会有这样的问题!因为不存在Application 的这个因素。
于是我们只要将主窗体强行置前之前,首先将Application 恢复:
if IsIconic(Application.Handle) then
begin
DefWindowProc(Application.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
end;
具体程序关键代码片段如下:
--------------------------------------------
procedure TfrmMain.acLockExecute(Sender: TObject); //锁屏动作
var
tHandle:HWND;
function ApplicationHasModalForm:HWND; //找到模态窗体
var
i:integer;
begin
result:=0;
for i:=0 to Screen.FormCount-1 do
begin
if Screen.Forms[i] is TForm then
begin
if fsModal in Screen.Forms[i].FormState then
begin
result:=Screen.Forms[i].Handle;
break;
end;
end;
end;
end;
begin
if LockForm <> nil then
Exit;
cnMain.Pause := True;
frmMain.Hide; //隐藏主程序
tHandle:=ApplicationHasModalForm;//获得模态窗体句柄
if tHandle>0 then
ShowWindow(tHandle,SW_HIDE); //隐藏模态窗体
LockForm := TLockForm.Create(Application);
LockForm.ShowModal;
LockForm.Free;
LockForm := nil;
dwLastInputTick := GetTickCount;
cnMain.Pause := False;
frmMain.Show; //显示主程序
if tHandle>0 then
ShowWindow(tHandle,SW_SHOW); //显示模态窗体
Application.Restore; //恢复窗体状态,否则"最小化"可能失败
if acNavigate.Checked then
begin
ShowDockForm(frmNavigate);
end;
end;
上面代码的加粗部分即是关键代码部分。