2011-12-03 8 views
8

Esperemos que esto no es demasiado oscura para el SO, pero considere lo siguiente P/Invoke firma:¿Cómo se puede usar un SafeHandle en una firma de P/Invoke que requiere un puntero nulo en ciertos casos?

[DllImport("odbc32.dll", CharSet = CharSet.Unicode)] 
internal static extern OdbcResult SQLAllocHandle(
    OdbcHandleType HandleType, 
    IntPtr InputHandle, 
    ref IntPtr OutputHandlePtr); 

me gustaría rediseñar esta firma a utilizar SafeHandles, de la siguiente manera:

[DllImport("odbc32.dll", CharSet = CharSet.Unicode)] 
internal static extern OdbcResult SQLAllocHandle(
    OdbcHandleType HandleType, 
    MySafeHandle InputHandle, 
    ref MySafeHandle OutputHandlePtr); 

Sin embargo, according to MSDN, el argumento InputHandle debe ser un puntero nulo cuando el argumento HandleType es SQL_HANDLE_ENV y un puntero no nulo en caso contrario.

¿Cómo capturo esa semántica en una sola firma de P/Invocar? Incluya un ejemplo de sitio de llamadas en su respuesta. Mi solución actual es usar dos firmas.

Respuesta

4

SafeHandle es una clase por lo que debería poder pasar null en lugar de SafeHandle. Una referencia nula se clasifica como un puntero nulo en P/Invoke.

SafeHandle handle = new SafeHandle(); 
OdbcResult result= SQLAllocHandle(OdbcHandleType.SQL_HANDLE_ENV, null, ref handle); 
+2

También es posible declarar un parámetro 'out SafeHandle'. –

+5

No funciona aquí, obtengo una 'ArgumentNullException' de' System.StubHelpers.StubHelpers.SafeHandleAddRef (SafeHandle pHandle, Boolean & success) '. – ordag

+0

Sí, @ shf301, esto no funciona en las llamadas P/Invoke. ¿Lo intentaste? – jnm2

1

The answer by shf301 pasa null para el argumento de entrada InputHandle. Esto no funciona en la mayoría de las API (quizás de alguna manera lo hace para el problema específico de OP, dado que aceptaron la respuesta).

Puedo usar este patrón:

[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] 
public class RegionHandle : SafeHandleZeroOrMinusOneIsInvalid 
{ 
    private RegionHandle() : base(true) {} 

    public static readonly RegionHandle Null = new RegionHandle(); 

    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
    override protected bool ReleaseHandle() 
    { 
     return Region.DeleteObject(handle); 
    } 
} 

Significa que puedo hacer esto para pasar un mango nulo:

SomeApi(RegionHandle.Null); 

Es similar a la forma en que hay un miembro estático IntPtr.Zero.

Cuestiones relacionadas