开源 C# 快速开发(十五)进程--windows消息

文章的目的为了记录使用C# 开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

相关链接:

开源 C# 快速开发(一)基础知识

开源 C# 快速开发(二)基础控件

开源 C# 快速开发(三)复杂控件

开源 C# 快速开发(四)自定义控件--波形图

开源 C# 快速开发(五)自定义控件--仪表盘

开源 C# 快速开发(六)自定义控件--圆环

开源 C# 快速开发(七)通讯--串口

开源 C# 快速开发(八)通讯--Tcp服务器端

开源 C# 快速开发(九)通讯--Tcp客户端

开源 C# 快速开发(十)通讯--http客户端

推荐链接:

开源 C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客

开源 C# .net mvc 开发(三)WEB内外网访问-CSDN博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示-CSDN博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客

开源 C# .net mvc 开发(六)发送邮件、定时以及CMD编程-CSDN博客

开源 C# .net mvc 开发(七)动态图片、动态表格和json数据生成-CSDN博客

开源 C# .net mvc 开发(八)IIS Express轻量化Web服务器的配置和使用-CSDN博客

开源 C# .net mvc 开发(九)websocket--服务器与客户端的实时通信-CSDN博客

本章节主要内容是:进程间 Windows 消息通信的例子。

目录:

1.源码分析

2.所有源码

3.效果演示

一、源码分析

  1. MessageHelper.cs 分析

Windows API 声明

复制代码
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

功能:根据类名或窗口标题查找窗口句柄

参数:

lpClassName:窗口类名,为null时按窗口标题查找

lpWindowName:窗口标题文本

返回值:找到的窗口句柄,未找到返回IntPtr.Zero

复制代码
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

功能:向指定窗口发送消息

参数:

hWnd:目标窗口句柄

Msg:消息类型(如WM_COPYDATA)

wParam:附加消息参数

lParam:附加消息参数(通常指向数据结构的指针)

数据结构定义

复制代码
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
    public IntPtr dwData;  // 自定义数据,可用于标识消息类型
    public int cbData;     // 数据长度(字节数)
    public IntPtr lpData;  // 指向实际数据的指针
}

作用:用于WM_COPYDATA消息的数据包装结构

LayoutKind.Sequential:确保字段在内存中按声明顺序排列

核心函数分析

SendMessageToProcess 函数

复制代码
public static bool SendMessageToProcess(string windowName, string message)
{
    // 1. 查找目标窗口
    IntPtr targetWindow = FindWindow(null, windowName);
    
    // 2. 字符串编码和内存分配
    byte[] data = Encoding.Unicode.GetBytes(message);
    IntPtr pData = Marshal.AllocCoTaskMem(data.Length);
    Marshal.Copy(data, 0, pData, data.Length);
    
    // 3. 构建COPYDATASTRUCT
    COPYDATASTRUCT cds = new COPYDATASTRUCT();
    cds.dwData = IntPtr.Zero;      // 不使用自定义标识
    cds.cbData = data.Length;      // 数据长度
    cds.lpData = pData;            // 数据指针
    
    // 4. 结构体序列化到指针
    IntPtr pCds = Marshal.AllocCoTaskMem(Marshal.SizeOf(cds));
    Marshal.StructureToPtr(cds, pCds, false);
    
    // 5. 发送消息
    IntPtr result = SendMessage(targetWindow, WM_COPYDATA, IntPtr.Zero, pCds);
    
    // 6. 资源清理
    Marshal.FreeCoTaskMem(pData);
    Marshal.FreeCoTaskMem(pCds);
    
    return result != IntPtr.Zero;
}

执行流程:

窗口查找 → 2. 数据编码 → 3. 内存分配 → 4. 结构构建 → 5. 消息发送 → 6. 资源清理

SendCustomMessage 函数

复制代码
public static bool SendCustomMessage(string windowName, string message)
{
    // 1. 注册系统唯一消息ID
    uint customMsg = (uint)RegisterWindowMessage("MyCustomMessage");
    
    // 2. 查找目标窗口
    IntPtr targetWindow = FindWindow(null, windowName);
    
    // 3. 字符串内存分配
    IntPtr pData = Marshal.StringToHGlobalUni(message);
    
    // 4. 发送自定义消息
    IntPtr result = SendMessage(targetWindow, customMsg, IntPtr.Zero, pData);
    
    // 5. 资源清理
    Marshal.FreeHGlobal(pData);
    
    return result != IntPtr.Zero;
}

特点:使用RegisterWindowMessage确保消息ID在系统范围内唯一

  1. MessageReceiverForm.cs 分析

构造函数

复制代码
public MessageReceiverForm()
{
    InitializeComponent();
    customMessage = (uint)RegisterWindowMessage("MyCustomMessage");
    this.Text = $"消息接收器 - PID: {Process.GetCurrentProcess().Id}";
    UpdateStatus("就绪 - 等待接收消息");
}

注册与发送方相同的自定义消息ID

在窗口标题显示进程ID便于调试

消息处理核心 - WndProc

复制代码
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case WM_COPYDATA:
            HandleCopyData(ref m);          // 处理WM_COPYDATA
            break;
        case int msg when customMessage != 0 && msg == customMessage:
            HandleCustomMessage(ref m);     // 处理自定义消息
            break;
        default:
            base.WndProc(ref m);           // 其他消息交给基类处理
            break;
    }
}

消息路由机制:

所有Windows消息都经过此方法

根据消息类型分发给对应的处理器

未处理的消息传递给基类

WM_COPYDATA 消息处理

复制代码
private void HandleCopyData(ref Message m)
{
    // 1. 反序列化COPYDATASTRUCT
    COPYDATASTRUCT cds = (COPYDATASTRUCT)Marshal.PtrToStructure(
        m.LParam, typeof(COPYDATASTRUCT));
    
    // 2. 从指针复制数据到字节数组
    byte[] data = new byte[cds.cbData];
    Marshal.Copy(cds.lpData, data, 0, cds.cbData);
    
    // 3. 字节数组解码为字符串
    string message = Encoding.Unicode.GetString(data);
    
    // 4. UI线程安全更新
    this.Invoke(new Action(() =>
    {
        listBoxMessages.Items.Add($"[WM_COPYDATA] {DateTime.Now:HH:mm:ss}: {message}");
        listBoxMessages.SelectedIndex = listBoxMessages.Items.Count - 1;
        UpdateStatus($"收到 WM_COPYDATA 消息: {message}");
    }));
    
    m.Result = (IntPtr)1;  // 告知发送方处理成功
}

关键点:

Marshal.PtrToStructure:将指针转换为结构体

Marshal.Copy:从非托管内存复制到托管数组

this.Invoke:确保UI操作在正确的线程执行

自定义消息处理

复制代码
private void HandleCustomMessage(ref Message m)
{
    // 直接从指针读取字符串
    string message = Marshal.PtrToStringUni(m.LParam);
    
    this.Invoke(new Action(() =>
    {
        listBoxMessages.Items.Add($"[自定义消息] {DateTime.Now:HH:mm:ss}: {message}");
        listBoxMessages.SelectedIndex = listBoxMessages.Items.Count - 1;
        UpdateStatus($"收到自定义消息: {message}");
    }));
    
    m.Result = (IntPtr)1;
}

简化处理:直接使用Marshal.PtrToStringUni转换字符串

UI交互函数

复制代码
private void btnSendToSender_Click(object sender, EventArgs e)
{
    string message = txtSendMessage.Text.Trim();
    if (!string.IsNullOrEmpty(message))
    {
        // 双向通信:接收方也可以发送消息
        bool success = MessageHelper.SendMessageToProcess("消息发送器", message);
        if (success)
        {
            listBoxMessages.Items.Add($"[发送] {DateTime.Now:HH:mm:ss}: {message}");
            listBoxMessages.SelectedIndex = listBoxMessages.Items.Count - 1;
            UpdateStatus($"已发送消息到发送器: {message}");
        }
    }
}

线程安全的状态更新

复制代码
private void UpdateStatus(string status)
{
    if (lblStatus.InvokeRequired)
    {
        lblStatus.Invoke(new Action<string>(UpdateStatus), status);
    }
    else
    {
        lblStatus.Text = $"{DateTime.Now:HH:mm:ss} - {status}";
    }
}

InvokeRequired机制:

检查调用线程是否是创建控件的线程

如果不是,通过Invoke跨线程安全更新UI

  1. MessageSenderForm.cs 分析

消息发送函数

复制代码
private void btnSendCopyData_Click(object sender, EventArgs e)
{
    string message = txtMessage.Text.Trim();
    string receiverWindow = txtReceiverWindow.Text.Trim();
    
    if (!string.IsNullOrEmpty(message) && !string.IsNullOrEmpty(receiverWindow))
    {
        bool success = MessageHelper.SendMessageToProcess(receiverWindow, message);
        if (success)
        {
            // 本地记录发送历史
            listBoxSent.Items.Add($"[WM_COPYDATA] {DateTime.Now:HH:mm:ss}: {message}");
            listBoxSent.SelectedIndex = listBoxSent.Items.Count - 1;
            UpdateStatus($"WM_COPYDATA 消息发送成功: {message}");
        }
    }
}

进程启动功能

复制代码
private void btnStartReceiver_Click(object sender, EventArgs e)
{
    // 构建接收器程序路径
    string receiverPath = System.IO.Path.Combine(
        AppDomain.CurrentDomain.BaseDirectory, 
        "MessageReceiver.exe");
    
    if (System.IO.File.Exists(receiverPath))
    {
        Process.Start(receiverPath);  // 启动外部进程
        UpdateStatus("接收器进程已启动");
    }
}
  1. 通信流程总结

WM_COPYDATA 通信流程:

发送方:

  1. FindWindow("消息接收器") → 获取窗口句柄

  2. Encoding.Unicode.GetBytes() → 字符串转字节数组

  3. Marshal.AllocCoTaskMem() → 分配非托管内存

  4. Marshal.StructureToPtr() → 结构体序列化

  5. SendMessage(WM_COPYDATA) → 发送消息

接收方:

  1. WndProc(WM_COPYDATA) → 接收消息

  2. Marshal.PtrToStructure() → 反序列化结构体

  3. Marshal.Copy() → 复制数据到托管内存

  4. Encoding.Unicode.GetString() → 字节数组转字符串

  5. UI更新 → 显示接收到的消息

二、所有源码

项目源码由2部分组成,windowsmessageRecv和windowsmessageSend

windowsmessageRecv中包括MessageHelper.cs和MessageReceiverForm。

windowsmessageSend中包括MessageHelper.cs和MessageSenderForm。

MessageHelper.cs文件源码

复制代码
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace windowsmessageRecv
{
    public static class MessageHelper
    {
        // Windows API
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        public static extern int RegisterWindowMessage(string lpString);

        [DllImport("user32.dll")]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, string lParam);

        // 常量
        public const int WM_COPYDATA = 0x004A;
        public const int WM_SETTEXT = 0x000C;

        // COPYDATASTRUCT 结构
        [StructLayout(LayoutKind.Sequential)]
        public struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;
            public IntPtr lpData;
        }

        public static bool SendMessageToProcess(string windowName, string message)
        {
            try
            {
                // 查找接收窗口
                IntPtr targetWindow = FindWindow(null, windowName);

                if (targetWindow == IntPtr.Zero)
                {
                    MessageBox.Show($"未找到目标窗口: {windowName}");
                    return false;
                }

                // 准备数据
                byte[] data = Encoding.Unicode.GetBytes(message);
                IntPtr pData = Marshal.AllocCoTaskMem(data.Length);
                Marshal.Copy(data, 0, pData, data.Length);

                // 创建 COPYDATASTRUCT
                COPYDATASTRUCT cds = new COPYDATASTRUCT();
                cds.dwData = IntPtr.Zero;
                cds.cbData = data.Length;
                cds.lpData = pData;

                IntPtr pCds = Marshal.AllocCoTaskMem(Marshal.SizeOf(cds));
                Marshal.StructureToPtr(cds, pCds, false);

                // 发送消息
                IntPtr result = SendMessage(targetWindow, WM_COPYDATA, IntPtr.Zero, pCds);

                // 清理资源
                Marshal.FreeCoTaskMem(pData);
                Marshal.FreeCoTaskMem(pCds);

                return result != IntPtr.Zero;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"发送消息时出错: {ex.Message}");
                return false;
            }
        }

        public static bool SendCustomMessage(string windowName, string message)
        {
            try
            {
                // 注册自定义消息
                uint customMsg = (uint)RegisterWindowMessage("MyCustomMessage");

                if (customMsg == 0)
                {
                    MessageBox.Show("注册自定义消息失败");
                    return false;
                }

                IntPtr targetWindow = FindWindow(null, windowName);

                if (targetWindow == IntPtr.Zero)
                {
                    MessageBox.Show($"未找到目标窗口: {windowName}");
                    return false;
                }

                // 发送字符串消息
                IntPtr pData = Marshal.StringToHGlobalUni(message);
                IntPtr result = SendMessage(targetWindow, customMsg, IntPtr.Zero, pData);
                Marshal.FreeHGlobal(pData);

                return result != IntPtr.Zero;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"发送自定义消息时出错: {ex.Message}");
                return false;
            }
        }

        public static bool SendTextMessage(string windowName, string message)
        {
            try
            {
                IntPtr targetWindow = FindWindow(null, windowName);

                if (targetWindow == IntPtr.Zero)
                {
                    MessageBox.Show($"未找到目标窗口: {windowName}");
                    return false;
                }

                IntPtr result = SendMessage(targetWindow, WM_SETTEXT, IntPtr.Zero, message);
                return result != IntPtr.Zero;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"发送文本消息时出错: {ex.Message}");
                return false;
            }
        }
    }
}

MessageReceiverForm.cs文件源码

复制代码
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace windowsmessageRecv
{
    public partial class MessageReceiverForm : Form
    {
        // Windows API 常量
        private const int WM_COPYDATA = 0x004A;

        // 注册自定义消息
        [DllImport("user32.dll")]
        private static extern int RegisterWindowMessage(string lpString);

        private uint customMessage;
        private ListBox listBoxMessages;
        private Button btnClear;
        private Button btnStartSender;
        private Label lblStatus;
        private TextBox txtSendMessage;
        private Button btnSendToSender;
        private Process senderProcess;

        public MessageReceiverForm()
        {
            InitializeComponent();

            // 注册自定义消息
            customMessage = (uint)RegisterWindowMessage("MyCustomMessage");

            // 显示当前进程ID,方便调试
            //this.Text = $"消息接收器 - PID: {Process.GetCurrentProcess().Id}";
            this.Text = "消息接收器";
            UpdateStatus("就绪 - 等待接收消息");
        }

        private void InitializeComponent()
        {
            this.listBoxMessages = new ListBox();
            this.btnClear = new Button();
            this.btnStartSender = new Button();
            this.lblStatus = new Label();
            this.txtSendMessage = new TextBox();
            this.btnSendToSender = new Button();
            this.SuspendLayout();

            // listBoxMessages
            this.listBoxMessages.Dock = DockStyle.Top;
            this.listBoxMessages.FormattingEnabled = true;
            this.listBoxMessages.Location = new Point(0, 0);
            this.listBoxMessages.Size = new Size(500, 200);
            this.listBoxMessages.BackColor = Color.LightYellow;

            // lblStatus
            this.lblStatus.Dock = DockStyle.Top;
            this.lblStatus.Location = new Point(0, 200);
            this.lblStatus.Size = new Size(500, 25);
            this.lblStatus.BackColor = Color.LightBlue;
            this.lblStatus.TextAlign = ContentAlignment.MiddleCenter;

            // txtSendMessage
            this.txtSendMessage.Location = new Point(10, 235);
            this.txtSendMessage.Size = new Size(300, 20);
            this.txtSendMessage.Text = "这是来自接收器的回复消息";

            // btnSendToSender
            this.btnSendToSender.Location = new Point(320, 235);
            this.btnSendToSender.Size = new Size(80, 23);
            this.btnSendToSender.Text = "发送回复";
            this.btnSendToSender.Click += new EventHandler(this.btnSendToSender_Click);

            // btnStartSender
            this.btnStartSender.Location = new Point(410, 235);
            this.btnStartSender.Size = new Size(80, 23);
            this.btnStartSender.Text = "启动发送器";
            this.btnStartSender.Click += new EventHandler(this.btnStartSender_Click);

            // btnClear
            this.btnClear.Location = new Point(210, 265);
            this.btnClear.Size = new Size(80, 23);
            this.btnClear.Text = "清空消息";
            this.btnClear.Click += new EventHandler(this.btnClear_Click);

            // MessageReceiverForm
            this.ClientSize = new Size(500, 300);
            this.Controls.Add(this.btnClear);
            this.Controls.Add(this.btnStartSender);
            this.Controls.Add(this.btnSendToSender);
            this.Controls.Add(this.txtSendMessage);
            this.Controls.Add(this.lblStatus);
            this.Controls.Add(this.listBoxMessages);
            this.Text = "消息接收器";
            this.StartPosition = FormStartPosition.CenterScreen;

            this.ResumeLayout(false);
            this.PerformLayout();
        }

        // 重写 WndProc 方法来处理 Windows 消息
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_COPYDATA:
                    HandleCopyData(ref m);
                    break;

                case int msg when customMessage != 0 && msg == customMessage:
                    HandleCustomMessage(ref m);
                    break;

                default:
                    base.WndProc(ref m);
                    break;
            }
        }

        // 处理 WM_COPYDATA 消息
        private void HandleCopyData(ref Message m)
        {
            try
            {
                // 解析 COPYDATASTRUCT
                COPYDATASTRUCT cds = (COPYDATASTRUCT)Marshal.PtrToStructure(
                    m.LParam, typeof(COPYDATASTRUCT));

                // 读取数据
                byte[] data = new byte[cds.cbData];
                Marshal.Copy(cds.lpData, data, 0, cds.cbData);

                string message = Encoding.Unicode.GetString(data);

                // 在 UI 线程中更新界面
                this.Invoke(new Action(() =>
                {
                    listBoxMessages.Items.Add($"[WM_COPYDATA] {DateTime.Now:HH:mm:ss}: {message}");
                    listBoxMessages.SelectedIndex = listBoxMessages.Items.Count - 1;
                    UpdateStatus($"收到 WM_COPYDATA 消息: {message}");
                }));

                m.Result = (IntPtr)1; // 表示消息处理成功
            }
            catch (Exception ex)
            {
                MessageBox.Show("处理 WM_COPYDATA 消息时出错: " + ex.Message);
                m.Result = IntPtr.Zero;
            }
        }

        // 处理自定义消息
        private void HandleCustomMessage(ref Message m)
        {
            try
            {
                // 读取字符串数据
                string message = Marshal.PtrToStringUni(m.LParam);

                this.Invoke(new Action(() =>
                {
                    listBoxMessages.Items.Add($"[自定义消息] {DateTime.Now:HH:mm:ss}: {message}");
                    listBoxMessages.SelectedIndex = listBoxMessages.Items.Count - 1;
                    UpdateStatus($"收到自定义消息: {message}");
                }));

                m.Result = (IntPtr)1;
            }
            catch (Exception ex)
            {
                MessageBox.Show("处理自定义消息时出错: " + ex.Message);
                m.Result = IntPtr.Zero;
            }
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            listBoxMessages.Items.Clear();
            UpdateStatus("消息列表已清空");
        }

        private void btnStartSender_Click(object sender, EventArgs e)
        {
            try
            {
                // 启动发送器进程
                string senderPath = System.IO.Path.Combine(
                    AppDomain.CurrentDomain.BaseDirectory,
                    "windowsmessageSend.exe");

                if (System.IO.File.Exists(senderPath))
                {
                    senderProcess = Process.Start(senderPath);
                    UpdateStatus("发送器进程已启动");
                }
                else
                {
                    MessageBox.Show($"找不到发送器程序: {senderPath}\n请先编译 MessageSender 项目。");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("启动发送器失败: " + ex.Message);
            }
        }

        private void btnSendToSender_Click(object sender, EventArgs e)
        {
            string message = txtSendMessage.Text.Trim();
            if (!string.IsNullOrEmpty(message))
            {
                // 发送消息到发送器窗口
                bool success = MessageHelper.SendMessageToProcess("消息发送器", message);
                if (success)
                {
                    listBoxMessages.Items.Add($"[发送] {DateTime.Now:HH:mm:ss}: {message}");
                    listBoxMessages.SelectedIndex = listBoxMessages.Items.Count - 1;
                    UpdateStatus($"已发送消息到发送器: {message}");
                }
                else
                {
                    UpdateStatus($"发送失败,请确保发送器正在运行");
                }
            }
        }

        private void UpdateStatus(string status)
        {
            if (lblStatus.InvokeRequired)
            {
                lblStatus.Invoke(new Action<string>(UpdateStatus), status);
            }
            else
            {
                lblStatus.Text = $"{DateTime.Now:HH:mm:ss} - {status}";
            }
        }

        // COPYDATASTRUCT 结构定义
        [StructLayout(LayoutKind.Sequential)]
        public struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;
            public IntPtr lpData;
        }
    }

    // 程序入口

}

MessageSenderForm.cs文件源码

复制代码
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;

namespace windowsmessageSend
{
    public partial class MessageSenderForm : Form
    {
        private TextBox txtMessage;
        private Button btnSendCopyData;
        private Button btnSendCustom;
        private ListBox listBoxSent;
        private Button btnClear;
        private TextBox txtReceiverWindow;
        private Label lblStatus;
        private Button btnStartReceiver;

        public MessageSenderForm()
        {
            InitializeComponent();
            //this.Text = $"消息发送器 - PID: {Process.GetCurrentProcess().Id}";
            this.Text = "消息发送器";
            UpdateStatus("就绪 - 可以发送消息");
        }

        private void InitializeComponent()
        {
            this.txtMessage = new TextBox();
            this.btnSendCopyData = new Button();
            this.btnSendCustom = new Button();
            this.listBoxSent = new ListBox();
            this.btnClear = new Button();
            this.txtReceiverWindow = new TextBox();
            this.lblStatus = new Label();
            this.btnStartReceiver = new Button();
            this.SuspendLayout();

            // txtReceiverWindow
            this.txtReceiverWindow.Location = new Point(10, 10);
            this.txtReceiverWindow.Size = new Size(300, 20);
            this.txtReceiverWindow.Text = "消息接收器";

            // txtMessage
            this.txtMessage.Location = new Point(10, 40);
            this.txtMessage.Size = new Size(300, 20);
            this.txtMessage.Text = "这是一条测试消息";

            // btnSendCopyData
            this.btnSendCopyData.Location = new Point(320, 40);
            this.btnSendCopyData.Size = new Size(100, 23);
            this.btnSendCopyData.Text = "发送 WM_COPYDATA";
            this.btnSendCopyData.Click += new EventHandler(this.btnSendCopyData_Click);

            // btnSendCustom
            this.btnSendCustom.Location = new Point(430, 40);
            this.btnSendCustom.Size = new Size(100, 23);
            this.btnSendCustom.Text = "发送自定义消息";
            this.btnSendCustom.Click += new EventHandler(this.btnSendCustom_Click);

            // btnStartReceiver
            this.btnStartReceiver.Location = new Point(320, 10);
            this.btnStartReceiver.Size = new Size(100, 23);
            this.btnStartReceiver.Text = "启动接收器";
            this.btnStartReceiver.Click += new EventHandler(this.btnStartReceiver_Click);

            // listBoxSent
            this.listBoxSent.Dock = DockStyle.Bottom;
            this.listBoxSent.FormattingEnabled = true;
            this.listBoxSent.Location = new Point(0, 120);
            this.listBoxSent.Size = new Size(550, 150);
            this.listBoxSent.BackColor = Color.LightGreen;

            // lblStatus
            this.lblStatus.Dock = DockStyle.Bottom;
            this.lblStatus.Location = new Point(0, 100);
            this.lblStatus.Size = new Size(550, 20);
            this.lblStatus.BackColor = Color.LightBlue;
            this.lblStatus.TextAlign = ContentAlignment.MiddleCenter;

            // btnClear
            this.btnClear.Location = new Point(230, 70);
            this.btnClear.Size = new Size(80, 23);
            this.btnClear.Text = "清空列表";
            this.btnClear.Click += new EventHandler(this.btnClear_Click);

            // MessageSenderForm
            this.ClientSize = new Size(550, 270);
            this.Controls.Add(this.btnClear);
            this.Controls.Add(this.listBoxSent);
            this.Controls.Add(this.lblStatus);
            this.Controls.Add(this.btnStartReceiver);
            this.Controls.Add(this.btnSendCustom);
            this.Controls.Add(this.btnSendCopyData);
            this.Controls.Add(this.txtMessage);
            this.Controls.Add(this.txtReceiverWindow);
            this.Text = "消息发送器";
            this.StartPosition = FormStartPosition.CenterScreen;

            this.ResumeLayout(false);
            this.PerformLayout();
        }

        private void btnSendCopyData_Click(object sender, EventArgs e)
        {
            string message = txtMessage.Text.Trim();
            string receiverWindow = txtReceiverWindow.Text.Trim();

            if (!string.IsNullOrEmpty(message) && !string.IsNullOrEmpty(receiverWindow))
            {
                try
                {
                    bool success = MessageHelper.SendMessageToProcess(receiverWindow, message);
                    if (success)
                    {
                        listBoxSent.Items.Add($"[WM_COPYDATA] {DateTime.Now:HH:mm:ss}: {message}");
                        listBoxSent.SelectedIndex = listBoxSent.Items.Count - 1;
                        UpdateStatus($"WM_COPYDATA 消息发送成功: {message}");
                    }
                    else
                    {
                        UpdateStatus($"发送失败,请确保接收器正在运行");
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"发送消息失败: {ex.Message}");
                    UpdateStatus($"发送失败: {ex.Message}");
                }
            }
            else
            {
                MessageBox.Show("请输入消息内容和接收窗口标题");
            }
        }

        private void btnSendCustom_Click(object sender, EventArgs e)
        {
            string message = txtMessage.Text.Trim();
            string receiverWindow = txtReceiverWindow.Text.Trim();

            if (!string.IsNullOrEmpty(message) && !string.IsNullOrEmpty(receiverWindow))
            {
                try
                {
                    bool success = MessageHelper.SendCustomMessage(receiverWindow, message);
                    if (success)
                    {
                        listBoxSent.Items.Add($"[自定义消息] {DateTime.Now:HH:mm:ss}: {message}");
                        listBoxSent.SelectedIndex = listBoxSent.Items.Count - 1;
                        UpdateStatus($"自定义消息发送成功: {message}");
                    }
                    else
                    {
                        UpdateStatus($"发送失败,请确保接收器正在运行");
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"发送自定义消息失败: {ex.Message}");
                    UpdateStatus($"发送失败: {ex.Message}");
                }
            }
            else
            {
                MessageBox.Show("请输入消息内容和接收窗口标题");
            }
        }

        private void btnStartReceiver_Click(object sender, EventArgs e)
        {
            try
            {
                // 启动接收器进程
                string receiverPath = System.IO.Path.Combine(
                    AppDomain.CurrentDomain.BaseDirectory,
                    "MessageReceiver.exe");

                if (System.IO.File.Exists(receiverPath))
                {
                    Process.Start(receiverPath);
                    UpdateStatus("接收器进程已启动");
                }
                else
                {
                    MessageBox.Show($"找不到接收器程序: {receiverPath}\n请先编译 MessageReceiver 项目。");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("启动接收器失败: " + ex.Message);
            }
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            listBoxSent.Items.Clear();
            UpdateStatus("发送列表已清空");
        }

        private void UpdateStatus(string status)
        {
            if (lblStatus.InvokeRequired)
            {
                lblStatus.Invoke(new Action<string>(UpdateStatus), status);
            }
            else
            {
                lblStatus.Text = $"{DateTime.Now:HH:mm:ss} - {status}";
            }
        }
    }


}

三、效果演示

启动接收器和发送器的exe,发送器中点击发送,接收器收到消息。

相关推荐
唐青枫18 分钟前
别把 Razor 当成几段 HTML:C#.NET Razor Pages、MVC 视图与实战详解
c#·.net
SL_staff37 分钟前
ERP排程总在纸上谈兵?JVS-APS如何用真实产能约束打通计划与执行闭环
java·人工智能·开源
一次旅行3 小时前
2026‑09‑16 AI产业深度解读|GPT‑5.5即将下线迁移、AI安全路线大辩论、Perplexity自研数据库大幅降本
人工智能·安全·开源
ZStack开发者社区3 小时前
评估 ZSvirt 是否适合替代 VMware:一份从部署到迁移的 PoC 指南
开源·vmware·虚拟化·poc
ι:3 小时前
NUC12 Pro 安装 Ubuntu 24.04 LTS 详细教程
linux·windows·ubuntu·nuc
奈斯先生Vector3 小时前
当模型版本不断变化,RelayRouter 能否帮助 AI 应用摆脱深度绑定
android·java·人工智能·开源·aigc
YCOSA20254 小时前
微软宣布将于10月7日举行Windows与Surface发布会,聚焦“Windows、NVIDIA RTX Spark、Surface,以及更广泛PC生态”
windows
Source.Liu4 小时前
【A11】 labelprinter(Tauri 版)新建步骤
windows·rust
魔众4 小时前
写歌、翻唱、可编辑乐谱,YuE2-3B 在 AIGCPanel 一键跑通
人工智能·开源
Punchline2785 小时前
解决 Windows 环境变量 Path 设置时提示“字符太长”的完整指南
windows·环境变量·path