2010-11-14 14 views
9

tengo que importar dll no administrado en mi aplicación C#, Quiero saber cuál es la diferencia entre IntPtr y ref, y lo que me recomendó utilizar y por qué? Tenga en cuenta que ambas formas me funcionan. Por ejemplo:IntPtr vs ref C#

[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)] 
static extern Result Init(IntPtr versionInfo); 

[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)] 
public static extern Result Init(ref Version versionInfo); 

Respuesta

3

Editar: En base a algunas críticas me obliga a reconsiderar estas y otras investigaciones más a fondo. Admito libremente que IntPtr es propenso a errores, creo que puede haber cierto debate sobre lo que significa "más difícil", pero si el marco puede ordenarlo automágicamente y evitar que tengas que anclar cosas, etc., si no usas IntPtr, entonces creo Estoy de acuerdo en que IntPtr sería "más difícil. Me parece después de más investigaciones y pensé que sería más fácil (cuando te fuerzan a P/Invocar y no puedes crear un contenedor C++/CLI) declarar tu estructura (en su caso version) en el código administrado y luego pasarlo a través del parámetro ref.

[StructLayout(LayoutKind.Sequential)] 
struct Version 
{ 
    // Data members 
} 

[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)] 
public static extern Result Init(ref Version versionInfo); 

Mi conjetura sería que usted quiere la versión IntPtr. la diferencia es que IntPtr es una clase que maneja un puntero a memoria, mientras que declarar un parámetro como referencia (palabra clave ref) en una lista de argumentos significa que estás pasando por referencia. En general, cuando paso datos hacia y desde una dll no administrada a través de una llamada P/Invoke, a menos que los datos que se pasen sean de tipo vainilla (int, doble, cadena - con decoración de Mariscal adecuada) paso datos como IntPtr. A continuación, puede Marshal the IntPtr a su declaración gestionada de la estructura no gestionada.

Debe retirar este enlace para más información sobre P/Invoke llamadas: http://msdn.microsoft.com/en-us/magazine/cc164123.aspx

+0

Es lamentable que esté haciendo las cosas de la manera difícil, propensa a errores. Es aún más desafortunado que le enseñe a otras personas a hacerlo también. –

+0

@Ben Me complace aprender una manera más fácil/menos propensa a errores para hacerlo. ¿Tiene un enlace o material de lectura sugerido?No paso mucho tiempo P/Invocando cosas, la mayoría de las veces escribo un wrapper de C++/CLI, pero cuando tengo que usar P/Invoke solo he usado los parámetros ref para cosas como int, string (StringBuilder en casos ven), etc., y luego usó IntPtr para las otras cosas. – pstrjds

+1

C++/CLI para la victoria :) Para aquellas ocasiones en las que no se puede usar (Windows CE, o no se puede estar seguro de que se instalará el tiempo de ejecución de Visual C++), entonces http://pinvoke.net tiene un horrible muchos ejemplos Generalmente, no solo los tipos primitivos como 'int' y' double' se pueden usar con seguridad en firmas p/invoke, sino también tipos definidos por el usuario compuestos por tipos primitivos. Los ejemplos serían 'POINT',' POINTF', 'RECT',' FONTMETRIC', 'BITMAPFILEHEADER', etc. –

5

Si Version es una estructura que es compatible con la estructura que la función extern Init espera, no hay una diferencia significativa entre los dos, excepto que la versión ref será un lote más fácil de usar desde C#, ya que el tiempo de ejecución administrará todas las referencias y ajustes por usted. A menos que realmente quiera hacer todo ese trabajo, me quedaré con la opción ref.

Por supuesto, sin ver el prototipo de la función C, la estructura Version en C#, y la estructura utilizada en C para ese parámetro, realmente solo puedo adivinar.

+2

Hay una diferencia potencialmente significativa: el uso de IntPtr permite pasar IntPtr.Zero como un valor, mientras que 'ref Version' no lo hace. Por lo tanto, no es inusual que necesite la sobrecarga IntPtr, dependiendo de lo que requiera el código nativo. – jonp

+2

@jonp: en ese caso, querría ** ambas ** versiones (una función sobrecargada), para que pueda pasar una 'Versión' por referencia cuando tenga una, y' IntPtr.Zero' cuando quiera pasar nulo. O haga de 'Versión' una clase en lugar de una estructura, luego puede pasar nulo (y ya no necesita la palabra clave' ref', pero los atributos de anotación p/invocar son necesarios en la clase). –

-1

Es mejor usar IntPtr porque hará que su código sea más flexible cuando se trata de compilar el código en x64. IntPtr ampliará su tamaño automáticamente.

IntPtr = 4 bytes on x86 
IntPtr = 8 bytes on x64 

hay casos en que ciertos tipos de c Haga doble que el tamaño cuando se compila para x64, pero los homólogos de C# (que se declaran para PInvoke) no. Entonces la llamada a la función fallará. Si la clase Version contiene algún campo como ese, entonces es mejor que use IntPtr.

+0

'ref' también pasará un parámetro de tamaño de puntero. El resto de su respuesta parece que está hablando de las entrañas de la estructura 'Versión ', no del parámetro en sí. Si bien es posible que algunos de los campos de 'Version' tengan el tipo' IntPtr', no es motivo para no utilizar 'ref Version' en la declaración p/invoke. –

+0

@Ben No entendiste la idea, creo que creo que me expresé muy bien. No dije que usar ref no funcionaría, pero en mi opinión habría usado IntPtr. Pero gracias por el voto negativo cuando no respondes. –

+0

@ Liviu: He votado negativamente (y evidentemente no soy el único) porque tu respuesta es INCORRECTA. Usted dijo que "' IntPtr' hará que su código sea más flexible "wrt el tamaño del puntero, y esto es simplemente falso. 'ref' e' IntPtr' son del mismo tamaño en todas las plataformas. –

2

IntPtr le permitirá mover todos sus Externs a una clase, sin dependencias de las estructuras que se utilizan en las llamadas, que pueden definirse en otro lugar. Me gusta mantener el adaptador de API separado de los datos struct/class defs que parecen más naturales para poner en la siguiente capa.