6

Functions such as CreateProcess have signatures taking pointers to structs. In C I would just pass NULL as a pointer for the optional parameters, instead of creating a dummy struct object on the stack and passing a pointer to the dummy.

In C#, I have declared it as (p/invoke)

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern bool CreateProcess(
            string lpApplicationName,
            string lpCommandLine,
            ref SECURITY_ATTRIBUTES lpProcessAttributes,
            ref SECURITY_ATTRIBUTES lpThreadAttributes,
            bool bInheritHandles,
            CreateProcessFlags dwProcessCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            ref PROCESS_INFORMATION lpProcessInformation);

But if I try to pass null for the lpProcessAttributes argument or lpThreadAttributes argument, I get a compiler error:

Error 2 Argument 3: cannot convert from '<null>' to 'ref Debugging.Wrappers.SECURITY_ATTRIBUTES'

How can I modify the above function signature so that I can just pass null for the SECURITY_ATTRIBUTES arguments, without this compiler error? (And also be able to pass a real struct if I want to?)

Tim Lovell-Smith
  • 15,310
  • 14
  • 76
  • 93
  • Looks like my question is the same as this one, although I wouldn't have guessed it from the title. http://stackoverflow.com/questions/1049623/how-to-pass-a-nullable-type-to-a-p-invoked-function – Tim Lovell-Smith Jul 06 '10 at 04:45

2 Answers2

6

OK, I finally(!) found an even better way of doing this:

Declare SECURITY_ATTRIBUTES as class instead of struct and don't pass it by ref. :-)

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool CreateProcess(
        string lpApplicationName,
        StringBuilder lpCommandLine,
        SECURITY_ATTRIBUTES lpProcessAttributes,
        SECURITY_ATTRIBUTES lpThreadAttributes,
        bool bInheritHandles,
        CreateProcessFlags dwCreationFlags,
        IntPtr lpEnvironment,
        string lpCurrentDirectory,
        STARTUPINFO lpStartupInfo, /// Required
        PROCESS_INFORMATION lpProcessInformation //Returns information about the created process
        );

/// <summary>
/// See http://msdn.microsoft.com/en-us/library/aa379560(v=VS.85).aspx
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class SECURITY_ATTRIBUTES
{
    public uint nLength;
    public IntPtr lpSecurityDescriptor;
    [MarshalAs(UnmanagedType.Bool)] public bool bInheritHandle;
}

Bonus: this also lets you declare a decent constructor on SECURITY_ATTRIBUTES which initializes nLength.

Tim Lovell-Smith
  • 15,310
  • 14
  • 76
  • 93
  • It works no matter what you rename the class to. Great answer! – Gerhard Powell Aug 04 '15 at 21:22
  • 1
    I wonder though if this is safe. The `StructLayout` attribute may only affect structs, not classes. So perhaps .NET's JIT may lay out the fields in this class any way it wants. For instance, I wonder if this might fail on x64 or other platforms. I may not be a safe trick. – Andrew Arnott Oct 27 '15 at 17:49
  • I just heard from an MSFT internal CLR expert that this is a safe trick. :) – Andrew Arnott Oct 27 '15 at 19:15
3

null is only valid for Reference types in .Net. your SECURITY_ATTRIBUTES is a struct, which is a ValueType. Rather than passing null, you need to pass an empty SECURITY_ATTRIBUTES structure. (just say new SECURITY_ATTRIBUTES()) in your call.

A cleaner method is to add a static Empty property to your struct, and just pass SECURITY_ATTRIBUTES.Empty

[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES {
    public int nLength;
    public IntPtr lpSecurityDescriptor;
    public int bInheritHandle;

    public static SECURITY_ATTRIBUTES Empty {
        get {
            return new SECURITY_ATTRIBUTES {
                nLength = sizeof(int)*2 + IntPtr.Size,
                lpSecurityDescriptor = IntPtr.Zero,
                bInheritHandle = 0,
            };
        }
    }
}

Or better yet, rather than using P/Invoke to create a Process, check out the System.Diagnostics.Process class, which should probably do what you need it to.

Mark H
  • 13,797
  • 4
  • 31
  • 45
  • Thanks, I like this SECURITY_ATTRIBUTES.Empty pattern. – Tim Lovell-Smith Jul 06 '10 at 04:45
  • By the way, I was using p/invoke to create a process because I want to do things with process privileges that aren't going to be supported by managed APIs. But otherwise yeah, I would have used System.Diagnostics.Process class. – Tim Lovell-Smith Sep 13 '12 at 18:35
  • But the problem with Empty property is that you can't pass it as a ref parameter. Given that many of Win32 APIs dealing with structs expects an address, ref is needed. – Adarsha Oct 19 '13 at 18:40