REALbasic 中使用 ShellExecute 执行命令

在 REALbasic 中,如果需要执行 cmd 命令,可以直接使用 Shell 类,但是这样的话,编译成 Windows 程序时会额外需要一个 Shell.dll 的动态链接库,这对于我这样的 1exe 爱好者是不能忍受的。但是对于 Mac OS X 和 Linux 的生成目标来说,是不存在这个问题的,因为 Mac OS X 的应用程序本身就是一个文件夹,而 Linux 的目标不会生成额外的链接库。因此,需要针对 Windows 进行特殊处理。于是在网上搜索解决方案,找到了 VB 中执行程序的几种方法:

1. 使用 CreateProcess

通过 CreateProcess 以及使用管道,可以执行外部程序并获取输出,但是这个方法过于烦琐,并且我也不需要外部进程执行完毕后的输出结果,因此不采用。

2. 使用 Shell 方法

VB 里有一个 Shell 方法,但是在 RB 中并没有,所以此路不通。

3. 使用 ShellExecute

这个方法同样是一个系统 API,可以直接通过 RB 的 declare 声明并调用它,在测试之后,使用 declare 来使用系统 API 不会生成额外的 dll,正是我需要的。

首先在 RB 的某个模块中添加一个方法 ShellExecute,用来封装对系统 API 的请求:

function ShellExecute(hWnd as Integer, lpOperation as String, _
lpFile as String, lpParameters as String, lpDirectory as String, nShowCmd as Integer)

注意,在 RB 中添加方法时,参数列表中的 _ 需要去掉,这里是为了排版的需要而加上的。

这个 API 是定义在 shell32.dll 中的,在 ShellExecute 方法中先需要声明:

soft declare function ShellExecuteA Lib “shell32.dll” _
(ByVal hWnd As Integer, ByVal lpOperation As CString, _
ByVal lpFile As CString, ByVal lpParameters As CString, _
ByVal lpDirectory As CString, ByVal nShowCmd As Integer) As Integer

soft declare function ShellExecuteW Lib “shell32.dll” _
(ByVal hWnd As Integer, ByVal lpOperation As CString, _
ByVal lpFile As CString, ByVal lpParameters As CString, _
ByVal lpDirectory As CString, ByVal nShowCmd As Integer) As Integer

然后来调用这个 API:

  dim ret as Integer
    Try
      ret = ShellExecuteW(hWnd, ConvertEncoding(lpOperation, Encodings.UTF16), _
      ConvertEncoding(lpFile, Encodings.UTF16), _
      ConvertEncoding(lpParameters, Encodings.UTF16), _
      ConvertEncoding(lpDirectory, Encodings.UTF16), nShowCmd)
    Catch
      ret = ShellExecuteA(hWnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd)
    End
    return ret

把 ShellExecuteW 放在 Try … Catch 中是为了兼容的需要,因为像 Windows 98 这样比较老的系统中,系统内部编码还不是 Unicode,还没有 ShellExecuteW 这样结尾是 W 的 API。

另外,因为 RB 内部是使用的 UTF-8 编码,而系统 API ShellExecuteW 使用的编码是 Unicode,所以参数在传递前需要进行转码,将字符串参数值转换为 Unicode 编码,否则在执行一个明明正确的命令时,会出现“找不到文件”的错误。

好了,这样就可以使用 ShellExecute 方法来执行外部程序了,例如:

  #if TargetWin32
    dim ret as Integer
    ret = Win32Helper.ShellExecute(hWnd, "open", "cmd.exe", _
    "/c shutdown -t 10", "", 0)
  #endif

上面的代码就调用 cmd.exe 执行了命令 shutdown -s -t 10,也就是 10 秒后关机。

其实这个方法也可以用来打开文档或者网址,具体的用法可以去 MSDN 找一找。

当然,如果不在乎生成的 Windows 目标还有额外的 dll 的话,完全可以使用 RB 自带的 Shell 类,功能也强上不少。

发表评论?

7 条评论。

  1. 请问博主,REALBasic中我想用一个按钮打开软件所在根目录下的某个目录。请问可以怎么实现?、我是想做一个光盘自启动导航程序。
    是不是1exe没关系。。。。初学,RB的语言参考没找到。

    • 这个应该是很方便的,直接用 FolderItem 的 Launch 方法就可以了,取当前目录的话,可以用 App.ExecutableFile 取得当前执行文件的路径,App 是一个 Application 对象。

  2. 谢谢,我试一下。。。

  3. 有完整的例子吗?
    初学不是很懂。呵呵

    • 文章里就差不多是一个完整的例子了。在 REALbasic 里给模块或者类添加一个方法,方法名为 ShellExecute,参数为:


      hWnd as Integer, lpOperation as String, lpFile as String, lpParameters as String, lpDirectory as String, nShowCmd as Integer

      返回类型为 Integer,然后方法的代码如下:

      \#if TargetWin32
          soft declare function ShellExecuteA Lib "shell32.dll" _
          (ByVal hWnd As Integer, ByVal lpOperation As CString, _
          ByVal lpFile As CString, ByVal lpParameters As CString, _
          ByVal lpDirectory As CString, ByVal nShowCmd As Integer) As Integer
          soft declare function ShellExecuteW Lib "shell32.dll" _
          (ByVal hWnd As Integer, ByVal lpOperation As CString, _
          ByVal lpFile As CString, ByVal lpParameters As CString, _
          ByVal lpDirectory As CString, ByVal nShowCmd As Integer) As Integer
          dim ret as Integer
          Try
              ret = ShellExecuteW(hWnd, ConvertEncoding(lpOperation, Encodings.UTF16), _
              ConvertEncoding(lpFile, Encodings.UTF16), _
              ConvertEncoding(lpParameters, Encodings.UTF16), _
              ConvertEncoding(lpDirectory, Encodings.UTF16), nShowCmd)
          Catch
              ret = ShellExecuteA(hWnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd)
          End
          return ret
      \#endif
      

      按文章里的说明来调用就可以了

  4. ret = Win32Helper.ShellExecute(hWnd, “open”, “cmd.exe”, “/c shutdown -t 10”, “”, 0)
    这句是调用的是吧,参数hWnd填写什么值啊

  5. ret = Win32Helper.ShellExecute(1000, “open”, “cmd.exe”,”/c shutdown -t 1000″, “”, 0)
    那个shutdown后面我写的1000啊,怎么一下就注销了,不是关机

发表评论


注意 - 你可以用以下 HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>