如何实现双击文件在FireMonkey运行实例中打开(Win32/OSX)
实现FireMonkey单实例打开关联文件(Win32 + OSX)
Win32平台实现
1. 完善单实例检测与文件路径传递
你已经用互斥量实现了单实例阻止,现在需要让第二个启动的实例把双击的文件路径传递给已运行的实例。这里推荐用WM_COPYDATA消息,它比自定义消息更安全,适合传递字符串类数据:
修改你的.dpr文件代码如下:
program MyFMXApp; uses Vcl.Forms, System.SysUtils, Winapi.Windows, MainForm in 'MainForm.pas' {Form1}; const MUTEX_NAME = 'MyUniqueFMXAppMutex'; // 务必确保这个名称唯一,避免和其他应用冲突 var OneInstanceMutex: THandle; MainWnd: HWND; CopyData: TCopyDataStruct; begin OneInstanceMutex := CreateMutex(nil, True, PChar(MUTEX_NAME)); if (GetLastError = ERROR_ALREADY_EXISTS) then begin // 查找第一个实例的主窗口(这里用应用标题定位,也可以用窗口类名更可靠) MainWnd := FindWindow(nil, PChar(Application.Title)); if MainWnd <> 0 then begin // 准备传递的文件路径(ParamStr(1)是系统传入的双击文件路径) CopyData.dwData := 0; CopyData.cbData := (Length(ParamStr(1)) + 1) * SizeOf(Char); CopyData.lpData := PChar(ParamStr(1)); // 发送消息传递路径 SendMessage(MainWnd, WM_COPYDATA, 0, LPARAM(@CopyData)); // 把已运行实例窗口激活到前台 SetForegroundWindow(MainWnd); end; Exit; end; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; ReleaseMutex(OneInstanceMutex); CloseHandle(OneInstanceMutex); end.
2. 在主窗体中接收并处理文件
在主窗体代码里添加WM_COPYDATA的消息处理逻辑:
unit MainForm; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs; type TForm1 = class(TForm) private procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA; procedure OpenFile(const FilePath: string); // 你的自定义文件打开逻辑 public { Public declarations } end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.WMCopyData(var Msg: TWMCopyData); var FilePath: string; begin inherited; if Msg.CopyDataStruct.dwData = 0 then begin // 解析传递过来的文件路径 FilePath := PChar(Msg.CopyDataStruct.lpData); // 调用自定义逻辑打开文件 OpenFile(FilePath); end; Msg.Result := 1; // 告知发送方已成功接收 end; procedure TForm1.OpenFile(const FilePath: string); begin // 这里写你的文件加载逻辑,比如读取文件内容到控件 ShowMessage('已打开文件:' + FilePath); end; end.
OSX平台实现
OSX的逻辑和Win32差异较大,依赖Apple的Launch Services机制,会自动将文件路径传递给已运行的应用实例:
1. 自定义Application Delegate处理文件打开
在主窗体或.dpr中添加OSX条件编译的代码:
{$IFDEF MACOS} uses Macapi.AppKit, Macapi.Foundation, FMX.Platform.Mac; type TCustomAppDelegate = class(TOSXApplicationDelegate) public function applicationOpenFile(Application: NSApplication; filename: NSString): Boolean; override; end; function TCustomAppDelegate.applicationOpenFile(Application: NSApplication; filename: NSString): Boolean; var FilePath: string; begin Result := inherited; // 将NSString转换为Delphi字符串 FilePath := UTF8ToString(filename.UTF8String); // 调用主窗体的打开方法 if Assigned(Form1) then begin Form1.OpenFile(FilePath); // 激活应用到前台 Application.activateIgnoringOtherApps(True); end; end; {$ENDIF}
然后在.dpr的Application.Initialize之前设置自定义Delegate:
{$IFDEF MACOS} var CustomDelegate: TCustomAppDelegate; begin CustomDelegate := TCustomAppDelegate.Create; TOSXApplicationDelegate.SetApplicationDelegate(CustomDelegate); {$ENDIF}
2. 设置文件关联(修改Info.plist)
需要在应用的Info.plist中添加文件类型关联,让OSX识别你的文件扩展名并绑定到应用。添加以下XML内容(替换成你的文件类型信息):
<key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeName</key> <string>我的自定义文件</string> <key>CFBundleTypeRole</key> <string>Editor</string> <key>LSHandlerRank</key> <string>Owner</string> <key>LSItemContentTypes</key> <array> <string>com.yourcompany.yourappfile</string> </array> </dict> </array> <key>UTExportedTypeDeclarations</key> <array> <dict> <key>UTTypeIdentifier</key> <string>com.yourcompany.yourappfile</string> <key>UTTypeDescription</key> <string>我的自定义文件格式</string> <key>UTTypeConformsTo</key> <array> <string>public.data</string> </array> <key>UTTypeTagSpecification</key> <dict> <key>public.filename-extension</key> <array> <string>myext</string> <!-- 替换成你的文件扩展名 --> </array> </dict> </dict> </array>
额外注意事项
- Win32下如果应用标题可能动态变化,建议用窗口类名定位主窗口(可以通过Spy++查看FMX主窗口的类名,一般对应你的窗体类名,比如
TForm1)。 - OSX测试文件关联时,建议将应用安装到Applications目录,否则Launch Services可能无法正确识别关联关系。
- 两个平台的
OpenFile方法可以统一实现,利用FMX的跨平台API处理文件读取,减少重复代码。
内容的提问来源于stack exchange,提问作者Duns
相关产品推荐
相关产品推荐

