views:

53

answers:

2

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

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 when I try to pass null as 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'

I would like to know how to modify the above function signature so that I can pass null for the SECURITY_ATTRIBUTES arguments without this compiler error. (But preferably can also decide to pass a real struct if I want to).

A: 

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
Thanks, I like this SECURITY_ATTRIBUTES.Empty pattern.
Tim Lovell-Smith
A: 

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