【问题标题】:Using Process.Start() to start a process as a different user from within a Windows Service使用 Process.Start() 从 Windows 服务中以不同用户身份启动进程
【发布时间】:2010-09-26 14:52:25
【问题描述】:

我想定期在 Windows 服务的指定用户帐户下运行任意 .NET exe。

到目前为止,我已经让我的 Windows 服务使用逻辑来决定目标进程是什么以及何时运行它。 目标进程的启动方式如下:

  1. Windows 服务使用“管理员”凭据启动。
  2. 时机成熟时,将执行一个中间 .NET 进程,其中包含详细说明应启动哪个进程的参数(文件名、用户名、域、密码)。
  3. 此进程创建一个新的 System.Diagnostics.Process,关联一个填充了传递给它的参数的 ProcessStartInfo 对象,然后在进程对象上调用 Start()。

第一次发生这种情况,目标进程执行良好,然后正常关闭。然而,每次后续的时间,一旦目标进程启动,它就会抛出错误“应用程序无法正确初始化(0xc0000142)”。重新启动 Windows 服务将使进程再次成功运行(第一次执行)。

当然,目标是让目标进程每次都能成功执行。

关于上面的第 2 步:要以不同的用户身份运行进程 .NET 调用 win32 函数 CreateProcessWithLogonW。此功能需要一个窗口句柄来登录指定的用户。由于 Windows 服务未在交互模式下运行,因此它没有窗口句柄。这个中间进程解决了这个问题,因为它有一个可以传递给目标进程的窗口句柄。

请不要建议使用 psexec 或 windows 任务规划器。我已经接受了我的生活,这包括以上述方式解决问题。

【问题讨论】:

  • 请告诉我们最后发生了什么,当你关闭时
  • 是的 - 目前还没有解决,但我是 :) 我一直在度假,直到新年。

标签: c# .net windows-services process service


【解决方案1】:

我似乎有一个适用于以下场景的有效实现(Works On My Machine(TM)):

批处理文件、.NET 控制台程序集、.NET Windows 窗体应用程序。

方法如下:

我有一个以管理员用户身份运行的 Windows 服务。 我将以下策略添加到管理员用户:

  • 作为服务登录
  • 作为操作系统的一部分
  • 调整进程的内存配额
  • 替换进程级令牌

这些策略可以通过打开控制面板/管理工具/本地安全策略/用户权限分配来添加。设置后,策略在下次登录之前不会生效。您可以使用其他用户而不是管理员,这可能会使事情更安全:)

现在,我的 Windows 服务具有以其他用户身份启动作业所需的权限。当需要启动作业时,服务会执行一个单独的程序集(“Starter”.NET 控制台程序集),它会为我启动该过程。

以下代码位于 Windows 服务中,执行我的“Starter”控制台程序集:

Process proc = null;
System.Diagnostics.ProcessStartInfo info;
string domain = string.IsNullOrEmpty(row.Domain) ? "." : row.Domain;
info = new ProcessStartInfo("Starter.exe");
info.Arguments = cmd + " " + domain + " " + username + " " + password + " " + args;
info.WorkingDirectory = Path.GetDirectoryName(cmd);
info.UseShellExecute = false;
info.RedirectStandardError = true;
info.RedirectStandardOutput = true;
proc = System.Diagnostics.Process.Start(info);

然后控制台程序集通过互操作调用启动目标进程:

class Program
{
    #region Interop

    [StructLayout(LayoutKind.Sequential)]
    public struct LUID
    {
        public UInt32 LowPart;
        public Int32 HighPart;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct LUID_AND_ATTRIBUTES
    {
        public LUID Luid;
        public UInt32 Attributes;
    }

    public struct TOKEN_PRIVILEGES
    {
        public UInt32 PrivilegeCount;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public LUID_AND_ATTRIBUTES[] Privileges;
    }

    enum TOKEN_INFORMATION_CLASS
    {
        TokenUser = 1,
        TokenGroups,
        TokenPrivileges,
        TokenOwner,
        TokenPrimaryGroup,
        TokenDefaultDacl,
        TokenSource,
        TokenType,
        TokenImpersonationLevel,
        TokenStatistics,
        TokenRestrictedSids,
        TokenSessionId,
        TokenGroupsAndPrivileges,
        TokenSessionReference,
        TokenSandBoxInert,
        TokenAuditPolicy,
        TokenOrigin,
        TokenElevationType,
        TokenLinkedToken,
        TokenElevation,
        TokenHasRestrictions,
        TokenAccessInformation,
        TokenVirtualizationAllowed,
        TokenVirtualizationEnabled,
        TokenIntegrityLevel,
        TokenUIAccess,
        TokenMandatoryPolicy,
        TokenLogonSid,
        MaxTokenInfoClass
    }

    [Flags]
    enum CreationFlags : uint
    {
        CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
        CREATE_DEFAULT_ERROR_MODE = 0x04000000,
        CREATE_NEW_CONSOLE = 0x00000010,
        CREATE_NEW_PROCESS_GROUP = 0x00000200,
        CREATE_NO_WINDOW = 0x08000000,
        CREATE_PROTECTED_PROCESS = 0x00040000,
        CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
        CREATE_SEPARATE_WOW_VDM = 0x00001000,
        CREATE_SUSPENDED = 0x00000004,
        CREATE_UNICODE_ENVIRONMENT = 0x00000400,
        DEBUG_ONLY_THIS_PROCESS = 0x00000002,
        DEBUG_PROCESS = 0x00000001,
        DETACHED_PROCESS = 0x00000008,
        EXTENDED_STARTUPINFO_PRESENT = 0x00080000
    }

    public enum TOKEN_TYPE
    {
        TokenPrimary = 1,
        TokenImpersonation
    }

    public enum SECURITY_IMPERSONATION_LEVEL
    {
        SecurityAnonymous,
        SecurityIdentification,
        SecurityImpersonation,
        SecurityDelegation
    }

    [Flags]
    enum LogonFlags
    {
        LOGON_NETCREDENTIALS_ONLY = 2,
        LOGON_WITH_PROFILE = 1
    }

    enum LOGON_TYPE
    {
        LOGON32_LOGON_INTERACTIVE = 2,
        LOGON32_LOGON_NETWORK,
        LOGON32_LOGON_BATCH,
        LOGON32_LOGON_SERVICE,
        LOGON32_LOGON_UNLOCK = 7,
        LOGON32_LOGON_NETWORK_CLEARTEXT,
        LOGON32_LOGON_NEW_CREDENTIALS
    }

    enum LOGON_PROVIDER
    {
        LOGON32_PROVIDER_DEFAULT,
        LOGON32_PROVIDER_WINNT35,
        LOGON32_PROVIDER_WINNT40,
        LOGON32_PROVIDER_WINNT50
    }

    #region _SECURITY_ATTRIBUTES
    //typedef struct _SECURITY_ATTRIBUTES {  
    //    DWORD nLength;  
    //    LPVOID lpSecurityDescriptor;  
    //    BOOL bInheritHandle;
    //} SECURITY_ATTRIBUTES,  *PSECURITY_ATTRIBUTES,  *LPSECURITY_ATTRIBUTES;
    #endregion
    struct SECURITY_ATTRIBUTES
    {
        public uint Length;
        public IntPtr SecurityDescriptor;
        public bool InheritHandle;
    }

    [Flags] enum SECURITY_INFORMATION : uint
    {
        OWNER_SECURITY_INFORMATION        = 0x00000001,
        GROUP_SECURITY_INFORMATION        = 0x00000002,
        DACL_SECURITY_INFORMATION         = 0x00000004,
        SACL_SECURITY_INFORMATION         = 0x00000008,
        UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000,
        UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
        PROTECTED_SACL_SECURITY_INFORMATION   = 0x40000000,
        PROTECTED_DACL_SECURITY_INFORMATION   = 0x80000000
    }

    #region _SECURITY_DESCRIPTOR
    //typedef struct _SECURITY_DESCRIPTOR {
    //  UCHAR  Revision;
    //  UCHAR  Sbz1;
    //  SECURITY_DESCRIPTOR_CONTROL  Control;
    //  PSID  Owner;
    //  PSID  Group;
    //  PACL  Sacl;
    //  PACL  Dacl;
    //} SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
    #endregion
    [StructLayoutAttribute(LayoutKind.Sequential)]
    struct SECURITY_DESCRIPTOR
    {
        public byte revision;
        public byte size;
        public short control; // public SECURITY_DESCRIPTOR_CONTROL control;
        public IntPtr owner;
        public IntPtr group;
        public IntPtr sacl;
        public IntPtr dacl;
    }

    #region _STARTUPINFO
    //typedef struct _STARTUPINFO {  
    //    DWORD cb;  
    //    LPTSTR lpReserved;  
    //    LPTSTR lpDesktop;  
    //    LPTSTR lpTitle;  
    //    DWORD dwX;  
    //    DWORD dwY;  
    //    DWORD dwXSize;  
    //    DWORD dwYSize;  
    //    DWORD dwXCountChars;  
    //    DWORD dwYCountChars;  
    //    DWORD dwFillAttribute;  
    //    DWORD dwFlags;  
    //    WORD wShowWindow;  
    //    WORD cbReserved2;  
    //    LPBYTE lpReserved2;  
    //    HANDLE hStdInput;  
    //    HANDLE hStdOutput;  
    //    HANDLE hStdError; 
    //} STARTUPINFO,  *LPSTARTUPINFO;
    #endregion
    struct STARTUPINFO
    {
        public uint cb;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string Reserved;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string Desktop;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string Title;
        public uint X;
        public uint Y;
        public uint XSize;
        public uint YSize;
        public uint XCountChars;
        public uint YCountChars;
        public uint FillAttribute;
        public uint Flags;
        public ushort ShowWindow;
        public ushort Reserverd2;
        public byte bReserverd2;
        public IntPtr StdInput;
        public IntPtr StdOutput;
        public IntPtr StdError;
    }

    #region _PROCESS_INFORMATION
    //typedef struct _PROCESS_INFORMATION {  
    //  HANDLE hProcess;  
    //  HANDLE hThread;  
    //  DWORD dwProcessId;  
    //  DWORD dwThreadId; } 
    //  PROCESS_INFORMATION,  *LPPROCESS_INFORMATION;
    #endregion
    [StructLayout(LayoutKind.Sequential)]
    struct PROCESS_INFORMATION
    {
        public IntPtr Process;
        public IntPtr Thread;
        public uint ProcessId;
        public uint ThreadId;
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool InitializeSecurityDescriptor(IntPtr pSecurityDescriptor, uint dwRevision);
    const uint SECURITY_DESCRIPTOR_REVISION = 1;

    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool SetSecurityDescriptorDacl(ref SECURITY_DESCRIPTOR sd, bool daclPresent, IntPtr dacl, bool daclDefaulted);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    extern static bool DuplicateTokenEx(
        IntPtr hExistingToken,
        uint dwDesiredAccess,
        ref SECURITY_ATTRIBUTES lpTokenAttributes,
        SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
        TOKEN_TYPE TokenType,
        out IntPtr phNewToken);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool LogonUser(
        string lpszUsername,
        string lpszDomain,
        string lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        out IntPtr phToken
        );

    #region GetTokenInformation
    //BOOL WINAPI GetTokenInformation(
    //  __in       HANDLE TokenHandle,
    //  __in       TOKEN_INFORMATION_CLASS TokenInformationClass,
    //  __out_opt  LPVOID TokenInformation,
    //  __in       DWORD TokenInformationLength,
    //  __out      PDWORD ReturnLength
    //);
    #endregion
    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool GetTokenInformation(
        IntPtr TokenHandle,
        TOKEN_INFORMATION_CLASS TokenInformationClass,
        IntPtr TokenInformation,
        int TokenInformationLength,
        out int ReturnLength
        );


    #region CreateProcessAsUser
    //        BOOL WINAPI CreateProcessAsUser(
    //  __in_opt     HANDLE hToken,
    //  __in_opt     LPCTSTR lpApplicationName,
    //  __inout_opt  LPTSTR lpCommandLine,
    //  __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,
    //  __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,
    //  __in         BOOL bInheritHandles,
    //  __in         DWORD dwCreationFlags,
    //  __in_opt     LPVOID lpEnvironment,
    //  __in_opt     LPCTSTR lpCurrentDirectory,
    //  __in         LPSTARTUPINFO lpStartupInfo,
    //  __out        LPPROCESS_INFORMATION lpProcessInformation);
    #endregion
    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool CreateProcessAsUser(
        IntPtr Token, 
        [MarshalAs(UnmanagedType.LPTStr)] string ApplicationName,
        [MarshalAs(UnmanagedType.LPTStr)] string CommandLine,
        ref SECURITY_ATTRIBUTES ProcessAttributes, 
        ref SECURITY_ATTRIBUTES ThreadAttributes, 
        bool InheritHandles,
        uint CreationFlags, 
        IntPtr Environment, 
        [MarshalAs(UnmanagedType.LPTStr)] string CurrentDirectory, 
        ref STARTUPINFO StartupInfo, 
        out PROCESS_INFORMATION ProcessInformation);

    #region CloseHandle
    //BOOL WINAPI CloseHandle(
    //      __in          HANDLE hObject
    //        );
    #endregion
    [DllImport("Kernel32.dll")]
    extern static int CloseHandle(IntPtr handle);

    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);

    [DllImport("advapi32.dll", SetLastError = true)]
    internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct TokPriv1Luid
    {
        public int Count;
        public long Luid;
        public int Attr;
    }

    //static internal const int TOKEN_QUERY = 0x00000008;
    internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
    //static internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;

    internal const int TOKEN_QUERY = 0x00000008;
    internal const int TOKEN_DUPLICATE = 0x0002;
    internal const int TOKEN_ASSIGN_PRIMARY = 0x0001;

    #endregion

    [STAThread]
    static void Main(string[] args)
    {
        string username, domain, password, applicationName;
        username = args[2];
        domain = args[1];
        password = args[3];
        applicationName = @args[0];

        IntPtr token = IntPtr.Zero;
        IntPtr primaryToken = IntPtr.Zero;
        try
        {
            bool result = false;

            result = LogonUser(username, domain, password, (int)LOGON_TYPE.LOGON32_LOGON_NETWORK, (int)LOGON_PROVIDER.LOGON32_PROVIDER_DEFAULT, out token);
            if (!result)
            {
                int winError = Marshal.GetLastWin32Error();
            }

            string commandLine = null;

            #region security attributes
            SECURITY_ATTRIBUTES processAttributes = new SECURITY_ATTRIBUTES();

            SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR();
            IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(sd));
            Marshal.StructureToPtr(sd, ptr, false);
            InitializeSecurityDescriptor(ptr, SECURITY_DESCRIPTOR_REVISION);
            sd = (SECURITY_DESCRIPTOR)Marshal.PtrToStructure(ptr, typeof(SECURITY_DESCRIPTOR));

            result = SetSecurityDescriptorDacl(ref sd, true, IntPtr.Zero, false);
            if (!result)
            {
                int winError = Marshal.GetLastWin32Error();
            }

            primaryToken = new IntPtr();
            result = DuplicateTokenEx(token, 0, ref processAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);
            if (!result)
            {
                int winError = Marshal.GetLastWin32Error();
            }
            processAttributes.SecurityDescriptor = ptr;
            processAttributes.Length = (uint)Marshal.SizeOf(sd);
            processAttributes.InheritHandle = true;
            #endregion

            SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES();
            threadAttributes.SecurityDescriptor = IntPtr.Zero;
            threadAttributes.Length = 0;
            threadAttributes.InheritHandle = false;

            bool inheritHandles = true;
            //CreationFlags creationFlags = CreationFlags.CREATE_DEFAULT_ERROR_MODE;
            IntPtr environment = IntPtr.Zero;
            string currentDirectory = currdir;

            STARTUPINFO startupInfo = new STARTUPINFO();
            startupInfo.Desktop = "";

            PROCESS_INFORMATION processInformation;

            result = CreateProcessAsUser(primaryToken, applicationName, commandLine, ref processAttributes, ref threadAttributes, inheritHandles, 16, environment, currentDirectory, ref startupInfo, out processInformation);
            if (!result)
            {
                int winError = Marshal.GetLastWin32Error();
                File.AppendAllText(logfile, DateTime.Now.ToLongTimeString() + " " + winError + Environment.NewLine);
            }
        }
        catch
        {
            int winError = Marshal.GetLastWin32Error();
            File.AppendAllText(logfile, DateTime.Now.ToLongTimeString() + " " + winError + Environment.NewLine);
        }
        finally
        {
            if (token != IntPtr.Zero)
            {
                int x = CloseHandle(token);
                if (x == 0)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                x = CloseHandle(primaryToken);
                if (x == 0)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
            }
        }
    }

基本流程是:

  1. 让用户登录
  2. 将给定令牌转换为主令牌
  3. 使用此令牌,执行流程
  4. 完成后关闭手柄。

这是我机器上的新开发代码,几乎无法在生产环境中使用。这里的代码仍然有问题 - 对于初学者:我不确定句柄是否在正确的位置关闭,并且上面定义的一些互操作函数不是必需的。单独的启动程序也让我很恼火。理想情况下,我希望将所有这些工作内容打包在一个程序集中,以供我们的 API 和此服务使用。如果有人在这里有任何建议,他们将不胜感激。

【讨论】:

  • 不确定安全性是否是一个问题,但 NULL-DACL 通常是一个非常糟糕的主意,安全方面。阅读SetSecurityDescriptorDacl的备注部分:msdn.microsoft.com/en-us/library/windows/desktop/…
  • 天哪,这确实有效。我已经为此奋斗了几个星期,放弃并回来,再次谷歌搜索。这是唯一让我从作为本地系统、本地服务或网络服务运行的 Windows 服务运行任意进程的程序,从 Windows 服务模拟其他用户
  • 是否需要中间“Starter”流程?我不立即明白为什么会这样。
  • 中间“入门”流程背后的初衷或想法是什么?你为什么把它作为工作流程的一部分?它是否绕过了您遇到的一些问题?
  • @Ryan 哇,这是很久以前的事了,但回顾上面的代码,我认为这与我不想/不能从服务中调用 LogonUser 的事实有关.
【解决方案2】:

我既不建议使用 psexec 也不建议使用任务规划器。但是,你看过Sudowin吗?

它几乎完全符合您的要求,除了在执行该过程之前要求输入密码。

此外,由于是开源的,您可以一次又一次地看到它如何从相关服务中执行流程。

【讨论】:

  • 根据您的建议,我现在正在浏览 sudowin 的 svn 存储库。似乎他们完全忽略了 Systme.Diagnostics.Process,并互操作 CreateProcessAsUser 而不是 CreateProcessWithLogonW。也许这就是诀窍......
  • 有完整源代码示例的最终解决方案吗?使用 sudowin ?
【解决方案3】:

只是一个猜测 - 您是否使用 LoadUserProfile=true 和开始信息? CreateProcessWithLogonW 默认不加载用户注册表配置单元,除非你告诉它。

【讨论】:

  • 我认为我不需要注册表或用户配置文件,但我刚刚尝试了您的建议,看看它是否有任何积极作用。不幸的是没有.. :(
  • 这当然只有在启动的应用程序需要访问用户配置单元时才相关。
  • 如上文原帖所述:目标进程执行成功ONCE。如果它可以执行一次,则意味着它可以访问所需的所有资源。
【解决方案4】:

第一次调用失败的原因很可能是因为它使用了“默认”安全描述符(不管是什么)。

来自msdn

lpProcessAttributes [输入,可选]

指向 SECURITY_ATTRIBUTES 的指针 指定证券的结构 新进程对象的描述符 并判断子进程是否 可以继承返回的句柄 过程。如果 lpProcessAttributes 是 NULL 或 lpSecurityDescriptor 为 NULL, 该进程获得默认安全性 描述符和句柄不能 遗传。默认安全 描述符是用户的 在 hToken 参数中引用。 此安全描述符可能不允许 调用者的访问权限,在这种情况下 该进程可能无法再次打开 运行后。 进程句柄 是有效的并将继续拥有 完全访问权限。

我猜 CreateProcessWithLogonW 正在创建这个默认的安全描述符(无论如何,我没有指定一个)。

是时候开始互操作了...

【讨论】:

    【解决方案5】:

    您不需要窗口句柄来使用 CreateProcessWithLogonW,我不确定您的信息来自哪里。

    应用程序初始化失败错误有很多原因,但几乎总是与安全性或用户资源耗尽有关。如果没有关于您正在运行的内容和正在运行的上下文的更多信息,很难诊断出这个问题。但要研究的是:提供的用户是否具有访问可执行文件目录的正确权限,是否用户有权访问正在启动它的窗口站和桌面,它是否对初始化时需要加载的任何 dll 文件具有正确的权限等。

    【讨论】:

    • 这不是安全问题。如果是这样,我根本无法成功启动目标进程。
    • 直接从服务启动目标进程并提供用户凭据总是会导致“无效句柄”异常。在不尝试更改凭据的情况下启动中间进程,然后从该中间进程启动目标进程,可以解决问题。
    • 您的服务使用什么帐户运行? “您不能从在 LocalSystem 帐户下运行的进程调用 CreateProcessWithLogonW”(请参阅​​ MSDN 上的 CreateProcessWithLogonW 函数)
    • 如果 Window Station 或 Desktop 存在安全问题,该进程将启动,然后在尝试进行 user32 初始化时中止。当发生其他安全错误时,我必须仔细检查。
    • @liggett78 如原帖所述:我目前以“管理员”身份运行该服务 - 而不是 LocalSystem
    【解决方案6】:

    我刚刚在 msdn (http://msdn.microsoft.com/en-us/library/ms682431(VS.85).aspx) 上阅读了此评论:

    不要用这个来调用用户应用程序 功能!克里斯蒂安维默 |
    编辑 |显示历史请稍候 如果你要调用用户模式 提供文件的应用程序 编辑和类似的东西(如 Word), 所有未保存的数据都将丢失。这是 因为通常的关机顺序 不适用于启动的进程 使用 CreateProcessWithLogonW。在这个 方式启动的应用程序不 获取 WM_QUERYENDSESSION、WM_ENDSESSION 和最重要的 WM_QUIT 信息。所以他们不要求储蓄 数据或清理他们的东西。他们 将在没有警告的情况下退出。这 功能不是用户友好的,并且 应谨慎使用。

    这只是“糟糕的用户体验”。 没有人期望它。

    这可以解释我观察到的情况:第一次工作。以后每次都失败。这加强了我的信念,即内部没有正确清理某些东西

    【讨论】:

      【解决方案7】:

      您说“Windows 服务是使用“管理员”凭据启动的”

      您是指实际的“管理员”帐户,还是“管理员”组中的用户?以管理员身份启动服务为我解决了这个问题。

      【讨论】:

      • 您可能有一个问题,您的管理员用户必须始终登录,是吗?
      【解决方案8】:

      当我尝试在 Windows 服务中使用“runas”-verb 启动 PhantomJS 二进制文件时,我遇到了类似的问题。我现在已经使用以下程序解决了这个问题:

      • 模拟用户
      • 启动进程(无 UI)
      • 模仿回来

      您可以使用Impersonator-Class 进行模拟。在 ProcessStartInfo 中设置以下属性也很重要,因此应用程序不会尝试访问 Windows UI:

      var processStartInfo = new ProcessStartInfo()
      {
          FileName = $@"{assemblyFolder}\PhantomJS\phantomjs.exe",
          Arguments = $"--webdriver={port}",
          RedirectStandardOutput = true,
          RedirectStandardError = true,
          RedirectStandardInput = true,
          UseShellExecute = false,
          CreateNoWindow = true,
          ErrorDialog = false,
          WindowStyle = ProcessWindowStyle.Hidden
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-11
        • 1970-01-01
        相关资源
        最近更新 更多