使用Process.Start()以Windows服务中的不同用户身份启动进程

时间:2022-05-01 22:48:03

I'd like to periodically run an arbitrary .NET exe under a specified user account from a Windows Service.

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

So far I've got my windows service running with logic to decide what the target process is, and when to run it. The target process is started in the following manner:

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

  1. The Windows Service is started using "administrator" credentials.
  2. Windows服务使用“管理员”凭据启动。
  3. When the time comes, an intermediate .NET process is executed with arguments detailing which process should be started (filename, username, domain, password).
  4. 到时候,执行一个中间的.NET进程,其参数详细说明应该启动哪个进程(文件名,用户名,域名,密码)。
  5. This process creates a new System.Diagnostics.Process, associates a ProcessStartInfo object filled with the arguments passed to it, and then calls Start() on the process object.
  6. 此过程创建一个新的System.Diagnostics.Process,将ProcessStartInfo对象与传递给它的参数相关联,然后在进程对象上调用Start()。

The first time this happens, the target process executes fine and then closes normally. Every subsequent time however, as soon as the target process is started it throws the error "Application failed to initalize properly (0xc0000142)". Restarting the Windows Service will allow the process to run successfully once again (for the first execution).

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

Naturally, the goal is to have target process execute successfully every time.

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

Regarding step 2 above: To run a process as a different user .NET calls the win32 function CreateProcessWithLogonW. This function requires a window handle to log the specified user in. Since the Windows Service isn't running in Interactive Mode it has no window handle. This intermediate process solves the issue, as it has a window handle which can be passed to the target process.

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

Please, no suggestions of using psexec or the windows task planner. I've accepted my lot in life, and that includes solving the problem in the manner stated above.

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

8 个解决方案

#1


22  

I seem to have a working implementation (Works On My Machine(TM)) for the following scenarios:

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

Batch File, .NET Console Assembly, .NET Windows Forms application.

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

Here's how:

就是这样:

I have a windows service running as the Administrator user. I add the following policies to the Administrator user:

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

  • Log on as a service
  • 以服务身份登录
  • Act as part of the operating system
  • 作为操作系统的一部分
  • Adjust memory quotas for a process
  • 调整进程的内存配额
  • Replace a process level token
  • 替换进程级令牌

These policies can be added by opening Control Panel/ Administrative Tools / Local Security Policy / User Rights Assignment. Once they are set, the policies don't take effect until next login. You can use another user instead of the Administrator, which might make things a bit safer :)

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

Now, my windows service has the required permissions to start jobs as other users. When a job needs to be started the service executes a seperate assembly ("Starter" .NET console assembly) which initiates the process for me.

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

The following code, located in the windows service, executes my "Starter" console assembly:

位于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);

The console assembly then starts the target process via interop calls:

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

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());
            }
        }
    }

The basic procedure is:

基本程序是:

  1. Log the user on
  2. 记录用户
  3. convert the given token into a primary token
  4. 将给定的令牌转换为主令牌
  5. Using this token, execute the process
  6. 使用此令牌,执行该过程
  7. Close the handle when finished.
  8. 完成后关闭手柄。

This is development code fresh from my machine and no way near ready for use in production environments. The code here is still buggy - For starters: I'm not sure whether the handles are closed at the right point, and there's a few interop functions defined above that aren't required. The separate starter process also really annoys me. Ideally I'd like all this Job stuff wrapped up in an assembly for use from our API as well as this service. If someone has any suggestions here, they'd be appreciated.

这是我的机器新鲜的开发代码,并没有准备好在生产环境中使用。这里的代码仍然是错误的 - 对于初学者:我不确定句柄是否在正确的位置关闭,并且上面定义了一些不需要的互操作函数。单独的启动程序也让我很烦。理想情况下,我希望所有这些Job包装在一个程序集中,以便从我们的API以及此服务中使用。如果有人在这里有任何建议,他们将不胜感激。

#2


1  

I won't suggest neither psexec nor the task planner. But, have you looked at Sudowin?

我不会建议psexec和任务规划师。但是,你看过Sudowin吗?

It does almost exactly what you wish, with the exception it asks for a password before executing the process.

它几乎完全符合您的要求,但在执行流程之前需要输入密码。

Also, being open source and all, you can see how it executes processes from the associated service time and again.

此外,作为开源和所有,你可以看到它如何从相关的服务时间和次数执​​行进程。

#3


1  

Just a guess - are you using LoadUserProfile=true with the start info? CreateProcessWithLogonW does not load user registry hive by default, unless you tell it to.

只是一个猜测 - 您是否在启动信息中使用LoadUserProfile = true?默认情况下,CreateProcessWithLogonW不会加载用户注册表配置单元,除非您告诉它。

#4


1  

The reason that the call fails after the first time is very probably because it uses a "default" security descriptor (whatever that is).

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

from msdn:

来自msdn:

lpProcessAttributes [in, optional]

lpProcessAttributes [in,optional]

A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new process object and determines whether child processes can inherit the returned handle to the process. If lpProcessAttributes is NULL or lpSecurityDescriptor is NULL, the process gets a default security descriptor and the handle cannot be inherited. The default security descriptor is that of the user referenced in the hToken parameter. This security descriptor may not allow access for the caller, in which case the process may not be opened again after it is run. The process handle is valid and will continue to have full access rights.

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

I guess CreateProcessWithLogonW is creating this default security descriptor (in any case, I'm not specifying one).

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

Time to start Interopping...

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

#5


0  

You do not need a window handle to use CreateProcessWithLogonW, I'm not sure where your information came from.

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

The application failed to initialize error has many causes, but it is almost always related to security or exhausted user resources. It is extremely difficult to diagnose this without a lot more information about what you are running and the context that you're running in. But things to look into are: does the user provided have the correct permissions to access the executable file's directory, does the user have permission to access the window station and desktop in which it is being launched, does it have correct permissions on any dll files it needs to load at initialization, etc.

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

#6


0  

I've just read this comment over at msdn (http://msdn.microsoft.com/en-us/library/ms682431(VS.85).aspx):

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

Don't call user applications with this function! ChristianWimmer |
Edit | Show History Please Wait If you going to call user mode applications that offer document editing and similar stuff (like Word), all unsaved data will be lost. This is because the usual shutdown sequence does not apply to processes launched with CreateProcessWithLogonW. In this way the launched applications do not get WM_QUERYENDSESSION, WM_ENDSESSION and the most important WM_QUIT message. So they don't ask for saving the data or clean up their stuff. They will just quit without warning. This function is not user friendly and should be used with caution.

不要使用此功能调用用户应用程序! ChristianWimmer |编辑|显示历史记录请稍候如果您要调用提供文档编辑和类似内容(如Word)的用户模式应用程序,则所有未保存的数据都将丢失。这是因为通常的关闭序列不适用于使用CreateProcessWithLogonW启动的进程。通过这种方式,启动的应用程序不会获得WM_QUERYENDSESSION,WM_ENDSESSION和最重要的WM_QUIT消息。所以他们不要求保存数据或清理他们的东西。他们将在没有警告的情况下退出。此功能不是用户友好的,应谨慎使用。

It is just "bad user experience". Nobody expects it.

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

This could explain what I've observed: Works the first time. Fails every subsequent time. That reinforces my belief that something isn't being cleaned up properly internally

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

#7


0  

You say that "The Windows Service is started using "administrator" credentials"

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

Do you mean the actual 'Administrator' account, or a user in the 'Administrators' group? Starting the service as Administrator solved this for me.

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

#8


0  

I had similar issues, when I tried to start the PhantomJS-binary with the "runas"-verb within a windows service. I have now solved the Problem using the following procedure:

当我尝试在Windows服务中使用“runas”-verb启动PhantomJS-binary时,我遇到了类似的问题。我现在使用以下过程解决了问题:

  • Impersonate to User
  • 冒充用户
  • Start Process (without UI)
  • 启动流程(无UI)
  • Impersonate back
  • 冒充回来

You can use the Impersonator-Class for impersonation. It is also important to set the following Properties in the ProcessStartInfo, so the application does not try to access the Windows 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
};

#1


22  

I seem to have a working implementation (Works On My Machine(TM)) for the following scenarios:

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

Batch File, .NET Console Assembly, .NET Windows Forms application.

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

Here's how:

就是这样:

I have a windows service running as the Administrator user. I add the following policies to the Administrator user:

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

  • Log on as a service
  • 以服务身份登录
  • Act as part of the operating system
  • 作为操作系统的一部分
  • Adjust memory quotas for a process
  • 调整进程的内存配额
  • Replace a process level token
  • 替换进程级令牌

These policies can be added by opening Control Panel/ Administrative Tools / Local Security Policy / User Rights Assignment. Once they are set, the policies don't take effect until next login. You can use another user instead of the Administrator, which might make things a bit safer :)

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

Now, my windows service has the required permissions to start jobs as other users. When a job needs to be started the service executes a seperate assembly ("Starter" .NET console assembly) which initiates the process for me.

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

The following code, located in the windows service, executes my "Starter" console assembly:

位于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);

The console assembly then starts the target process via interop calls:

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

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());
            }
        }
    }

The basic procedure is:

基本程序是:

  1. Log the user on
  2. 记录用户
  3. convert the given token into a primary token
  4. 将给定的令牌转换为主令牌
  5. Using this token, execute the process
  6. 使用此令牌,执行该过程
  7. Close the handle when finished.
  8. 完成后关闭手柄。

This is development code fresh from my machine and no way near ready for use in production environments. The code here is still buggy - For starters: I'm not sure whether the handles are closed at the right point, and there's a few interop functions defined above that aren't required. The separate starter process also really annoys me. Ideally I'd like all this Job stuff wrapped up in an assembly for use from our API as well as this service. If someone has any suggestions here, they'd be appreciated.

这是我的机器新鲜的开发代码,并没有准备好在生产环境中使用。这里的代码仍然是错误的 - 对于初学者:我不确定句柄是否在正确的位置关闭,并且上面定义了一些不需要的互操作函数。单独的启动程序也让我很烦。理想情况下,我希望所有这些Job包装在一个程序集中,以便从我们的API以及此服务中使用。如果有人在这里有任何建议,他们将不胜感激。

#2


1  

I won't suggest neither psexec nor the task planner. But, have you looked at Sudowin?

我不会建议psexec和任务规划师。但是,你看过Sudowin吗?

It does almost exactly what you wish, with the exception it asks for a password before executing the process.

它几乎完全符合您的要求,但在执行流程之前需要输入密码。

Also, being open source and all, you can see how it executes processes from the associated service time and again.

此外,作为开源和所有,你可以看到它如何从相关的服务时间和次数执​​行进程。

#3


1  

Just a guess - are you using LoadUserProfile=true with the start info? CreateProcessWithLogonW does not load user registry hive by default, unless you tell it to.

只是一个猜测 - 您是否在启动信息中使用LoadUserProfile = true?默认情况下,CreateProcessWithLogonW不会加载用户注册表配置单元,除非您告诉它。

#4


1  

The reason that the call fails after the first time is very probably because it uses a "default" security descriptor (whatever that is).

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

from msdn:

来自msdn:

lpProcessAttributes [in, optional]

lpProcessAttributes [in,optional]

A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new process object and determines whether child processes can inherit the returned handle to the process. If lpProcessAttributes is NULL or lpSecurityDescriptor is NULL, the process gets a default security descriptor and the handle cannot be inherited. The default security descriptor is that of the user referenced in the hToken parameter. This security descriptor may not allow access for the caller, in which case the process may not be opened again after it is run. The process handle is valid and will continue to have full access rights.

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

I guess CreateProcessWithLogonW is creating this default security descriptor (in any case, I'm not specifying one).

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

Time to start Interopping...

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

#5


0  

You do not need a window handle to use CreateProcessWithLogonW, I'm not sure where your information came from.

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

The application failed to initialize error has many causes, but it is almost always related to security or exhausted user resources. It is extremely difficult to diagnose this without a lot more information about what you are running and the context that you're running in. But things to look into are: does the user provided have the correct permissions to access the executable file's directory, does the user have permission to access the window station and desktop in which it is being launched, does it have correct permissions on any dll files it needs to load at initialization, etc.

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

#6


0  

I've just read this comment over at msdn (http://msdn.microsoft.com/en-us/library/ms682431(VS.85).aspx):

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

Don't call user applications with this function! ChristianWimmer |
Edit | Show History Please Wait If you going to call user mode applications that offer document editing and similar stuff (like Word), all unsaved data will be lost. This is because the usual shutdown sequence does not apply to processes launched with CreateProcessWithLogonW. In this way the launched applications do not get WM_QUERYENDSESSION, WM_ENDSESSION and the most important WM_QUIT message. So they don't ask for saving the data or clean up their stuff. They will just quit without warning. This function is not user friendly and should be used with caution.

不要使用此功能调用用户应用程序! ChristianWimmer |编辑|显示历史记录请稍候如果您要调用提供文档编辑和类似内容(如Word)的用户模式应用程序,则所有未保存的数据都将丢失。这是因为通常的关闭序列不适用于使用CreateProcessWithLogonW启动的进程。通过这种方式,启动的应用程序不会获得WM_QUERYENDSESSION,WM_ENDSESSION和最重要的WM_QUIT消息。所以他们不要求保存数据或清理他们的东西。他们将在没有警告的情况下退出。此功能不是用户友好的,应谨慎使用。

It is just "bad user experience". Nobody expects it.

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

This could explain what I've observed: Works the first time. Fails every subsequent time. That reinforces my belief that something isn't being cleaned up properly internally

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

#7


0  

You say that "The Windows Service is started using "administrator" credentials"

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

Do you mean the actual 'Administrator' account, or a user in the 'Administrators' group? Starting the service as Administrator solved this for me.

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

#8


0  

I had similar issues, when I tried to start the PhantomJS-binary with the "runas"-verb within a windows service. I have now solved the Problem using the following procedure:

当我尝试在Windows服务中使用“runas”-verb启动PhantomJS-binary时,我遇到了类似的问题。我现在使用以下过程解决了问题:

  • Impersonate to User
  • 冒充用户
  • Start Process (without UI)
  • 启动流程(无UI)
  • Impersonate back
  • 冒充回来

You can use the Impersonator-Class for impersonation. It is also important to set the following Properties in the ProcessStartInfo, so the application does not try to access the Windows 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
};