⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 021.txt

📁 会变语言实现的一些程序
💻 TXT
📖 第 1 页 / 共 2 页
字号:
第二十一课 管道


--------------------------------------------------------------------------------

这一讲将探索一下管道,看看它是什么、有什么用。为使之更加生动有趣,我将用怎样改变 Edit 控件的背景色和文本颜色来说明此技术。

理论:

管道,顾名思义就是有两个端的通道。可以使用管道在进程间、同一进程内进行数据交换,就像手提式无线电话机一样。把管道的一端给另一方,他就可以借助管道和你通讯了。

有两种管道,即有名管道和匿名管道。匿名管道就是没有名字的管道了,也就是说在使用它们时不需要知道其名字。而有名管道正好相反,在使用前必须知道其名字。

也可以根据管道的特性来分类,即是单向的还是双向的。单向管道,数据只能沿一个方向移动,从一端流向另一端,而双向管道数据可以在两端间自由交换。匿名管道通常是单向的而有名管道通常是双向的。有名管道常用于一个服务器联络多个客户端的网络环境。 

这一讲将详细讨论一下匿名管道。匿名管道主要目的是作为父进程与子进程、子进程之间通讯的联结通路。在处理控制台问题时,匿名管道是相当有用的。控制台应用程序就是使用控制台作为输入和输出的一种 Win32 应用程序。一个控制台就像一个 DOS 窗口。但控制台应用程序的的确确是32位的应用程序,它可以向其它图形程序一样使用 GUI 函数,只不过它碰巧使用了控制台罢了。

控制台应用程序有三个用于输入输出的标准句柄,它们是标准输入、标准输出和标准错误句柄。标准输入用于从控制台读或取信息而标准输出用于往控制台写或打印信息。标准错误用于汇报输出不能重定向的错误。

控制台应用程序可以通过调用函数 GetStdHandle 来获得这三个句柄。一个图形应用程序没有控制台,如果在其中调用GetStdHandle 就会返回错误;如果的确要使用控制台,可以调用AllocConsole 来分配一个新的控制台以使用,但当处理完成时,别忘了调用 FreeConsole 来释放控制台。

匿名管道用得最多的功能就是 重定向子进程的标准输入和标准输出。父进程可以是一个控制台或者是图形程序,而子进程必须是控制台应用程序。众所周知,控制台应用使用标准输入输出句柄。若要重定向输入输出,就得用指向管道一端的句柄来替换这个标准句柄。控制台应用程序不会知道我们使用了指向管道任一端的句柄,它会把这个句柄作为标准句柄来看待。借用面向对象的术语,这就是多态性的一种。因为子进程不需作任何改动,因此这种方法是非常有用的。

关于控制台应用程序应该掌握的另一点就是它从哪获得标准句柄。当一个控制台应用程序被创建时,父进程有两种选择:为子进程创建一个新的控制台或者是让子进程继承自己的控制台。若使用后者,那父进程本身必须是一个控制台应用程序,或者如果是 GUI 应用程序,它必须首先调用 AllocConsole 分配了一个控制台。

通过调用 CreatePipe 来创建一个匿名管道,它的原型为:

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

pReadHandle 双字指针变量,指向管道读端的句柄。 
pWriteHandle 双字指针变量,指向管道写端的句柄 
pPipeAttributes 双字指针变量,指向SECURITY_ATTRIBUTES 结构,其用于决定读写句柄是否可以被子进程继承 
nBufferSize 建议管道留给用户使用的缓冲区的大小,这仅仅是个建议值,可以用 NULL 来使用缺省值 
如果函数调用成功返回值为非零,否则为零。成功调用之后,就会得到两个句柄,一个指向管道的读出端,另一个指向管道的写入端。现在我将要把重点放到重定向子控制台程序的标准输出到自己进程的所需的步骤上。注意我的这个方法不同于Borland 公司的 API 参考上的例子。Win32 API 参考上假设父进程是控制台应用程序,因此子进程可以继承它的标准句柄。然而大多数情况下我们需要重定向控制台应用程序的输出到 GUI 应用程序。

创建匿名管道使用 CreatePipe ,同时别忘了把 SECURITY_ATTRIBUTES 结构成员bInheritable 设置为TRUE,这样才可以继承句柄。

现在要准备好创建进程的函数即CreateProcess 的参数,只有它才可以装载子控制台应用程序。STARTUPINFO 是一个重要的结构,它决定了子进程出现时主窗口的外观,它对于我们的目标也是至关重要的。通过这个结构就可以隐藏主窗口并且把管道句柄传递给子进程。

以下就是必须要填写的成员:

cb STARTUPINFO结构的大小 
dwFlags 二进制标志位,它决定本结构的哪些成员有效,也决定主窗口是显示还是隐藏的状态。在我们的程序中使用STARTF_USESHOWWINDOW 和 STARTF_USESTDHANDLES的组合 
hStdOutput 和hStdError 你想要子进程使用的标准输出和标准错误句柄,对我们来说,我们将把管道的写端作为子进程的标准输出和错误。因此当子进程往标准输出或标准错误发送信息时,它实际上把这些信息通过管道传给了父进程 
wShowWindow 决定主窗口是显示还是隐藏。我们不希望显示子进程的主窗口,因此把该成员置成SW_HIDE 
调用CreateProcess 来创建子进程,但调用成功后子进程仍然不处于激活状态。它被装进了内存但并没有立即运行。

在父进程中关闭管道的写端也是必须的。这是因为父进程并不使用管道的写句柄,而且如果一个管道有两个写入端也就不会工作,因此我们在从管道往外读数据之前必须关闭管道的写端。但是不能在调用CreateProcess 之前关闭,否则管道就坏了。你应当在CreateProcess 刚刚返回并且在读数据之前关闭管道的写端。

现在就可以通过函数ReadFile 在管道的读端读数据了。通过使用ReadFile ,可以使子进程处于运行状态。它将开始执行,并且当它往标准输出( 实际上是管道的写端 )上写数据时,数据就会被送至管道的读端。应当不停调用ReadFile 直至它的返回值为 0 ,也就是说再也没有数据可读了。对从管道读来的数据你可以进行任何处理,在我们的例子中它被显示在 Edit 控件中。

记得用完后关闭管道的读句柄。

代码举例: 


.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 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -