Accessing CreateProcess from C# and VB.NET

I came across this issue recently. I was trying to access CreateProcess using DLLImport from managed code using C# and VB.Net. Here is my code from C#:


using System;

using System.Diagnostics;

using System.Runtime.InteropServices;




    public IntPtr hProcess;

    public IntPtr hThread;

    public uint dwProcessId;

    public uint dwThreadId;



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 int length;

    public IntPtr lpSecurityDescriptor;

    public bool bInheritHandle;



public class Program


    public static void Main()




        CreateProcess(“C:\\WINDOWS\\SYSTEM32\\Calc.exe”, null, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref si, out pi);





static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,

                        bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment,

                        string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo,out PROCESS_INFORMATION lpProcessInformation);




This works like a charm. I initially had problems with this VB code. I looked at the IL code for both and figured out that I had constructed structures in C# for Process_Information, Security_Attricutes etc, but had marked them as classes in VB. This was mking the call to fail. I figured this out after looking into the IL from VB and C# to see what was different between the two.  I changed the VB to struct and it works like a charm in VB too. Check the VB code below!



Imports System.Runtime.InteropServices

Imports System.Security.Permissions

Imports System.Reflection


Module Module1

    Sub Main()



    End Sub

    Sub StartupNotepad()

        Dim retValue As Boolean


        Dim sInfo As STARTUPINFO = New STARTUPINFO()


        retValue = CreateProcess(“c:\\windows\\system32\\NotePad.exe”, Nothing, IntPtr.Zero, IntPtr.Zero, False, 0, IntPtr.Zero, Nothing, sInfo, pInfo)


    End Sub

    <StructLayout(LayoutKind.Sequential)> _

    Public Structure PROCESS_INFORMATION

        Public hProcess As IntPtr

        Public hThread As IntPtr

        Public dwProcessID As UInteger

        Public dwThreadID As UInteger



    <StructLayout(LayoutKind.Sequential)> _

    Public Structure SECURITY_ATTRIBUTES

        Public nLength As Integer

        Public lpSecurityDescriptor As IntPtr

        Public bInheritHandle As Boolean



    <StructLayout(LayoutKind.Sequential)> _

    Public Structure STARTUPINFO

        Public cb As UInteger

        Public lpReserved As String

        Public lpDesktop As String

        Public lpTitle As String

        Public dwX As UInteger

        Public dwY As UInteger

        Public dwXSize As UInteger

        Public dwYSize As UInteger

        Public dwXCountChars As UInteger

        Public dwYCountChars As UInteger

        Public dwFillAttribute As UInteger

        Public dwFlags As UInteger

        Public wShowWindow As Short

        Public cbReserved2 As Short

        Public lpReserved2 As IntPtr

        Public hStdInput As IntPtr

        Public hStdOutput As IntPtr

        Public hStdError As IntPtr

    End Structure ‘STARTINFO

    <DllImport(“kernel32.dll”)> _

    Function CreateProcess( _

    ByVal lpApplicationName As String, _

    ByVal lpCommandLine As String, _

    ByVal lpProcessAttributes As IntPtr, _

    ByVal lpThreadAttributes As IntPtr, _

    ByVal bInheritHandles As Boolean, _

    ByVal dwCreationFlags As UInteger, _

    ByVal lpEnvironment As IntPtr, _

    ByVal lpCurrentDirectory As String, _

    ByRef lpStartupInfo As STARTUPINFO, _

    ByRef lpProcessInformation As PROCESS_INFORMATION) As Boolean

    End Function

End Module



Comments (22)

  1. ShayEr says:

    a. Any reason why the Process class is not enough?

    b. are you aware of

  2. Andrés says:

    Could you explain a little bit more about why structs are compiled as classes and what are the implications of that? What about that attribute??


  3. says:

    Using porocess is another alternative. I am aware of The sample for CreateProcess from there did not work for me.

    It was my mistake on the wording. I had authored the struct as classes in VB and hence the problem. I changed them to structs and it worked fine.

  4. RonO says:

    Did you update the sample (and anything else you found to be incorrect) for the pinvoke entry?  🙂

  5. Thottam Sriram says:

    The sample represented PROCESS_INFORMATION and SECURITY_ATTRIBUTES and STARTUPINFO as classes in VB. This made the call to fail. I changed them to struct in VB and I could get notepad up and running from the VB code! The working sample is the code above.

  6. Austin says:

    I don’t understand the desire to use P/Invoke for this.  The managed world already has a Process class for doing all these sorts of things.  It’ll not only be more performant (by avoiding P/Invoke), but also be easier for future maintainers of any code to understand and modify, and less error-prone.

    Can you explain why you would ever want to use this over the normal Process class of the .NET Base Class Library?

  7. Thottam Sriram says:

    I totally agree with you and would think that to be a better decision.

    There were a few questions on the web relating to this not working and I just wanted to aaddress that. Regardig using this over existing Process class, I agree with you that it will be a better choice.

    Question to users:

    If you are using it, can you explain the scenario that force you to using this?

  8. Andy says:

    One reason for wanting to do this is:

    Open a socket for listening in the parent process.

    Create a child process using Process class.

    Kill parent.

    Socket is still open for listening and doesn’t close until the child process exits. I cannot see anyway of just using the Process class from stopping this from working. I suspect the call to CreateProcess has inherit handle set to true. Which seems like a bug to me.

  9. Dewald Swaneoel says:

    Here’s one reason why one would prefer to use CreateProcess this way as opposed to the managed code of System.Diagnostics.Process is that the Process class quite simply does not exist in the .NET Compact Framework 1.0

    So while Microst assumes that the minute hey shipped the newer version of the compact framework all older devices stopped existing, a few unlucy souls such as myself still have to develop for devices running the CF 1.0

    I am very grateful to have stmbled across this article for that very reason.

  10. jon nolen says:

    Sorry to be a month late on this>  you also have to pInvoke to create a process if you are trying to run the process in a different user context (some WTS applications).

  11. says:

    These are some good scenarios for this.

    Can’t you use System.Diagnostics.Process to create a process after you impersonate an user? I have not tried it, but would be interesting to know.

  12. Vladimir Jelovac says:

    Great article!!! Thanks for help 🙂


  13. SuspendedUser says:

    Another reason would be that you cannot create a process in suspended mode with the .NET Process class.

  14. Er. Kartikay Malhotra says:

    Beauty lies in utility & simplicity.

    Thank you!

  15. Reasonator says:

    One reason to use this instead of Process.Start is that Process.Start has a upper limit of maximum 2048 characters passed as parameter to the process.

    This method has a maximum of 32000-something

  16. doboloh says:

    thanks very useful in creating a new Process group with C#,

    i think System.Diagnostics.Process lacks this

  17. Yaniv says:

    Thank you, that was very very useful.

    Tell me, how do you find the Window’s handle ? (HWND) from createprocess ?

  18. Troy Howard says:

    A little late on this very old blog post, but I recently used CreateProcess to enable a weird situation.

    Basically, I had an existing commandline application, which was originally developed for *nix… For it’s output, it needed a seekable output file handle. The standard file handles presented to a process are all either read or write (ie, the file handles created from CreatePipe), so I wrote a simple wrapper application that setup a pipe for STDIN, but setup a seekable file handle for it’s output via CreateFile. Without access to the STARTUPINFO struct, I could not have redirected the output in the correct way. The redirection provided by System.Diagnostics.Process only allow for TextReaders/TextWriters, and that’s because they are operating against handles created via the standard CreatePipe call.

    So, in short, there was no way to do what I needed to do with the managed classes, so I had to re-create all that functionality with P/Invoke calls…

    Which reminds me… A commenter here posted that using P/Invoke is slower for this task,as compared to just the managed Process class. I invite him to disassemble the code using Reflector and realize that all that class does is wrap P/Invoke calls. In fact, it does a lot of extra work, than simply calling CreateProcess directly, and is likely slower due to the additional overhead. Though the difference in speed is probably so slight that it would be hard to measure.

    Regarding the last poster’s question (Yaniv from Dec. 2008)  you can obtain the window handle(s) (there can be more than one per process) for a given process id by iterating across all windows (Call GetTopWindow, then GetNextWindow in a loop), then passing the window handle to GetWindowThreadProcessId to retreive it’s processid, and check to see if that’s the processid you’re looking for… It’s a little roundabout, but it does the job. You obtain the processid from PROCESS_INFORMATION.hProcess field after calling CreateProcess.



  19. Venu says:

    I tried this code, it works on Win xp and does not on citrix.

    I use createprocess to call AcroRd32.exe with switches (args) to print.

    AcroRd32.exe /h /t "mypdf" "myprinter"

    Any ideas why this does not work on citrix?

    How can i debug this code.?

    Appreciate any input.


  20. vCillusion says:

    That worked fine but how could we spawn this process in C#.

  21. Kalyan Karta says:

    Problem found in spawning the ftp process. Kindly help out !!