VB6 - 如何从作为服务运行的应用程序中打开文件

时间:2015-01-10 21:29:16

标签: vb6 windows-services shellexecute createprocess shellexecuteex

我一直在努力解决这个问题近一个星期了。我需要一个vb6应用程序,它作为服务运行来打开文件。我不需要对文件做任何事情,我只需要打开它。我已尝试使用ShellExecute和ShellExecuteEx以及使用CreateProcess尝试从命令行启动该文件。当这些实现都不起作用时,我尝试启动另一个应用程序(使用CreateProcess),其唯一任务是打开文件然后自行关闭。

这些解决方案在应用程序正常运行时都有效,但在作为服务运行时无效。非常重要的是,应用程序能够在作为服务运行时直接或间接打开文件,它只需要能够触发它。

据我所知,Windows已经锁定了自Windows Vista以来服务与桌面交互的能力,但我确信必须有一种方法可以从服务中触发文件打开命令。我开发的应用程序能够从命令行运行pg_dump.exe(postgres数据库的备份可执行文件),使用CreateProcess备份数据库文件,同时作为服务运行。这就是为什么我从服务中启动exe来间接打开文件可能会起作用。但是,由于某种原因,应用程序运行pg_dump.exe很好,但不会运行我创建的可执行文件。我想知道我创建的exe是否期望在桌面上有某种存在,这就是服务不想启动它的原因。我在辅助exe中更改了主窗体的属性,这样表单就不会显示,也不会显示在任务栏上,但有些东西告诉我这还不够。

这是我的CreateProcess代码(我没有写大部分内容所以请原谅我的无知):

Private Declare Function WaitForSingleObject Lib "KERNEL32" (ByVal _
   hHandle As Long, ByVal dwMilliseconds As Long) As Long

Private Declare Function CloseHandle Lib "KERNEL32" _
   (ByVal hObject As Long) As Long

Private Declare Function GetExitCodeProcess Lib "KERNEL32" _
   (ByVal hProcess As Long, lpExitCode As Long) As Long

'create a new win process.
Private Declare Function CreateProcessA Lib "KERNEL32" (ByVal _
   lpApplicationName As String, 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 String, _
   lpStartupInfo As STARTUPINFO, lpProcessInformation As _
   PROCESS_INFORMATION) As Long

'used by CreateProcess
Private 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

Private Type PROCESS_INFORMATION
   hProcess As Long
   hThread As Long
   dwProcessID As Long
   dwThreadID As Long
End Type

Const NORMAL_PRIORITY_CLASS = &H20&
Const INFINITE = -1&

Public Function ExecSynchronousCmd(cmdline As String) As Long

    ' - Used to force a shelled command to run synchronously (code will
    '   suspend where this function is called until shelled process
    '   returns a return value)
    ' - There is no time out - it will wait forever!!
    ' - Function returns exit value for shelled process

    Dim proc As PROCESS_INFORMATION
    Dim start As STARTUPINFO
    Dim ret As Long

    'Initialize the STARTUPINFO structure:
    start.cb = Len(start)

    'Start the shelled application:
    ret = CreateProcessA(vbNullString, cmdline$, 0&, 0&, 1&, _
        NORMAL_PRIORITY_CLASS, 0&, vbNullString, start, proc)

    'Wait for the shelled application to finish:
    ret = WaitForSingleObject(proc.hProcess, INFINITE)
    Call GetExitCodeProcess(proc.hProcess, ret&)
    Call CloseHandle(proc.hThread)
    Call CloseHandle(proc.hProcess)
    ExecSynchronousCmd = ret

End Function

这是运行pg_dump.exe的实现,它在运行exe服务和创建数据库备份文件时是成功的:

i = ExecSynchronousCmd(Chr$(34) & "C:\Program Files (x86)\PostgreSQL\9.3\bin\pg_dump.exe" & Chr$(34) & _
                " -Ft " & _
                " -f " & Chr$(34) & tempName & Chr$(34) & _
                " -U " & s1 & _
                " -h " & s3 & _
                " -p " & s4 & _
                " " & sDB(0, x))

这是一个类似的实现,它试图运行将尝试打开相关文件的辅助exe:

i = ExecSynchronousCmd(Chr$(34) & "C:\Program Files (x86)\GranDocsNP\GDNPOpener.exe" & Chr$(34))

当应用程序作为服务运行时,上述代码不起作用。为什么pg_dump.exe成功运行但我自己的GDNPOpener.exe不是?

正如我上面所说,我也尝试使用ShellExecute和ShellExecuteEx直接从服务打开文件,但这不起作用。 (我在二级exe(GDNPOpener.exe)中使用ShellExecuteEx打开文件)

如果有人知道如何修复我的exe以便我的服务运行它,我将非常感谢帮助!如果有人知道从服务中打开文件的任何其他方法,那也将不胜感激,谢谢!

1 个答案:

答案 0 :(得分:4)

您不能简单地在作为服务运行的进程的上下文中调用Shellexecute()。即使它成功(从未真正测试过),启动的应用程序/文档仍然不会在登录用户的桌面上显示,因为它们是两个不同的会话,彼此隔离(除非你是'在Windows XP或Windows Server 2003上;对于那些,服务和控制台会话应用程序在会话0中运行,但仍需要将服务标记为交互式以与桌面交互。

请参阅http://blogs.technet.com/b/askperf/archive/2007/04/27/application-compatibility-session-0-isolation.aspx或搜索"会话0隔离"有关此概念的更多详情。

要解决此问题,您主要有两种选择:(**如果我错过了一个,请随时纠正我!)

  1. 使用客户端/服务器路由;

    e.g。您可以编写一个将在用户空间中运行的小实用程序(例如,在会话启动时启动),并通过某些选择的进程间通信(IPC;命名管道,映射内存等)与您的服务进行通信。然后,您的服务可以要求该实用程序在登录用户的上下文中打开文件(例如,它将是调用ShellExecute()的实用程序。)

    要知道从哪里开始,请查看为VB6编写的进程间通信的示例。

    重要:由于服务器(您的服务)和客户端(用户空间实用程序)将跨会话和不同的用户令牌进行通信,因此请注意有关使用的安全描述符和访问权限的说明在创建和访问通信渠道(命名管道等)时需要,以免让您的客户获得拒绝访问#34;错误。

    警告:此处适用有关特权升级风险的常见安全警告。既然你基本上打开了一扇门"在你的服务中,要特别注意不要让任何流氓应用程序冒充你的客户端/实用程序来控制你的服务(缓冲区溢出等)或让它做一些它不应该做的事情。

    < / LI>
  2. 利用专用API启动不同会话的流程;

    取决于(1)您的服务是在LocalSystem帐户还是其他管理帐户下运行; (2)是否要在登录用户的上下文中启动文档/应用程序,或者您不关心创建新会话,以及(3)您是否拥有用户的凭据(用户+密码),存在一些API,允许服务与桌面通信或在另一个用户的上下文中启动应用程序。

    查看API CreateProcessAsUser()CreateProcessWithLogonW()WTSQueryUserToken()及相关API,或使用它们搜索示例。此外,以下文章可能是一个很好的阅读:Launching an interactive process from Windows Service in Windows Vista and later

  3. 希望这能回答你的问题!至少,它应该指出你下一步该做什么。

相关问题