# 管道操作

**URL:** <https://perak.org/t/%E7%AE%A1%E9%81%93%E6%93%8D%E4%BD%9C/60129>\
**Category:** 程序设计\
**Created:** [2006 年8 月 17 日 14:51 UTC](https://perak.org/t/%E7%AE%A1%E9%81%93%E6%93%8D%E4%BD%9C/60129 "2006-08-17T14:51:49Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

作者： ![microsys](https://perak.org/letter_avatar_proxy/v4/letter/m/d26b3c/32.png) [@microsys](https://perak.org/u/microsys)\
发布日期： [2006 年8 月 17 日 14:51 UTC](https://perak.org/t/%E7%AE%A1%E9%81%93%E6%93%8D%E4%BD%9C/60129/1 "2006-08-17T14:51:49Z")

</div>

概述

Windows 引入了多进程和多线程机制。同时也提供了多个进程之间的通信手段，包括剪贴板、DDE、OLE、管道等，和其他通信手段相比，管道有它自己的限制和特点，管道实际上是一段共享内存区，进程把共享消息放在那里。并通过一些 API 提供信息交换。  
管道是两个头的东西，每个头各连接一个进程或者同一个进程的不同代码，按照管道的类别分有两种管道，匿名的和命名的；按照管道的传输方向分也可以分成两种，单向的双向的。根据管道的特点，命名管道通常用在网络环境下不同计算机上运行的进程之间的通信（当然也可以用在同一台机的不同进程中）它可以是单向或双向的；而匿名管道只能用在同一台计算机中，它只能是单向的。匿名管道其实是通过用给了一个指定名字的有名管道来实现的。  
使用管道的好处在于：读写它使用的是对文件操作的 api，结果操作管道就和操作文件一样。即使你在不同的计算机之间用命名管道来通信，你也不必了解和自己去实现网络间通信的具体细节。

我们简单的介绍一下命名管道的使用。

命名管道是由服务器端的进程建立的，管道的命名必须遵循特定的命名方法，就是 “\.\pipe\管道名”，当作为客户端的进程要使用时，使用"\计算机名\pipe\管道名" 来打开使用，具体步骤如下：

服务端通过函数 CreateNamedPipe 创建一个命名管道的实例并返回用于今后操作的句柄，或为已存在的管道创建新的实例。  
服务端侦听来自客户端的连接请求，该功能通过 ConnectNamedPipe 函数实现。  
客户端通过函数 WaitNamedPipe 来等待管道的出现，如果在超时值变为零以前，有一个管道可以使用，则 WaitNamedPipe 将返回 True，并通过调用 CreateFile 或 CallNamedPipe 来呼叫对服务端的连接。  
此时服务端将接受客户端的连接请求，成功建立连接，服务端 ConnectNamedPipe 返回 True  
建立连接之后，客户端与服务器端即可通过 ReadFile 和 WriteFile，利用得到的管道文件句柄，彼此间进行信息交换。  
当客户端与服务端的通信结束，客户端调用 CloseFile，服务端接着调用 DisconnectNamedPipe。最后调用函数CloseHandle来关闭该管道。  
由于命名管道使用时作为客户端的程序必须知道管道的名称，所以更多的用在同一“作者”编写的服务器/工作站程序中，你不可能随便找出一个程序来要求它和你写的程序来通过命名管道通信。而匿名管道的使用则完全不同，它允许你和完全不相干的进程通信，条件是这个进程通过控制台“console”来输入输出，典型的例子是老的 Dos 应用程序，它们在运行时 Windows 为它们开了个 Dos 窗口，它们的输入输出就是 console 方式的。还有一些标准的 Win32 程序也使用控制台输入输出，如果在 Win32 编程中不想使用图形界面，你照样可以使用 AllocConsole 得到一个控制台，然后通过 GetStdHandle 得到输入或输出句柄，再通过 WriteConsole 或 WriteFile 把结果输出到控制台（通常是一个象 Dos 窗口）的屏幕上。虽然这些程序看起来象 Dos 程序，但它们是不折不扣的 Win32 程序，如果你在纯 Dos 下使用，就会显示“The program must run under Windows!”。

一个控制台有三个句柄：标准输入、标准输出和和标准错误句柄，标准输入、标准输出句柄是可以重新定向的，你可以用匿名管道来代替它，这样一来，你可以在管道的另一端用别的进程来接收或输入，而控制台一方并没有感到什么不同，就象 Dos 下的 \> 或者 \< 可以重新定向输出或输入一样。通常控制台程序的输入输出如下：

(控制台进程output) write ----\> 标准输出设备（一般是屏幕）  
(控制台进程input) read \<---- 标准输入设备（一般是键盘）

而用管道代替后：

(作为子进程的控制台进程output) write ----\> 管道1 ----\> read (父进程)  
(作为子进程的控制台进程input) read \<----\> 管道2 \<---- write (父进程)

使用匿名管道的步骤如下：

使用 CreatePipe 建立两个管道，得到管道句柄，一个用来输入，一个用来输出  
准备执行控制台子进程，首先使用 GetStartupInfo 得到 StartupInfo  
使用第一个管道句柄代替 StartupInfo 中的 hStdInput，第二个代替 hStdOutput、hStdError，即标准输入、输出、错误句柄  
使用 CreateProcess 执行子进程，这样建立的子进程输入和输出就被定向到管道中  
父进程通过 ReadFile 读第二个管道来获得子进程的输出，通过 WriteFile 写第一个管道来将输入写到子进程  
父进程可以通过 PeekNamedPipe 来查询子进程有没有输出  
子进程结束后，要通过 CloseHandle 来关闭两个管道。  
下面是具体的说明和定义：

1. 建立匿名管道使用 CreatePipe 原形如下：

BOOL CreatePipe(  
PHANDLE hReadPipe, // address of variable for read handle  
PHANDLE hWritePipe, // address of variable for write handle  
LPSECURITY\_ATTRIBUTES lpPipeAttributes, // pointer to security attributes  
DWORD nSize // number of bytes reserved for pipe  
);

当管道建立后，结构中指向的 hReadPipe 和 hWritePipe 可用来读写管道，当然由于匿名管道是单向的，你只能使用其中的一个句柄，参数中的 SECURITY\_ATTRIBUTES 的结构必须填写，定义如下：

typedef struct\_SECURITY\_ATTRIBUTES{  
DWORD nLength: //定义以字节为单位的此结构的长度  
LPVOID lpSecurityDescriptor; //指向控制这个对象共享的安全描述符,如果为NULL这个对象将被分配一个缺省的安全描述  
BOOL bInheritHandle; //当一个新过程被创建时,定义其返回是否是继承的.供系统API函数使用.  
}SECURITY\_ATTRIBUTES;

1. 填写创建子进程用的 STARTUPINFO 结构，一般我们可以先用 GetStartupInfo 来填写一个缺省的结构，然后改动我们用得到的地方，它们是：

hStdInput – 用其中一个管道的 hWritePipe 代替  
hStdOutput、hStdError – 用另一个管道的 hReadPipe 代替  
dwFlags – 设置为 STARTF\_USESTDHANDLES or STARTF\_USESHOWWINDOW 表示输入输出句柄及 wShowWindow 字段有效  
wShowWindow – 设置为 SW\_HIDE，这样子进程执行时不显示窗口。  
填写好以后，就可以用 CreateProcess 来执行子进程了，具体有关执行子进程的操作可以参考上一篇教程《进程控制》

1. 在程序中可以用 PeekNamedPipe 查询子进程有没有输出，原形如下：

BOOL PeekNamedPipe(  
HANDLE hNamedPipe, // handle to pipe to copy from  
LPVOID lpBuffer, // pointer to data buffer  
DWORD nBufferSize, // size, in bytes, of data buffer  
LPDWORD lpBytesRead, // pointer to number of bytes read  
LPDWORD lpTotalBytesAvail, // pointer to total number of bytes available  
LPDWORD lpBytesLeftThisMessage // pointer to unread bytes in this message  
);

我们可以将尝试读取 nBuffersize 大小的数据，然后可以通过返回的 BytesRead 得到管道中有多少数据，如果不等于零，则表示有数据可以读取。

1. 用 ReadFile 和 WriteFile 来读写管道，它们的参数是完全一样的，原形如下：

ReadFile or WriteFile(  
HANDLE hFile, // handle of file to read 在这里使用管道句柄  
LPVOID lpBuffer, // address of buffer that receives data 缓冲区地址  
DWORD nNumberOfBytesToRead, // number of bytes to read 准备读写的字节数  
LPDWORD lpNumberOfBytesRead, // address of number of bytes read，实际读到的或写入的字节数  
LPOVERLAPPED lpOverlapped // address of structure for data 在这里用 NULL  
);

1. 用 CloseHandle 关闭管道一和管道二的 hReadPipe和 hWritePipe 这四个句柄。

下面给出了一个例子程序，这个程序是上篇教程《进程控制》的例子的扩充，如果你对有的 api 感到陌生的话，请先阅读上一篇教程。

源程序 - 汇编源文件

DEBUG equ 0  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
; Programmed by 罗云彬, [bigluo@telekbird.com.cn](mailto:bigluo@telekbird.com.cn)  
; Website: [http://asm.yeah.net](http://asm.yeah.net)  
; LuoYunBin’s Win32 ASM page (罗云彬的编程乐园)  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
; 版本信息  
; 汇编教程附带例子程序 - 管道例子  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
.386  
.model flat, stdcall  
option casemap :none ; case sensitive  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
; Include 数据  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
include windows.inc  
include user32.inc  
include kernel32.inc  
include comctl32.inc  
include comdlg32.inc  
include gdi32.inc

includelib user32.lib  
includelib kernel32.lib  
includelib comctl32.lib  
includelib comdlg32.lib  
includelib gdi32.lib  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
; Equ 数据  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
ICO\_MAIN equ 1000  
MENU\_MAIN equ 2000  
IDM\_EXEC equ 2001  
IDM\_EXIT equ 2002

F\_RUNNING equ 0001h ;进程在运行中  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
; 数据段  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
.data?

stStartUp STARTUPINFO \<?\>

hInstance dd ?  
hMenu dd ?  
hWinMain dd ?  
hWinText dd ?  
hFont dd ?  
hRunThread dd ?  
hRead1 dd ?  
hWrite1 dd ?  
hRead2 dd ?  
hWrite2 dd ?  
szBuffer db 512 dup (?)

dwFlag dd ?  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>

.data

szMenuExecute db ‘连接 MS-&DOS 方式’,0  
szExcuteError db ‘启动应用程序错误！’,0  
szCaption db ‘管道示例程序 … [http://asm.yeah.net](http://asm.yeah.net)’,0  
szClassName db ‘PipeExample’,0  
;szDllName db ‘riched32.dll’,0  
;szClassNameRedit db ‘RichEdit’,0  
szDllName db ‘riched20.dll’,0  
szClassNameRedit db ‘richedit20a’,0  
szCommand db ‘c:\command.com’,0

stLogFont LOGFONT \<24,0,0,0,FW\_NORMAL,  
0,0,0,ANSI\_CHARSET,OUT\_DEFAULT\_PRECIS,  
CLIP\_STROKE\_PRECIS,DEFAULT\_QUALITY,  
DEFAULT\_PITCH or FF\_SWISS,“Fixedsys”\>

;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
; 代码段  
;\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>  
.code

if DEBUG  
include Debug.asm  
endif  
include Win.asm

;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
; 执行程序用的线程  
; 1. 用 CreateProcess 建立进程  
; 2. 用 WaitForSingleOject 等待进程结束  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
\_RunThread proc uses ebx ecx edx esi edi,  
dwParam:DWORD  
local @stSecurity:SECURITY\_ATTRIBUTES  
local @dwExitCode  
local @dwBytesRead  
local @stRange:CHARRANGE

or dwFlag,F\_RUNNING  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
; “执行”菜单改为“结束”  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
invoke EnableMenuItem,hMenu,IDM\_EXEC,MF\_GRAYED  
invoke EnableMenuItem,hMenu,IDM\_EXIT,MF\_GRAYED  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
; 建立管道  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
mov @stSecurity.nLength,sizeof SECURITY\_ATTRIBUTES  
mov @stSecurity.lpSecurityDescriptor,NULL  
mov @stSecurity.bInheritHandle,TRUE  
invoke CreatePipe,addr hRead1,addr hWrite1,addr @stSecurity,NULL  
invoke CreatePipe,addr hRead2,addr hWrite2,addr @stSecurity,NULL

;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
; 执行文件，如果成功则等待程序结束  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
invoke GetStartupInfo,addr stStartUp  
mov eax,hRead1  
mov stStartUp.hStdInput,eax  
mov eax,hWrite2  
mov stStartUp.hStdOutput,eax  
mov stStartUp.hStdError,eax  
mov stStartUp.dwFlags,STARTF\_USESTDHANDLES or STARTF\_USESHOWWINDOW  
mov stStartUp.wShowWindow,SW\_HIDE  
invoke CreateProcess,NULL,addr szCommand,NULL,NULL,  
NULL,NORMAL\_PRIORITY\_CLASS,NULL,NULL,offset stStartUp,offset stProcInfo  
.if eax != 0  
.while TRUE  
invoke GetExitCodeProcess,stProcInfo.hProcess,addr @dwExitCode  
.break .if @dwExitCode != STILL\_ACTIVE  
invoke PeekNamedPipe,hRead2,addr szBuffer,511,addr @dwBytesRead,NULL,NULL  
.if @dwBytesRead != 0  
invoke RtlZeroMemory,addr szBuffer,512  
invoke ReadFile,hRead2,addr szBuffer,@dwBytesRead,addr @dwBytesRead,NULL  
mov @stRange.cpMin,-1  
mov @stRange.cpMax,-1  
invoke SendMessage,hWinText,EM\_EXSETSEL,0,addr @stRange  
invoke SendMessage,hWinText,EM\_REPLACESEL,FALSE,addr szBuffer  
invoke SendMessage,hWinText,EM\_SCROLLCARET,NULL,NULL  
invoke SendMessage,hWinText,WM\_SETFONT,hFont,0  
.endif  
.endw  
invoke CloseHandle,stProcInfo.hProcess  
invoke CloseHandle,stProcInfo.hThread  
.else  
invoke MessageBox,hWinMain,addr szExcuteError,NULL,MB\_OK or MB\_ICONERROR  
.endif  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
; 关闭管道  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
invoke CloseHandle,hRead1  
invoke CloseHandle,hWrite1  
invoke CloseHandle,hRead2  
invoke CloseHandle,hWrite2  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
; 把“结束”菜单改为“执行”  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
invoke EnableMenuItem,hMenu,IDM\_EXEC,MF\_ENABLED  
invoke EnableMenuItem,hMenu,IDM\_EXIT,MF\_ENABLED  
invoke EnableWindow,hWinText,FALSE  
and dwFlag,not F\_RUNNING  
ret

\_RunThread endp

;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
; 窗口程序  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
WndMainProc proc uses ebx edi esi,   
hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD

mov eax,wMsg  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
.if eax == WM\_CREATE  
mov eax,hWnd  
mov hWinMain,eax  
call \_Init  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
.elseif eax == WM\_SIZE  
mov edx,lParam  
mov ecx,edx  
shr ecx,16  
and edx,0ffffh  
invoke MoveWindow,hWinText,0,0,edx,ecx,TRUE  
invoke PostMessage,hWinText,WM\_SIZE,wParam,lParam  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
.elseif eax == WM\_CLOSE  
test dwFlag,F\_RUNNING  
.if ZERO?  
invoke DestroyWindow,hWinMain  
invoke PostQuitMessage,NULL  
.endif  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
.elseif eax == WM\_COMMAND  
mov eax,wParam  
.if ax == IDM\_EXEC  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
; 如果没有在执行中(dwFlag 没有置位) 则建立线程，在线程中执行程序  
; 如果已经在执行中，则用 TerminateProcess 终止执行  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
test dwFlag,F\_RUNNING  
.if ZERO?  
invoke EnableWindow,hWinText,TRUE  
invoke SetFocus,hWinText  
invoke CreateThread,NULL,NULL,offset \_RunThread,  
NULL,NULL,offset hRunThread  
.else  
invoke TerminateProcess,stProcInfo.hProcess,-1  
.endif  
.elseif ax == IDM\_EXIT  
invoke DestroyWindow,hWinMain  
invoke PostQuitMessage,NULL  
.endif  
.else  
invoke DefWindowProc,hWnd,wMsg,wParam,lParam  
ret  
.endif  
xor eax,eax  
ret

WndMainProc endp  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
; 程序入口  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
start:  
call \_WinMain  
invoke ExitProcess,NULL  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
\_WinMain proc  
local @stWcMain:WNDCLASSEX  
local @stMsg:MSG  
local @hRichEdit

invoke LoadLibrary,offset szDllName  
mov @hRichEdit,eax

invoke InitCommonControls  
invoke GetModuleHandle,NULL  
mov hInstance,eax  
invoke LoadMenu,hInstance,MENU\_MAIN  
mov hMenu,eax  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* 注册窗口类 \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
invoke LoadCursor,0,IDC\_ARROW  
mov @stWcMain.hCursor,eax  
mov @stWcMain.cbSize,sizeof WNDCLASSEX  
mov @stWcMain.hIconSm,0  
mov @stWcMain.style,CS\_HREDRAW or CS\_VREDRAW  
mov @stWcMain.lpfnWndProc,offset WndMainProc  
mov @stWcMain.cbClsExtra,0  
mov @stWcMain.cbWndExtra,0  
mov eax,hInstance  
mov @stWcMain.hInstance,eax  
invoke LoadIcon,hInstance,ICO\_MAIN  
mov @stWcMain.hIcon,eax  
mov @stWcMain.hbrBackground,COLOR\_BTNFACE+1  
mov @stWcMain.lpszClassName,offset szClassName  
mov @stWcMain.lpszMenuName,0  
invoke RegisterClassEx,addr @stWcMain  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* 建立输出窗口 \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
invoke CreateWindowEx,NULL,  
offset szClassName,offset szCaption,  
WS\_OVERLAPPEDWINDOW,  
0,0,680,420,  
NULL,hMenu,hInstance,NULL

invoke ShowWindow,hWinMain,SW\_SHOWNORMAL  
invoke UpdateWindow,hWinMain  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
.while TRUE  
invoke GetMessage,addr @stMsg,NULL,0,0  
.break .if eax == 0  
invoke TranslateMessage,addr @stMsg  
invoke DispatchMessage,addr @stMsg  
.endw  
invoke FreeLibrary,@hRichEdit  
invoke DeleteObject,hFont  
ret

\_WinMain endp

;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
; 输入程序  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
\_InputProc proc uses ebx edi esi,   
hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD  
local @szBuffer[4]:BYTE  
local @dwBytesWrite

mov eax,uMsg  
.if eax == WM\_CHAR  
mov eax,wParam  
movzx eax,al  
mov dword ptr @szBuffer,eax  
test dwFlag,F\_RUNNING  
.if !ZERO?  
invoke WriteFile,hWrite1,addr @szBuffer,1,addr @dwBytesWrite,NULL  
.endif  
xor eax,eax  
ret  
.endif  
invoke GetWindowLong,hWnd,GWL\_USERDATA  
invoke CallWindowProc,eax,hWnd,uMsg,wParam,lParam  
ret

\_InputProc endp  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
\_Init proc

;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* 建立输出 RICHEDIT 窗口 \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
invoke CreateWindowEx,WS\_EX\_CLIENTEDGE,offset szClassNameRedit,  
NULL,WS\_CHILD OR WS\_VISIBLE OR WS\_VSCROLL OR WS\_HSCROLL  
OR ES\_MULTILINE OR ES\_AUTOHSCROLL OR ES\_AUTOVSCROLL,  
0,0,0,0,  
hWinMain,NULL,hInstance,NULL  
mov hWinText,eax  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* 设置字体 \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
invoke CreateFontIndirect,offset stLogFont  
mov hFont,eax  
invoke SendMessage,hWinText,WM\_SETFONT,hFont,0  
invoke SendMessage,hWinText,EM\_SETREADONLY,TRUE,NULL

invoke SetWindowLong,hWinText,GWL\_WNDPROC,offset \_InputProc  
invoke SetWindowLong,hWinText,GWL\_USERDATA,eax  
invoke EnableWindow,hWinText,FALSE

invoke \_CenterWindow,hWinMain  
invoke SetFocus,hWinText

ret

\_Init endp  
;\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
end start

程序的分析和要点

在程序中，我先建立了一个 Richedit 控件用来显示子进程的输出，同时将 RichEdit 子类化，截取它的键盘输入以便把它发给子进程

invoke SetWindowLong,hWinText,GWL\_WNDPROC,offset \_InputProc

这条语句将 RichEdit 的过程指到了 \_InputProc 中，然后在 \_InputProc 的 WM\_CHAR 中将键入的字符 WriteFile 到管道中，我在程序中先建立了两个管道，然后执行 c:\command.com，这样就得到了一个 dos 的命令行进程，然后在循环中通过 PeekNamedPipe 检测子进程有无输出，如果有的话则通过 ReadFile 读出，在显示到 RichEdit 中。

在运行例子程序的时候要注意，你可以在这个“[Command.com](http://Command.com)” 中执行几乎所有的别的程序，但是不要执行如 ucdos,pctools 之类不使用标准输入输出的程序（就是在 dos 下用不了“\>”或者“\<”重定向的程序），由于我们在装载子进程的时候用了 WS\_HIDE，所以原来的 [command.com](http://command.com) 的窗口是隐藏的，如果你执行了这种程序那就意味着你失去的对子进程的控制，因为它们不使用标准输入来接收键盘，你也就无法通过管道让它们退出。

在这里还可以引申出匿名管道的另一个用法，如果你执行的不是 [command.com](http://command.com) 而是类似于 arj.exe 的程序，然后也不用把它的输出显示到 RichEdit 中，而是在程序中处理，那么，你就可以编写一个 winarj，当然你只需编写窗口界面和 arj.exe 之间的配合而已。

---

<div class="post-metadata">

作者： ![jollygoh](https://perak.org/letter_avatar_proxy/v4/letter/j/9fc29f/32.png) [@jollygoh](https://perak.org/u/jollygoh)\
发布日期： [2006 年8 月 17 日 15:06 UTC](https://perak.org/t/%E7%AE%A1%E9%81%93%E6%93%8D%E4%BD%9C/60129/2 "2006-08-17T15:06:09Z")

</div>

多谢分享。。。  
迟下再睇。。。

---

<div class="post-metadata">

作者： ![红文](https://perak.org/letter_avatar_proxy/v4/letter/%E7%BA%A2/54ee81/32.png) [@红文](https://perak.org/u/%E7%BA%A2%E6%96%87)\
发布日期： [2006 年8 月 17 日 15:07 UTC](https://perak.org/t/%E7%AE%A1%E9%81%93%E6%93%8D%E4%BD%9C/60129/3 "2006-08-17T15:07:28Z")

</div>

这个这个 …[s:19]

---

<div class="post-metadata">

作者： ![microsys](https://perak.org/letter_avatar_proxy/v4/letter/m/d26b3c/32.png) [@microsys](https://perak.org/u/microsys)\
发布日期： [2006 年8 月 17 日 15:22 UTC](https://perak.org/t/%E7%AE%A1%E9%81%93%E6%93%8D%E4%BD%9C/60129/4 "2006-08-17T15:22:20Z")

</div>

[s:21] [s:23] [s:29]
