📄 lion-tutorial21.htm
字号:
<html>
<head>
<link rel="stylesheet" href="../../asm.css">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Iczelion's win32 asm tutorial</title>
</head>
<body bgcolor="#FFFFFF" background="../../images/back01.jpg">
<p align="center">Tutorial 21: Pipe</p>
<hr size="1">
<strong> </strong> In this tutorial, we will explore pipe, what it is and what
we can use it for. To make it more interesting, I throw in the technique on how
to change the background and text color of an edit control. <br>
Download the example <a href="files/tut21.zip" style="text-decoration:none">here</a>.
<h3> Theory:</h3>
Pipe is a communication conduit or pathway with two ends. You can use pipe to
exchange the data between two different processes, or within the same process.
It's like a walkie-talkie. You give the other party one set and he can use it
to communicate with you. <br>
There are two types of pipes: anonymous and named pipes. Anonymous pipe is, well,
anonymous: that is, you can use it without knowing its name. A named pipe is the
opposite: you have to know its name before you can use it. <br>
You can also categorize pipes according to its property: one-way or two-way. In
a one-way pipe, the data can flow only in one direction: from one end to the other.
While in a two-way pipe, the data can be exchanged between both ends. <br>
An anonymous pipe is always one-way while a named pipe can be one-way or two-way.
A named pipe is usually used in a network environment where a server can connect
to several clients. <br>
In this tutorial, we will examine anonymous pipe in some detail. Anonymous pipe's
main purpose is to be used as a communcation pathway between a parent and child
processes or between child processes. <br>
Anonymous pipe is really useful when you deal with a console application. A console
application is a kind of win32 program which uses a console for its input &
output. A console is like a DOS box. However, a console application is a fully
32-bit program. It can use any GUI function, the same as other GUI programs. It
just happens to have a console for its use. <br>
A console application has three handles it can use for its input & output.
They are called standard handles. There are three of them: standard input, standard
output and standard error. Standard input handle is used to read/retrieve the
information from the console and standard output handle is used to output/print
the information to the console. Standard error handle is used to report error
condition since its output cannot be redirected. <br>
A console application can retrieve those three standard handles by calling GetStdHandle
function, specifying the handle it wants to obtain. A GUI application doesn't
have a console. If you call GetStdHandle, it will return error. If you really
want to use a console, you can call AllocConsole to allocate a new console. However,
don't forget to call FreeConsole when you're done with the console. <br>
Anonymous pipe is most frequently used to redirect input and/or output of a child
console application. The parent process may be a console or a GUI application
but the child must be a console app. for this to work. As you know, a console
application uses standard handles for its input and output. If we want to redirect
the input and/or output of a console application, we can replace the handle with
a handle to one end of a pipe. A console application will not know that it's using
a handle to one end of a pipe. It'll use it as a standard handle. This is a kind
of polymorphism, in OOP jargon. This approach is powerful since we need not modify
the child process in anyway. <br>
Another thing you should know about a console application is where it gets those
standard handles from. When a console application is created, the parent process
has two choices: it can create a new console for the child or it can let the child
inherit its own console. For the second approach to work, the parent process must
be a console application or if it's a GUI application, it must call AllocConsole
first to allocate a console. <br>
Let's begin the work. In order to create an anonymous pipe you need to call CreatePipe.
CreatePipe has the following prototype:
<blockquote><b>CreatePipe proto pReadHandle:DWORD, \</b> <br>
<b> pWriteHandle:DWORD,\</b> <br>
<b> pPipeAttributes:DWORD,\</b> <br>
<b> nBufferSize:DWORD</b></blockquote>
<ul>
<li> pReadHandle is a pointer to a dword variable that will receive the handle
to the read end of the pipe</li>
<li> pWriteHandle is a pointer to a dword variable that will receive the handle
to the write end of the pipe.</li>
<li> pPipeAttributes points to a SECURITY_ATTRIBUTES structure that determines
whether the returned read & write handles are inheritable by child processes</li>
<li> nBufferSize is the suggested size of the buffer the pipe will reserve for
use. This is a suggested size only. You can use NULL to tell the function
to use the default size.</li>
</ul>
If the call is successful, the return value is nonzero. If it failed, the return
value is zero. <br>
After the call is successful, you will get two handles, one to read end of the
pipe and the other to the write end. Now I will outline the steps needed for redirecting
the standard output of a child console program to your own process.Note that my
method differs from the one in Borland's win32 api reference. The method in win32
api reference assumes the parent process is a console application and thus the
child can inherit the standard handles from it. But most of the time, we will
need to redirect output from a console application to a GUI one.
<ol>
<li> Create an anonymous pipe with CreatePipe. Don't forget to set the bInheritable
member of SECURITY_ATTRIBUTES to TRUE so the handles are inheritable.</li>
<li> Now we must prepare the parameters we will pass to CreateProcess since
we will use it to load the child console application. One important structure
is the STARTUPINFO structure. This structure determines the appearance of
the main window of the child process when it first appears. This structure
is vital to our purpose. You can hide the main window and pass the pipe handle
to the child console process with it. Below is the members you must fill:</li>
<ul>
<li> cb : the size of STARTUPINFO structure</li>
<li> dwFlags : the binary bit flags that determine which members of the structure
are valid also it governs the show/hide state of the main window. For our
purpose, you should use a combination of STARTF_USESHOWWINDOW and STARTF_USESTDHANDLES</li>
<li> hStdOutput and hStdError : the handles you want the child process to
use as standard output/error handles. For our purpose, we will pass write
handle of the pipe as the standard output and error of the child. So when
the child outputs something to the standard output/error, it actually passes
the info via the pipe to the parent process.</li>
<li> wShowWindow governs the show/hide state of the main window. For our purpose,
we don't want the console window of the child to show so we put SW_HIDE
into this member.</li>
</ul>
<li> Call CreateProcess to load the child application. After CreateProcess is
successful, the child is still dormant. It is loaded into memory but it doesn't
run immediately</li>
<li> Close the write pipe handle. This is necessary. Because the parent process
has no use for the write pipe handle, and the pipe won't work if there are
more than one write end, we MUST close it before reading the data from the
pipe. However, don't close the write handle before calling CreateProcess,
your pipe will be broken. You should close it just after CreateProcess returns
and before you read data from the read end of the pipe.</li>
<li> Now you can read data from the read end of the pipe with ReadFile. With
ReadFile, you kick the child process into running mode. It will start execution
and when it writes something to the standard output handle (which is actually
the handle to the write end of the pipe), the data are sent through the pipe
to the read end. You can think of ReadFile as sucking data from the read end
of the pipe. You must call ReadFile repeatedly until it returns 0 which means
there are no more data to be read. You can do anything with the data you read
from the pipe. In our example, I put them into an edit control.</li>
<li> Close the read pipe handle.</li>
</ol>
<h3> Example:</h3>
<blockquote><b>.386</b> <br>
<b>.model flat,stdcall</b> <br>
<b>option casemap:none</b> <br>
<b>include \masm32\include\windows.inc</b> <br>
<b>include \masm32\include\user32.inc</b> <br>
<b>include \masm32\include\kernel32.inc</b> <br>
<b>include \masm32\include\gdi32.inc</b> <br>
<b>includelib \masm32\lib\gdi32.lib</b> <br>
<b>includelib \masm32\lib\user32.lib</b> <br>
<b>includelib \masm32\lib\kernel32.lib</b>
<p><b>WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD</b>
<p><b>.const</b> <br>
<b>IDR_MAINMENU equ 101 ;
the ID of the main menu</b> <br>
<b>IDM_ASSEMBLE equ 40001</b>
<p><b>.data</b> <br>
<b>ClassName
db "PipeWinClass",0</b> <br>
<b>AppName
db "One-way Pipe Example",0 EditClass db "EDIT",0</b> <br>
<b>CreatePipeError db "Error during pipe creation",0</b>
<br>
<b>CreateProcessError db "Error during process creation",0</b>
<br>
<b>CommandLine db "ml /c /coff /Cp test.asm",0</b>
<p><b>.data?</b> <br>
<b>hInstance HINSTANCE ?</b> <br>
<b>hwndEdit dd ?</b>
<p><b>.code</b> <br>
<b>start:</b> <br>
<b> invoke GetModuleHandle, NULL</b> <br>
<b> mov hInstance,eax</b> <br>
<b> invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT</b>
<br>
<b> invoke ExitProcess,eax</b>
<p><b>WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD</b>
<br>
<b> LOCAL wc:WNDCLASSEX</b> <br>
<b> LOCAL msg:MSG</b> <br>
<b> LOCAL hwnd:HWND</b> <br>
<b> mov wc.cbSize,SIZEOF WNDCLASSEX</b> <br>
<b> mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc,
OFFSET WndProc</b> <br>
<b> mov wc.cbClsExtra,NULL</b> <br>
<b> mov wc.cbWndExtra,NULL</b> <br>
<b> push hInst</b> <br>
<b> pop wc.hInstance</b> <br>
<b> mov wc.hbrBackground,COLOR_APPWORKSPACE</b> <br>
<b> mov wc.lpszMenuName,IDR_MAINMENU</b> <br>
<b> mov wc.lpszClassName,OFFSET ClassName</b> <br>
<b> invoke LoadIcon,NULL,IDI_APPLICATION</b> <br>
<b> mov wc.hIcon,eax</b> <br>
<b> mov wc.hIconSm,eax</b> <br>
<b> invoke LoadCursor,NULL,IDC_ARROW</b> <br>
<b> mov wc.hCursor,eax</b> <br>
<b> invoke RegisterClassEx, addr wc</b> <br>
<b> invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR
AppName,\ WS_OVERLAPPEDWINDOW+WS_VISIBLE,CW_USEDEFAULT,\ CW_USEDEFAULT,400,200,NULL,NULL,\
hInst,NULL</b> <br>
<b> mov hwnd,eax</b> <br>
<b> .while TRUE</b> <br>
<b> invoke GetMessage, ADDR msg,NULL,0,0</b>
<br>
<b> .BREAK .IF (!eax)</b> <br>
<b> invoke TranslateMessage, ADDR
msg</b> <br>
<b> invoke DispatchMessage, ADDR
msg</b> <br>
<b> .endw</b> <br>
<b> mov eax,msg.wParam</b> <br>
<b> ret</b> <br>
<b>WinMain endp</b>
<p><b>WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM</b> <br>
<b> LOCAL rect:RECT</b> <br>
<b> LOCAL hRead:DWORD</b> <br>
<b> LOCAL hWrite:DWORD</b> <br>
<b> LOCAL startupinfo:STARTUPINFO</b> <br>
<b> LOCAL pinfo:PROCESS_INFORMATION</b> <br>
<b> LOCAL buffer[1024]:byte</b> <br>
<b> LOCAL bytesRead:DWORD</b> <br>
<b> LOCAL hdc:DWORD</b> <br>
<b> LOCAL sat:SECURITY_ATTRIBUTES</b> <br>
<b> .if uMsg==WM_CREATE</b> <br>
<b> invoke CreateWindowEx,NULL,addr
EditClass, NULL, WS_CHILD+ WS_VISIBLE+ ES_MULTILINE+ ES_AUTOHSCROLL+ ES_AUTOVSCROLL,
0, 0, 0, 0, hWnd, NULL, hInstance, NULL</b> <br>
<b> mov hwndEdit,eax</b> <br>
<b> .elseif uMsg==WM_CTLCOLOREDIT</b> <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -