Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1747828
  • 博文数量: 290
  • 博客积分: 10653
  • 博客等级: 上将
  • 技术积分: 3178
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-24 23:08
文章存档

2013年(6)

2012年(15)

2011年(25)

2010年(86)

2009年(52)

2008年(66)

2007年(40)

分类:

2008-01-11 23:48:49

Tutorial 21: Pipe

第二十一课:管道


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.

在这一课中,我们将探究一下管道,它是什么和我们能用它来做什么。为了让它更加有趣,我将此技术放在如何改变一个编辑控件的背景和文本颜色上。
Download the example here.

Theory:

原理:

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.

管道是一通讯管道或者是有两端的通路。你能用管道在两个不同的进程或同一个进程之间交换数据。它看起来像步话机。你给对方一个装置然后他就能用它来和你进行通讯。
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.

这里有两种类型的管道:有名管道和匿名管道。匿名管道的意思是:你使用它的时候不用知道它的名字。有名管道正好相反:你必须知道它的名字才能使用它。
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.

你也能依据管道的特性来对它加以分类:单向的或双向的。在一个单向管道中,数据只能在一个方向流动:即从一端流向另一端。而在双向管道中,数据能在管道的两端之间进行交换。


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.

一个匿名管道总是单向的,而一个已命名的管道则是单向或双向中的一种。有名管道通常用于一个服务器连接多个客户端的网络环境中。


In this tutorial, we will examine anonymous pipe in some detail. Anonymous pipe's main purpose is to be used as a communication pathway between a parent and child processes or between child processes.

在这一课中,我们将详细地讨论匿名管道。匿名管道的主要用途是作为父进程和子进程之间或者是子进程之间的通讯通路。
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.

当你涉及到控制台程序时,命名管道是很有用的。控制台应用程序是Win32程序的一种,用于控制它们的输入和输出。一个控制台就像一个DOS窗口。然而,一个控制台应用程序又的的确确是32位的应用程序。它和其它的图形用户界面程序一样,可以使用任何GUI函数。它仅是偶然的使用一下控制台罢了。


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.

一个控制台应用程序有三个句柄,它能用作它的输入和输出。它们是被调用的标准句柄。这三个句柄是:标准输入,标准输出和标准错误句柄。标准输入句柄被用于从一个控制台中读或者是获取信息;标准输出句柄被用于输出或打印控制台信息。标准错误句柄用于报告错误状态,因为控制台的输出可能被重定向。


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.

一个控制台应用程序能通过调用GetStdHandle函数来获得这三个标准句柄,只需要指定你想要的句柄就能够得到它。一个图像用户界面程序没有控制台。如果你调用GetStHandle函数,它将返回错误代码。如果你真的想使用控制台,你可以调用AllocConsole来分配一个新个控制台。然后,当你完成控制台的工作时,不要忘记调用FreeConsole函数释放它。

 

 

 

 


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.

匿名管道被频繁的用于重定向 子控制台应用程序的输入和输出。父进程可能是一个控制台或者是一个GUI应用程序,但是子进程必须是一个控制台应用程序。为了这个工作,像你所知道的一样,一个控制台应用程序为它的输入和输出使用标准句柄。如果你想重定向一个控制台应用程序的输入和(或)输出,我们必须用一个管道一端的句柄来替换这个句柄,而控制台应用程序并不知道它使用的句柄是一个管道的一端。它仍将把它当作一个标准句柄使用。在面向对象的行话中,这属于多态性的一种。这是一种很有效的方法,因为我们并不需要用任何途径来修改子进程。


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.

其它你还应该知道的事,就是一个控制台程序从哪儿获取这些标准句柄。当一个控制台应用程序被创建时,父进程有两个选择:为子进程创建一个新的控制台 或者让子进程继承它已有的控制台。如果选用的是第二种方法,父进程必须是一个控制台应用程序,如果它是一个图形用户界面程序,那么它必须首先调用AllocConsole函数来分配一个控制台。

 

Let's begin the work. In order to create an anonymous pipe you need to call CreatePipe. CreatePipe has the following prototype:

让我们开始工作吧!为了创建一个匿名管道,你需要调用CreatePipe函数,CreatePipe句法如下:

 

CreatePipe proto pReadHandle:DWORD, \
       pWriteHandle:DWORD,\
       pPipeAttributes:DWORD,\
       nBufferSize:DWORD

  • pReadHandle is a pointer to a dword variable that will receive the handle to the read end of the pipe

pReadHandle是一指向DWORD类型变量的指针,这个变量接收管道读的那端的句柄。

  • pWriteHandle is a pointer to a dword variable that will receive the handle to the write end of the pipe.

pWriteHandle 是一指向DWORD类型变量的指针,这个变量接收管道写的那端的句柄。

  • pPipeAttributes points to a SECURITY_ATTRIBUTES structure that determines whether the returned read & write handles are inheritable by child processes

pPipeAttributes 指向一个SECURITY_ATTRIBUTES结构体的指针,这个结构用于确定读写句柄是否能被子进程继承。

  • 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.

nBufferSize 建议为用户保留的缓冲区大小。这仅仅是一个建议的大小。你能使用NULL来告诉函数使用默认的大小。

If the call is successful, the return value is nonzero. If it failed, the return value is zero.

如果这个调用是成功的,返回值非零。如果失败,返回值为零(0)。
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.

在这个调用成功之后,你将得到两个句柄,一个是管道读端的句柄,另一个是写端的句柄。现在我将重点放在重定向子控制台程序的标准输出到你拥有的进程所需要的步骤上。注意,我使用的方法和在Borlandwin32 api 手册中的方法有所不同。在win32 api手册中的这种方法如下:父进程是一个控制台程序从而子进程能从它那继承这些标准句柄。但是大所数时候,我们将需要从一个控制台程序中重定向输出到一个图形用户界面程序。

  1. Create an anonymous pipe with CreatePipe. Don't forget to set the bInheritable member of SECURITY_ATTRIBUTES to TRUE so the handles are inheritable.

CreatePipe函数创建一个匿名管道,不要忘记设置SECUTITY_ATTRIBUTES结构的bInheritable成员的值为TRUE,如此,这些句柄才能被继承。

  1. 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:

现在我们必须准备一下将传递给CreateProcess函数的参数了,因为我们将使用它加载一个子控制台应用程序。一个重要的结构是STARTUPINF结构体。这个结构确定当子进程第一次出现时主窗口的外部特征。这个结构对我们的目的来说是必不可少的。你能用它隐藏主窗口并把管道句柄传递给子控制台进程。下面这些成员是你必须填写的:

    • cb : the size of STARTUPINFO structure

cbSTARTUPINFO结构体的大小。

    • 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

dwFlags:二进制位标志字。它确定了结构的那一个成员是有效的 同时还支配着主窗口的显示或隐藏状态。为了我们的目标你应该用STARTF_USESHOWWINDOWSTARTF_USESTDHANDLES的组合值。

    • 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.
      hStdOutput
      hSedError: 这是你想在子进程中使用的标准输出和标准错误的句柄。为了达到我们的目标,我们将传递管道写端的句柄作为子进程的标准输出和标准错误的句柄。所以当子进程输出一些东西到标准输出和错误时,它实际上是通过管道把这些信息发送给父进程。
    • 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.

WshowWindow 管理主窗口的显示和隐藏状态。为达到我们的目标,我们并不想子进程的控制窗口显示,所以我们把SW_HIDE放进这个成员中。

  1. 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

调用CreateProcess来加载子应用程序。在CreateProcess调用成功后,子进程处于睡眠状态,它被装进内存但是它不立即运行。

  1. 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.

关闭些管道句柄。这一步是必须的。因为父进程并不需要写管道句柄,如果这里有超过一个写端,这个管道将不工作,为了从一个管道中读进数据我们必须关闭它。然而,如果在调用CreateProcess之前,不关闭写句柄,你们的管道将是破裂的。你应该在CreateProcess刚刚返回并且在你从一个管道的一个读端读数据之前,关闭管道写端。

 

  1. 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.

现在你可以用ReadFile函数从管道的读端读取数据。通过使用ReadFile函数,你能把子进程踢进运行模式。它将开始执行并且当它写一东西到标准输出句柄(实际上是管道的写端句柄)时,这些数据通过管道被送到读端。你可以把ReadFile函数看作是从管道的读端上吸食数据。你应该重复的调用ReadFile函数直到它的返回值为0. 返回0意味着这里没有更多数据能被读取了。你能对从管道读来的数据做任何处理。在我们的例子中,我把它们放在编辑控件中。

  1. Close the read pipe handle.

关闭读管道句柄。

Example:

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \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 "PipeWinClass",0
AppName              db "One-way Pipe Example",0 EditClass db "EDIT",0
CreatePipeError     db "Error during pipe creation",0
CreateProcessError     db "Error during process creation",0
CommandLine     db "ml /c /coff /Cp test.asm",0

.data?
hInstance HINSTANCE ?
hwndEdit 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:LPARAM
    LOCAL 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,hwndEdit,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
                .else
                    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
                    .else
                        invoke CloseHandle,hWrite
                        .while TRUE
                            invoke RtlZeroMemory,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,hwndEdit,EM_REPLACESEL,FALSE,addr buffer
                        .endw
                    .endif
                    invoke CloseHandle,hRead
                .endif
            .endif
        .endif
    .elseif uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret
    .endif
    xor eax,eax
    ret
WndProc endp
end start

Analysis:

分析:

The example will call ml.exe to assemble a file named test.asm and redirect the output of ml.exe to the edit control in its client area.
When the program is loaded, it registers the window class and creates the main window as usual. The first thing it does during main window creation is to create an edit control which will be used to display the output of ml.exe.

这个例子将调用Ml.EXE来汇编一个名字为test.asm的文件并重定向ml.exe的输出到编辑框的客户区中。

当程序被装载时,它照常注册一个窗口类,并创建一个主窗口。它要做的第一件事就是在主窗口创建的时候创建一个编辑控件。这个编辑控件被用于显示ml.exe的输出。

 


Now the interesting part, we will change the text and background color of the edit control. When an edit control is going to paint its client area, it sends WM_CTLCOLOREDIT message to its parent.
wParam contains the handle to the device context that the edit control will use to write its own client area. We can use this opportunity to modify the characteristics of the HDC.

现在来到我们感兴趣的部分,我们将改变编辑控件的背景色和文本颜色。当编辑控件将要绘制它的客户区时,它发送WM_CTLCOLOREDIT消息给它的父亲。

wParam包含的是设备描述表的句柄,设备描述表将被编辑控件用来写它的客户区。我们能利用这个时机来修改HDC(设备描述表也称设备环境)的特征属性值。

.elseif uMsg==WM_CTLCOLOREDIT
        invoke SetTextColor,wParam,Yellow
        invoke SetTextColor,wParam,Black
        invoke GetStockObject,BLACK_BRUSH
        ret

 

SetTextColor changes the text color to yellow. SetTextColor changes the background color of the text to black. And lastly, we obtain the handle to the black brush which we return to Windows. With WM_CTLCOLOREDIT message, you must return a handle to a brush which Windows will use to paint the background of the edit control. In our example, I want the background to be black so I return the handle to the black brush to Windows.
Now when the user selects Assemble menuitem, it creates an anonymous pipe.

SetTextColor 改变文本颜色为黄色。SetTextColor改变文本的背景色为黑色。最后,我们获得了黑色画刷的句柄并将它返回给Windows。在处理WM_CTLCOLOREDIT消息时,你必须返回一个画刷的句柄。Windows将用这个画刷来绘制编辑控件的背景色。在我们的例子中,我想让背景为黑色所以我返回一个黑色的画刷给Windows。现在,当用户选择Assemble菜单项时,它将创建一个匿名管道。

            .if ax==IDM_ASSEMBLE
                mov sat.niLength,sizeof SECURITY_ATTRIBUTES
                mov sat.lpSecurityDescriptor,NULL
                mov sat.bInheritHandle,TRUE

Prior to calling CreatePipe, we must fill the SECURITY_ATTRIBUTES structure first. Note that we can use NULL in lpSecurityDescriptor member if we don't care about security. And the bInheritHandle member must be TRUE so that the pipe handles are inheritable to the child process.

在调用CreatePipe之前,我们必须首先填充SECURITY_ATTRIBUTES结构。如果我们不关心安全性描述我们能设置ipSecurityDescriptor成员的值为NULL。还有,我们的bInheritHandle成员必须为TRUE以至于让我们的管道句柄能被子进程继承。

             invoke CreatePipe,addr hRead,addr hWrite,addr sat,NULL

 After that, we call CreatePipe which, if successful, will fill hRead and hWrite variables with the handles to read and write ends of the pipe respectively.

在上面工作完成之后,我们调用CreatePipe函数,如果这个函数调用成功,hReadhWrite变量将被分别填充到管道的读端和写端的句柄中。

                   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

Next we must fill the STARTUPINFO structure. We call GetStartupInfo to fill the STARTUPINFO structure with default values of the parent process. You MUST fill the STARTUPINFO structure with this call if you intend your code to work under both win9x and NT. After GetStartupInfo call returns, you can modify the members that are important. We copy the handle to the write end of the pipe into hStdOutput and hStdError since we want the child process to use it instead of the default standard output/error handles. We also want to hide the console window of the child process, so we put SW_HIDE value into wShowWidow member. And lastly, we must indicate that hStdOutput, hStdError and wShowWindow members are valid and must be used by specifying the flags STARTF_USESHOWWINDOW and STARTF_USESTDHANDLES in dwFlags member.

下面我们将填写STARTUPINFO结构。我们调用GetStartupInfo函数来用父进程的默认值填充STARTUPINFO结构。如果你打算让你的代码在Win9xNT中都能工作,那么你必须用这个调用来填充这个STARTUPINFO结构。在GetStartupInfo调用返回后,你能修改那些重要的成员。我们复制管道的写端句柄给hStdOutputhStdError因为我们想让子进程使用它来代替默认的标准输出/错误句柄。我们还需要隐藏子进程的控制台窗口,所以我们设置wShowWIndow成员的值为SW_HIDE。在最后,我们必须指出hStdOutput ,hStdErrorwShowWindow成员是有效的并且必须把被指定的标志STARTF_USESHOWWINDOWSTARTF_USESTDHANDLES的组合值填进dwFlags成员中。

                   invoke CreateProcess, NULL, addr CommandLine, NULL, NULL, TRUE, NULL, NULL, NULL, addr startupinfo, addr pinfo

We now create the child process with CreateProcess call. Note that the bInheritHandles parameter must be set to TRUE for the pipe handle to work.

现在,我们调用CreateProcess函数创建了子进程。注意,为了能让管道句柄能工作,我们必须把bInheritHandles参数值设置为TRUE

                       invoke CloseHandle,hWrite

After we successfully create the child process, we must close the write end of the pipe. Remember that we passed the write handle to the child process via STARTUPINFO structure. If we don't close the write handle from our end, there will be two write ends. And that the pipe will not work. We must close the write handle after CreateProcess but before we read data from the read end of the pipe.

在成功创建了子进程之后,我们必须关闭管道的写端。记住,我们通过STARTUPINFO结构来传递写端句柄给子进程。如果我们不关闭管道一端的写句柄,这个管道将有两个写端。这样,管道就不能工作了。我们必须在CreateProcess调用后但是要在从管道的读端读数据之前关闭写端句柄。

                        .while TRUE
                            invoke RtlZeroMemory,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,hwndEdit,EM_REPLACESEL,FALSE,addr buffer
                        .endw

Now we are ready to read the data from the standard output of the child process. We will stay in an infinite loop until there are no more data left to read from the pipe. We call RtlZeroMemory to fill the buffer with zeroes then call ReadFile, passing the read handle of the pipe in place of a file handle. Note that we only read a maximum of 1023 bytes since we need the data to be an ASCIIZ string which we can pass on to the edit control.

现在,我们准备从子进程的标准输出中读数据。我们将驻留在一个死循环中,直到从管道中再也没有数据读出为止才离开。我们调用RtlZeroMemory函数用零填充缓冲区然后传递管道的读端句柄给ReadFile函数的文件句柄并调用它。注意,我们能读的最大字节数为1023字节,因为我们需要把数据转换成能传递给编辑控件的ASCIIZ字符串。
When ReadFile returns with the data in the buffer, we fill the data into the edit control. However, there is a slight problem here. If we use SetWindowText to put the data into the edit control, the new data will overwrite existing data! We want the data to append to the end of the existing data.

ReadFile把数据置于缓冲区中并返回时,我们把数据填进编辑控件中。然而,这里有一个小问题。如果我们用SetWindowText函数来把数据放入编辑控件中,新的数据将覆盖已经存在的数据!我们希望数据能给补充在存在数据的后面。


To achieve that goal, we first move the caret to the end of the text in the edit control by sending EM_SETSEL message with wParam==-1. Next, we append the data at that point with EM_REPLACESEL message.

为完成这个目标,首先,我们通过在发送EM_SETSEL消息的时候设置wParam参数的值为-1来把插入符移动到文本的末尾。然后,我们发送一个EM_REPLACESEL消息来添加加数据。

 

                   invoke CloseHandle,hRead

When ReadFile returns NULL, we break out of the loop and close the read handle.

ReadFile函数返回值为NULL时,我们退出循环并且关闭读端句柄。


This article come from Iczelion's asm page

风向改变翻译于2008-1-11

 

阅读(2112) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~