.net 远程处理,委托在错误的进程中被调用

.net remoting, delegate getting called in wrong process

我正在对 .net 远程处理进行一些测试,发现在使用委托时存在问题。

我有一个既是服务器又是客户端的应用程序。当用户 运行 第一次从资源管理器访问应用程序时,它 运行 作为服务器并作为客户端启动一个新进程。两者都很好。现在,当用户 运行 在服务器和客户端进程仍在 运行ning 时再次使用它时,它假设成为客户端并向服务器发送有关新进程启动的消息,然后自行终止。

一切正常,除了委托在服务器进程中执行 客户。

这是代码。

    const string PIPE_NAME = "testPipeName33";
    const string OBJECT_NAME = "test";
    static RemoteObject remoteObject;
    static void RegisterClient()
    {
        IpcClientChannel chan = new IpcClientChannel();
        ChannelServices.RegisterChannel(chan, false);

        remoteObject = (RemoteObject)Activator.GetObject(typeof(RemoteObject),
                string.Format("ipc://{0}/{1}", PIPE_NAME, OBJECT_NAME));
    }
    static void RegisterServer()
    {
        BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
        serverProvider.TypeFilterLevel = TypeFilterLevel.Full;

        IpcServerChannel chan = new IpcServerChannel("", PIPE_NAME, serverProvider);
        ChannelServices.RegisterChannel(chan, false);

        RemotingServices.Marshal(new RemoteObject(), OBJECT_NAME);
    }

    [STAThread]
    static void Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        if ((args.Length == 0 || args[0] == "s"))
        {
            try
            {
                RegisterServer();
            }
            catch (RemotingException)
            {
                // try to register it with the pipe name. If it fails, means server is already running.
                //bad idea, I know, but it's just for barebone quick test
                RegisterClient();
                remoteObject.OnNewProcessStarted("test");
                Application.Exit();
                return;
            }

            Process.Start(Application.ExecutablePath, "c");
            Application.Run(new Form1("Server"));

        }
        else
        {
            IsClient = true;
            RegisterClient();
            remoteObject.SetOnNewProcessStarted(OnNewProcessStarted);

            Application.Run(new Form1("Client"));

        }
    }


    static bool IsClient = false;
    static bool OnNewProcessStarted(string commandLine)
    {
        MessageBox.Show("Is Client : " + IsClient);//problem here, IsClient should be true
        return true;
    }

RemoteObject class.

public delegate bool OnNewProcessStartedDelegate(string text);

internal class RemoteObject : MarshalByRefObject
{
    public OnNewProcessStartedDelegate OnNewProcessStartedHandler;
    public bool OnNewProcessStarted(string commandLine)
    {
        if (OnNewProcessStartedHandler != null)
            return OnNewProcessStartedHandler(commandLine);
        return false;
    }

    public void SetOnNewProcessStarted(OnNewProcessStartedDelegate onNewProcessStarted)
    {
        OnNewProcessStartedHandler = onNewProcessStarted;
    }

    public override object InitializeLifetimeService()
    {
        return null;
    }
}

PS : 只能有一台服务器和一台客户端。

在应用程序的第二个 运行 中,您没有在 RemoteObject 中设置委托,因此使用之前设置的委托对象(它是在您的第一个 运行 中设置的申请)。

我认为,如果您 运行 您的第二个应用程序使用 'c' 这样的命令参数,委托将在客户端进程中 运行。

代理在服务器上 运行,因为新客户端正在调用服务器上 RemoteObject 的代理。服务器和第一个客户端之间没有注册回调。服务器将不知道要回调哪个进程,因为第一个客户端中没有人在监听请求。 通常使用 WCF 等技术,我们可以拥有 DualHttpBindings,这将允许双工通信,但在这里我们没有任何回调。所以我们必须在第一个客户端中使用 RemoteObject 注册一个回调通道,这将确保在第一个客户端进程中调用委托。我已经尝试过了,它正在工作。我添加了一些进程 ID 用于调试目的。

  static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            const string PIPE_NAME = "testPipeName33";
            const string OBJECT_NAME = "test";
            const string CALLBACK_PIPE_NAME = "testPipeName34";
            const string CALLBACK_OBJECT_NAME = "testclient";

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            if ((args.Length == 0 || args[0] == "s"))
            {
                try
                {
                   IPCRegistration.RegisterServer(PIPE_NAME,OBJECT_NAME);
                }
                catch (RemotingException)
                {
                                        remoteObject = IPCRegistration.RegisterClient(typeof(RemoteObject),PIPE_NAME,OBJECT_NAME);
                    remoteObject.OnNewProcessStarted("test");
                    Application.Exit();
                    return;
                }
                MessageBox.Show("Server:" + Process.GetCurrentProcess().Id);
                Process.Start(Application.ExecutablePath, "c");
                Application.Run(new Form1("Server"));

            }
            else
            {
                IsClient = true;
                remoteObject = IPCRegistration.RegisterClient(typeof(RemoteObject), PIPE_NAME, OBJECT_NAME);
                IPCRegistration.RegisterServer(CALLBACK_PIPE_NAME, CALLBACK_OBJECT_NAME); // Here Client will listen on this channel.
                remoteObject.SetOnNewProcessStarted(OnNewProcessStarted,Process.GetCurrentProcess().Id.ToString());
                MessageBox.Show("Client:" + Process.GetCurrentProcess().Id);
                Application.Run(new Form1("Client"));

            }
        }


        static RemoteObject remoteObject;

        static bool IsClient = false;
        static bool OnNewProcessStarted(string commandLine)
        {
            MessageBox.Show("saved:"+commandLine+" Currrent:"+Process.GetCurrentProcess().Id);
            MessageBox.Show("Is Client : " + IsClient);//problem here, IsClient should be true
            return true;
        }
    }

    public delegate bool OnNewProcessStartedDelegate(string text);

    internal class RemoteObject : MarshalByRefObject
    {
        public OnNewProcessStartedDelegate OnNewProcessStartedHandler;
        public string value;
        public bool isCallBack = false;
        const string PIPE_NAME = "testPipeName33";
        const string OBJECT_NAME = "test";
        const string CALLBACK_PIPE_NAME = "testPipeName34";
        const string CALLBACK_OBJECT_NAME = "testclient";
        RemoteObject remoteObject;
        public bool OnNewProcessStarted(string commandLine)
        {
            if (!isCallBack)
            {
                remoteObject.isCallBack = true;
                return remoteObject.OnNewProcessStarted(commandLine);
            }

            if (OnNewProcessStartedHandler != null)
                return OnNewProcessStartedHandler(value);
            return false;
        }



        public void SetOnNewProcessStarted(OnNewProcessStartedDelegate onNewProcessStarted,string value)
        {
            this.value = value;
            OnNewProcessStartedHandler = onNewProcessStarted;
            if (!isCallBack)
            {
                remoteObject = IPCRegistration.RegisterClient(typeof(RemoteObject), CALLBACK_PIPE_NAME, CALLBACK_OBJECT_NAME);
                remoteObject.isCallBack = true;
                remoteObject.SetOnNewProcessStarted(onNewProcessStarted, Process.GetCurrentProcess().Id.ToString());
            }
        }

        public override object InitializeLifetimeService()
        {
            return null;
        }
    }

    internal class IPCRegistration
    {
       public  static RemoteObject RegisterClient(Type remoteObject,string PIPE_NAME,string OBJECT_NAME)
        {
            IpcClientChannel chan = new IpcClientChannel();
            ChannelServices.RegisterChannel(chan, false);

            RemoteObject remoteObjectInstance = (RemoteObject)Activator.GetObject(remoteObject,
                    string.Format("ipc://{0}/{1}", PIPE_NAME, OBJECT_NAME));
            return remoteObjectInstance;
        }
        public static void RegisterServer(string pipeName, string objectName)
        {
            BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
            serverProvider.TypeFilterLevel = TypeFilterLevel.Full;

            IpcServerChannel chan = new IpcServerChannel("", pipeName, serverProvider);
            ChannelServices.RegisterChannel(chan, false);

            RemotingServices.Marshal(new RemoteObject(), objectName);
        }
    }