2010-07-06 14 views
6

Las funciones como CreateProcess tienen firmas que toman punteros a structs. En C, simplemente pasaría NULL como un puntero para los parámetros opcionales, en lugar de crear un objeto struct ficticio en la pila y pasar un puntero al maniquí.P/Función de invocación que toma el puntero a struct

En C#, que han declarado como (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); 

Pero si trato de pasar null para el argumento lpProcessAttributes o lpThreadAttributes argumento, me sale un error de compilación:

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

¿Cómo puedo modificar la firma de función anterior para que pueda pasar null por los argumentos SECURITY_ATTRIBUTES, sin este error de compilación? (¿Y también podrá pasar una estructura real si quiero?)

+0

Parece que mi pregunta es la misma que esta, aunque no lo habría adivinado por el título. http://stackoverflow.com/questions/1049623/how-to-pass-a-nullable-type-to-a-p-invoked-function –

Respuesta

3

null es solo válido para los tipos de referencia en .Net. su SECURITY_ATTRIBUTES es struct, que es ValueType. En lugar de pasar nulo, debe pasar una estructura SECURITY_ATTRIBUTES vacía. (solo diga new SECURITY_ATTRIBUTES()) en su llamada.

Un método más limpio es añadir una propiedad vacía estática a su estructura, y sólo tiene que pasar 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, 
      }; 
     } 
    } 
} 

O mejor aún, en lugar de utilizar P/Invoke para crear un proceso, echa un vistazo a la clase System.Diagnostics.Process, que probablemente debería hacer lo que usted necesita. (!)

+0

Gracias, me gusta este SECURITY_ATTRIBUTES.Empty pattern. –

+0

Por cierto, estaba usando p/invoke para crear un proceso porque quiero hacer cosas con privilegios de proceso que las API administradas no van a admitir. Pero de lo contrario, sí, habría utilizado System.Diagnostics.Process class. –

+0

Pero el problema con la propiedad Empty es que no se puede pasar como un parámetro ref. Dado que muchas de las API de Win32 que tratan con estructuras esperan una dirección, se necesita una referencia. – Adarsha

6

bien, finalmente he encontrado una mejor forma de hacer esto:

Declarar SECURITY_ATTRIBUTES como clase en lugar de estructura y no debe dárselo por 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; 
} 

Bono: esto también permite declarar un constructor decente en SECURITY_ATTRIBUTES que inicializa nLongitud.

+0

Funciona sin importar a qué cambie el nombre de la clase. ¡Gran respuesta! –

+1

Me pregunto si esto es seguro. El atributo 'StructLayout' solo puede afectar las estructuras, no las clases. Entonces quizás el JIT de .NET pueda diseñar los campos en esta clase de la forma que quiera. Por ejemplo, me pregunto si esto podría fallar en x64 u otras plataformas. Puede que no sea un truco seguro. –

+0

Acabo de escuchar a un experto interno de CLR de MSFT que este es un truco seguro. :) –

Cuestiones relacionadas