首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 實現自訂訊息處理:單一執行個體應用程式與跨進程訊息傳遞

     編輯:關於C語言

背景:委派/事件 vs Win32 訊息

.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;
        }
    }
}

關鍵技術解析

  • WM_USER (0x400) 以上:自訂訊息範圍,避免衝突
  • 跨進程資料傳遞:SendMessage 僅能傳遞數值,利用 暫存檔 + 隨機 Token 交換複雜資料
  • Marshal 限制:自訂結構跨進程需 Hook,暫存檔方案最簡單
  • UI 執行緒安全:Control.Invoke 確保檔案開啟在 UI 執行緒
  • 單一實例檢測:進程名 + 模組路徑雙重驗證
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved