为什么这个过程一启动就会崩溃?

nat*_*vin 13 c# system.diagnostics process windows-server-2012

我们有一个IIS WCF服务,它以另一个用户的身份启动另一个进程(app.exe).我完全控制了两个应用程序(现在这是一个开发环境).IIS应用程序池以我的身份运行,域用户(DOMAIN \nirvin),也是该框的本地管理员.第二个进程应该作为本地用户运行(svc-low).我正在System.Diagnostics.Process.Start(ProcessStartInfo)用来启动这个过程.该过程成功启动 - 我知道因为没有抛出异常,我得到了一个进程ID.但是进程立即死亡,我在事件日志中看到如下错误:

错误应用程序名称:app.exe,版本:1.0.3.0,时间戳:0x514cd763

错误模块名称:KERNELBASE.dll,版本:6.2.9200.16451,时间戳:0x50988aa6

异常代码:0xc06d007e

故障偏移:0x000000000003811c

错误进程id:0x10a4

错误应用程序启动时间:0x01ce274b3c83d62d

错误的应用程序路径:C:\ Program Files\company\app\app.exe

错误模块路径:C:\ Windows\system32\KERNELBASE.dll

报告编号:7a45cd1c-933e-11e2-93f8-005056b316dd

错误包全名:

错误包相关的应用程序ID:

我已经在app.exe(现在)中进行了非常彻底的日志记录,因此我认为它不会在.NET代码中引发错误(不再).

这是真正令人讨厌的部分:我认为我只是启动了错误的进程,所以我Process.Start()在一个愚蠢的WinForms应用程序中复制了我的调用并在机器上像我一样运行它,希望能够修改,直到我得到正确的参数.因此,当然这是第一次和每次都有效:我能够始终如一地启动第二个流程并使其按预期运行.它只从IIS启动不起作用.

我已经尝试过"以批处理作业登录"的svc-low权限,并且我已尝试授予自己"替换进程级别令牌"(在本地安全策略中)的权限,但似乎都没有任何区别.

救命!

环境细节

  • Windows Server 2012
  • .NET 4.5(提到的所有应用程序)

额外细节

起初app.exe是一个控制台应用程序.尝试启动是让conhost.exe在事件日志中生成错误,因此我将app.exe切换为Windows应用程序.这让conhost脱离了这个等式,但是留给我这里描述的情况.(通过这个问题引导了这条道路.)

ProcessStartInfo我使用的对象如下所示:

new ProcessStartInfo
{
    FileName = fileName,
    Arguments = allArguments,
    Domain = domainName,
    UserName = userName,  
    Password = securePassword,
    WindowStyle = ProcessWindowStyle.Hidden,
    CreateNoWindow = true,  
    UseShellExecute = false,
    RedirectStandardOutput = false
    //LoadUserProfile = true  //I've done it with and without this set
};
Run Code Online (Sandbox Code Playgroud)

一个现有的问题说我应该转向本机API,但是a)该问题解决了不同的情况; b)愚蠢的WinForms应用程序的成功表明这Process.Start是一个可行的工作选择.

nat*_*vin 16

我最后向微软公开了一个案例,这是我给出的信息:

当指定凭据时,Process.Start在内部调用CreateProcessWithLogonW(CPLW).无法从Windows服务环境(例如IIS WCF服务)调用 CreateProcessWithLogonW .它只能从Interactive Process(由通过CTRL-ALT-DELETE登录的用户启动的应用程序)调用.

(这是支持工程师的逐字逐句;强调我的)

他们建议我CreateProcessAsUser改用.他们给了我一些有用的示例代码,然后根据我的需要调整,现在一切都很好!

最终结果如下:

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;

public class ProcessHelper
{
    static ProcessHelper()
    {
        UserToken = IntPtr.Zero;
    }

    private static IntPtr UserToken { get; set; }

    public int StartProcess(ProcessStartInfo processStartInfo)
    {
        LogInOtherUser(processStartInfo);

        Native.STARTUPINFO startUpInfo = new Native.STARTUPINFO();
        startUpInfo.cb = Marshal.SizeOf(startUpInfo);
        startUpInfo.lpDesktop = string.Empty;

        Native.PROCESS_INFORMATION processInfo = new Native.PROCESS_INFORMATION();
        bool processStarted = Native.CreateProcessAsUser(UserToken, processStartInfo.FileName, processStartInfo.Arguments,
                                                         IntPtr.Zero, IntPtr.Zero, true, 0, IntPtr.Zero, null,
                                                         ref startUpInfo, out processInfo);

        if (!processStarted)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        uint processId = processInfo.dwProcessId;
        Native.CloseHandle(processInfo.hProcess);
        Native.CloseHandle(processInfo.hThread);
        return (int) processId;
    }

    private static void LogInOtherUser(ProcessStartInfo processStartInfo)
    {
        if (UserToken == IntPtr.Zero)
        {
            IntPtr tempUserToken = IntPtr.Zero;
            string password = SecureStringToString(processStartInfo.Password);
            bool loginResult = Native.LogonUser(processStartInfo.UserName, processStartInfo.Domain, password,
                                                Native.LOGON32_LOGON_BATCH, Native.LOGON32_PROVIDER_DEFAULT,
                                                ref tempUserToken);

            if (loginResult)
            {
                UserToken = tempUserToken;
            }
            else
            {
                Native.CloseHandle(tempUserToken);
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
        }
    }

    private static String SecureStringToString(SecureString value)
    {
        IntPtr stringPointer = Marshal.SecureStringToBSTR(value);
        try
        {
            return Marshal.PtrToStringBSTR(stringPointer);
        }
        finally
        {
            Marshal.FreeBSTR(stringPointer);
        }
    }

    public static void ReleaseUserToken()
    {
        Native.CloseHandle(UserToken);
    }
}

internal class Native
{
    internal const int LOGON32_LOGON_INTERACTIVE = 2;
    internal const int LOGON32_LOGON_BATCH = 4;
    internal const int LOGON32_PROVIDER_DEFAULT = 0;

    [StructLayout(LayoutKind.Sequential)]
    internal struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public uint dwProcessId;
        public uint dwThreadId;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct STARTUPINFO
    {
        public int cb;
        [MarshalAs(UnmanagedType.LPStr)]
        public string lpReserved;
        [MarshalAs(UnmanagedType.LPStr)]
        public string lpDesktop;
        [MarshalAs(UnmanagedType.LPStr)]
        public string lpTitle;
        public uint dwX;
        public uint dwY;
        public uint dwXSize;
        public uint dwYSize;
        public uint dwXCountChars;
        public uint dwYCountChars;
        public uint dwFillAttribute;
        public uint dwFlags;
        public short wShowWindow;
        public short cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public System.UInt32 nLength;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    }

    [DllImport("advapi32.dll", EntryPoint = "LogonUserW", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    internal extern static bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUserA", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    internal extern static bool CreateProcessAsUser(IntPtr hToken, [MarshalAs(UnmanagedType.LPStr)] string lpApplicationName, 
                                                    [MarshalAs(UnmanagedType.LPStr)] string lpCommandLine, IntPtr lpProcessAttributes,
                                                    IntPtr lpThreadAttributes, bool bInheritHandle, uint dwCreationFlags, IntPtr lpEnvironment,
                                                    [MarshalAs(UnmanagedType.LPStr)] string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, 
                                                    out PROCESS_INFORMATION lpProcessInformation);      

    [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    internal extern static bool CloseHandle(IntPtr handle);
}
Run Code Online (Sandbox Code Playgroud)

使这段代码工作有一些先决条件.运行它的用户必须具有"替换进程级令牌"和"调整进程的内存配额"的用户权限,而"其他用户"必须具有"以批处理作业登录"的用户权限.可以在本地安全策略下(或可能通过组策略)找到这些设置.如果更改它们,则需要重新启动.

UserToken是一个可以关闭的属性,ReleaseUserToken因为我们将StartProcess反复调用,并且我们被告知不要一次又一次地登录其他用户.

这个SecureStringToString()方法取自这个问题.使用SecureString不是微软推荐的一部分; 我是这样做的,以免破坏与其他代码的兼容性.

  • 是的,自2013年5月下旬以来,此代码一直在高负载下运行,没有错误. (3认同)

Han*_*ant 5

  Exception code: 0xc06d007e
Run Code Online (Sandbox Code Playgroud)

这是一个特殊于Microsoft Visual C++,设施代码0x6d的例外.错误代码为0x007e(126),ERROR_MOD_NOT_FOUND,"无法找到指定的模块".无法找到延迟加载的DLL时引发此异常.大多数程序员都拥有在他们的机器上生成此异常的代码,即Visual Studio安装目录中的vc/include/delayhlp.cpp.

嗯,这是典型的"找不到文件"的错误,特定于DLL.如果您不知道缺少什么DLL,那么您可以使用SysInternals的ProcMon实用程序.你会看到程序搜索DLL而不是在炸弹之前找到它.

使用Process.Start()使设计不佳的程序崩溃的一种经典方法是不将ProcessStartInfo.WorkingDirectory属性设置为存储EXE的目录.它通常是偶然的,但在使用Process类时不会.看起来不像你这样做首先解决.


归档时间:

查看次数:

18854 次

最近记录:

10 年,10 月 前