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

 

 Json參考:http://www.newtonsoft.com/json


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM