c# multiprocess communication. Today, it's coming

introduction

In c#, most people may use practice to make perfect communication between multiple threads. They can easily use AsyncLocal and ThreadLocal, as well as GetData and SetData methods that support the transmission between threads in various static classes. In fact, multi process communication is also used more, but compared with multithreading, people who can use practice to make perfect are more familiar, There are still some gaps, so yesterday I sorted out several communication methods between multi processes, which do not include various message middleware and database, Grpc, WebSocket or Signalr. Just take c# code as an example. C# multi process communication is roughly divided into these categories, shared memory, With the help of Windows MSMQ message queue service, named pipes and anonymous pipes, as well as IPC HTTP TCP channels, as well as commonly used sockets, multi process communication is realized with the help of Win32 SendMessage Api, and the last one is Mutex related to semaphores between multi processes. I will put the code at the end of the article. You can download it if you need, Next, I'll give it to you one by one.

Shared memory

In fact c# there are many ways to implement shared memory, mainly through Win32 Api and MemoryMappedFile. The former requires the introduction of multiple Win32 dll methods, while the latter is relatively simple to use. You only need to call the class's createnew method to set the name and size of the memory mapping file, And operation permission can be realized. It supports access and Stream to read and write at the same time, but the performance is certainly good in Win32, and Win32 is not limited by language. At present, I don't know whether this class is limited by language. Next, let's look at the way the client and server use shared memory and the code to obtain data.

Server:

      

 MemoryMappedFile memoryAccessor = MemoryMappedFile.CreateNew("ProcessCommunicationAccessor", 500, MemoryMappedFileAccess.ReadWrite);//Create a shared memory mapping file object. The first parameter is the mapping name, which corresponds to the needs of the client. 500 is the size, and the unit is bytes, MemoryMappedFileAccess Is access permission, read-write or read-only write only, which cannot be used here Using Otherwise leave Using Will be released and the client cannot get the memory mapped object with this name

            using (var accessor = memoryAccessor.CreateViewAccessor())//Gets the view of the mapped file object
            {
                var helo = Encoding.UTF8.GetBytes("Accessor");
                accessor.WriteArray(0, helo, 0, helo.Length);//Writes the given value to this view
                richTextBox1.Text += Environment.NewLine + "Accessor Send Val:Accessor";
            }
            MemoryMappedFile memoryStream = MemoryMappedFile.CreateNew("ProcessCommunicationStream", 500, MemoryMappedFileAccess.ReadWrite);//Create a mapping file object for the stream
            using (var stream = memoryStream.CreateViewStream())//Gets the stream of the mapping file
            {
                var helo = Encoding.UTF8.GetBytes("Stream");
                stream.Write(helo, 0, helo.Length);//Writes the given value to this memory stream
                richTextBox1.Text += Environment.NewLine + "Accessor Send Val:Stream";
            }

Client:

      

MemoryMappedFile memoryAccessor = MemoryMappedFile.OpenExisting("ProcessCommunicationAccessor");//Get the definition of the server ProcessCommunicationAccessor The name of the memory map file is then invoked. ReadArray Method to read the data written by the server
            using (var accessor = memoryAccessor.CreateViewAccessor())
            {
                var s = new byte[999];
                var read = accessor.ReadArray(0, s, 0, s.Length);
                var str = Encoding.UTF8.GetString(s);
                richTextBox1.Text += Environment.NewLine + "Accessor Read Val:" + str.ToString();
            }
            MemoryMappedFile memoryStream = MemoryMappedFile.OpenExisting("ProcessCommunicationStream");//Get the definition of the server ProcessCommunicationStream The name of the memory map file is then invoked. ReadToEnd Method to read the data written by the server
            using (var stream = memoryStream.CreateViewStream())
            {
                using (var reader = new StreamReader(stream))
                {
                    var str = reader.ReadToEnd();
                    richTextBox1.Text += Environment.NewLine + "Stream Read Val:" + str + "\r\n";
                }
            }

We can see that on the server side, we define a MemoryMappedFile of type Accessor, which is written in the way of MemortViewAccessor when writing data, and then define a way of using Stream to write data. On the client side, we directly use the OpenExisting method to judge whether this object exists. If it exists, The createnew object defined by the server is used. If it does not exist, it is Null. Of course, other methods can also be used to obtain it, such as CreateOrOpen to determine whether it is obtained or recreated. On the client, we use ReadArray and ReadToEnd to read the data of Accessor and Stream written by the server, Then we can carry out a communication of data transmission between the client and the server.

MSMQ for Windows

The premise of using MSMQ is to install message queuing on this computer. The installation method needs to enable or close the program in the control panel, programs and functions, find the message queuing (MSMQ) server we need in the list, and then install it. After the installation is completed, We can right-click My computer to find the lowest service and application to see the message queue we installed, and then find the special queue. We create a new queue here, and then we can use it in our code. Here, I just write a simple demonstration, actually in the Messaging namespace, It also supports the control of message queue permissions, and so on. Next, let's see how to use message queue in code.

In the server, we have defined the type and name of the message queue we need to use. If the name is standardized, you can also refer to the introduction of name definition on the official website. We also support the definition of names in other ways. After definition, we will send a message message Message HelloWorld

    

MessageQueue queue = new MessageQueue(".\\Private$\\MessageQueue");//Right click my computer, click manage to find the service and application, find the private queue, and the name of the created private queue is MessageQueue
            queue.Send("Message HelloWorld");//Then send a message
            richTextBox1.Text += Environment.NewLine + "MessageQueue Send Val:Message HelloWorld";

In the client, we also define an object of a message queue with the server. Then we listen to the event of receiving messages in the message queue and start receiving messages asynchronously. After receiving, we will go to the completion event of ReceiveCompleted written by us, and then we end receiving asynchronously and get the messages sent by the server, Then use the XmlMessageFormatter object to format the message sent by our server. Here, the Type is the message Type sent by the server. The two need to correspond. After receiving and displaying it to the UI, we start asynchronous reception.

 var context = WindowsFormsSynchronizationContext.Current;
            MessageQueue myQueue = new MessageQueue(".\\Private$\\MessageQueue");//Define the message queue object, which is the same as the address of the server,
            myQueue.ReceiveCompleted += (a, b) =>//Define when acceptance is complete
            {
                var cts = context;
                var queue = a as MessageQueue;//Queue object
                queue.EndReceive(b.AsyncResult);
                var msg = b.Message;//Received message object
                msg.Formatter = new XmlMessageFormatter() { TargetTypes = new Type[] { typeof(string) } };//Set how the received message is formatted
                var msgVal = msg.Body;//Here is the specific message object sent by the server
                cts.Send(new System.Threading.SendOrPostCallback(s =>
                {

                    richTextBox1.Text += Environment.NewLine + "MessageQueue Read Val:" + msgVal + "\r\n";
                }), null);
                queue.BeginReceive();
            };
            myQueue.BeginReceive();

name pipes

Named pipes and anonymous pipes are located in system Io. Under the pipe namespace, as the name suggests, naming a pipe requires us to name the pipe so that it can be connected to the client. We need to define the name of the pipe, specify the direction of the pipe, whether it is input or output or input and output, and also define the maximum number of server instances and whether the Message type transmitted is Byte or Message, And whether asynchronous is enabled. Next, let's look at the code for communication between the server and the client.

Server side: We defined the pipe name as ProcessCommunicationPipe, which can be input or output, 10 instances, and use the Message transmission type to start asynchronous communication. Then we asynchronously wait for the client link. After the link is successful, we notify the UI client that it has been linked to the server, Then asynchronously receive the Message sent by the client and display it on the UI.

     

  ///Define a named pipe. The first parameter is the pipe name, and the second parameter represents input type or output type or input-output type,And setting the largest server instance, setting the transmission type, and enabling asynchronous read and write
            namedPipeServerStream = new NamedPipeServerStream("ProcessCommunicationPipe", PipeDirection.InOut, 10, PipeTransmissionMode.Message, PipeOptions.Asynchronous);
            //Asynchronously wait for client links if the above Options no Asynchronous If asynchronous, an error will be reported
            namedPipeServerStream.WaitForConnectionAsync().ContinueWith(s =>
            {
                var cts = synchronizationContext;
                //Refresh UI Notify of client links
                cts.Send(new System.Threading.SendOrPostCallback(b =>
                {
                    richTextBox1.Text += Environment.NewLine + "Client Is Connected;";
                }), null);
                var valByte = new byte[1024];
                //Asynchronously read messages sent by clients
                namedPipeServerStream.ReadAsync(valByte, 0, valByte.Length).ContinueWith(m =>
                 {
                     var val = valByte;
                     var str = Encoding.UTF8.GetString(val);
                     cts.Send(new System.Threading.SendOrPostCallback(b =>
                     {
                         richTextBox1.Text += Environment.NewLine + "Server Receive Val:" + str;
                     }), null);
                 });
            });

Server sending code: we define a Send button and a text box for sending content, and then we only need to call WriteAsync of the server to write our data to the server and Send it to the client.

     

  //Named pipes send messages to clients
            var data = Encoding.UTF8.GetBytes(textBox1.Text);
            //Send message to client
            namedPipeServerStream.WriteAsync(data, 0, data.Length);
            richTextBox1.Text += Environment.NewLine + "Server Send Val:" + textBox1.Text;

Client:

We define a Client object Represents the current computer and the same pipe name as the server. It is also defined as enabling asynchrony and input / output type. Then asynchronously link the server, update the UI, notify that the link has been successful, and asynchronously wait for the server to send a message to the Client, so as to display it on the UI.

 var cts = WindowsFormsSynchronizationContext.Current;
            //Define pipe objects,If necessary, it is communication between networks.Replace with the server name and of the server pipeName
            namedPipeClientStream = new NamedPipeClientStream(".", "ProcessCommunicationPipe", PipeDirection.InOut, PipeOptions.Asynchronous);
            //Asynchronous link server
            namedPipeClientStream.ConnectAsync().ContinueWith(s =>
            {
                var cs = cts;
                cs.Send(new System.Threading.SendOrPostCallback(b =>
                {
                    richTextBox1.Text += Environment.NewLine + "Server Is Connected;";
                }), null);
                var valByte = new byte[1024];
                //Asynchronously wait for the message sent by the server, and then update to UI
                namedPipeClientStream.ReadAsync(valByte, 0, valByte.Length).ContinueWith(sb =>
                {
                    var val = valByte;
                    var str = Encoding.UTF8.GetString(val);
                    cts.Send(new System.Threading.SendOrPostCallback(b =>
                    {
                        richTextBox1.Text += Environment.NewLine + "Client Receive Val:" + str;
                    }), null);
                });
            });

Client sending code: just like the server, if you write our data, the server will go to the ReadAsync method, and the server can receive the data we sent and display it to the UI,

     

    //The named pipe sends messages to the server
            var data = Encoding.UTF8.GetBytes(textBox1.Text);
            namedPipeClientStream.WriteAsync(data, 0, data.Length);
            richTextBox1.Text += Environment.NewLine + "Client Send Val:" + textBox1.Text;

 

Anonymous Pipe

The anonymous pipeline is our server and the parent Process. Our server needs to use Process to enable and open our child processes, and then pass in the handle of our client to the client. The client then links to the server according to the passed parameters, so as to realize communication. However, the anonymous pipeline does not support communication between networks and input and output, Only input or output is supported, and the anonymous pipeline provides PipeAccessRule to control access rights. Next, let's take a look at how the client communicates with the server, and how the server starts the client.

Server: the server defines the Process, sets the subprocess we need to start, then defines our anonymous pipeline, then transmits the Handlestring linked by the client to the client, then starts our client, defines the callback after asynchronously receiving the message, and then displays it on the page.

 //Define customer terminal process
            Process Client = new Process();
            //Child process path
            Client.StartInfo.FileName = @"E:\CoreRepos\ProcessCommunicationClient\bin\Debug\ProcessCommunicationClient.exe";
           //Define anonymous pipes,
            AnonymousPipeServerStream anonymousPipeServerStream = new AnonymousPipeServerStream(PipeDirection.In,
            HandleInheritability.Inheritable);
            Client.StartInfo.Arguments = anonymousPipeServerStream.GetClientHandleAsString();
            Client.StartInfo.UseShellExecute = false;
            Client.Start();
            //Turn off clients for local replication
            anonymousPipeServerStream.DisposeLocalCopyOfClientHandle();
            var byteVal = new byte[1024];
            //Accept received messages asynchronously
            anonymousPipeServerStream.ReadAsync(byteVal, 0, byteVal.Length).ContinueWith(s =>
            {
                var cts = synchronizationContext;
                var val = byteVal;
                var str = Encoding.UTF8.GetString(val);
                cts.Send(new System.Threading.SendOrPostCallback(b =>
                {
                    richTextBox1.Text += Environment.NewLine + "anonymous Server Receive Val:" + str;
                }), null);
            });

Client: in the client, we need to add a string array parameter to the Main method of Winform's Program, and then pass it into our form, so that the anonymous client pipeline link server can link successfully.

     

 //The object of the anonymous pipeline is defined here, Vs[0]From the server Process of Arguments The value of the property
            anonymousPipeClientStream = new AnonymousPipeClientStream(PipeDirection.Out, Vs[0]);

Client send code:

We directly call the WriteAsync method to write our data, and the server can receive the information we sent.

  //Send message to anonymous pipeline server
            var vss = Encoding.UTF8.GetBytes(textBox2.Text);
            anonymousPipeClientStream.WriteAsync(vss, 0, vss.Length);
            richTextBox1.Text += Environment.NewLine + "anonymous Client Send Val:" + textBox2.Text;

Channel

There are three types of channels: IPC,HTTP and TCP. The three types provide ClientChannel and ServerChannel and Channel classes. The Channel class simplifies the operation of Server and Client. You can directly use Channel to define the object of communication between Server and Client. Next, let's take a look at the way of Ipc communication.

     IPC

We define an IpcChannel object and specify that the ip is 127.0.0.1 and the port is 8081. Then we need to register our pipeline information with the pipeline service, and then register the type we need to inject, the URL address of the resource, and whether the life cycle is a single instance or each acquisition is different. There are only these two cycles, Then let's look at the code used by the client.

Server:

  ///definition IPC channel,Port and ip,You can also define ports directly
            ipcChannel = new IpcChannel("127.0.0.1:8081");
            //Register the current pipeline with the channel
            ChannelServices.RegisterChannel(ipcChannel, true);
            //Inject the object into the server, and specify the name of this object URL,And the life cycle, whether it is a single instance or each acquisition is different
            RemotingConfiguration.RegisterWellKnownServiceType(typeof(ProcessCommunicationIpc), "Ipc.rem", WellKnownObjectMode.Singleton);
            richTextBox1.Text += Environment.NewLine + "IPCServer Is Open;";

Client:

We define an empty pipeline information and register it, then define the type we need to obtain and the URL resource address of the type, and call the RegisterWellKnownClientType method. My opinion is that this method is equivalent to telling the server the resources we need to use, and then we directly New this object and call the SetName method, Communication can be realized. If the server how to obtain data, some students will ask, don't worry, let's look at the next section of code.

IpcChannel ipcChannel = new IpcChannel();//Define a IPC Pipeline objects also need to be registered in the pipeline service
            ChannelServices.RegisterChannel(ipcChannel, true);
            WellKnownClientTypeEntry entry = new WellKnownClientTypeEntry(typeof(ProcessCommunicationIpc), "ipc://127.0.0.1:8081/Ipc.rem");//Define the type we need to get and the type of Url
            RemotingConfiguration.RegisterWellKnownClientType(entry);//This is equivalent to informing the server of the objects we need to use
            ProcessCommunicationIpc processCommunicationIpc = new ProcessCommunicationIpc();//Define this object
            processCommunicationIpc.SetName(textBox3.Text);//And then call this. SetNama method
            richTextBox1.Text += Environment.NewLine + "IPCClient Send Val:" + textBox3.Text;

The server receives code: we directly call the GetObject method of Activator to get the address we have defined from the server to our registered type, and then call the Name attribute to see that Name is the data written by our client, because the life cycle defined by us is singleton, so the communication between the client and the server can be realized here. In fact, the use of HTTP and TCP is similar to that of IPC. We can see the codes used by HTTP and TCP.

   

    //From what we defined IPCurl Get the proxy object, and then judge whether the value changes
            var processCommunicationIpc = Activator.GetObject(typeof(ProcessCommunicationIpc), "ipc://127.0.0.1:8081/Ipc.rem") as ProcessCommunicationIpc;
            var name = processCommunicationIpc.Name;
            richTextBox1.Text += Environment.NewLine + "IPCServer Receive Val:" + name;

     Http

Server:

///definition HTTP channel,port
            HttpChannel httpChannel = new HttpChannel(8082);
            //Register the current pipeline with the channel
            ChannelServices.RegisterChannel(httpChannel, false);
            //Inject the object into the server, and specify the name of this object URL,And the life cycle, whether it is a single instance or each acquisition is different
            RemotingConfiguration.RegisterWellKnownServiceType(typeof(ProcessCommunicationHttp), "Http.rem", WellKnownObjectMode.Singleton);
            richTextBox1.Text += Environment.NewLine + "HttpServer Is Open;";

Server receiving:

    

//From what we defined Http url Get the proxy object, and then judge whether the value changes
            var processCommunicationIpc = Activator.GetObject(typeof(ProcessCommunicationHttp), "http://127.0.0.1:8082/Http.rem") as ProcessCommunicationHttp;
            var name = processCommunicationIpc.Name;
            richTextBox1.Text += Environment.NewLine + "HttpServer Receive Val:" + name;

Client:

    

 HttpChannel httpChannel=new HttpChannel();//Define a HTTP Pipeline objects also need to be registered in the pipeline service
            ChannelServices.RegisterChannel(httpChannel, false);
            WellKnownClientTypeEntry entry = new WellKnownClientTypeEntry(typeof(ProcessCommunicationHttp), "http://127.0.0.1:8082/Http.rem");//Define the type we need to get and the type of Url
            RemotingConfiguration.RegisterWellKnownClientType(entry);//This is equivalent to informing the server of the objects we need to use
            ProcessCommunicationHttp processCommunicationIpc = new ProcessCommunicationHttp();//Define this object
            processCommunicationIpc.SetName(textBox4.Text);//And then call this. SetNama method
            richTextBox1.Text += Environment.NewLine + "HttpClient Send Val:" + textBox4.Text;

     TCP

Server:

      

///definition Tcp channel,port
            TcpChannel tcpChannel = new TcpChannel(8083);
            //Register the current pipeline with the channel
            ChannelServices.RegisterChannel(tcpChannel, true);
            //Inject the object into the server, and specify the name of this object URL,And the life cycle, whether it is a single instance or each acquisition is different
            RemotingConfiguration.RegisterWellKnownServiceType(typeof(ProcessCommunicationTcp), "Tcp.rem", WellKnownObjectMode.Singleton);
            richTextBox1.Text += Environment.NewLine + "TcpServer Is Open;";

Server receiving:

     

   //From what we defined Tcp url Get the proxy object, and then judge whether the value changes
            var processCommunicationIpc = Activator.GetObject(typeof(ProcessCommunicationTcp), "tcp://127.0.0.1:8083/Tcp.rem") as ProcessCommunicationTcp;
            var name = processCommunicationIpc.Name;
            richTextBox1.Text += Environment.NewLine + "TcpServer Receive Val:" + name;

Client:

     

TcpChannel tcpChannel = new TcpChannel();//Define a TCP Pipeline objects also need to be registered in the pipeline service
            ChannelServices.RegisterChannel(tcpChannel, true);
            WellKnownClientTypeEntry entry = new WellKnownClientTypeEntry(typeof(ProcessCommunicationTcp), "tcp://127.0.0.1:8083/Tcp.rem");//Define the type we need to get and the type of Url
            RemotingConfiguration.RegisterWellKnownClientType(entry);//This is equivalent to informing the server of the objects we need to use
            ProcessCommunicationTcp processCommunicationIpc = new ProcessCommunicationTcp();//Define this object
            processCommunicationIpc.SetName(textBox5.Text);//And then call this. SetNama method
            richTextBox1.Text += Environment.NewLine + "TcpClient Send Val:" + textBox5.Text;

You can see that they are basically the same, but some places are different. I didn't write that part of the code here. For example, Http can be configured with HttpHandler, and other aspects are similar.

Socket

Socket may be the most commonly used process communication. It not only supports communication between processes, but also supports communication between networks. At the same time, there are many protocol types. It also supports two-way communication and can send files. I won't introduce it too much here. I'll go directly to the code

Server:

We directly define the server object, specify the address and port, start listening and wait for the link asynchronously,

 //definition Socket Object, protocol, transport type
            Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
            var ipAddress = IPAddress.Parse("127.0.0.1");
            var endpoint = new IPEndPoint(ipAddress, 8084);
            //Specify bound ip And ports
            socket.Bind(endpoint);
            //Maximum length of link
            socket.Listen(10);
            socket.BeginAccept(Accept, socket);//Asynchronous wait link
            richTextBox1.Text += Environment.NewLine + "Socket Server Is Listening;";

The server accepts the code asynchronously: after there is a connection, we directly get the Socket of the linked client object and assign it to our Socket global variable, then update the UI and asynchronously read the messages sent by the client.

   

 private void Accept(IAsyncResult asyncResult)
        {
            var socket = asyncResult.AsyncState as Socket;
            var client = socket.EndAccept(asyncResult);//Get linked clients
            if (client != null)
            {
                var cs = synchronizationContext;
                Client=client;
                //to update UI Prompt already linked
                cs.Send(new System.Threading.SendOrPostCallback(b =>
                {
                    richTextBox1.Text += Environment.NewLine + "Socket Client Is Connected;";
                }), null);

                //Asynchronously accept messages
                client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, Read, client);
            }
            socket.BeginAccept(Accept, socket);
        }

Data code received by the server:

After receiving the message from the client, we parse it into a string and update it to the UI.

   var cts = synchronizationContext;
            var client = asyncResult.AsyncState as Socket;
            var data=client.EndReceive(asyncResult);//Gets the accepted data length
            var str = Encoding.UTF8.GetString(buffer);//Convert to characters and display to the interface
            cts.Send(new System.Threading.SendOrPostCallback(b =>
            {
                richTextBox1.Text += Environment.NewLine + "Socket Server Receive Val:" + str;
            }), null);

Server sending code:

We can directly call the Socket object of the Client we obtained and send the message we need to send.

//Send message to client
            var sendVal=Encoding.UTF8.GetBytes(textBox2.Text);
            Client.Send(sendVal,SocketFlags.None);
            richTextBox1.Text += Environment.NewLine + "Socket Server Send Val:" + textBox2.Text;

Client: define the IP and port of the server, and then we link asynchronously. After the link is successful, we send our data to the server, and wait asynchronously for the server to send us a message.

    

var cs = cts;
            //definition Socket Client object
            Socket socket = new Socket(SocketType.Stream,ProtocolType.Tcp);
            var ipAddress = IPAddress.Parse("127.0.0.1");
            var endpoint = new IPEndPoint(ipAddress, 8084);
            //Define the of the server to be linked IP And port, and then asynchronously link the server
            socket.ConnectAsync(endpoint).ContinueWith(s =>
            {
                //Send a message to the server after linking
                var arg = new SocketAsyncEventArgs();
                var sendVal=Encoding.UTF8.GetBytes(textBox6.Text);
                arg.SetBuffer(sendVal,0, sendVal.Length);
                socket.SendAsync(arg);
                cs.Send(new System.Threading.SendOrPostCallback(b =>
                {
                    richTextBox1.Text += Environment.NewLine + "Socket Client Send Val:" + textBox6.Text;
                }), null);
                //Asynchronously waiting for messages sent by the server
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, Read, socket);
            });

 

Client receiving code:

We directly read our data from our server Socket object and display it on the UI.

 var cs = cts;
            var client = asyncResult.AsyncState as Socket;
            var data = client.EndReceive(asyncResult);
            //Get the message sent by the server to the client
            var str = Encoding.UTF8.GetString(buffer);
            cs.Send(new System.Threading.SendOrPostCallback(b =>
            {
                richTextBox1.Text += Environment.NewLine + "Socket Client Receive Val:" + str;
            }), null);

 

Win32 Api SendMessage

In the form program, we can rewrite the DefWndProc method of the form to realize the message communication between processes. We need to introduce the Win32 SendMessage method. This method can send messages between one or more forms. We can specify the handle of the form we need to send, The Code of the message type we send can also be written by ourselves, and the parameters we need to pass can be defined as a structure for transmission. The receiver can use it by converting the handle from memory to the corresponding structure. The transmission data type I use here is Int data. If it is necessary to pass the structure, The imported Dll settings can be set at the SendMessage method, and the operation class Marshal l class that the receiver needs to use memory can be converted to a structure. Next, let's see how the client communicates with the server.

Server side: after we rewrite this method, just wait for the client to send us a message. m.msg is the message type agreed with the client.

    

 protected override void DefWndProc(ref System.Windows.Forms.Message m)
        {
            if (m.Msg == 0x1050)
            {
                var paraA =(int) m.WParam;
                var paramB = (int)m.LParam;
                richTextBox1.Text += Environment.NewLine + "Win32 Msg Receive Val:"+paraA;
                richTextBox1.Text += Environment.NewLine + "Win32 Msg Receive Val:" + paramB;
            }
            base.DefWndProc(ref m);
        }

Client code:

We need to introduce the SendMessage method we use

  [DllImport("user32.dll", EntryPoint = "SendMessage")]
        private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam,int  lParam);

Sending code:

We need to get the process we want to send to, then get the handle of the main program, and then pass in our message code and our parameter information, so that the server can receive the past 10 and 20 data sent by our client,

 //Get the process of the form we need to send to, then get its main form handle, and send our message 10,20 Send to the specified form and execute DefWndProc Method, and then judge in the method msg Is the type the same as the 0 sent here x1050 Consistent, you can receive the message sent by the client. The second parameter is the message type defined by us. You can define the number yourself or according to the Win32 api You can also use the corresponding functions specified in it
            var process=Process.GetProcessesByName("ProcessCommunication").FirstOrDefault();
            SendMessage(process.MainWindowHandle, 0x1050, 10,20);

Mutex semaphore

In the previous multi-threaded blog post, I said that Mutex can be between processes, at the operating system level. We can use WaitOne to enter our code segment, and only one thread can enter. After the end, we need to release the lock and get it from other threads. Since Mutex is between processes, it can also be obtained, Multiple processes can also share A Mutex object. When process A uses WaitOnd, process B can only wait for process A to release.

Server code:

We define the Mutex object, and then open a thread to perform an endless loop to refresh the UI information. Then we lock the lock inside the loop, notify the UI, and then release the lock. In this way, the same code on the client can go in to update the UI inside the loop only after ReleaseMutex.

 var isNew = false;
            //definition Mutex Object, parameter 1, whether it has initial weight, the second is the name in the system, and the third represents whether it is new;
            var mutex = new Mutex(false, "ProcessCommunication", out isNew);//It is used to use the same object as the client. In the loop, only one process can use this object, that is, the child process is using it WaitOne Method, the parent process has no way to enter the loop body, only the child process is called ReleaseMutex Method can only be used by the parent process; This is usually used to enable multiple processes to access the same file, etc.
            Task.Run(() => {
                var cs = synchronizationContext;
                int i = 0;
                while (true)
                {
                    mutex.WaitOne();
                    cs.Send(new SendOrPostCallback(s =>
                    {
                        richTextBox1.Text += Environment.NewLine + i;
                    }), null);
                    i++;
                    mutex.ReleaseMutex();
                }
            });

Client:

The client code is the same as the server code, but when running with breakpoints, you can see that the client has entered CS After send, the server has no way to enter. It can only enter after the client releases mutex. This is a scenario I mentioned earlier that can be used to implement multi process operation objects.

 var isNew = false;
            //establish Mutex object
            var mutex = new Mutex(false,"ProcessCommunication",out isNew);//It is used to use the same object as the client. In the loop, only one process can use this object, that is, the child process is using it WaitOne Method, the parent process has no way to enter the loop body, only the child process is called ReleaseMutex Method can only be used by the parent process; This is usually used to enable multiple processes to access the same file, etc.
            Task.Run(() => {
                var cs = cts;
                int i = 0;
                while (true)
                {
                    mutex.WaitOne();
                    cs.Send(new SendOrPostCallback(s =>
                    {
                        richTextBox1.Text += Environment.NewLine+i;
                    }), null);
                    i++;
                    mutex.ReleaseMutex();
                }
            });

end

That's all for today's multi process sharing. In fact, there are many ways to realize multi process communication, network communication, message queue, WebSocket, Api, Grpc, etc. here is just a demonstration c# in which most support multi process communication under FrameWork. If you don't understand anything, you can add a group to find me, Or check whether there is one Net group called Sichuan observation. It's also me. If you don't understand, you can ask me at any time. I'm here. I'll share the code for you. You can go and have a look.

Code address: http://121.43.235.192:8082/s/DjJkmyaj6Lk6sXj

 

Keywords: C#

Added by djddb on Sun, 09 Jan 2022 08:13:24 +0200