2012-10-30 155 views
1

我有一個使用Delphi 7創建的Windows服務,使用StartType = stSystem。LogonUser + CreateProcessAsUser at Service = error 1314

現在我需要啓動一個應用程序來爲我做一些事情。 此應用程序有一個MainForm和其他GDI資源。 傳遞給應用程序的參數對於某些控件賦值(如編輯和記事),然後點擊一個按鈕....

我想這一點:

var 
    token: cardinal; 
    si: TStartupInfo; 
    pi: TProcessInformation; 
begin 
    if not LogonUser('admintest', '', 'secret123', LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) then 
    RaiseLastOSError; 

    try 
    if not ImpersonateLoggedOnUser(token) then 
     RaiseLastOSError; 

    fillchar(si, sizeof(si), 0); 
    si.cb := sizeof(si); 
    si.lpDesktop := PChar('winsta0\default'); 
    if not CreateProcessAsUser(token, nil, '"c:\...\myapp.exe" -doCrazyThings', nil, nil, false, NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE, nil, nil, si, pi) then 
     RaiseLastOSError; 

    CloseHandle(pi.hThread); 
    waitForSingleObject(pi.hProcess, INFINITE); 
    CloseHandle(pi.hProcess); 
    finally 
    CLoseHandle(token); 
    end; 
end; 

當我跑我的服務可執行文件作爲普通應用程序(-noservice),它將作爲Forms.Application啓動並創建一個帶有「開始」按鈕的MainForm。 * 該按鈕運行與服務運行相同的代碼,但它不起作用,並且它正在createprocessasuser中傳遞錯誤代碼1314。 *

爲什麼? SYSTEM服務和管理員啓動的正常應用程序有什麼不同?

我的環境是Windows 7專業版64位

我在做什麼錯? 我該如何解決這個問題? 有人可以發表一個例子嗎?

+1

[CreateProcessAsUser error 1314](http://stackoverflow.com/questions/1475577/createprocessasuser-error-1314) –

+0

[閱讀文檔](http://msdn.microsoft.com/en-us /library/windows/desktop/ms682429.aspx)。您用於以noservice模式運行應用程序的用戶帳戶可能沒有必要的權限,但在服務模式下運行應用程序時,SYSTEM帳戶確實具有權限。 –

+0

如果您正在運行請求LOCALSYSTEM,那麼您不需要LogonUser和明文密碼。如果你在桌面上運行,你可以簡單地調用CreateProcess。 –

回答

3

錯誤1314是ERROR_PRIVILEGE_NOT_HELD,這意味着您的調用線程缺少運行CreateProcessAsUser()所需的特權。您不需要也不應該模擬用戶令牌,以便在用戶的桌面上啓動新的進程。撥打CreateProcessAsUser()時,您應讓線程使用服務的憑證,而不是用戶的憑證。它將確保新過程在您的用戶帳戶和桌面內部運行,因此請撥打ImpersonateLoggedOnUser()並查看CreateProcessAsUser()是否開始工作。

更新read the documentation

通常情況下,調用CreateProcessAsUser函數必須具有SE_INCREASE_QUOTA_NAME特權,可能需要SE_ASSIGNPRIMARYTOKEN_NAME特權如果令牌不可轉讓的過程。如果此函數因ERROR_PRIVILEGE_NOT_HELD(1314)而失敗,請改用CreateProcessWithLogonW函數。 CreateProcessWithLogonW不需要特殊權限,但必須允許指定的用戶帳戶以交互方式登錄。通常,最好使用CreateProcessWithLogonW創建具有備用憑證的進程。

+0

我已經調用ImpersonateLoggedOnUser()... –

+0

我知道,我說你應該刪除**調用ImpersonateLoggedOnUser()。在服務中調用'CreateProcessAsUser()'時,您不應該模擬用戶。 –

+0

工作!我試圖將代碼作爲程序運行而不是服務。我將重新制定我的問題... –

1

在Vista和更高版本上,Windows服務在會話0中運行。交互式用戶存在於會話1和以上。這意味着Windows服務無法顯示用戶界面,並確實無法輕鬆地在交互式會話中啓動進程。

現在,有辦法launch interactive processes from a service。如果您不願意從您的服務中啓動互動過程,那麼該文章會告訴您所有您需要知道的信息。但是這樣的技術非常棘手,絕對不推薦。建議您在服務和交互式桌面之間找到​​不同的溝通方式。

正常的方法是運行標準的桌面應用程序,也許開始使用HKLM\Software\Microsoft\Windows\CurrentVersion\Run。並使用某種形式的IPC在桌面應用程序和服務之間進行通信。

+0

「windows服務無法......在交互式會話中啓動進程」這是完全不正確的。這正是在服務中使用CreateProcessAsUser()的原因。我有幾個使用它的服務,如果使用正確,它工作得很好。即使微軟的會話0隔離文檔也是如此。 –

+0

@Remy是的,如果你碰巧有純文本的用戶密碼是真的。請給我看看建議會話0服務啓動交互過程的MS文檔的一部分。 –

+0

確定雷米,你可以發表你是如何做到這一點?謝謝! –

1

您應該創建服務來完成後臺工作,而GUI應用程序只應該調用該服務。 IE你總是有一個服務運行。

考慮在後端使用DataSnap的東西。 Delphi中的MVC方法並非純粹的,就像其他語言一樣。控制器可以在任何方便的地方使用。數據集大多是妥協,而唯一真正快速的數據處理方式是使用DBexpress和客戶端上的一些組件來保持它的所有快樂。但它有效,值得學習。

服務不能有gui控件。沒有TForm後代允許。僅TService。德爾福項目/服務應用程序下的新項目。你得到一個與數據模塊幾乎相同的單元/表單的項目。即不允許視覺控制。原因很明顯。你需要學習記錄,對象設計等來使用服務。 Datasnap是你最好的選擇。它爲你做了大部分艱苦的工作。

鮑勃博士在XE2/3上有一本相當不錯的書。如果您是面向對象設計的新手,則需要先徹底學習。

0

這裏是我用來做這種事情的代碼

procedure CreateNewProcess; 
var 
    CmdLine: AnsiString; 
    ErrorCode: Cardinal; 
    ConnSessID: Cardinal; 
    Token: Cardinal; 
    App: AnsiString; 
    FProcessInfo: _PROCESS_INFORMATION; 
    FStartupInfo: _STARTUPINFOA; 
begin 
    ZeroMemory(@FStartupInfo, SizeOf(FStartupInfo)); 
    FStartupInfo.cb := SizeOf(FStartupInfo); 
    FStartupInfo.lpDesktop := nil; 

    ConnSessID := WTSGetActiveConsoleSessionId; 

    if WTSQueryUserToken(ConnSessID, Token) then 
    begin 
    if CreateProcessAsUser(Token, PAnsiChar(App), PAnsiChar(CmdLine), 
     nil, nil, false, 0, nil, nil, FStartupInfo, FProcessInfo) = False 
    then 
    begin 
     ErrorCode := GetLastError; 
     try 
     RaiseLastOSError(ErrorCode); 
     except on E: Exception do 
     EventLog.LogError(e.ClassName +': '+ e.Message); 
     end; 
    end; 
    end; 
end; 

希望這有助於

,如果你希望服務等待新的進程繼續添加此

之前終止
if CreateProcessAsUser(Token, PAnsiChar(App), PAnsiChar(CmdLine), 
     nil, nil, false, 0, nil, nil, FStartupInfo, FProcessInfo) = False 
    then 
    begin 
     ErrorCode := GetLastError; 
     try 
     RaiseLastOSError(ErrorCode); 
     except on E: Exception do 
     EventLog.LogError(e.ClassName +': '+ e.Message); 
     end; 
    end 
    else 
     WaitForSingleObject(FProcessInfo.hProcess, INFINITE); 

雖然和無限的等待可能是不可取的。

相關問題