主题:新年快乐~~问一个高难度问题
freedom12
[专家分:50] 发布于 2007-02-19 15:28:00
首先祝大家新年快乐,心想事成~~
shell "test.bat 1>1.txt 2>2.txt", vbHide
运行BAT文件就会弹出一个类似DOS的CMD窗口,我把那个CMD窗口隐藏掉,显示进程的信息以1.txt 和2.txt文件保存
如果我要用程序查看这些信息内容就要扫描这两个txt文件用text1显示,有没有办法在不生成txt文件的情况下直接用text1控件得到这些信息内容?
我的目的就像用自制程序代替系统CMD窗口显示进程信息
回复列表 (共12个回复)
沙发
tanchuhan [专家分:15140] 发布于 2007-02-19 23:37:00
[color=#0000FF]选自Iczelion的Win32汇编教程:[/color]
第二十一课 管道
--------------------------------------------------------------------------------
这一讲将探索一下管道,看看它是什么、有什么用。为使之更加生动有趣,我将用怎样改变 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 控件中。
记得用完后关闭管道的读句柄。
代码举例:
;----------------------省略---------------------------------
分析:
现在当用户选择 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时,就跳出循环并关闭管道的读句柄。
板凳
freedom12 [专家分:50] 发布于 2007-02-20 00:31:00
[em10]看不明白
3 楼
tjestar [专家分:3520] 发布于 2007-02-20 12:06:00
Dim lFileAttr As Long
lFileAttr = GetAttr("c:\test.txt")
lFileAttr = lFileAttr Or vbHidden
SetAttr "c:\test.txt", lFileAttr
4 楼
tanchuhan [专家分:15140] 发布于 2007-02-20 22:51:00
帮楼主搞定了,源代码如下:
-----------声明部分:
Private Const STARTF_USESHOWWINDOW = &H1
Private Const STARTF_USESTDHANDLES = &H100
Private Const SW_HIDE = 0
Private Const INFINITE = &HFFFF ' Infinite timeout
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Private Type STARTUPINFO
cb As Long
lpReserved As Long
lpDesktop As Long
lpTitle As Long
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
Private Declare Sub GetStartupInfo Lib "kernel32" Alias "GetStartupInfoA" (lpStartupInfo As STARTUPINFO)
Private Declare Function CreatePipe Lib "kernel32" (phReadPipe As Long, phWritePipe As Long, lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Long) As Long
Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As Long, ByVal lpCommandLine As String, lpProcessAttributes As Long, lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDriectory As Long, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
-----------实现部分:
Private Function GetStdOut(ByVal cmdLine As String) As String
Dim sec_attrib As SECURITY_ATTRIBUTES, hPipe_Read As Long, hPipe_Write As Long
Dim sta_info As STARTUPINFO, proc_info As PROCESS_INFORMATION
Dim buffer() As Byte, nPos As Long, size_readed As Long, size_count As Long
'create a pipe, the bInheritHandle must be true
sec_attrib.nLength = Len(sec_attrib)
sec_attrib.bInheritHandle = 1
CreatePipe hPipe_Read, hPipe_Write, sec_attrib, 0
'set the startupinfo struct of process which we will create
With sta_info
.cb = Len(sta_info)
GetStartupInfo sta_info
.hStdError = hPipe_Write
.hStdOutput = hPipe_Write
.dwFlags = STARTF_USESHOWWINDOW Or STARTF_USESTDHANDLES
.wShowWindow = SW_HIDE
End With
If CreateProcess(0, cmdLine, ByVal 0, ByVal 0, 1, 0, ByVal 0, 0, sta_info, proc_info) Then
WaitForSingleObject proc_info.hProcess, INFINITE 'wait until it exit
size_count = GetFileSize(hPipe_Read, ByVal 0)
If size_count = 0 Then GoTo ln: 'no information
ReDim buffer(size_count - 1)
Do Until nPos = size_count
Call ReadFile(hPipe_Read, buffer(nPos), size_count, size_readed, ByVal 0)
nPos = nPos + size_readed 'move the current write position
Loop
GetStdOut = StrConv(buffer, vbUnicode)
Erase buffer
End If
ln:
CloseHandle hPipe_Write
CloseHandle hPipe_Read
End Function
-----------调用示例:
MsgBox GetStdOut("Defrag /?")
5 楼
tanchuhan [专家分:15140] 发布于 2007-02-20 23:18:00
源码下载:[url]http://upload.programfan.com/upfile/200702202314919.rar[/url]
运行效果:[img]http://upload.programfan.com/upfile/200702202323559.rar[/img]
6 楼
freedom12 [专家分:50] 发布于 2007-02-21 00:29:00
感谢tanchuhan
7 楼
freedom12 [专家分:50] 发布于 2007-09-24 10:41:00
tanchuhan大大,还有一个问题需要您帮忙解决!
WisMencoder 视频转换软件你用过吗?
WisMencoder的安装目录中有一个mencoder.exe的转换核心文件
我用以下命令行将一个RMVB文件转换为AVI格式
=========================
"D:\WisMencoder\mencoder.exe" -ofps 23.976 -vf harddup,scale=320:240 -ovc lavc -ffourcc DX50 -lavcopts vcodec=mpeg4:vhq:vqscale=2 -srate 44100 -oac mp3lame -lameopts aq=3:cbr:br=128:vol=1 "F:\交响情人梦\[Nodame Cantabile][12].rmvb" -o "C:\Documents and Settings\Administrator\桌面\[Nodame Cantabile][12].avi"
=========================
以上内容保存在test.bat文件
然后prjPipe.exe运行test.bat,prjPipe.exe就没有响应了
CMD窗口每秒刷新转换进度,能不能在prjPipe.exe用Text2代替CMD窗口显示转换进度
望指教!!
8 楼
tanchuhan [专家分:15140] 发布于 2007-09-24 10:47:00
能
9 楼
freedom12 [专家分:50] 发布于 2007-09-24 11:05:00
等你~~~
10 楼
tanchuhan [专家分:15140] 发布于 2007-09-24 11:30:00
[quote]WaitForSingleObject proc_info.hProcess, INFINITE 'wait until it exit[/quote]
这个就是等待进程结束的语句,你可以把INFINITE改成你想要等待的间隔时间,读取完后再回头等待。
我来回复