當應用程式關聯檔案類型時,雙擊檔案若程式已在執行,應啟動現有實例開啟檔案而非新建進程。本文示範如何用 自訂 Windows 訊息 實現單一實例與跨進程檔案列表傳遞。
WndProc 接收自訂訊息,解析臨時檔案並開啟檔案。private static readonly int CUSTOM_MESSAGE = 0x400 + 2; // WM_USER + 2
private static readonly int RESULT_FILES_HANDLED = 2;
public static bool OpenFilesInPreviousInstance(string[] fileList) {
int currentPid = Process.GetCurrentProcess().Id;
string currentExe = Assembly.GetEntryAssembly().Location;
string tempFile = Path.Combine(Path.GetTempPath(), $"sd{new Random().Next()}.tmp");
try {
File.WriteAllLines(tempFile, fileList); // 將檔案列表寫入暫存檔
foreach (Process p in Process.GetProcessesByName("MyApplication")) {
if (p.Id == currentPid) continue;
if (string.Equals(currentExe, p.MainModule.FileName, StringComparison.OrdinalIgnoreCase)) {
IntPtr hWnd = p.MainWindowHandle;
if (hWnd != IntPtr.Zero) {
long result = NativeMethods.SendMessage(hWnd, CUSTOM_MESSAGE, new IntPtr(tempFile.GetHashCode()), IntPtr.Zero).ToInt64();
if (result == RESULT_FILES_HANDLED) return true;
}
}
}
return false;
} finally {
File.Delete(tempFile);
}
}
protected override void WndProc(ref Message m) {
if (!SingleInstanceHelper.PreFilterMessage(ref m, this))
base.WndProc(ref m);
}
internal static bool PreFilterMessage(ref Message m, Form mainForm) {
if (m.Msg != CUSTOM_MESSAGE) return false;
long fileNumber = m.WParam.ToInt64();
m.Result = new IntPtr(RESULT_FILES_HANDLED);
try {
// 切換到前景
mainForm.Invoke((MethodInvoker)delegate {
NativeMethods.SetForegroundWindow(mainForm.Handle);
});
string tempFile = Path.Combine(Path.GetTempPath(), $"sd{fileNumber}.tmp");
foreach (string file in File.ReadAllLines(tempFile)) {
mainForm.Invoke((MethodInvoker)delegate {
// 開啟檔案邏輯
});
}
return true;
} catch { return false; }
}
Marshal 跨進程傳遞複雜型別,避免序列化與安全性問題。SetForegroundWindow 確保舊實例取得焦點。此模式廣泛應用於 Visual Studio、瀏覽器、編輯器等專業軟體,是單一實例應用的標準解法。