Twenty-first class pipeline
This lecture will explore the pipeline to see what it is, what is used. In order to make it more vivid, I will use how to change the background color and text color of the Edit control.
theory:
Pipeline, as the name suggests, there are two end channels. You can use the pipeline between the processes, data exchange within the same process, just like a portable radiotelephone. Give one end of the pipe to the other, he can communicate with you with the pipeline.
There are two pipelines, namely, a famous pipe and anonymous pipe. Anonymous pipeline is a pipe with no name, that is, do not need to know its name when using them. The famous pipeline is just the opposite, and it must be known before use.
It is also possible to classify according to the characteristics of the pipe, ie the one-way or two-way. One-way pipe, data can only be moved in one direction, flow from one end to the other, and the bidirectional conduit data can be freely exchanged between the two ends. Anonymous pipelines are usually unidirectional and famous pipes are usually two-way. A well-known pipe is often used in a server contact multiple client network environments.
This lecture will discuss anonymous pipeline in detail. The main purpose of anonymous pipeline is a linkage pathway that communicates between the parent process and the child process. The anonymous pipeline is quite useful when processing a console problem. The console application is a Win32 application using the console as an input and output. A console is like a DOS window. But the console application is indeed a 32-bit application, which can use the GUI function as other graphics programs, but it happens to use the console.
The console application has three standard handles for inputting output, which are standard input, standard output, and standard error handles. Standard input is used to read or take information from the console to write or print information. Standard Error is used to report errors that cannot be redirected.
The console application can get these three handles by calling the function getStdHandle. A graphics application has no console. If you call getStdHandle, it will return an error; if you do use a console, you can call allocConsole to assign a new console for use, but don't forget to call FreeConsole when the processing is complete. Release the console.
The function of the anonymous pipeline is used to redirect the standard input and standard output of the sub-process. The parent process can be a console or a graphics, while the child process must be a console application. It is well known that console applications use standard input and output handles. To redirect the input and output, you have to replace this standard handle with the handle to point to the pipe. The console application will not know that we use the handle to any end of the pipe, which will treat this handle as a standard handle. By borrowing object-oriented terms, this is one of the polymorphism. This method is very useful because the child process does not need to be changed.
Another point to the console application should be mastered is where it gives a standard handle. When a console application is created, the parent process has two options: Creating a new console for the child process or allows the child to inherit your console. If you use the latter, the parent process itself must be a console application, or if it is a GUI application, it must first call allocConsole allocated a console.
Create an anonymous pipe by calling createpipe, its prototype:
CreatePipe Proto Preadhandle: DWORD, / PWRITEHANDLE: DWORD, / PPIPEATTRIBUTES: DWORD, / NBUFFERSIZE: DWORD
The preadhandle double-word pointer variable, points to the handle of the pipe reading end. PWRiteHandle double-word pointer variable, pointing to the PPIPEATTRIBUTES dual pointer variable, pointing to the security_attributes structure, which is used to determine whether the read and write handle can inherit the NBuffersize suggestion the size of the buffer to be used, this is just a suggestion. Value, you can use NULL to use the default value if the function call successfully returns to zero, otherwise zero. After successful call, two handles are obtained, a read end pointing to the pipe, and the other pointing to the pipeline. Now I am going to put the focus on the criteria for the sub-console program to output to the desired steps of their own process. Note that my method is different from the example of Borland's API reference. Win32 API Referring is assumed that the parent process is a console application, so the child process can inherit its standard handle. However, most of the cases we need to redirect the output to the GUI application.
Create an anonymous pipeline using createpipe, and don't forget to set the Security_Attributes structural member binheritable to TRUE, so that the handle can be inherited.
Now you have to prepare a function of creating a process, the parameters of CreateProcess, and only it can load the child console application. StartupInfo is an important structure that determines the appearance of the main window when the child process appears, which is also crucial for our goals. Through this structure, you can hide the main window and pass the pipe handle to the child process.
The following is a member you have to fill in:
The size of the CB StartupInfo structure DWFLAGS binary logo, which determines which members of this structure are valid, and also determine whether the main window is displayed or hidden. In our program, using Startf_USESHOWWINDOW and STARTF_USESTDHANDLES combined HSTDOUTPUT and HSTDERROR you want to use the standard output and standard error handles used by the sub-process, for us, we will put the pipelines as the standard output and errors of the child process. Therefore, when the child process is sent to standard output or standard error, it actually passes this information through the pipe to the parent process wshowWindow determines the main window is displayed or hidden. We do not want to display the main window of the sub-process, so put the member into sw_hide
Call CREATEPROCESS to create a child process, but the successful postus is still not in the active state. It is put into memory but does not run immediately.
The write end of the shutdown pipe in the parent process is also necessary. This is because the parent process does not use the write handle of the pipe, and if a pipe has two write-in, we must turn off the writing of the pipe before reading the data from the pipe. But you can't turn it off before calling createprocess, otherwise the pipeline is broken. You should turn off the pipe in CreateProcess just closing the pipe before reading data.
Now you can read the data through the function readfile read the data in the pipeline. By using the readfile, you can make the child process in operation. It will begin to execute, and when it writes data to standard output (actually the write end of the pipe), the data will be sent to the read end of the pipe. It should not be invisible to the READFILE until its return value is 0, that is, no data can be read again. You can do any processing from the data read from the pipe, which is displayed in the Edit control in our example.
Remember to close the read handles of the pipe after use.
Code example:
.386
.Model flat, stdcall
Option CaseMAP: NONE
INCLUDE /MASM32/INCLUDE/Windows.inc
INCLUDE /MASM32/INCLUDE/USER32.INC
INCLUDE /MASM32/INCLUDE / WANEL32.INC
Include /masm32/include/gdi32.incincludelib /masm32/lib/gdi32.lib
INCLUDELIB /MASM32/LIB/USER32.LIB
INCLUDELIB /MASM32/LIB/kernel32.lib
Winmain Proto: DWORD,: DWORD,: DWORD,: DWORD
.const
IDR_MAINMENU EQU 101; The id of the main menu
IDM_ASSEMBLE EQU 40001
.DATA
ClassName DB "PirewinClass", 0
Appname DB "One-Way Pipe Example", 0 Editclass DB "Edit", 0
CreatePipeError DB "Error During Pipe Creation", 0
CreateProcesSserror DB "Error During Process Creation", 0
CommandLine DB "ml / c / coff / cp test.asm", 0
.DATA?
Hinstance Hinstance?
HWndIt DD?
.code
Start:
Invoke getModuleHandle, NULL
Mov Hinstance, EAX
Invoke Winmain, Hinstance, Null, NULL, SW_SHOWDEFAULT
Invoke EXITPROCESS, EAX
Winmain Proc Hinst: DWORD, HPREVINST: DWORD, CMDLINE: DWORD, CMDSHOW: DWORD
Local WC: WNDCLASSEX
Local MSG: MSG
Local hwnd: hwnd
Mov wc.cbsize, sizeof wndclassex
Mov wc.style, cs_hredraw or cs_vredraw mov wc.lpfnwndproc, Offset WndProc
Mov wc.cbclsextra, NULL
Mov wc.cbwndextra, null
Push hinst
POP wc.hinstance
Mov wc.hbrbackground, Color_AppWorkspace
Mov wc.lpszMenuname, IDR_MainMenu
MOV wc.lpszclassname, Offset ClassName
Invoke Loadicon, NULL, IDI_APPLICATION
Mov wc.hicon, EAX
Mov wc.hiconsm, EAX
Invoke loadcursor, null, IDC_ARROW
Mov wc.hcursor, EAX
Invoke RegisterClassex, Addr WC
Invoke CreateWindowex, WS_EX_CLIENTEDGE, ADDR CLASSNAME, AddR Appname, / WS_OVERLAPPEDWINDOW WS_VISible, CW_USEDEFAULT, / CW_USEDEFAULT, 400, 200, NULL, NULL, / HINST, NULL
MOV HWND, EAX
.While true
Invoke GetMessage, Addr MSG, NULL, 0, 0
.Break .if (! EAX)
Invoke TranslateMessage, Addr MSG
Invoke DispatchMessage, Addr MSG
.endw
Mov Eax, Msg.wParam
RET
Winmain ENDP
WndProc Proc HWND: HWND, UMSG: UINT, WPARAM: WPARAM, LPARAM: LPARAMLOCAL RECT: RECT
Local Hread: DWORD
Local HWRITE: DWORD
Local Startupinfo: StartupInfo
Local Pinfo: Process_Information
Local Buffer [1024]: Byte
Local BytesRead: DWORD
Local HDC: DWORD
Local Sat: Security_Attributes
.IF uMSG == WM_CREATE
Invoke CreateWindowex, Null, Addr Editclass, NULL, WS_CHILD WS_VISible ES_MULTILINE ES_AUTOHSCROLL ES_AUTOVSCROLL, 0, 0, 0, 0, HWND, NULL, HINSTANCE, NULL
Mov hwndedit, EAX
.ELSEIF uMSG == WM_CTLCOLOREDIT
Invoke setTextColor, WPARAM, YELLOW
Invoke SetBkcolor, WPARAM, Black
Invoke GetStockObject, Black_brush
RET
.ELSEIF uMSG == WM_SIZE
Mov Edx, LParam
MOV ECX, EDX
SHR ECX, 16
And EDX, 0FFFFH
Invoke MoveWindow, Hwndit, 0,0, EDX, ECX, True
.ELSEIF uMSG == WM_COMMAND
.IF lparam == 0
Mov Eax, WPARAM
.IF AX == IDM_ASSEMBLE
Mov Sat.nilength, Sizeof Security_Attributes
Mov Sat.lpsecurityDescriptor, Null
Mov sat.binherithandle, True
Invoke CreatePipe, Addr Hread, Addr HWRITE, AddR Sat, Null
.IF EAX == NULL
Invoke Messagebox, HWnd, Addr CreatePipeError, Addr Appname, MB_ICONERROR MB_OK
.lse
Mov Startupinfo.cb, Sizeof Startupinfo
Invoke GetStartupinfo, Addr Startupinfo
Mov Eax, HWRITE
Mov Startupinfo.hstdoutput, EAX
Mov Startupinfo.hstderror, EAX
Mov Startupinfo.dwflags, Startf_useshowWindow Startf_usestdhandles
Mov Startupinfo.WshowWindow, SW_HIDE
Invoke CreateProcess, Null, Addr Commandline, Null, NULL, TRUE, NULL, NULL, NULL, ADDR Startupinfo, Addr Pinfo
.IF EAX == NULL
Invoke Messagebox, HWnd, Addr CreateProcesserror, Addr Appname, MB_ICONERROR MB_OK
.lse
Invoke Closehandle, Hwrite
.While true
Invoke RTLZERMEMORY, ADDR BUFFER, 1024
Invoke Readfile, Hread, Addr Buffer, 1023, Addr Bytesread, Null.if Eax == Null
.break
.endif
Invoke SendMessage, HWndedit, EM_SETSEL, -1, 0
Invoke SendMessage, Hwndit, EM_REPLAASEL, FALSE, ADDR BUFFER
.endw
.endif
Invoke Closehandle, HREAD
.endif
.endif
.endif
.ELSEIF uMSG == WM_DESTROY
Invoke PostquitMessage, NULL
.lse
Invoke DefWindowProc, HWND, UMSG, WPARAM, LPARAM RET
.endif
XOR EAX, EAX
RET
WNDPROC ENDP
End Start
analysis:
This example calls ml.exe to assemble a program named Test.asm and redirect Ml.exe's output to the EDIT control of the client area. When the program is loaded, I want to register the window class and create a main window. The first thing to do during the process of the main window is created is to create an Edit control used to display Ml.exe output.
Now interesting things, we will change the text color and background colors of this Edit control. When the EDIT control will seize the WM_CTLCOLOREDIT message to the parent window when the EDIT control will override the customer area. Parameters WPARAM contain handles (HDCs) for painting your own client area device descriptor. We can use this mechanism to modify the characteristics of the HDC.
.ELSEIF uMSG == WM_CTLCOLOREDIT
Invoke setTextColor, WPARAM, YELLOW
Invoke SetTextColor, WPARAM, Black
Invoke GetStockObject, Black_brush
RET
SetTextColor turns a text color into yellow, background color becomes black. Finally, we return a handle that gets a black brush by calling getStockObject. Handling WM_CTLCOLOREDIT must return a handle of a brush because Windows will use this brush to redraw Edit control background. In this example, I hope the background is black, so I returned a handle of a black brush.
Now when the user selects the Assemble submenu, an anonymous pipe will be created.
.IF AX == IDM_ASSEMBLE
Mov Sat.nilength, Sizeof Security_Attributes
Mov Sat.lpsecurityDescriptor, Null
Mov sat.binherithandle, True
Before calling CREATEPIPE, you must fill in the Security_Attributes structure. If we don't care about security, you can fill in NULL in the LPSecurityDescriptor member. BinheritHandle must be True so that the handle of the pipe can inherit the quilt process.
Invoke CreatePipe, Addr Hread, Addr HWRITE, AddR Sat, Null
After that, we call CreatePipe to create a pipe. If successful, the variable HREAD and HWRITE will be filled in the corresponding pipe read end and the handle of the write end.
Mov Startupinfo.cb, Sizeof Startupinfo
Invoke GetStartupinfo, Addr Startupinfo
Mov Eax, HWRITE
Mov Startupinfo.hstdoutput, EAX
Mov Startupinfo.hstderror, Eaxmov Startupinfo.dwflags, StartF_USESHOWINDOW Startf_usestdhandles
Mov Startupinfo.WshowWindow, SW_HIDE
The next step is to fill in the StartupInfo structure. Call the GetStartupInfo's default value of the Parent process to fill in the StartupInfo structure. If you want to make the program under Windows9x and Windows NT, you must call GetStartupInfo to fill in the StartupInfo structure. Once the call is returned, you can modify important members. Because we want the child process to output to the parent process rather than the default standard output and standard errors, we assign HSTDOUTPUT and HSTDERROR to the handle of the pipeline. In order to hide the main window of the child process, the member variable wshowwidow must be assigned to sw_hide. Finally, the member HSTDOUTPUT, HSTDESTPUT, HSTDESTPUT, HSTDESTPUT, HSTDESTPUT, HSTDESTPUT, HSTDESTPUT, HSTDESTPUT, HSTDERROR, and WSHOWWINDOW are specifically assigned to StartF_USESTDHANDOW and STARTF_USESTDHANDOW and STARTF_USESTDHANDOW.
Invoke CreateProcess, Null, Addr Commandline, Null, NULL, TRUE, NULL, NULL, NULL, ADDR Startupinfo, Addr Pinfo
CreateProcess is now called to create a child process. Note that the pipeline must be set to TRUE. Invoke CloseHandle, HWRITE successfully created a child process, must close the write of the pipe in the parent process. We have passed the handle of the write to the child process through the structure startupinfo. If it is not closed, then the pipe has two writes, and such a pipe will not work. So you must turn this handle before you create a sub-process but before reading the data.
.While true
Invoke RTLZERMEMORY, ADDR BUFFER, 1024
Invoke Readfile, Hread, Addr Buffer, 1023, Addr Bytesread, Null
.IF EAX == NULL
.break
.endif
Invoke SendMessage, HWndedit, EM_SETSEL, -1, 0
Invoke SendMessage, Hwndit, EM_REPLAASEL, FALSE, ADDR BUFFER
.endw
It is now ready to read data from the standard output of the child process. Until no data is no longer, readfile returns to null, will exit cycles, otherwise it will wait for data. We call RTLZERMEMORY before calling readfile to empty the memory and use the pipeline reader to instead of the file handle. Note that the maximum length of reading data is 1023 (sizeof (buffer) -1), because we need to turn the accepted characters into an ASCII string that can be processed by an Edit control. When ReadFile returns, this data is passed to the Edit control. However, this has a small problem. If you write data in the EDIT control using the SetWindowText API, new data will overwrite the existing old data, and we want to add new data to the existing data. To reach the purpose, first move the input focus of the EDIT control to the end of the text by sending a WPARAM to -1; then send an EM_REPLACESEL message to add the data back.
Invoke Closehandle, HREAD
When ReadFile returns NULL, you jump out of the loop and close the reader of the pipe.