📄 021.txt
字号:
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
分析:
这个例子调用 ml.exe 来汇编一个名为 test.asm 的程序,并且重定向 ml.exe 的输出到客户区的 Edit 控件中。当程序被加载时,象往常一样要注册窗口类和创建主窗口。在主窗口被创建的过程中要做的第一件事就是创建用于显示程序 ml.exe 输出的 Edit 控件。
现在有趣的事来了,我们将改变此 Edit 控件的文本颜色和背景色。当 Edit 控件将要重画客户区时,它会给父窗口发送 WM_CTLCOLOREDIT 消息。参数 wParam 包含了用于画控件自己的客户区设备描述符的句柄 (HDC) 。我们可以利用这种机制来修改 HDC 的特性。
.elseif uMsg==WM_CTLCOLOREDIT
invoke SetTextColor,wParam,Yellow
invoke SetTextColor,wParam,Black
invoke GetStockObject,BLACK_BRUSH
ret
SetTextColor 把文本颜色变为黄色,背景颜色变为黑色。最后我们返回一个通过调用GetStockObject 而得到黑色刷子的句柄。处理WM_CTLCOLOREDIT 必须返回一个刷子的句柄,因为 Windows 将要使用这个刷子来重画 Edit 控件的背景。在这个例子中,我希望背景是黑色,所以返回了一个黑色刷子的句柄。
现在当用户选择 Assemble 子菜单时,就会创建一个匿名管道。
.if ax==IDM_ASSEMBLE
mov sat.niLength,sizeof SECURITY_ATTRIBUTES
mov sat.lpSecurityDescriptor,NULL
mov sat.bInheritHandle,TRUE
在调用CreatePipe 之前,必须要填写SECURITY_ATTRIBUTES 结构。如果我们不关心安全性的话,可以在lpSecurityDescriptor 成员中填入 NULL 。bInheritHandle 则必须为 TRUE ,这样管道的句柄才可以被子进程继承。
invoke CreatePipe,addr hRead,addr hWrite,addr sat,NULL
在此之后,我们调用CreatePipe 来创建管道,如果成功,那么变量hRead 和 hWrite 将分别被填入相应的管道的读出端和写入端的句柄。
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
下一步就是填写STARTUPINFO 结构了。调用 GetStartupinfo 用父进程的缺省值来填写STARTUPINFO 结构。如果要使程序同时工作在 Windows9x 和 Windows NT 下,就必须调用GetStartupInfo 来填写STARTUPINFO 结构。调用返回后,就可以修改重要的成员了。因为我们要子进程输出到父进程而不是缺省的标准输出和标准错误,所以我们把hStdOutput 和 hStdError 都赋成管道写端的句柄。为了隐藏子进程的主窗口,必须把成员变量wShowWidow 赋值为SW_HIDE 。最后通过把成员 dwFlags 赋值为STARTF_USESHOWWINDOW 和 STARTF_USESTDHANDLES 来指明成员hStdOutput, hStdError 和 wShowWindow 是有效的。
invoke CreateProcess, NULL, addr CommandLine, NULL, NULL, TRUE, NULL, NULL, NULL, addr startupinfo, addr pinfo
现在调用CreateProcess 来创建子进程。注意为使管道工作,参数bInheritHandles 必须设置为TRUE。 invoke CloseHandle,hWrite 成功创建子进程之后,在父进程中必须关闭管道的写端。我们已经把写端的句柄通过结构STARTUPINFO 传给了子进程。如果不关闭,那么管道就有两个写入端,而这样的管道是不会工作的。所以必须在创建子进程后但在读数据前关闭这个句柄。
.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
现在已经准备好从子进程的标准输出读数据了。直到再也没有数据了,即 ReadFile 返回为 NULL时才会退出循环,否则一直会等待数据。我们调用ReadFile 之前先调用RtlZeroMemory 来清空内存,并且用管道的读句柄代替文件句柄。注意读数据的最大长度为 1023 ( sizeof(buffer)-1 ),因为我们需要把接受的字符变为一个 Edit 控件可以处理的 ASCII 串。当ReadFile 返回时,就把此数据传给 Edit 控件。然而这有一个小小的问题,如果使用SetWindowText API 往 Edit 控件中写数据,新数据就会覆盖已存在的旧数据,而我们想把新数据添加在已有数据的后面。为达此目的,首先通过发送一个 wParam 为-1的 EM_SETSEL 消息,把 Edit 控件的输入焦点移动到文本的末端;然后发送一个 EM_REPLACESEL 消息把数据添加后面。
invoke CloseHandle,hRead
当ReadFile 返回为NULL时,就跳出循环并关闭管道的读句柄。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -