第一章:sendmessage的底层原理揭秘
核心思想:窗口句柄作为进程通信的"身份证"
// 窗口句柄(hwnd)的本质:进程身份的唯一标识符 public class windowhandlemanager { [dllimport("user32.dll", setlasterror = true)] private static extern intptr findwindow(string lpclassname, string lpwindowname); /// <summary> /// 获取目标进程的主窗口句柄 /// </summary> /// <param name="processname">进程名称(不含.exe后缀)</param> /// <returns>窗口句柄</returns> public static intptr getwindowhandle(string processname) { // 查找同名进程 process[] processes = process.getprocessesbyname(processname); if (processes.length == 0) throw new exception($"未找到进程 {processname}"); // 获取主窗口句柄 intptr hwnd = processes[0].mainwindowhandle; if (hwnd == intptr.zero) throw new exception("未找到窗口句柄"); return hwnd; } }
灵魂拷问:
为什么必须通过窗口句柄通信?
windows系统为每个窗口分配唯一句柄,如同进程的“身份证号”,是跨进程通信的唯一身份验证方式。
第二章:构建跨进程通信桥梁
目标:发送端与接收端的完整握手流程
2.1 定义通信协议
// 自定义消息结构体(必须与c++定义完全一致) [structlayout(layoutkind.sequential)] public struct copydatastruct { public intptr dwdata; // 自定义标识符(如类型id) public int cbdata; // 数据长度(字节数) public intptr lpdata; // 数据指针 } // 消息常量定义 public static class windowmessages { public const uint wm_copydata = 0x004a; // 标准消息id public const uint custom_msg_base = 0x8000; // 自定义消息起始点 }
设计哲学:
- 使用
wm_copydata
标准消息确保跨语言兼容性 dwdata
字段可扩展为消息类型标识符(如0x0001表示文本,0x0002表示二进制)
2.2 接收端实现
// 接收端窗体类 public class messagereceiver : form { protected override void wndproc(ref message m) { // 拦截自定义消息 if (m.msg == (int)windowmessages.wm_copydata) { try { // 解析消息结构体 var cds = (copydatastruct)marshal.ptrtostructure( m.lparam, typeof(copydatastruct) ); // 将指针转换为字符串 string receiveddata = marshal.ptrtostringansi(cds.lpdata); // 处理数据(示例:显示消息) messagebox.show($"收到数据: {receiveddata}"); } catch (exception ex) { console.writeline($"消息处理异常: {ex.message}"); } } base.wndproc(ref m); } public static void main() { application.run(new messagereceiver()); console.writeline("接收端启动,等待消息..."); } }
高级技巧:
- 使用
try-catch
包裹消息处理防止程序崩溃 - 添加日志记录便于调试
- 使用
gchandle
管理内存生命周期(详见下文)
2.3 发送端实现
public class processcommunicator { [dllimport("user32.dll", setlasterror = true)] private static extern intptr sendmessage( intptr hwnd, uint msg, intptr wparam, ref copydatastruct lparam ); /// <summary> /// 向指定进程发送数据 /// </summary> /// <param name="targetprocessname">目标进程名</param> /// <param name="data">要发送的数据</param> public static void senddatatoprocess(string targetprocessname, string data) { // 1. 获取目标进程句柄 intptr hwnd = windowhandlemanager.getwindowhandle(targetprocessname); // 2. 将字符串转换为字节数组 byte[] bytes = encoding.utf8.getbytes(data); // 3. 固定内存地址(防止gc回收) gchandle handle = gchandle.alloc(bytes, gchandletype.pinned); try { // 4. 构造消息结构体 copydatastruct cds = new copydatastruct { dwdata = intptr.zero, // 自定义标识符 cbdata = bytes.length, // 数据长度 lpdata = handle.addrofpinnedobject() // 数据指针 }; // 5. 发送消息 intptr result = sendmessage( hwnd, windowmessages.wm_copydata, intptr.zero, ref cds ); // 6. 错误处理 if (result == intptr.zero) { int errorcode = marshal.getlastwin32error(); throw new exception($"发送失败,错误码:{errorcode}"); } } finally { // 7. 释放内存 handle.free(); } } }
性能优化:
- 使用
gchandle.alloc
固定内存地址防止gc干扰 - 批量发送时复用内存缓冲区
- 添加发送频率限制(如每秒不超过100次)
第三章:突破sendmessage的性能瓶颈
让进程通信速度提升10倍的关键技巧
3.1 异步通信模式
// 异步发送任务(避免阻塞主线程) public static async task senddataasync( string targetprocessname, string data, cancellationtoken cancellationtoken) { await task.run(() => { try { senddatatoprocess(targetprocessname, data); } catch (exception ex) { console.writeline($"异步发送异常: {ex.message}"); } }, cancellationtoken); }
设计原则:
- 使用
task.run
隔离耗时操作 - 添加取消令牌支持中断机制
- 使用
valuetask
优化小数据场景
3.2 高吞吐量优化
// 批量发送优化器 public class batchmessageoptimizer { private readonly queue<string> _messagequeue = new queue<string>(); private readonly object _lock = new object(); private timer _timer; public batchmessageoptimizer(int intervalms) { _timer = new timer(sendbatch, null, timeout.infinite, timeout.infinite); _timer.change(intervalms, intervalms); } public void enqueue(string message) { lock (_lock) { _messagequeue.enqueue(message); } } private void sendbatch(object state) { list<string> batch; lock (_lock) { batch = _messagequeue.tolist(); _messagequeue.clear(); } foreach (var msg in batch) { processcommunicator.senddatatoprocess("receiver", msg); } } }
性能对比:
模式 | 吞吐量(条/秒) | 适用场景 |
---|---|---|
单条发送 | 50 | 实时性要求高 |
批量发送 | 500+ | 大批量数据传输 |
第四章:解决sendmessage的致命缺陷
从理论到实践的全面加固
4.1 窗口句柄失效问题
// 带重试机制的句柄获取 public static intptr getwindowhandlewithretry( string processname, int maxretries = 5, int retryintervalms = 1000) { for (int i = 0; i < maxretries; i++) { try { return windowhandlemanager.getwindowhandle(processname); } catch (exception ex) { console.writeline($"第{i + 1}次尝试失败: {ex.message}"); thread.sleep(retryintervalms); } } throw new exception("多次尝试获取句柄失败"); }
防御策略:
- 设置最大重试次数防止单点故障
- 添加指数退避算法(每次重试间隔翻倍)
- 集成健康检查接口
4.2 内存泄漏预防
// 使用对象池复用copydatastruct public class copydatapool { private readonly objectpool<copydatastruct> _pool; public copydatapool(int initialsize) { _pool = new objectpool<copydatastruct>(() => new copydatastruct(), initialsize); } public copydatastruct get() { return _pool.get(); } public void return(copydatastruct item) { // 重置字段 item.dwdata = intptr.zero; item.cbdata = 0; item.lpdata = intptr.zero; _pool.return(item); } }
内存管理守则:
- 对象池减少gc压力
- 显式重置结构体字段
- 使用弱引用跟踪未释放资源
第五章:扩展应用场景
让sendmessage发挥最大价值
5.1 传输复杂数据结构
// 自定义数据序列化 [structlayout(layoutkind.sequential)] public struct usermessage { [marshalas(unmanagedtype.byvaltstr, sizeconst = 256)] public string username; public int score; public datetime timestamp; } // 发送复杂结构 public static void sendusermessage(intptr hwnd, usermessage message) { int size = marshal.sizeof(message); byte[] buffer = new byte[size]; gchandle handle = gchandle.alloc(buffer, gchandletype.pinned); try { intptr pbuffer = handle.addrofpinnedobject(); marshal.structuretoptr(message, pbuffer, false); copydatastruct cds = new copydatastruct { dwdata = (intptr)1, // 自定义消息类型 cbdata = size, lpdata = pbuffer }; sendmessage(hwnd, windowmessages.wm_copydata, intptr.zero, ref cds); } finally { handle.free(); } }
设计考量:
- 使用
structlayout
控制内存布局 - 添加版本号字段支持协议升级
- 使用二进制序列化提升效率
5.2 实现双向通信
// 双向通信示例 public class bidirectionalcommunicator { private intptr _serverhandle; private int _messageid = windowmessages.custom_msg_base + 1; public void connect(string serverprocessname) { _serverhandle = windowhandlemanager.getwindowhandle(serverprocessname); } public void sendrequest(string request) { // 构造请求 byte[] requestbytes = encoding.utf8.getbytes(request); gchandle requesthandle = gchandle.alloc(requestbytes, gchandletype.pinned); // 注册回调 intptr callbackptr = marshal.getfunctionpointerfordelegate( new messagecallback(onresponsereceived) ); // 发送请求 copydatastruct cds = new copydatastruct { dwdata = (intptr)_messageid, cbdata = requestbytes.length, lpdata = requesthandle.addrofpinnedobject() }; intptr result = sendmessage( _serverhandle, windowmessages.wm_copydata, callbackptr, ref cds ); requesthandle.free(); } private void onresponsereceived(intptr hwnd, uint msg, intptr wparam, intptr lparam) { // 处理响应... } // 服务器端需要实现对应的回调处理 }
双向通信架构:
- 使用唯一
messageid
标识会话 - 回调函数地址作为
wparam
传递 - 需要双方约定完整的协议规范
通过本文,你已经掌握了:
sendmessage
的底层工作原理- 完整的进程间通信实现方案
- 高性能优化策略
- 常见异常的防御性编程
- 复杂场景的扩展应用
墨工的终极建议:
- 对于高可靠性场景,建议结合
memorymappedfile
实现共享内存 - 使用
performancecounter
监控通信性能 - 开发可视化调试工具(如wireshark插件)
以上就是c#使用sendmessage实现进程间通信的示例代码的详细内容,更多关于c# sendmessage进程间通信的资料请关注代码网其它相关文章!
发表评论