C# y P/Invoke

Supongamos que estamos programando nuestra nueva gran aplicación en C# y nos encontramos con que necesitamos de forma imperiosa hacer uso de una función del API WIN32 que no está encapsulada por el framework .NET, o cualquier otra función con código no administrado. ¿Qué deberíamos hacer? ¿abandonar el proyecto? ¿abandonar la plataforma .NET? ¿abandonar la programación enteramente y dedicarnos a otra cosa?

Son distintas soluciones, pero si ninguna de ellas te convence, una alternativa mejor pasa por utilizar P/Invoke (Platform Invocation Services o Servicios de Invocación de Plataforma), que es una de las formas que tenemos de utilizar código no administrado en .NET.

El uso de P/Invoke es de lo más sencillo: para poder llamar a una función de código no administrado basta con informar al compilador de C# del aspecto que tiene dicha función utilizando el atributo DllImport del espacio de nombres System.Runtime.InteropServices. En la mayoría de los casos el framework se encargará de adivinar cómo traducir los parámetros y el valor de retorno entre los tipos administrados y no administrados (en caso contrario tendríamos que hacer uso del atributo MarshalAs).

DllImport tiene como parámetros básicos el nombre de la dll que contiene el código no administrado y el nombre de la función a llamar (parámetro EntryPoint).

[DllImport("milibreria.dll", EntryPoint="suma")]

Después del atributo DllImport irá la declaración de la función que utilizaremos para llamar al código externo. Esta función debe acompañarse de los modificadores static y extern, de los que ya hablamos en Introducción a C# para programadores Java. Además, evidentemente, los tipos y valores de retorno deben corresponderse con las contrapartidas no administradas de la función original.

[DllImport("milibreria.dll", EntryPoint="suma")]
public static extern int Sumar(int num1, int num2);

En este paso te puede ser de utilidad la web pinvoke.net, un wiki en el que podemos encontrar los prototipos para llamar a cientos de funciones de Win32 y otras APIs no administradas.

Y ya está; es así de fácil, con esto ya podremos llamar a la nueva función como si de una función normal se tratara. De hecho, si no nos importa utilizar como nombre para nuestra nueva función el nombre de la función no administrada, podemos incluso ahorrarnos el parámetro EntryPoint, con lo que el código final tendría el siguiente aspecto:

using System.Runtime.InteropServices;

class Program {

    [DllImport("milibreria.dll")]
    static extern int suma(int num1, int num2);

    static void Main(string[] args) {
        suma(3, 4);
    }
}


Comentarios
  1. Gracias @Zootropo por compartir conocimientos e información sobre los diversos lenguajes de programación.

    Responder

  2. Yo estoy cuestinandome si darle una oportunidad a C# vía Mono, pero aún no me decido 🙁

    Responder

    • Bueno, puedes echarle un pequeño vistazo por simple curiosidad y seguir con él si te va gustando lo que ves.

      Responder

      • Es que sigo preocupado por la cuestión de las patentes, a pesar del community promise.

        Responder

  3. […] C# y P/Invoke Share and Enjoy: […]

    Responder

  4. Definitivamente si uno quiere hacer una aplicacion seria en c# es casi 100% problable que tengas q hacer uso del pinvoke.

    y su pagina http://www.pinvoke.net/ tiene todo lo que puedas necesitar, saludos.

    Responder

  5. nifnis

    mu buenas
    Bueno mi pregunta (despues de horas buscando la respuesta) es simple y creo que se la respuesta pero…
    ¿si utilizo como S.O ubuntu 9.04 no puedo realizar las llamadas a las api de windows verdad?
    mi proyecto utiliza la user32.dll(para findwindow)
    y no consigo que ande aunque se ejecuta sin error…
    bueno gracias por todo y perdonarme si el post no va aqui gracias

    Responder

  6. nifnis

    ok gracias por responder
    le echare un vistazo a ver que pasa

    Responder

  7. Smille

    Hola, tengo un proyecto que usa el pinvoke y DllImport tal como lo explicaste , pero aun asi no funciona no puedo agregar la referencia al archivo.dll me sale un error:
    No se peude tener acceso a la referencia : XX/xxx/x.dll asegurese de que tiene acceso al archivo y de que es un ensamblado o componente COM valido ( es obvio)

    en k parte le agrego el archivo DLL al que hago referencia al usar el pinvoke ??
    respondeme por favor, estare muy agradecida

    Responder

    • LinKat

      A mi tbm m sale eso quien podra ayudarnos, yo digo q es x el sistema operativo o xq no contamos con el tipo de puerto a conectar mi caso es el d un semaforo y yo no cuento con puerto LPT1

      Responder

  8. LinKat

    Una preguntita yo también uso esto del dll la cosa es q mi Laptop no cuenta con puerto LPT1 y encima mi sistema es windows 7. y necesito agregar una referencia llamada inpout32.dll y no me jala xq no es compatible con mi sistema xq yo utilizo el d 64 bits y no el d 32 🙁 alguna sugerencia parqa este error?

    Una llamada a la función PInvoke ‘Puerto_paralelo!Puerto_paralelo.PortInterop::Input’ impidió la correspondencia de la pila. Es posible que la razón sea que la firma PInvoke administrada no coincida con la firma de destino no administrada. Compruebe que la convención y los parámetros de llamada de la firma PInvoke coinciden con la firma no administrada de destino.

    Responder

  9. Eder Govea

    Hola que tal, tengo una duda, ojala me puedas ayudar. Tengo un proyecto en C++ en la que uso archivos .h, .lib y Dll compilados por mi. Migré a C# y ahora necesito utilizar una función de un .h, te pregunto, ¿Es posible hacer eso con el DllImport? si no es así, sabrías que otra opción puedo tomar?
    Saludos

    Responder

Deja un comentario