Why Is This Process Crashing as Soon as It Is Launched

Why is this process crashing as soon as it is launched?

I ended up opening a case with Microsoft, and this is the information I was given:

Process.Start internally calls CreateProcessWithLogonW(CPLW) when credentials are specified. CreateProcessWithLogonW cannot be called from a Windows Service Environment (such as an IIS WCF service). It can only be called from an Interactive Process (an application launched by a user who logged on via CTRL-ALT-DELETE).

(that's verbatim from the support engineer; emphasis mine)

They recommended I use CreateProcessAsUser instead. They gave me some useful sample code, which I then adapted to my needs, and now everything works great!

The end result was this:

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

There are some pre-requisites to making this code work. The user running it must have the user right to 'Replace a process level token' and 'Adjust memory quotas for a process', while the 'other user' must have the user right to 'Log on as a batch job'. These settings can be found under the Local Security Policy (or possibly through Group Policy). If you change them, a restart will be required.

UserToken is a property that can be closed via ReleaseUserToken because we will call StartProcess repeatedly and we were told not to log the other user on again and again.

That SecureStringToString() method was taken from this question. Using SecureString was not part of Microsoft's recommendation; I did it this way so as not to break compatibility with some other code.

Process crashes when started from systemd

Ok guys, the actual issue with my process-A was that it had some sort of dependency with another process-C during startup and took some inputs from another process-C which usually would be garbage during startup. This garbage input was reason for crash when launched through Systemd during startup. Later on manual start the input values used to be valid, thats why no crash.

Thanks for the help anyways.
Also points mentioned by @Mark Stosberg are useful and valid for debugging systemd related bugs.

Let know in case of any questions.
see ya.

App crashing when launching from AppStore or TestFlight but working fine elseways

I found the solution of my problem.

As expected it was coming from func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool

Inside the function I had let userInfo = launchOpts[UIApplicationLaunchOptionsRemoteNotificationKey] as! NSDictionary.

I changed it to let userInfo = launchOpts[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary and then checked if userInfo != nil to make all the code running userInfointo the bracket.

If it helps anyone or if anyone is struggling with this issue, check all yours ! and swap these to ? if needed (on didFinishLaunchingWithOptions) since launching from TestFlight/AppStore seems to have different options than from installed application (just a guess, not pro enough to claim it).

Debugging a C# executable that crashes on launch

Crashing at launch might be due to a missing dependency. Run fuslogvw.exe before starting your application and see whether any of the binding operations fail.

If that doesn't help, it is generally a good practice to have diagnostic logging in place. You can use a dedicated logging library, e.g. log4net, or at least you should be using the simplest form of logging via System.Diagnostics.Trace. You can listen to the trace messages by either configuring a trace listener in the app.config or using third-party tools like a debugger or DebugView from Sysinternals.

If you actually want to attach a debugger you can insert a breakpoint programmatically:

System.Diagnostics.Debugger.Break();

I haven't checked how this works with a remote debugger, but as a last resort you can have your application sleep long enough to allow you to attach a debugger:

System.Threading.Thread.Sleep(30000);

Application crashing on startup using Process.Start

Your code is not how one starts a process:

Console.WriteLine("Running"); 
Process pr = new Process();
pr.StartInfo.FileName = "Notepad.exe";
pr.StartInfo.Arguments = "test.dat";
pr.Start();
while (pr.HasExited == false)
if ((DateTime.Now.Second % 5) == 0)
{
// Show a tick every five seconds.
Console.Write(".");
System.Threading.Thread.Sleep(1000);
}

I go into further detail in my blog post How to Launch an External Application in C#:



Related Topics



Leave a reply



Submit