.NET BCL 封裝 Win32 控制項,開發者鮮少接觸訊息迴圈。但 WPF 仍保留 Win32 訊息支援,且某些功能 (如單一執行個體啟動檔案關聯) 必須使用自訂訊息。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
namespace MyApplication
{
internal static class Program
{
[STAThread]
private static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
List<string> fileList = new List<string>();
foreach (string filename in args)
if (File.Exists(filename)) fileList.Add(filename);
if (!SingleInstanceHelper.OpenFilesInPreviousInstance(fileList.ToArray()))
Application.Run(new MyWindow());
}
}
public class MyWindow : Form
{
protected override void WndProc(ref Message msg)
{
if (!SingleInstanceHelper.PreFilterMessage(ref msg, this))
base.WndProc(ref msg);
}
}
internal static class SingleInstanceHelper
{
private static readonly int CUSTOM_MESSAGE = 0x400 + 2; // WM_USER + 2
private static readonly int RESULT_FILES_HANDLED = 2;
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr SetForegroundWindow(IntPtr hWnd);
public static bool OpenFilesInPreviousInstance(string[] fileList)
{
int currentPid = Process.GetCurrentProcess().Id;
string currentPath = Assembly.GetEntryAssembly().Location;
int token = new Random().Next();
string tempFile = Path.Combine(Path.GetTempPath(), "sd" + token + ".tmp");
try
{
File.WriteAllLines(tempFile, fileList);
foreach (Process p in Process.GetProcessesByName("MyApplication"))
{
if (p.Id == currentPid) continue;
if (string.Equals(currentPath, p.MainModule.FileName, StringComparison.OrdinalIgnoreCase))
{
IntPtr hWnd = p.MainWindowHandle;
if (hWnd != IntPtr.Zero)
{
long result = SendMessage(hWnd, CUSTOM_MESSAGE, new IntPtr(token), IntPtr.Zero).ToInt64();
if (result == RESULT_FILES_HANDLED) return true;
}
}
}
return false;
}
finally { File.Delete(tempFile); }
}
internal static bool PreFilterMessage(ref Message m, Form mainForm)
{
if (m.Msg != CUSTOM_MESSAGE) return false;
long token = m.WParam.ToInt64();
m.Result = new IntPtr(RESULT_FILES_HANDLED);
try
{
MethodInvoker invoker = delegate { SetForegroundWindow(mainForm.Handle); };
mainForm.Invoke(invoker);
string tempFile = Path.Combine(Path.GetTempPath(), "sd" + token + ".tmp");
foreach (string file in File.ReadAllLines(tempFile))
{
invoker = delegate { /* 開啟檔案邏輯 */ };
mainForm.Invoke(invoker);
}
}
catch { return false; }
return true;
}
}
}
SendMessage 僅能傳遞數值,利用 暫存檔 + 隨機 Token 交換複雜資料Control.Invoke 確保檔案開啟在 UI 執行緒