Click here to Skip to main content
Click here to Skip to main content

A tiny application to launch a suspended Process

, 24 Jul 2011
Rate this:
Please Sign up or sign in to vote.
A tiny application to launch a process suspended to allow, e.g., attaching a remote debugger.

Introduction

In this article, I present a simple WinForms-application that starts an arbitrary .exe via drag and drop but leaves the process suspended. This way, it is possible to attach a (remote) debugger to the process and set breakpoints in startup functions.

Background

Sometimes, remote debugging is the last resort for finding a bug in an application. In our particular case, the crash (well, it was no hard crash, otherwise we could have generated a mini-dump) would make the application shut down just a few seconds after startup.

In such a case, remote debugging with msvsmon is not directly possible, since msvsmon can only attach to running processes but not launch a new process on the remote machine.

Using the Code

Just start the application and drop the .exe onto the appearing form.

screenshot.png

Your application will be started, stay alive for a few milliseconds (determined via the trackbar), and is then suspended again. Now you can attach the debugger to the process and set breakpoints at the appropriate places (e.g., in InitInstance() if you are using MFC).

started_dialog.png

As soon as you click the message box, the application continues and will eventually reach your breakpoint.

How the Code Works

Launching an Application

It is not directly possible from .NET to launch an application in suspended mode; instead, P/Invoke is necessary. In particular, we need methods to create the process suspended and to afterwards resume it again. Also, a method to suspend the main thread again will be needed later (see this post at blogs.msdn for details).

public static class NativeMethods
{
    [DllImport("kernel32.dll")]
    public static extern bool CreateProcess(string lpApplicationName, 
           string lpCommandLine, IntPtr lpProcessAttributes, 
           IntPtr lpThreadAttributes,
           bool bInheritHandles, ProcessCreationFlags dwCreationFlags, 
           IntPtr lpEnvironment, string lpCurrentDirectory, 
           ref STARTUPINFO lpStartupInfo, 
           out PROCESS_INFORMATION lpProcessInformation);

    [DllImport("kernel32.dll")]
    public static extern uint ResumeThread(IntPtr hThread);

    [DllImport("kernel32.dll")]
    public static extern uint SuspendThread(IntPtr hThread);
}

The parameters STARTUPINFO and PROCESS_INFORMATION correspond to the .NET ProcessStartInfo and Process classes in the Systems.Diagnostics namespace, respectively.

public struct STARTUPINFO
{
    public uint cb;
    public string lpReserved;
    public string lpDesktop;
    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;
}

public struct PROCESS_INFORMATION
{
    public IntPtr hProcess;
    public IntPtr hThread;
    public uint dwProcessId;
    public uint dwThreadId;
}

The essential difference to invoking Process.Start() is the possibility to supply ProcessCreationFlags to the method CreateProcess:

[Flags]
public enum ProcessCreationFlags : uint
{
    ZERO_FLAG = 0x00000000,
    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_SHARED_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,
    INHERIT_PARENT_AFFINITY = 0x00010000
}

Using these methods and structures, we can now start the given application. The important point is the flag ProcessCreationFlags.CREATE_SUSPENDED in the last but first line:

STARTUPINFO si = new STARTUPINFO();
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
bool success = NativeMethods.CreateProcess(processpath, null, 
    IntPtr.Zero, IntPtr.Zero, false, 
    ProcessCreationFlags.CREATE_SUSPENDED, 
    IntPtr.Zero, null, ref si, out pi);

The ref and out parameters si and pi contain important information about the created process, in particular a handle to the main thread (pi.hThread) and the PID of the process.

Resuming and Suspending the Application

To finally start the application, the main thread must be resumed:

IntPtr ThreadHandle = pi.hThread;
NativeMethods.ResumeThread(ThreadHandle);

For suspending it again, the method SuspendThread can be called:

NativeMethods.SuspendThread(ThreadHandle);

In particular, I resume the application for a few milliseconds (with just a blocking wait) and suspend the thread again. Afterwards, I show the message box. This way, the application has booted far enough for the debugger to attach.

If I skip this initial delay, I would get a crash in the debugger on attaching:

Debugger:: An unhandled non-continuable exception was thrown during process load

Points of Interest

The program will only start applications (.exe), it does not launch links.

History

  • July 22, 2011: Initial post.
  • July 23, 2011: Added some description of the code for launching, resuming, and suspending.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

hofingerandi
Software Developer
Austria Austria
No Biography provided

Comments and Discussions

 
GeneralMy vote of 2 PinmemberCristian Amarie22-Jul-11 20:49 
GeneralRe: My vote of 2 Pinmemberhofingerandi23-Jul-11 1:13 
GeneralRe: My vote of 2 Pinmemberhofingerandi24-Jul-11 19:46 
GeneralThis is Not Yet An Article PinmvpAspDotNetDev22-Jul-11 8:44 
You have just provided a utility and some simple instructions on how to use it. You haven't described any of your code.
 
If you want to upload an open source utility without describing the code, perhaps CodePlex has all you need.
Martin Fowler wrote:
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

GeneralRe: This is Not Yet An Article Pinmemberhofingerandi23-Jul-11 2:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 25 Jul 2011
Article Copyright 2011 by hofingerandi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid