Click here to Skip to main content
15,879,053 members
Articles / Desktop Programming / Windows Forms

A tiny application to launch a suspended Process

Rate me:
Please Sign up or sign in to vote.
3.95/5 (9 votes)
24 Jul 2011CPOL2 min read 66.4K   3.9K   16   15
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).

C#
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.

C#
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:

C#
[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:

C#
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:

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

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

C#
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)


Written By
Software Developer
Austria Austria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionSwitch current directory for the new process Pin
Dmitry Grigoryev19-Sep-17 23:37
Dmitry Grigoryev19-Sep-17 23:37 
Question你好 Pin
jamesghq7-Nov-12 2:51
jamesghq7-Nov-12 2:51 
你好,maybe,we can discuss some questions!
GeneralMy vote of 1 Pin
Cristian Amarie24-Sep-11 8:32
Cristian Amarie24-Sep-11 8:32 
QuestionAnother technique for debugging .NET applications on startup Pin
JordanDay25-Jul-11 5:33
JordanDay25-Jul-11 5:33 
QuestionRe: Another technique for debugging .NET applications on startup Pin
hofingerandi25-Jul-11 10:16
hofingerandi25-Jul-11 10:16 
AnswerRe: Another technique for debugging .NET applications on startup Pin
JordanDay26-Jul-11 4:49
JordanDay26-Jul-11 4:49 
GeneralRe: Another technique for debugging .NET applications on startup Pin
hofingerandi27-Jul-11 10:21
hofingerandi27-Jul-11 10:21 
QuestionHow to suspend and resume a proess by PID or name? Pin
stevenyoung24-Jul-11 20:14
stevenyoung24-Jul-11 20:14 
AnswerRe: How to suspend and resume a proess by PID or name? Pin
hofingerandi24-Jul-11 21:12
hofingerandi24-Jul-11 21:12 
GeneralMy vote of 2 Pin
Cristian Amarie22-Jul-11 20:49
Cristian Amarie22-Jul-11 20:49 
GeneralRe: My vote of 2 Pin
hofingerandi23-Jul-11 1:13
hofingerandi23-Jul-11 1:13 
GeneralRe: My vote of 2 Pin
hofingerandi24-Jul-11 19:46
hofingerandi24-Jul-11 19:46 
GeneralThis is Not Yet An Article PinPopular
AspDotNetDev22-Jul-11 8:44
protectorAspDotNetDev22-Jul-11 8:44 
GeneralRe: This is Not Yet An Article Pin
hofingerandi23-Jul-11 2:03
hofingerandi23-Jul-11 2:03 

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

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