簡體   English   中英

從Windows服務打印時,CreateProcessAsUser不使用LogonUser令牌

[英]CreateProcessAsUser not working with LogonUser token when printing from a Windows Service

我正在LocalSystem帳戶下構建一個Windows服務,它將在某個時間間隔內打印pdf。

為此,我使用LogonUser創建用戶令牌

IntPtr currentToken = IntPtr.Zero;
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_PROVIDER_DEFAULT = 0;
bool loggedOn = LogonUser(user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref currentToken);

並在CreateProcessAsUser傳遞此currentToken

CreateProcessAsUser(primaryToken, null, command, ref Security1, ref Security2, false, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, null, ref StartupInfo, out processInfo_);

但它沒有做到這一點

如果我使用以下代碼獲取當前用戶令牌。

public static IntPtr GetCurrentUserToken()
{
    IntPtr currentToken = IntPtr.Zero;
    IntPtr primaryToken = IntPtr.Zero;
    IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;

    int dwSessionId = 0;
    IntPtr hUserToken = IntPtr.Zero;
    IntPtr hTokenDup = IntPtr.Zero;

    IntPtr pSessionInfo = IntPtr.Zero;
    int dwCount = 0;

    WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount);

    Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));

    Int32 current = (int)pSessionInfo;
    for (int i = 0; i < dwCount; i++)
    {
        WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
        if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State)
        {
            dwSessionId = si.SessionID;
            break;
        }

        current += dataSize;
    }

    WTSFreeMemory(pSessionInfo);

    bool bRet = WTSQueryUserToken(dwSessionId, out currentToken);
    if (bRet == false)
    {
        return IntPtr.Zero;
    }

    bRet = DuplicateTokenEx(currentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);
    if (bRet == false)
    {
        return IntPtr.Zero;
    }

    return primaryToken;
}

然后CreateProcessAsUser works fine. But I need to create token by CreateProcessAsUser works fine. But I need to create token by LogonUser CreateProcessAsUser works fine. But I need to create token by because after user logoff GetCurrentUserToken`方法沒有返回用戶令牌,我也想在注銷后獲得用戶令牌。

更新我在調用CreateProcessAsUser之后檢查上一個錯誤

uint exitCode; 

if (!GetExitCodeProcess(processInfo_.hProcess, out exitCode)) 
{ 
    int lastError = Marshal.GetLastWin32Error(); 
    Logger.LogService(" GetExitCodeProcess Error " + lastError); 
} 

但GetExitCodeProcess返回true。 我沒有發現任何錯誤

首先

與上次發布此問題( 從Windows服務打印Pdf並在注銷后繼續工作 )一樣,您需要了解並解決特定API調用失敗的問題,然后調用GetLastError ,它將為您提供有關調用失敗原因的更多信息

GetLastError :檢索調用線程的最后錯誤代碼值

其次

可能是GetCurrentUserToken中的一個調用,例如WTSQueryUserToken可能只有一個權限問題或其他可以修復的東西(盡管我對此表示懷疑)

閱讀WTSQueryUserToken的文檔,似乎說明如下

WTSQueryUserToken :獲取會話ID指定的登錄用戶的主要訪問令牌。

此外, WTSQueryUserToken可能正在返回

ERROR_NO_TOKEN
1008

ERROR_NO_TOKEN :令牌查詢用於沒有用戶登錄的會話。 例如,當會話處於空閑狀態或SessionId為零時,會發生這種情況。

第三

在我看來,我認為這種方法不會在您的情況下發揮作用,並且實際上不建議正式推出服務打印。

請看看類似的問題

從Windows服務打印

從.NET服務打印

最后

我唯一能想到的是,在用戶帳戶下運行您的服務,可以訪問打印機或通過調用LogonUserLoadUserProfileImpersonateLoggedOnUser用戶,然后通過Gdi +打印(這有其自身的問題)

請注意

雖然可以通過獲取打印機的設備上下文句柄然后將該句柄傳遞給GDI + Graphics構造函數來將GDI +輸出發送到打印機,但不建議這樣做。 在Windows服務中不支持GDI +函​​數和類。 嘗試從Windows服務使用這些函數和類可能會產生意外問題,例如服務性能下降和運行時異常或錯誤:


簡而言之,您現在的解決方案和您目前擁有的源代碼不太可行,而且在我看來,您很可能會花很多時間在這個問題上嘗試做某些服務真的不是為了做(這也超出了當前問題的范圍)。

我真的認為你需要認真重新考慮你想做什么以及為什么,祝你好運

有關GDI打印和從服務打印的其他信息

如何安裝應該可以從Windows服務訪問的打印機?

從Windows服務打印

您沒有提供有關打印過程的任何信息,但是根據您提供的信息,我想它必須在交互式會話下運行。 你的第一種方式不起作用,因為LogonUser的token provede與調用進程的會話ID相同,而且你的第二種方式在注銷后無效,因為WTSQueryUserToken會因為它的假設而失敗。 所以我建議你嘗試winlogon解決方案,可以概括為查找交互式會話的winlogon進程,復制其令牌並使用令牌創建打印過程。 這篇文章包含了所有細節。

從鏈接頁面中大肆竊取。

通常,調用CreateProcessAsUser函數的進程必須具有SE_INCREASE_QUOTA_NAME。

LocalSystem帳戶具有以下權限:... SE_INCREASE_QUOTA_NAME(已禁用)因此,我很驚訝CreateProcessAsUser在LocalSystem上下文中工作。

CreateProcessAsUser不會將指定用戶的配置文件加載到HKEY_USERS注冊表項中。 因此,要訪問HKEY_CURRENT_USER注冊表項中的信息,必須在調用CreateProcessAsUser之前使用LoadUserProfile函數將用戶的配置文件信息加載到HKEY_USERS中。 在新進程退出后,請務必調用UnloadUserProfile。

CreateProcessAsUser似乎比CreateProcessWithLogonW更挑剔。 我會嘗試使用LOGON_WITH_PROFILE參數創建CreateProcessWithLogonW

您可以在代碼中的令牌上調用DuplicateTokenEx。 您可能希望使用LogonUser令牌嘗試此操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM