C# 进程间通信之二传递复杂数据类型(转)

C#下使用WM_COPYDATA传输数据说到Marshal的应用

笔者曾在一个项目的实施过程中,需要使用WM_COPYDATA在本地机器的两个进程间传输数据。在C++中实现非常简单,但在C#中实现时却出现了麻烦。由于没有指针,使用COPYDATASTRUCT结构传递数据时,无法正确传递lpData。从网上搜寻文档,找到一个例子,是将COPYDATASTRUCT结构的lpData声明为string。这样虽然能传递字符串,但不能传递随意的二进制数据。

偶然地,我查阅MSDN帮助时,发现了Marshal类。该类概述描述道:提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。这时,我豁然开朗,觉得找到了一个托管代码与非托管代码交互的桥梁。

于是我声明COPYDATASTRUCT如下:

[StructLayout(LayoutKind.Sequential)]

public struct COPYDATASTRUCT

{

public IntPtr dwData;

public int cbData;

public IntPtr lpData;

}

在发送数据时,我使用Marshal类分配一块全局内存,并将数据拷入这块内存,然后发送消息:

COPYDATASTRUCT cds;

cds.dwData = (IntPtr)flag;

cds.cbData = data.Length;

cds.lpData = Marshal.AllocHGlobal(data.Length);

Marshal.Copy(data,0,cds.lpData,data.Length);

SendMessage(WINDOW_HANDLER,WM_COPYDATA,0,ref cds);

在接收数据时,我使用Marshal类将数据从这块全局内存拷出,然后处理消息:

COPYDATASTRUCT cds = new COPYDATASTRUCT();

    Type mytype = cds.GetType();

cds = (COPYDATASTRUCT)m.GetLParam(mytype);

uint flag = (uint)(cds.dwData);

byte[] bt = new byte[cds.cbData];

Marshal.Copy(cds.lpData,bt,0,bt.Length);

详细源码如下:

/// <summary>

/// Windows 的COPYDATA消息封装类。

/// </summary>

public class Messager : System.Windows.Forms.Form

{

/// <summary>

/// 必需的设计器变量。

/// </summary>

private System.ComponentModel.Container components = null;

//消息标识

private const int WM_COPYDATA = 0x004A;

//消息数据类型(typeFlag以上二进制,typeFlag以下字符)

private const uint typeFlag = 0x8000;

/// <summary>

/// 重载CopyDataStruct

/// </summary>

[StructLayout(LayoutKind.Sequential)]

public struct COPYDATASTRUCT

{

public IntPtr dwData;

public int cbData;

public IntPtr lpData;

}

//

[DllImport("User32.dll",EntryPoint="SendMessage")]

private static extern int SendMessage(

int hWnd,                                  // handle to destination window

int Msg,                              // message

int wParam,                               // first message parameter

ref COPYDATASTRUCT lParam    // second message parameter

);

//

[DllImport("User32.dll",EntryPoint="FindWindow")]

private static extern int FindWindow(string lpClassName,string lpWindowName);

//接收到数据委托与事件定义

public delegate void ReceiveStringEvent(object sender,uint flag,string str);

public delegate void ReceiveBytesEvent(object sender,uint flag,byte[] bt);

public event ReceiveStringEvent OnReceiveString;

public event ReceiveBytesEvent OnReceiveBytes;

//发送数据委托与事件定义

public delegate void SendStringEvent(object sender,uint flag,string str);

public delegate void SendBytesEvent(object sender,uint flag,byte[] bt);

public event SendStringEvent OnSendString;

public event SendBytesEvent OnSendBytes;

//

public Messager()

{

//

// Windows 窗体设计器支持所必需的

//

InitializeComponent();

//

// TODO: 在 InitializeComponent 调用后添加任何构造函数代码

//

}

/// <summary>

/// 清理所有正在使用的资源。

/// </summary>

protected override void Dispose( bool disposing )

{

if( disposing )

{

if(components != null)

{

components.Dispose();

}

}

base.Dispose( disposing );

}

#region Windows 窗体设计器生成的代码

/// <summary>

/// 设计器支持所需的方法 - 不要使用代码编辑器修改

/// 此方法的内容。

/// </summary>

private void InitializeComponent()

{

//

// Messager

//

this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);

this.ClientSize = new System.Drawing.Size(200, 14);

this.Name = "Messager";

this.ShowInTaskbar = false;

this.Text = "Demo_Emluator";

this.WindowState = System.Windows.Forms.FormWindowState.Minimized;

}

#endregion

/// <summary>

///重载窗口消息处理函数

/// </summary>

/// <param name="m"></param>

protected override void DefWndProc(ref System.Windows.Forms.Message m)

{

switch(m.Msg)

{

//接收CopyData消息,读取发送过来的数据

case WM_COPYDATA:

COPYDATASTRUCT cds = new COPYDATASTRUCT();

                          Type mytype = cds.GetType();

cds = (COPYDATASTRUCT)m.GetLParam(mytype);

uint flag = (uint)(cds.dwData);

byte[] bt = new byte[cds.cbData];

Marshal.Copy(cds.lpData,bt,0,bt.Length);

if(flag <= typeFlag)

{

if(OnReceiveString != null)

{

OnReceiveString(this,flag,System.Text.Encoding.Default.GetString(bt));

}

}

else

{

if(OnReceiveBytes != null)

{

OnReceiveBytes(this,flag,bt);

}

}

break;

default:

base.DefWndProc(ref m);

break;

}

}

/// <summary>

/// 发送字符串格式数据

/// </summary>

/// <param name="destWindow">目标窗口标题</param>

/// <param name="flag">数据标志</param>

/// <param name="str">数据</param>

/// <returns></returns>

public bool SendString(string destWindow,uint flag,string str)

{

if(flag > typeFlag)

{

MessageBox.Show("要发送的数据不是字符格式");

return false;

}

int WINDOW_HANDLER = FindWindow(null,@destWindow);

if(WINDOW_HANDLER == 0) return false;

try

{

byte[] sarr = System.Text.Encoding.Default.GetBytes(str);

COPYDATASTRUCT cds;

cds.dwData = (IntPtr)flag;

cds.cbData = sarr.Length;

cds.lpData = Marshal.AllocHGlobal(sarr.Length);

Marshal.Copy(sarr,0,cds.lpData,sarr.Length);

SendMessage(WINDOW_HANDLER,WM_COPYDATA,0,ref cds);

if(OnSendString != null)

{

OnSendString(this,flag,str);

}

return true;

}

catch(Exception e)

{

MessageBox.Show(e.Message);

return false;

}

}

/// <summary>

/// 发送二进制格式数据

/// </summary>

/// <param name="destWindow">目标窗口</param>

/// <param name="flag">数据标志</param>

/// <param name="data">数据</param>

/// <returns></returns>

public bool SendBytes(string destWindow,uint flag,byte[] data)

{

if(flag <= typeFlag)

{

MessageBox.Show("要发送的数据不是二进制格式");

return false;

}

int WINDOW_HANDLER = FindWindow(null,@destWindow);

if(WINDOW_HANDLER == 0) return false;

try

{

COPYDATASTRUCT cds;

cds.dwData = (IntPtr)flag;

cds.cbData = data.Length;

cds.lpData = Marshal.AllocHGlobal(data.Length);

Marshal.Copy(data,0,cds.lpData,data.Length);

SendMessage(WINDOW_HANDLER,WM_COPYDATA,0,ref cds);

if(OnSendBytes != null)

{

OnSendBytes(this,flag,data);

}

return true;

}

catch(Exception e)

{

MessageBox.Show(e.Message);

return false;

}

}

时间: 2024-08-28 17:51:13

C# 进程间通信之二传递复杂数据类型(转)的相关文章

进程间通信(二)——Posix消息队列

1.概述 消息队列可认为是消息链表.有足够写权限的线程可以往队列中放置消息,有足够读权限的进程可以从队列中取走消息.每个消息是一个记录,由发送着赋予一个优先级. 在像队列中写入消息时,不需要某个进程在该队列上等待消息到达.这与管道不同,管道必须现有读再有写. 消息队列具有随内核的持续性,与管道不同.进程结束后,消息队列中消息不会消失.当管道最后一次关闭,其中的数据将丢弃. 消息队列具有名字,可用于非亲缘关系的进程间. Posix消息队列读总是返回最高优先级的最早消息,而System V消息队列的

嵌入式 Linux进程间通信(二)——exec族函数

嵌入式 Linux进程间通信(二)--exec族函数 exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件.这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件. exec族函数包含如下函数: #include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ...); int exec

Python爬虫总结(二)常见数据类型及其解析方法

Python爬虫总结(二)常见数据类型 上一篇我们简单介绍了如何用Python发送 http/https 请求获取网上数据,从web上采集回来的数据的数据类型有很多种,主要有: 放在HTML里. 直接放在javascript里. 放在JSON里. 放在XML里. 注意:这里很多概念都是web前端开发里的,因为我们采集的大多数数据都来自web,因此了解一些前端知识还是挺有必要的. 下面我简单介绍下各种数据类型,并结合一些实例介绍它们的解析方法. 数据类型 放在HTML里 HTML即超文本标记语言,

android 远程服务传递自定义数据类型

在Android系统中,进程间传递的数据包括Java语言支持的基本数据类型和用户自定义的数据类型,为了使数据能够穿越进程边界,所有数据都必须是"可打包".对于Java语言的基本数据类型,打包过程是自动完成的.但对于自定义的数据类型,用户需要实现Parcelable接口,使自定义的数据类型能够转换为系统级原语保存在Parcel对象中,穿越进程边界后可再转换为初始格式. AIDL支持的数据类型如下表: 类型 说明 需要引入 基本数据类型 boolean.byte.short.int.lon

redis使用基础(二) ——Redis数据类型

redis使用基础(二) --Redis数据类型 (转载请附上本文链接--linhxx)  一.概述 Redis是一种Key-Value类型的数据库,属于非关系型数据库,NoSQL的一种.Redis共有5种数据类型:字符串(string).散列(hash).列表(list).集合(set).有序集合(zset). 1.通配符 Redis支持部分通配符,包括?.*.[].\x,和正则表达式一致,?表示匹配0或1个,*匹配任意个,[]匹配框内的任意一个内容,\x转义,例如\?表示匹配?. 2.获取键

二:Redis数据类型

一.?nosql(非关系性数据库): mongoDB hbase redis nulch hive pig mahout zookeeper 二:redis 数据类型 1.存储string: 常用命令: 设置/取值: set key value get key getset key value del key 数值增减: incr key decr key 扩展命令: incrby key increment decrby key decrement append key value 2.存储h

Android进程间通信之----Aidl传递对象

转载请注明出处 CSDN废墟的树 前言 有关Android进程间通信之Aidl编程的基本使用步骤已经在上一篇博客中有讲解,Android studio 下的aidl编程实现Android的夸进程间通信.上一篇博客中只是演示了怎么利用Aidl实现跨进程间传递Java基本类型,以及Aidl传递Bitamap对象.可能在一些场景下你需要跨进程传递一个对象,那么Aidl是否能传递一个对象呢?答案是肯定的,网上也有很多相关的资料,之所以写这篇博客:一是当作自己学习笔记咯,二是把自己遇到的问题分享出来. A

mysql学习笔记(二)--- MySQL数据类型

[正文] 上一章节中,我们学习了MySQL软件的安装,既然软件都装好了,现在就正式开始MySQL的基础知识的学习吧,即使是零基础,也要一步一个脚印.恩,首先要学习的就是MySQL的数据类型. 一.数据类型: 1.整型(xxxint) 2.浮点型(float和double) 3.定点数(decimal) 4.字符串(char,varchar,xxxtext) 5.二进制数据(xxxBlob) 6.日期时间类型 二.数据类型介绍: 1.整型: 注:M表示最大的显示宽度.其中,int用的最多. 2.浮

Java事务(二) - 传递Connection

一. 为什么要传递Connection? 在前面的概述中我们知道, JDBC事务处理的作用对象为Connection, 因此要想控制操作在同一个事务里面, 我们必须要传递Connection, 确保使用的是同一个Connection. 二. 如何传递Connection? 本实例使用转账的例子: 即从A账户转100元到B账户, 这需要做两次update表操作 1. 代码结构图: 2. 建表语句: DROP TABLE IF EXISTS `account`; CREATE TABLE `acco