0

我一直在爲這個問題奮鬥了近一個星期。我需要一個作爲服務運行來打開文件的vb6應用程序。我不需要對文件做任何事情,我只需要打開它。我嘗試使用ShellExecute和ShellExecuteEx以及使用CreateProcess嘗試從命令行啓動文件。當這些實現都沒有工作時,我嘗試啓動另一個應用程序(使用CreateProcess),只打開文件然後關閉自己。VB6 - 如何從作爲服務運行的應用程序打開文件

這些解決方案在應用程序正常運行時都可以工作,但當它作爲服務運行時,這些解決方案都不工作。應用程序能夠在作爲服務運行時直接或間接地打開文件,這非常重要,只需要能夠觸發它即可。

據我所知,Windows已經鎖定了自Windows Vista以來服務與桌面交互的能力,但我確定必須有一種方法來從服務中觸發文件打開命令。我開發的應用程序能夠通過CreateProcess的命令行運行pg_dump.exe(postgres數據庫的備份可執行文件),以便在作爲服務運行時備份數據庫文件。這就是爲什麼我從服務啓動一個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直接從服務打開文件,這沒有奏效。 (我正在使用ShellExecuteEx在輔助EXE(GDNPOpener.exe)內打開文件)

如果有人知道如何修復我的exe文件,以便我的服務能夠運行它,我將非常感謝幫助!如果有人知道從服務中打開文件的其他方法,那也會很感激,謝謝!

+0

如果我理解正確,GNDPOpener.exe是您自己的代碼,就像服務一樣。他們爲什麼分開申請?你爲什麼不直接把'GNDPOpener.exe'中的代碼直接放入服務中? –

+0

因爲服務不想直接打開文件。所以我想如果我得到服務來啓動另一個應用程序,也許該應用程序將能夠打開該文件,因爲它不作爲服務運行,並且不會受到相同的限制。然而,服務不僅無法打開文件,它也無法啓動輔助應用程序(這很奇怪,因爲正如我在我的原始問題中所述,似乎可以啓動pg_dump.exe) – user2437443

+1

定義「打開一個文件「,因爲我認爲你的意思與真正含義完全不同。我的猜測是,你正在嘗試運行一些其他程序,如讀取文件並將其顯示在另一個會話(可能是會話1)中的某個用戶桌面上,而你不能這樣做。否則,如果您真的認爲* open *,那麼您遇到了文件安全問題。 – Bob77

回答

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()和相關的API,或搜索使用它們的示例。此外,下面的文章可能是一個很好的閱讀:Launching an interactive process from Windows Service in Windows Vista and later

希望這個答案您的問題!至少,它應該給你指示接下來要做什麼。

+0

問題中沒有跡象表明OP希望應用程序在登錄用戶的桌面上可見,或者甚至在應用程序啓動時必然會有任何人登錄。 –

+0

我不同意,我認爲這就是他想要做的。至少,他正試圖讓操作系統運行與服務中的文件類型相關的程序。只要他在登錄用戶的上下文中部署自動運行的第二個可執行文件來捕獲通知並執行命令,簡單IPC就可以很好地解決此問題。 – tcarvin

相關問題