0

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

这些解决方案在应用程序正常运行时都可以工作,但在作为服务运行时则不行。应用程序能够在作为服务运行时直接或间接打开文件非常重要,它只需要能够触发它即可。

我了解自 Windows Vista 以来 Windows 已锁定服务与桌面交互的能力,但我确信必须有一种方法可以从服务触发文件打开命令。我开发的应用程序能够使用 CreateProcess 从命令行运行 pg_dump.exe(postgres 数据库的备份可执行文件)来备份数据库文件,同时作为服务运行。这就是为什么我虽然从服务中启动一个 exe 来间接打开文件可能会起作用。但是,由于某种原因,该应用程序可以正常运行 pg_dump.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 以便我的服务运行它,我将不胜感激!如果有人知道从服务打开文件的任何替代方法,那也将不胜感激,谢谢!

4

1 回答 1

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. 使用客户端/服务器路由;

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

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

    重要提示:由于服务器(您的服务)和客户端(用户空间实用程序)将跨会话并在不同的用户令牌下进行通信,因此请注意有关使用的安全描述符和创建和访问通信通道(命名管道)时所需的访问权限的注释等),以免您的客户收到“拒绝访问”错误。

    警告:关于权限提升风险的常见安全警告适用于此处。由于您基本上为您的服务“打开了一扇门”,因此请特别注意不要让任何冒充您的客户端/实用程序的流氓应用程序控制您的服务(缓冲区溢出等)或让它做一些不应该做的事情.

  2. 利用专用 API 跨不同会话启动进程;

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

    查看 API CreateProcessAsUser()CreateProcessWithLogonW()WTSQueryUserToken()和相关的,或搜索使用它们的示例。此外,以下文章可能是一个很好的阅读:在 Windows Vista 和更高版本中从 Windows 服务启动交互式进程

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

于 2015-01-13T01:04:11.983 回答