c#进程之间对象传递方法
1. 起源
kv项目下载底层重构升级决定采用独立进程进行media下载处理,以能做到模块复用之目的,因此涉及到了独立进程间的数据传递问题。
目前进程间数据传递,多用wm_copydata、共享dll、内存映射、remoting等方式。相对来说,wm_copydata方式更为简便,网上更到处是其使用方法。
而且marshal这个静态类,其内置多种方法,可以很方便实现字符串、结构体等数据在不同进程间传递。
那么,对象呢?如何传递?
2、序列化
想到了,newtonsoft.json.dll这个神器。相对于内建的xmlserializer这个东西,我更喜欢用json。
那么,如此处理吧,我们来建个demo解决方案,里面有hostapp、clildapp两个项目,以做数据传递。
3、childapp项目
先说这个,我没有抽取共用的数据单独出来,而做为demo,直接写入此项目中,hostapp引用此项目,就可引用其中public出来的数据类型。
数据结构部分代码:
[structlayout(layoutkind.sequential)] public struct copydatastruct { public intptr dwdata; public int cbdata; [marshalas(unmanagedtype.lpstr)] public string lpdata; } [serializable] public class person { private string name; private int age; private list<person> children; public person(string name, int age) { this.name = name; this.age = age; this.children = new list<person>(); } public string name { get { return this.name; } set { this.name = value; } } public int age { get { return this.age; } set { this.age = value; } } public list<person> children { get { return this.children; } } public void addchildren() { this.children.add(new person("liuxm", 9)); this.children.add(new person("liuhm", 7)); } public override string tostring() { string info = string.format("姓名:{0},年龄:{1}", this.name, this.age); if (this.children.count != 0) { info += (this.children.count == 1) ? "\r\n孩子:" : "\r\n孩子们:"; foreach (var child in this.children) info += "\r\n" + child.tostring(); } return info; } }
窗体代码:
public partial class childform : form { public const int wm_copydata = 0x004a; private intptr hosthandle = intptr.zero; person person = new person("liujw", 1999); [dllimport("user32.dll", entrypoint = "sendmessage")] private static extern int sendmessage( intptr hwnd, // handle to destination window int msg, // message int wparam, // first message parameter ref copydatastruct lparam // second message parameter ); public childform(string[] args) { initializecomponent(); if (args.length != 0) this.hosthandle = (intptr)int.parse(args[0]); } private void btnsubmit_click(object sender, eventargs e) { this.person.name = txtname.text; int age; this.person.age = int.tryparse(txtage.text, out age) ? age : 0; this.person.addchildren(); if (this.hosthandle != intptr.zero) { string data = getpersionstr(); copydatastruct cds = new copydatastruct(); cds.dwdata = (intptr)901; cds.cbdata = data.length + 1; cds.lpdata = data; sendmessage(this.hosthandle, wm_copydata, 0, ref cds); } } private string getpersionstr() { return jsonconvert.serializeobject(this.person); } }
这样在窗体按钮btnsubmit_click事件中,完成了数据向hostapp的字符串形式传递。
如何获取宿主程序的窗口句柄呢?改造下childapp的program.cs过程即可:
/// <summary> /// 应用程序的主入口点。 /// </summary> [stathread] static void main(string[] args) { application.enablevisualstyles(); application.setcompatibletextrenderingdefault(false); application.run(new childform(args)); }
3、hostapp项目
我们权且称之为宿主项目吧,其窗体代码为:
public partial class mainform : form { public const int wm_copydata = 0x004a; public mainform() { initializecomponent(); } protected override void wndproc(ref message m) { base.wndproc(ref m); switch (m.msg) { case wm_copydata: copydatastruct copydata = new copydatastruct(); type type = copydata.gettype(); copydata = (copydatastruct)m.getlparam(type); string data = copydata.lpdata; restoreperson(data); break; } } private void restoreperson(string data) { var person = jsonconvert.deserializeobject<person>(data); if (person != null) txtinfo.text = person.tostring(); } private void btnsubmit_click(object sender, eventargs e) { runchildprocess(); } private void runchildprocess() { string apppath = path.getdirectoryname(application.executablepath); string childpath = path.combine(apppath, "childapp.exe"); process.start(childpath, this.handle.tostring()); } }
它的作用就是接收子进程传递回来的字串,用jsonconvert反序列化为person对象。
是不是很简单呢?
其实就是用了wm_copydata的字符串传递功能,加上json的序列化、反序列化,而实现c#不同进程间的对象传递
4、效果图:
5、2017-03-24追加:
今天又发现用json序列化较为复杂的字串时,出现转义错误,导致反序列化失败。于时改用二进制序列化,转其为base64字串进行传递,问题解决。
代码如下:
public static class serializehelper { /// <summary> /// 序列obj对象为base64字串 /// </summary> /// <param name="obj"></param> /// <returns></returns> public static string serialize(object obj) { if (obj == null) return string.empty; try { var formatter = new binaryformatter(); var stream = new memorystream(); formatter.serialize(stream, obj); stream.position = 0; byte[] buffer = new byte[stream.length]; stream.read(buffer, 0, buffer.length); stream.close(); return convert.tobase64string(buffer); } catch (exception ex) { throw new exception(string.format("序列化{0}失败,原因:{1}", obj, ex.message)); } } /// <summary> /// 反序列化字符串到对象 /// </summary> /// <param name="str">要转换为对象的字符串</param> /// <returns>反序列化出来的对象</returns> public static t deserialize<t>(string str) { var obj = default(t); if (string.isnullorempty(str)) return obj; try { var formatter = new binaryformatter(); byte[] buffer = convert.frombase64string(str); memorystream stream = new memorystream(buffer); obj = (t)formatter.deserialize(stream); stream.close(); } catch (exception ex) { throw new exception(string.format("序列化{0}失败,原因:{1}", obj, ex.message)); } return obj; } }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!