林怀青 (E-mail):我初学VB 5.0,想用SHELL函数调用三个可执行文件,但在VB 5.0中,SHELL函数是异步执行的,也就是说,第一个可执行文件调用完了,第二个文件还未运行完就跳到第三个可执行文件了,该如何使它们一个一个地运行呢?
李海:这个一个特别常见的问题,相信很多人都会遇到。使用过VB在Windows 3.x进行编程的人可能都还记得可以使用Windows API的GetModuleUsage函数可以解决这样的问题。但在Windows 95/NT中已经没有GetModuleUsage函数了。一般实现这种等待的方法有三种。
一种是利用是利用Win32 API的FindWindow函数,该函数可以搜索指定标题或类的窗口,你可以在调用第一个可执行文件后用FindWindow函数去找指定的窗口,如果找到了,就说明第一个文件还未运行完,等待,直到用FindWindow函数找不到指定窗口,就可以调用第二个文件。这种方法简单,但有个毛病,就是如果满足条件的窗口不只一个,比如用户打开了两个有相同类或标题的窗口(一个是VB程序打开的,而另一个可能是用户自行打开的),那么除非用户关闭这两个窗口,否则VB程序不会继续运行。比如Wise Install生成的安装程序就有这个毛病,它在安装过程中可能会用NotePad打开ReadMe文件,等用户看完ReadMe文件关闭窗口后继续安装,但如果用户此时用NotePad打开了其他文件,安装程序就会继续等待,应为FindWindow函数找到了NotePad窗口,可实际上这个窗口跟安装程序毫无关系。
第二种是利用Windows 95新增加的命令Start。这个命令可以调用Windows程序,通常用在批文件中。它有一个/w开关,在被调用的程序继续运行结束之前等待。比如,你要调用的三个可执行文件为Command1、Command2和Command3,你可以构造一个批文件如下:
Start/w Command1 Start/w Command2 Start/w Command3这个方法可以保证三个按既定顺序运行,但VB程序不能知道什么时候该批文件执行完毕。
第三中方法是利用Win32 API的CreateProcess函数和WaitForSingleObject函数来进行这一工作。它的原理比较复杂,在此只介绍如何使用。首先建立一个模块(module),然后输入以下语句:
Option Explicit Type STARTUPINFO cb As Long lpReserved As String lpDesktop As String lpTitle As String 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 Type PROCESS_INFORMATION hProcess As Long hThread As Long dwProcessID As Long dwThreadID As Long End Type Global Const NORMAL_PRIORITY_CLASS = &H20& Global Const INFINITE = -1& Declare Function CloseHandle Lib "kernel32" (hObject As Long) As Boolean Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long Declare Function CreateProcessA Lib "kernel32" ( _ ByVal lpApplicationName As Long, _ ByVal lpCommandLine As String, ByVal lpProcessAttributes As Long, ByVal _ lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal _ dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal _ lpCurrentDirectory As Long, lpStartupInfo As STARTUPINFO, _ lpProcessInformation As PROCESS_INFORMATION) As Long Public Sub ShellAndWait(cmdline$) Dim NameOfProc As PROCESS_INFORMATION Dim NameStart As STARTUPINFO Dim X As Long NameStart.cb = Len(NameStart) X = CreateProcessA(0&, cmdline$, 0&, 0&, 1&, NORMAL_PRIORITY_CLASS, _ 0&, 0&, NameStart, NameOfProc) X = WaitForSingleObject(NameOfProc.hProcess, INFINITE) X = CloseHandle(NameOfProc.hProcess) End Sub建立一个窗体,并放一个命令按钮(Command1)在其上。在Command1_Click事件中输入以下内容:
Private Sub Command1_Click() Dim AppToLaunch As String AppToLaunch = "c:\win95\notepad.exe" ShellAndWait AppToLaunch End Sub运行该程序,按下Command1,就会调用NotePad,在NotePad运行完毕之前,VB程序不会继续执行。你可以在程序中使用ShellAndWait来代替Shell命令。
广州·胡文奇 (E-mail):我是一位业余编程爱好者。最近我用中文VB 4.0专业版为单位编写一个报表打印程序时,遇到了一个奇怪的问题:当我在简体中文Windows 3.2下执行打印语句Printer.print "Hello!"时,程序运行正常;但是在简体中文Win 95下执行同样语句时,却出现了错误提示信息:“Run-time error '482': Application-defined or object-defined error”。VB的帮助文档对解决该问题没有任何有益的提示。请问这是怎么一回事?(我用的打印机是LEXMARK Color Jetprinter 1020。)
李海:Run-time error '482'的提示信息应该是"Printer Error.", 而不是"Application-defined or object-defined error"。出现 "Printer Error"错误,通常是打印机联机不当,如未开打印机, 或未按下联机键。你可以用Win95的记事本先打一页内容确认 打印机工作正常,再运行你的程序。我估计你的打印机有问题 的可能性不大,特别是你的古怪的错误信息使我怀疑你的程序 安装有误。Visual Basic 4.0编写的程序都要在系统注册簿(Registry)中注册系统信息,有些信息是在.exe文件执行时 自动完成的,而有些信息需要另行加入,而这些数据通常 是由安装程序来自动写入的,你无需了解细节。而如果不是 通过安装程序安装,只是将程序文件和所需的DLL拷贝到系统 中,有些程序不能正常运行。所以用VB4编写的程序 (除非特别简单的)一般都需要用Setup Wizard(或其它安装 软件)制作安装盘,然后再安装到其它机器中,这样程序方可 正常运行。如果你的程序不是通过安装程序安装到新机器中的, 可以尝试一些使用Setup Wizard制作安装盘再行安装。
如果您有任何建议,请给我发电子邮件:
。
版权所有 李海,热情软件屋 1997-2006