如何使用Delphi实现类似Office的单进程多窗口子应用功能
Delphi实现类似Excel 2016的单进程多窗口机制
核心原理
你要的效果本质是单实例进程+多顶层窗口架构,没有使用MDI,核心逻辑是二次启动时不创建新进程,而是把打开参数传递给已运行的进程,由已运行进程创建新的独立顶层窗口。
具体实现步骤
1. 实现单实例判断,禁止多进程启动
用系统互斥量(Mutex)做进程唯一性校验,启动时先判断是否已有实例运行:
program YourApp; uses Vcl.Forms, Windows, Messages, MainDM in 'MainDM.pas' {DataModule1: TDataModule}, DocForm in 'DocForm.pas' {frmDoc}; var hMutex: THandle; MsgId: UINT; CopyData: TCopyDataStruct; TargetWnd: HWND; begin // 创建全局唯一互斥量,字符串换成你程序的唯一标识 hMutex := CreateMutex(nil, False, 'YourApp_Unique_Mutex_Key_2024'); if GetLastError = ERROR_ALREADY_EXISTS then begin // 已有实例运行,传递启动参数(文件路径)到已有进程 MsgId := RegisterWindowMessage('YourApp_Custom_Open_Msg'); TargetWnd := FindWindow('TfrmDoc', nil); // 或者找你预设的消息接收窗口类名 if TargetWnd <> 0 then begin // 用WM_COPYDATA传递命令行参数(要打开的文件路径) CopyData.dwData := 0; CopyData.cbData := (Length(ParamStr(1)) + 1) * SizeOf(Char); CopyData.lpData := PChar(ParamStr(1)); SendMessage(TargetWnd, WM_COPYDATA, 0, LPARAM(@CopyData)); end; CloseHandle(hMutex); // 直接退出当前新启动的实例 Exit; end; Application.Initialize; Application.MainFormOnTaskbar := False; // 不要让主窗体占任务栏 // 创建隐藏的全局数据模块/主窗口,不要显示 Application.CreateForm(TMainDM, MainDM); // 如果启动时带文件参数,直接创建第一个文档窗口 if ParamCount >= 1 then begin with TfrmDoc.Create(nil) do begin LoadFile(ParamStr(1)); Show; end; end else begin // 没有参数则创建空白文档窗口 TfrmDoc.Create(nil).Show; end; Application.Run; CloseHandle(hMutex); end.
2. 进程间消息接收与新窗口创建
在全局数据模块或者基类窗口中添加WM_COPYDATA消息处理,收到参数后直接创建新的文档窗口:
unit MainDM; interface uses System.SysUtils, System.Classes, Vcl.Forms, Winapi.Messages, Winapi.Windows, DocForm; type TMainDM = class(TDataModule) procedure DataModuleCreate(Sender: TObject); procedure DataModuleDestroy(Sender: TObject); private FWindowCount: Integer; procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA; procedure UpdateWindowCount(Delta: Integer); public end; var MainDM: TMainDM; implementation {$R *.dfm} procedure TMainDM.DataModuleCreate(Sender: TObject); begin FWindowCount := 0; end; procedure TMainDM.WMCopyData(var Msg: TWMCopyData); var FilePath: string; NewForm: TfrmDoc; begin FilePath := PChar(Msg.CopyDataStruct.lpData); NewForm := TfrmDoc.Create(nil); NewForm.LoadFile(FilePath); NewForm.Show; Msg.Result := 1; end; procedure TMainDM.UpdateWindowCount(Delta: Integer); begin FWindowCount := FWindowCount + Delta; // 所有窗口关闭时退出进程 if FWindowCount <= 0 then Application.Terminate; end; // 文档窗口的OnCreate和OnClose事件关联计数逻辑 procedure TfrmDoc.FormCreate(Sender: TObject); begin MainDM.UpdateWindowCount(1); end; procedure TfrmDoc.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; MainDM.UpdateWindowCount(-1); end;
3. 窗口配置
所有文档窗口用普通的fsNormal样式,不要设置成MDI子窗口,默认就会在任务栏显示独立的按钮,Windows 10及以上的任务管理器「进程」标签页会把每个独立顶层窗口识别为单独的应用条目,「详细信息」标签页始终只有一个进程。
效果说明
首次启动时需要初始化VCL运行时、加载程序资源、读取配置,所以速度较慢;二次打开文件时,新启动的实例直接把参数传给已运行的进程就退出,已运行进程只需要创建新窗口加载文件,不需要重复初始化全局资源,速度会明显提升。
内容的提问来源于stack exchange,提问作者Para
相关产品推荐
相关产品推荐

