You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 12:12:02