We only introduce several simple inter-process communication mechanisms: clipboard, anonymous pipeline, named pipeline, anonymous pipeline and oil tank.
You have used clipboard in normal times. For example, you have selected a piece of text in the notepad, then Ctrl + C copied to the clipboard, and then press Ctrl + V in the word to copy it. This completes the communication between two processes: From notepad to word. In fact, the clipboard is a memory area of system maintenance and management. When copying data in a process, it copies the data to the memory area, and then paste the data in another process, is to retrieve data from this memory area and display it in the window.
Below we will write an example program to implement the clipboard function.
First, the appearance of the program: a dialog box program with two edit boxes on it, used to enter the data sent to the clipboard and display the data received from the clipboard. Then add two buttons to control sending and receiving data.
We respond to the send data button:
Void cch_16_clipboarddlg: onbntsend () {// todo: add your control notification handler code here // open the clipboard if (! Openclipboard () {MessageBox ("failed to open the Clipboard"); Return ;}// get the control of the clipboard and release the previous data if (! Emptyclipboard () {MessageBox ("failed to access Clipboard"); return;} // get the text cstring STR in the edit box; getdlgitemtext (idc_edit_send, STR); // memory object, allocate hglobal hclip from the global heap; hclip = globalalloc (gmem_moveable, // sizeof (STR) + 1); // size // get the pointer to the memory object char * pbuf; pbuf = (char *) globallock (hclip); // copy strcpy (pbuf, STR); // reduce the reference count and unlock globalunlock (hclip ); // put the data setclipboarddata (cf_text, // hclip text format) into the clipboard; // Memory Object // close the clipboard so that other processes can obtain the clipboard closeclipboard ();}
Run the program and enter some text on the sending clipboard. Then you can open a Notepad program and select "Paste". Then, paste the entered text to the notepad. This achieves the communication of our processes with the notepad process.
In general, this program is divided into the following steps:
1. Open the clipboard
2. Enter the clipboard
3. Obtain the data in the edit box.
4. Place the data in the clipboard.
5. Close the clipboard
However, the setclipboarddata function in the clipboard is worth discussing in detail:
Handle setclipboarddata (uint uformat, handle hmem );
Uformat indicates the format of the data to be imported. You can select either a registered format or a standard format. The standard text format is selected here.
Hmem is a handle with the specified format: If this parameter is null, it indicates that the call window will not provide data in the specified clipboard format until a request is sent to the Clipboard data. If the window uses the delay switching technology, the window must process the wm_renderformat and wm_renderallformats messages.
This setting aims to improve efficiency: When we copy data to the clipboard, the data occupies the memory space. In fact, we often copy a bunch of data but do not use them. Then we copy another piece of data. In this way, the data copied for the first time is wasted. Windows provides an empty clipboard. When a process needs to paste copied data, it sends
Wm_renderformat message. In the message response function, the copy operation should be completed before completion.
We didn't use this complex mechanism here, just fill in a memory object in it. This memory object must be allocated by globalalloc and the allocation mark must be gmem_moveable.
Globalalloc allocates a specified number of bits from the stack. Gmem_moveable indicates that the memory object can be moved in the heap. If necessary, Windows can move the memory to better support system memory management. Because it is movable, it cannot be accessed directly with a pointer. You can use the globallock function to convert the handle to a pointer. In this case, the address of the physical memory is obtained. With the pointer, we use strcyp to copy the data in the edit box to this memory object. After the copy is complete, use globalunlock to unlock it.
Let's take a look at the implementation of the data receiving function:
Void cch_16_clipboarddlg: onbtnrecv () {// todo: add your control notification handler code hereif (! Openclipboard () {MessageBox ("Clipboard cannot be opened"); Return ;}// determine whether the data in the clipboard is in the specified format if (! Isclipboardformatavailable (cf_text) {MessageBox ("Clipboard data format error"); Return ;}// gets the clipboard memory object handle hclip; hclip = getclipboarddata (cf_text ); // get the Memory Object Pointer char * pbuf = (char *) globallock (hclip); globalunlock (hclip); // set the text setdlgitemtext (idc_edit_recv, pbuf); // close the clipboard, allow other processes to obtain the clipboard closeclipboard ();}
In this case, enter the input in the edit box, click send, and then click receive to receive the previously sent data.
The procedure of the program is as follows:
1. Open the clipboard
2. Determine whether the text format in the clipboard is consistent with our expected format
3. If the match exists, receive the data and save it to the memory object.
4. Get the pointer of the Memory Object
5. display the data
In this way, the communication from the clipboard to the process is realized.
Next we will look at the anonymous pipeline. It is an unnamed one-way pipeline used to transmit data between the parent process and the child process. Anonymous pipelines can only communicate between two processes on the local machine, but cannot communicate across networks.
First, create a single-document application and add two member variables to the class:
private:HANDLE hWrite;HANDLE hRead;
In the constructor, set them to null. In the destructor, if they are not released before, release them:
CCH_17_ParentView::CCH_17_ParentView(){// TODO: add construction code herehRead = NULL;hWrite = NULL;}CCH_17_ParentView::~CCH_17_ParentView(){if(hRead){CloseHandle(hRead);}if(hWrite){CloseHandle(hWrite);}}
Add an "anonymous Pipeline" menu to the menu, and add three menu items: idm_pipe_create "create anonymous Pipeline", idm_pipe_read "send data", and idm_pipe_write "receive data ".
Void cch_17_parentview: onpipecreate () {// todo: add your command handler code here // defines the Security Attribute structure security_attributes SA; // It can be inherited from SA. binherithandle = true; // default security description SA. lpsecuritydescriptor = NULL; SA. nlength = sizeof (security_attributes); // create an anonymous pipeline if (createpipe (& hread, // read handle & hwrite, // write handle & SA, // Security Attribute 0 )) // default size {MessageBox ("failed to create an anonymous Pipeline"); Return ;}// startupinfo Sui; zeromemory (& Sui, sizeof (startupinfo); sui. CB = sizeof (startupinfo); sui. dwflags = startf_usestdhandles; sui. hstdinput = hread; sui. hstdoutput = hwrite; // obtain the standard input handle sui. hstderror = getstdhandle (std_error_handle); // information about the newly created process and main thread, filled in process_information PI by the function; // created sub-process if (! CreateProcess (".. \ ch_17_child \ debug \ ch_17_child.exe ", // The sub-process name is null, // the command line parameter is null, and // The new process uses the default security level null, // The new primary thread uses the default security level true, // The child process can inherit the parent Process Handle 0, // no special creation flag null, // The new process uses the environment block of the calling process to be null. // The sub-process and the parent process have the same current path & Sui, // start Information & PI )) // newly created process and main thread information {closehandle (hwrite); hwrite = NULL; closehandle (hread); hread = NULL; MessageBox ("failed to create a sub-process "); return;} else {// close the handle closehandle (Pi. hprocess); closehandle (Pi. hthread );}}
The program is not long, but there are many parameters in the CreateProcess function. Let's take a look at the last two points:
Startupinfo Sui is used to specify how the new process's main window will be displayed. It has many members:
typedef struct _STARTUPINFO { DWORD cb; LPTSTR lpReserved; LPTSTR lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO, *LPSTARTUPINFO;
In the face of such struct, we should first check whether there are any special members. For example, dwflags. This member determines which members of the entire struct are used. (Since not all functions are useful, you should first clear the struct before using it .) Startf_usestdhandles is used here. You only need to set the standard input, output, and error handle of the process. Set the input and output handles of sub-processes to the Read and Write handles of pipelines.
When a child process starts, it inherits all the opened handles that can be inherited by the parent process, but the child process does not know which handle is used to read the pipeline, and the handle is used to write the pipeline, this is because the sub-process is not specified during creation. So you need to set it here. The standard error handle can be obtained through the getstdhandle function. This handle is not used in this program, but we must fill it out.
The process_information type PI is a struct for the CreateProcess function to fill in information about newly created processes and threads. Its members are as follows:
typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION;
The first two are the process and thread handles, and the last two are the global identifiers of the process and thread. With it, we can release the handle later.
Let's look at the program for reading and writing anonymous pipelines:
Void cch_17_parentview: onpiperead () {// todo: add your command handler code herechar Buf [200]; DWORD dwread; If (! Readfile (hread, // read handle Buf, // buffer 100, // size to be read & dwread, // The actual read size is null )) // non-overlapping {MessageBox ("failed to read data"); return;} MessageBox (BUF);} void cch_17_parentview: onpipewrite () {// todo: add your command handler code herechar Buf [] = "data sent by the parent process"; DWORD dwwrite; If (! Writefile (hwrite, // handle Buf, // buffer strlen (BUF) + 1, // will read ELE. Me size & dwwrite, // actual size null )) // non-overlapping {MessageBox ("failed to write data"); Return ;}}
It is implemented using readfile and writefile that have been used before, so we will not talk about it here.
Next let's take a look at the compilation of sub-processes. First, place the child process in a directory that is equal to the parent process. Similarly, add two member variables to identify the Read and Write handles of the pipeline, initialize them in the constructor, and release them in the destructor. Set it to a single-document application, add a menu "anonymous Pipeline" to it, two menu items: idm_pipe_read "read data", and idm_pipe_write "write data ".
First, we need to obtain the standard input and output handle of the sub-process. We can complete the process in oninitialupdate, which is the first function called after the window is created.
void CCH_17_ChildView::OnInitialUpdate() {CView::OnInitialUpdate();// TODO: Add your specialized code here and/or call the base classhRead = GetStdHandle(STD_INPUT_HANDLE);hWrite = GetStdHandle(STD_OUTPUT_HANDLE);}
The input and output functions are relatively simple:
Void cch_17_childview: onpipewrite () {// todo: add your command handler code herechar Buf [] = "anonymous pipeline client data"; DWORD dwwrite; If (! Writefile (hwrite, Buf, strlen (BUF) + 1, & dwwrite, null) {MessageBox ("failed to write data"); Return ;}}
void CCH_17_ChildView::OnInitialUpdate() {CView::OnInitialUpdate();// TODO: Add your specialized code here and/or call the base classhRead = GetStdHandle(STD_INPUT_HANDLE);hWrite = GetStdHandle(STD_OUTPUT_HANDLE);}
When starting a program, we should start the parent process, and then use the "create anonymous Pipeline" menu item of the parent process to subscribe the process. Then you can implement one sending and the other receiving functions.
The following describes the named pipe. The named pipe communicates between processes through the network, but it shields the details of the network protocol, so that we can implement process communication without learning about the network protocol. The anonymous pipeline can communicate between parent and child processes. The named pipeline can not only communicate between two processes on the local machine, but also implement inter-network communication between two processes.
Because it is cross-network communication, it is naturally inseparable from the client/server communication system. The server of the named pipe creates the named pipe, and the client connects to the existing named pipe. The named pipe uses the windows "Named Pipe File System" interface. Therefore, clients and servers can use standard Win32 file system functions (readfile and writefile) to send and receive data.
We first compile the server-side program. Create a single-document application, add a menu named pipe, and add three menu items:
Idm_pipe_create "create Pipeline", idm_pipe_read "read data", and idm_pipe_write "write data ".
First, let's look at the message response function for creating an MPS queue:
Void cch_17_namedpipesrvview: onpipecreate () {// todo: add your command handler code herehpipe = createnamedpipe ("\\\\. \ PIPE \ mypipe ", // pipeline name pipe_access_duplex | // bidirectional pipeline file_flag_overlapped, // overlapping Mode 0, // pipeline uses byte type 1, // you can create up to 1024 reserved word segments for one instance, 1024 reserved word segments for the output buffer, and 0 reserved word segments for the input buffer. // The default timeout value is null ); // Default Security Attribute if (invalid_handle_value = hpipe) {MessageBox ("failed to create a named pipe"); hpipe = NULL; return;} // create an anonymous event object handle hevent; hevent = createeven T (null, // Default Security Attribute true, // manually reset false, // The creation thread does not get this object null); // No name if (! Hevent) {MessageBox ("failed to create event object"); closehandle (hpipe); hpipe = NULL; return ;}// overlapped structure overlapped ovlap; zeromemory (& ovlap, sizeof (overlapped); ovlap. hevent = hevent; // wait for the client to connect if (! Connectnamedpipe (hpipe, & ovlap) {// For the overlapping mode, you also need to determine whether to wait for processing or if (error_io_pending! = Getlasterror () {MessageBox ("failed to wait for client connection"); closehandle (hpipe); closehandle (hevent); hpipe = NULL; return ;}} // wait for the event object if (wait_failed = waitforsingleobject (hevent, infinite) {MessageBox ("wait for object failed"); closehandle (hpipe); closehandle (hevent ); hpipe = NULL; return ;}}
In general, this function does two things: 1. Create a named pipeline: createnamedpipe. 2. Wait for the client request to arrive: connectnamedpipe.
In createnamedpipe, the pipe name is in the default format :\\. \ PIPE \ pipename it doesn't matter if pipe is case-insensitive. For example, in C language, if you want to output one \ in double quotation marks, You need to output two, so there are many slashes. The bidirectional mode and overlapping mode are used here, so other parameters are not mentioned.
Because the createnamedpipe adopts the overlap mode, you must enter an overlapped struct address in the second parameter of connectnamedpipe, And the struct must contain a manually reset event object. Therefore, in our program, we first create an event object, then define the overlapped struct, and finally call the connectnamedpipe function. If connectnamedpipe fails, 0 is returned. However, you need to determine the cause of the failure to see if it is a real failure, or if it is not processed yet, it will be processed later.
Read/write functions are relatively simple:
Void cch_17_namedpipesrvview: onpiperead () {// todo: add your command handler code herechar Buf [100]; DWORD dwread; If (! Readfile (hpipe, Buf, 100, & dwread, null) {MessageBox ("failed to read data"); return;} MessageBox (BUF);} void cch_17_namedpipesrvview: onpipewrite () {// todo: add your command handler code herechar Buf [] = "name pipe server data"; DWORD dwwrite; If (! Writefile (hpipe, Buf, strlen (BUF) + 1, & dwwrite, null) {MessageBox ("failed to write data to server"); Return ;}}
Let's look at the client program again. Add a single document reference program in the workspace. Add a hpipe handle to it. Initialize to null in the constructor and delete it in the destructor. Add three menu items to the menu: "connection pipe" idm_pipe_connect, "read data" idm_pipe_read, and "Write Data" idm_pipe_write. And added the message response function.
Void cch_17_namedpipecltview: onpipeconnect () {// todo: add your command handler code hereif (! Waitnamedpipe ("\\\\. \ PIPE \ mypipe ", nmpwait_wait_forever) {MessageBox (" currently no namepipeline available "); return ;} // open the named pipe hpipe = createfile ("\\\\. \ PIPE \ mypipe ", // MPs queue name generic_read | // read generic_write, // write 0, // cannot share null, // Default Security Attribute open_existing, // open the existing pipeline file_attribute_normal, // normal file property null); If (invalid_handle_value = hpipe) {MessageBox ("failed to open the named Pipeline"); hpipe = NULL; return ;}} void cch_17_namedpipecltview: onpiperead () {// todo: ad D your command handler code herechar Buf [100]; DWORD dwread; If (! Readfile (hpipe, Buf, 100, & dwread, null) {MessageBox ("failed to read data"); return;} MessageBox (BUF);} void cch_17_namedpipecltview: onpipewrite () {// todo: add your command handler code herechar Buf [] = "Named Pipe client data"; DWORD dwwrite; If (! Writefile (hpipe, Buf, strlen (BUF) + 1, & dwwrite, null) {MessageBox ("failed to write data to server"); Return ;}}
Next we will look at the last inter-process communication mechanism-mail slot. The mail trough is designed based on the broadcast system and adopts non-connection and unreliable data transmission. The mail trough is a single communication mechanism. The server process that creates the mail trough reads data and the client process that opens the mail trough writes data. To ensure that the mail slot can work normally on various Windows platforms, we should limit the message to 424 bytes during message transmission.
Therefore, on the server side, we can add a menu item to respond to it:
Void cch_17_mailslotsrvview: onmailslotrecv () {// todo: add your command handler code herehandle hmaileslot; hmaileslot = createmailslot ("\\\\. \ Mailslot \ mymailslot ", // mail slot name 0, // Message Size of any length mailslot_wait_forever, // always wait for null ); // Default Security Attribute if (invalid_handle_value = hmaileslot) {MessageBox ("failed to create a slot"); return;} Char Buf [200]; DWORD dwread; If (! Readfile (hmaileslot, Buf, 200, & dwread, null) {MessageBox ("failed to read data"); closehandle (hmaileslot); return;} MessageBox (BUF ); closehandle (hmaileslot );}
Note that the server only receives data.
Add a menu item on the client to add a response:
Void cch_17_mailslotcltview: onmailslotsend () {// todo: add your command handler code herehandle hmailslot; hmailslot = createfile ("\\\\. \ Mailslot \ mymailslot ", // mail slot name generic_write, // write data file_pai_write, // write share null, // Default Security Attribute open_existing, // open the existing mail slot file_attribute_normal, // normal attribute null); // If (invalid_handle_value = hmailslot) {MessageBox ("failed to open mail slot"); return ;} char Buf [] = "client sends data"; DWORD dwwrite; If (! Writefile (hmailslot, Buf, sizeof (BUF), & dwwrite, null) {MessageBox ("failed to write data"); closehandle (hmailslot); return ;} closehandle (hmailslot );}
Start two processes, first click the server to receive data, and then click the client to send data. The server can receive the data sent by the client. To implement two-way communication, you need to add a data sending mechanism to the server, while adding a data receiving mechanism to the client.
To sum up, the four communication modes are as follows: clipboard and anonymous pipelines can only be used on the local machine; The Named Pipes and oil tanks can communicate across networks; in addition, the oil tanks can implement one-to-many communication modes, however, the data volume is small.