c#进程间通信是否可以传递对象?

时间:2022-08-28 20:48:36
进程间通信的方式有很多,比如管道,比如发送消息等,传递个字符串啥的好办些,但我要传递个对象呢?可以吗?怎么做?

具体描述如下:

一个项目生成一个EXE,里面包含两个窗体A、B。启动EXE的时候(比如不带参数运行这个EXE),显示窗体A。

窗体A上放个按钮,点击的时候带参数启动这个EXE的另一进程,进程启动的时候检测到参数,显示窗体B。

然后两个进程用某种途径实现通信。

现在问:B窗体能否得到A这个窗体对象?(当然是实现进程通信后)得到后,直接可以取得A的属性等。反过来也一样,A也要可以取得窗体B这个对象。

搜索了半天,好像可以序列化,但看得一头雾水,所以在此请教。COM类的可以CoMarshalInterface等实现,在.NET下就不知道怎么办了。

29 个解决方案

#1


对象也是在内存里,你把那块内存读出来,转换为对象就行了,传递内存地址(内存映射)

#2


不可以传递窗体,因为窗体不可以系列化,本身也不支持代理。

#3


只要是内存中,什么都可以传递,

#4


引用 1 楼 bdmh 的回复:
对象也是在内存里,你把那块内存读出来,转换为对象就行了,传递内存地址(内存映射)

+1

#5


引用 1 楼 bdmh 的回复:
对象也是在内存里,你把那块内存读出来,转换为对象就行了,传递内存地址(内存映射)


比如读出自身内存起始地址和长度,传递给另外一个进程?这个难度比较大吧





引用 2 楼 gomoku 的回复:
不可以传递窗体,因为窗体不可以系列化,本身也不支持代理。


如果无法实现,在需要做较多通信的时候,实在是有些麻烦啊。



感谢版主们回复。

#6


引用 3 楼 mlqxj35674 的回复:
只要是内存中,什么都可以传递,

内存是可以传递,但对一个进程有意义的内存数据,在另外一个进程就可能是垃圾。

#7


这个板块就是好,提问后那么多人帮忙解答,很欣慰……

在COM框架下,比如用VB6,列集IDispatch是可以成功的,在.NET很茫然。

#8


可以用Remoting做。其中MarshalByRefObject表示可远程代理。

1、新建一个WinForm项目作为服务方。
2、添加一个MyWindow.cs文件,并贴入内容(关键是接口不要有命名空间)。
3、打开WinForm项目的代码,在Form1.cs的构造函数中登记Remote Server。

// MyWindow.cs
using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;

public interface IMyWindow
{
    Point Location { get; set; }
    Size Size { get; set; }
}

public class MyWindow : MarshalByRefObject, IMyWindow
{
    public Point Location
    {
        get
        {
            Form form = Application.OpenForms.OfType<Form>().FirstOrDefault();
            return form == null ? Point.Empty : form.Location;
        }
        set
        {
            Form form = Application.OpenForms.OfType<Form>().FirstOrDefault();
            if (form != null) form.Location = value;
        }
    }

    public Size Size
    {
        get
        {
            Form form = Application.OpenForms.OfType<Form>().FirstOrDefault();
            return form == null ? Size.Empty : form.Size;
        }
        set
        {
            Form form = Application.OpenForms.OfType<Form>().FirstOrDefault();
            if (form != null) form.Size = value;
        }
    }
}


        public Form1()
        {
            InitializeComponent();

            // 登记Remote Server
            try
            {
                ChannelServices.RegisterChannel(new TcpChannel(54322), false);
                RemotingConfiguration.RegisterWellKnownServiceType(typeof(MyWindow), "MyWindow", WellKnownObjectMode.SingleCall);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }


4、再新建一个Winform项目作为客户。
5、拉一个PropertyGrid到窗体上。
6、打开该项目的Form1.cs
7、改成类似以下代码(关键是接口不要有任何命名空间,与Server保持一致)
8、记得要先运行server,再运行客户。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication4
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.propertyGrid1.SelectedObject = new MyController(); // -- 添加
        }
    }
}

// -- 添加
public interface IMyWindow
{
    Point Location { get; set; }
    Size Size { get; set; }
}

public class MyController
{
    IMyWindow remoteObject;
    public MyController()
    {
        try
        {
            remoteObject = Activator.GetObject(typeof(IMyWindow), "tcp://localhost:54322/MyWindow") as IMyWindow;
        }
        catch (Exception ex)
        {
            this.Error = "Failed remote activation:" + ex.Message;
        }
    }


    public string Error { get; private set; }
    public Point Location
    {
        get { return remoteObject == null ? Point.Empty : remoteObject.Location; }
        set { if (remoteObject != null) remoteObject.Location = value; }
    }
    public Size Size
    {
        get { return remoteObject == null ? Size.Empty : remoteObject.Size; }
        set { if (remoteObject != null) remoteObject.Size = value; }
    }
}
// -- 结束添加

#9


表示段位低,还需要仔细研究代码,非常敬佩+感谢。我先研究下8楼代码

#10


该回复于2014-08-01 16:19:22被管理员删除

#11


可以 用窗体句柄 和进程ID 可以互相通讯

#12


保护模式中,两个进程的同一个内存地址完全是两回事。
楼主还是多了解一下序列化吧。
另外序列化的对象应该是数据描述性的,而不是窗体这种东西。

#13


1.系统API。如:SendMessage与FindWindow
2.remoting。
3.WCF;
4.webService
等等。具体看你的应用。

#14


引用 楼主 hpygzhx520 的回复:
进程间通信的方式有很多,比如管道,比如发送消息等,传递个字符串啥的好办些,但我要传递个对象呢?可以吗?怎么做?


你了解过xml或者json吗?

#15


c#进程间通信是否可以传递对象?
楼上虽然说的对象也是存于内存之中并没有错,但如果其他进程需要访问这就不太可能
即使你将那一块内存完全复制出也是不行的,正确的办法是通过进行通信编写特定机制
来实现A进程类与B进程进行交互,否则编写COM+使用MSAA也是可以的,前提是你必
必须清楚暴露出的IID与发送捕获的消息是什么一般取WM_GetObject

#16


我实际使用中就是需要“窗体”这个对象,现在倒不是麻烦不麻烦的事情。比如窗体的public void Show(IWin32Window owner);,我想指定A进程的A窗体为B进程B窗体的owner

#17


序列化传过去再反序列化就好了

#18


也可以传递对象,但是更建议通过json序列化反序列化的方式来传递信息,
简单,方便,跨一切

#19


引用 16 楼 hpygzhx520 的回复:
我实际使用中就是需要“窗体”这个对象,现在倒不是麻烦不麻烦的事情。比如窗体的public void Show(IWin32Window owner);,我想指定A进程的A窗体为B进程B窗体的owner


没有你想象的这种东西。

即使是2003年前就开始被淘汰的 remoting 技术,它也是“对象代理”机制。因此就有一堆复杂的通讯层,和一堆的编程限制你需要遵守。例如 remoting 根本不能直接支持远程对象事件通知,必须通过手动进行复杂的封装来模拟。

remoting 与另外两种技术,在上个世纪90年代是主要的三大进程通讯技术。然后到了互联网时代,这些全都烟消云散了。逐渐地,soap等等基于 xml 进行封装处理的轻量级通讯技术成为了主流。最近5、6年由于ajax技术兴起,json较多地取代 xml。

#20


如果你做点局域网内的程序,那么你可以使用 remoting。感受一下.net 框架中早期的 remoting 技术是个好事。其实 remoting 还是相当好用的,而且也非常强大的。但是绝对达不到你要求的那种程度。

从互联网爆发出来的各种应用需要互联互通,于是浅显的东西占了上风,而且这些东西显然就比沉重的 rpc 通讯框架更快更轻便。如果你要做自己的远程控制,那么你需要定义5、6个通讯信令(例如开启、关闭命令,某种业务事件通知),定义通讯实体模型(例如对象的“唯一标识id”、大小、相对位置)等属性。然后在两个进程中实现灵活的通讯网关。这个协议可以写成文本,然后发给各种各样的公司采用完全不同的技术平台去实现。但是使用 remoting,那么对技术上的限制就很“死”了。

如果你自己不会设计更好的远程操控技术框架,那么你当然还是可以首选 remoting。使用remoting其实可以省去考虑消息序列化、解析、对象自动跟踪等问题,是一个相当伟大的框架。它是简单而完善(但是不完备)的,不仅仅用于远程通讯,它往往也是系统级平台“横切注入”技术的一个标准研究对象。remoting 的问题就是与当今主流编程不协调,公司找到的使用 remoting 进行系统开发人员很容易是又不懂、又漫天要价的。但是你自己学习他很好。

#21


VB6中可以用ActiveX.Exe轻松实现

.Net中,用前几年的方法,可以对指定的窗口进行一下包装,用一个相对简单的方式提取出一个包含必要操作的类,设计为COM+组件(服务器应用程序,等同于进程外组件(作用相当于COM中以-embed为参数的exe )),进程中不直接操作Form类,而是操作COM+组件.

对于轻量级的,也可以通过普通的进程通讯方式完成同一窗口跨进程数据同步.我个人习惯使用CreateFileMapping+WM_USER

这几年这方面的需求好象越来越少,不太清楚是用什么方法解决此类需求

#22


至于楼主的需求,由于是同一个EXE,完全可以通过互斥锁禁止重复启动,然后把内部搞成多线程去解决性能问题.这类问题我也遇到过,一般是同一台主机上多身份登录共同请求一项资源时常遇到的需求,一般可以通过优化软件功能或客户业务逻辑去解决.如果是两个不同的Exe,可以尝试一下上面的方法.

#23


引用 16 楼 hpygzhx520 的回复:
我实际使用中就是需要“窗体”这个对象,现在倒不是麻烦不麻烦的事情。比如窗体的public void Show(IWin32Window owner);,我想指定A进程的A窗体为B进程B窗体的owner


如果是要IWin32Window,那就简单的多了。就象贴创可贴一样,用不着进程通讯/RPC这些象试管婴儿的大动作。

class WindowWrapper : IWin32Window
{
    public IntPtr Handle {get; set;}
}
// 调用方法:
IntPtr intptr = FindWindow等等;
Show(new WindowWrapper (){Handle = intptr});

#25


只要这个程序是你自己写的就好办,A、B窗体不断地将自己的属性等B窗体要求的东西作为消息作为应答发给另一个窗体就可以了。也可以通过文件进行。把自己找到改为提醒对方给出就可以了。

#26


你能传字符串,就能传对象。比如用xml或者JSON格式的字符串封装一下,然后再还原就是了。

#27


感谢各位的回复和指点,我暂时不结贴,我仔细研究下各位的回复。

#28


特意上来回你  用json或者xml序列化 和反序列化 ,用文件 或者 memcached来管理序列化内容,还有要建立确定的命名机制

#29


c#进程间通信是否可以传递对象?

#1


对象也是在内存里,你把那块内存读出来,转换为对象就行了,传递内存地址(内存映射)

#2


不可以传递窗体,因为窗体不可以系列化,本身也不支持代理。

#3


只要是内存中,什么都可以传递,

#4


引用 1 楼 bdmh 的回复:
对象也是在内存里,你把那块内存读出来,转换为对象就行了,传递内存地址(内存映射)

+1

#5


引用 1 楼 bdmh 的回复:
对象也是在内存里,你把那块内存读出来,转换为对象就行了,传递内存地址(内存映射)


比如读出自身内存起始地址和长度,传递给另外一个进程?这个难度比较大吧





引用 2 楼 gomoku 的回复:
不可以传递窗体,因为窗体不可以系列化,本身也不支持代理。


如果无法实现,在需要做较多通信的时候,实在是有些麻烦啊。



感谢版主们回复。

#6


引用 3 楼 mlqxj35674 的回复:
只要是内存中,什么都可以传递,

内存是可以传递,但对一个进程有意义的内存数据,在另外一个进程就可能是垃圾。

#7


这个板块就是好,提问后那么多人帮忙解答,很欣慰……

在COM框架下,比如用VB6,列集IDispatch是可以成功的,在.NET很茫然。

#8


可以用Remoting做。其中MarshalByRefObject表示可远程代理。

1、新建一个WinForm项目作为服务方。
2、添加一个MyWindow.cs文件,并贴入内容(关键是接口不要有命名空间)。
3、打开WinForm项目的代码,在Form1.cs的构造函数中登记Remote Server。

// MyWindow.cs
using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;

public interface IMyWindow
{
    Point Location { get; set; }
    Size Size { get; set; }
}

public class MyWindow : MarshalByRefObject, IMyWindow
{
    public Point Location
    {
        get
        {
            Form form = Application.OpenForms.OfType<Form>().FirstOrDefault();
            return form == null ? Point.Empty : form.Location;
        }
        set
        {
            Form form = Application.OpenForms.OfType<Form>().FirstOrDefault();
            if (form != null) form.Location = value;
        }
    }

    public Size Size
    {
        get
        {
            Form form = Application.OpenForms.OfType<Form>().FirstOrDefault();
            return form == null ? Size.Empty : form.Size;
        }
        set
        {
            Form form = Application.OpenForms.OfType<Form>().FirstOrDefault();
            if (form != null) form.Size = value;
        }
    }
}


        public Form1()
        {
            InitializeComponent();

            // 登记Remote Server
            try
            {
                ChannelServices.RegisterChannel(new TcpChannel(54322), false);
                RemotingConfiguration.RegisterWellKnownServiceType(typeof(MyWindow), "MyWindow", WellKnownObjectMode.SingleCall);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }


4、再新建一个Winform项目作为客户。
5、拉一个PropertyGrid到窗体上。
6、打开该项目的Form1.cs
7、改成类似以下代码(关键是接口不要有任何命名空间,与Server保持一致)
8、记得要先运行server,再运行客户。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication4
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.propertyGrid1.SelectedObject = new MyController(); // -- 添加
        }
    }
}

// -- 添加
public interface IMyWindow
{
    Point Location { get; set; }
    Size Size { get; set; }
}

public class MyController
{
    IMyWindow remoteObject;
    public MyController()
    {
        try
        {
            remoteObject = Activator.GetObject(typeof(IMyWindow), "tcp://localhost:54322/MyWindow") as IMyWindow;
        }
        catch (Exception ex)
        {
            this.Error = "Failed remote activation:" + ex.Message;
        }
    }


    public string Error { get; private set; }
    public Point Location
    {
        get { return remoteObject == null ? Point.Empty : remoteObject.Location; }
        set { if (remoteObject != null) remoteObject.Location = value; }
    }
    public Size Size
    {
        get { return remoteObject == null ? Size.Empty : remoteObject.Size; }
        set { if (remoteObject != null) remoteObject.Size = value; }
    }
}
// -- 结束添加

#9


表示段位低,还需要仔细研究代码,非常敬佩+感谢。我先研究下8楼代码

#10


该回复于2014-08-01 16:19:22被管理员删除

#11


可以 用窗体句柄 和进程ID 可以互相通讯

#12


保护模式中,两个进程的同一个内存地址完全是两回事。
楼主还是多了解一下序列化吧。
另外序列化的对象应该是数据描述性的,而不是窗体这种东西。

#13


1.系统API。如:SendMessage与FindWindow
2.remoting。
3.WCF;
4.webService
等等。具体看你的应用。

#14


引用 楼主 hpygzhx520 的回复:
进程间通信的方式有很多,比如管道,比如发送消息等,传递个字符串啥的好办些,但我要传递个对象呢?可以吗?怎么做?


你了解过xml或者json吗?

#15


c#进程间通信是否可以传递对象?
楼上虽然说的对象也是存于内存之中并没有错,但如果其他进程需要访问这就不太可能
即使你将那一块内存完全复制出也是不行的,正确的办法是通过进行通信编写特定机制
来实现A进程类与B进程进行交互,否则编写COM+使用MSAA也是可以的,前提是你必
必须清楚暴露出的IID与发送捕获的消息是什么一般取WM_GetObject

#16


我实际使用中就是需要“窗体”这个对象,现在倒不是麻烦不麻烦的事情。比如窗体的public void Show(IWin32Window owner);,我想指定A进程的A窗体为B进程B窗体的owner

#17


序列化传过去再反序列化就好了

#18


也可以传递对象,但是更建议通过json序列化反序列化的方式来传递信息,
简单,方便,跨一切

#19


引用 16 楼 hpygzhx520 的回复:
我实际使用中就是需要“窗体”这个对象,现在倒不是麻烦不麻烦的事情。比如窗体的public void Show(IWin32Window owner);,我想指定A进程的A窗体为B进程B窗体的owner


没有你想象的这种东西。

即使是2003年前就开始被淘汰的 remoting 技术,它也是“对象代理”机制。因此就有一堆复杂的通讯层,和一堆的编程限制你需要遵守。例如 remoting 根本不能直接支持远程对象事件通知,必须通过手动进行复杂的封装来模拟。

remoting 与另外两种技术,在上个世纪90年代是主要的三大进程通讯技术。然后到了互联网时代,这些全都烟消云散了。逐渐地,soap等等基于 xml 进行封装处理的轻量级通讯技术成为了主流。最近5、6年由于ajax技术兴起,json较多地取代 xml。

#20


如果你做点局域网内的程序,那么你可以使用 remoting。感受一下.net 框架中早期的 remoting 技术是个好事。其实 remoting 还是相当好用的,而且也非常强大的。但是绝对达不到你要求的那种程度。

从互联网爆发出来的各种应用需要互联互通,于是浅显的东西占了上风,而且这些东西显然就比沉重的 rpc 通讯框架更快更轻便。如果你要做自己的远程控制,那么你需要定义5、6个通讯信令(例如开启、关闭命令,某种业务事件通知),定义通讯实体模型(例如对象的“唯一标识id”、大小、相对位置)等属性。然后在两个进程中实现灵活的通讯网关。这个协议可以写成文本,然后发给各种各样的公司采用完全不同的技术平台去实现。但是使用 remoting,那么对技术上的限制就很“死”了。

如果你自己不会设计更好的远程操控技术框架,那么你当然还是可以首选 remoting。使用remoting其实可以省去考虑消息序列化、解析、对象自动跟踪等问题,是一个相当伟大的框架。它是简单而完善(但是不完备)的,不仅仅用于远程通讯,它往往也是系统级平台“横切注入”技术的一个标准研究对象。remoting 的问题就是与当今主流编程不协调,公司找到的使用 remoting 进行系统开发人员很容易是又不懂、又漫天要价的。但是你自己学习他很好。

#21


VB6中可以用ActiveX.Exe轻松实现

.Net中,用前几年的方法,可以对指定的窗口进行一下包装,用一个相对简单的方式提取出一个包含必要操作的类,设计为COM+组件(服务器应用程序,等同于进程外组件(作用相当于COM中以-embed为参数的exe )),进程中不直接操作Form类,而是操作COM+组件.

对于轻量级的,也可以通过普通的进程通讯方式完成同一窗口跨进程数据同步.我个人习惯使用CreateFileMapping+WM_USER

这几年这方面的需求好象越来越少,不太清楚是用什么方法解决此类需求

#22


至于楼主的需求,由于是同一个EXE,完全可以通过互斥锁禁止重复启动,然后把内部搞成多线程去解决性能问题.这类问题我也遇到过,一般是同一台主机上多身份登录共同请求一项资源时常遇到的需求,一般可以通过优化软件功能或客户业务逻辑去解决.如果是两个不同的Exe,可以尝试一下上面的方法.

#23


引用 16 楼 hpygzhx520 的回复:
我实际使用中就是需要“窗体”这个对象,现在倒不是麻烦不麻烦的事情。比如窗体的public void Show(IWin32Window owner);,我想指定A进程的A窗体为B进程B窗体的owner


如果是要IWin32Window,那就简单的多了。就象贴创可贴一样,用不着进程通讯/RPC这些象试管婴儿的大动作。

class WindowWrapper : IWin32Window
{
    public IntPtr Handle {get; set;}
}
// 调用方法:
IntPtr intptr = FindWindow等等;
Show(new WindowWrapper (){Handle = intptr});

#24


#25


只要这个程序是你自己写的就好办,A、B窗体不断地将自己的属性等B窗体要求的东西作为消息作为应答发给另一个窗体就可以了。也可以通过文件进行。把自己找到改为提醒对方给出就可以了。

#26


你能传字符串,就能传对象。比如用xml或者JSON格式的字符串封装一下,然后再还原就是了。

#27


感谢各位的回复和指点,我暂时不结贴,我仔细研究下各位的回复。

#28


特意上来回你  用json或者xml序列化 和反序列化 ,用文件 或者 memcached来管理序列化内容,还有要建立确定的命名机制

#29


c#进程间通信是否可以传递对象?