.NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez
-
Upload
net-uy-meetup -
Category
Technology
-
view
584 -
download
0
description
Transcript of .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez
![Page 1: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/1.jpg)
Platform Invocation Services
P/Invoke
Juan Ramírez (@ichramm)@NETUYMeetup
![Page 2: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/2.jpg)
● Intro con dibujito● Qué es? Qué hace? Para qué sirve?● Analizando el problema
○ P/Invoke vs C++/CLI● DllImport y sus allegados● Data Marshalling
○ Data Types○ Estructuras○ Callbacks
Agenda
![Page 3: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/3.jpg)
Qué es? Que hace? Para qué sirve?● Es una funcionalidad del CLR ...
… que permite invocar código nativo (unmanaged) desde .Net (managed).
● Es muy flexible, y con defaults razonables.
![Page 4: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/4.jpg)
DLL
Common Language Runtime
Managedsourcecode
DLL Function
Unmanaged Managed
Assembly
Metadata
MSIL code
DLL Function
DLL Function
P/Invoke Compiler
“Permite ejecutar native code desde managed code”
![Page 5: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/5.jpg)
PARA QUÉSIRVE?
y...
![Page 6: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/6.jpg)
Problema● Se desea utilizar cierta API nativa, que no
tiene bindings para .Net.
● Debe ser Portable.
● No se dispone del código fuente de la librería.
![Page 7: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/7.jpg)
Ejemplos (poco concretos)
● API de Windows
● Librería que implementa determinado protocolo.
● Interoperabilidad con distintos frameworks
![Page 8: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/8.jpg)
Y qué pasa con C++/CLI?● Crear un wrapper sería más sencillo. ✓
● Buena performance, mejor que P/Invoke en algunos casos. ✓
● Estáticamente type-safe. ✓
● Transiciones entre managed y unmanaged son sencillas y seguras. ✓
![Page 9: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/9.jpg)
Y qué pasa con C++/CLI?● No es portable (solo Windows). ✘
● Se necesita el código fuente de la librería. ✘
● Hay que tocar otro lenguaje, una especie de C++ maquillado. ✘
![Page 10: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/10.jpg)
Y con P/Invoke?● Solo necesito la DLL. ✓
● Es portable (mono project). ✓
● Solo tengo que tocar C#. ✓
![Page 11: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/11.jpg)
Peeeero...● No es type-safe. ✘
● Más lento en ciertos casos. ✘
● Se puede volver muy complicado. ✘
![Page 12: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/12.jpg)
En resumen
● Es la solución a un problema
● Permite utilizar funcionalidades que de otra manera no se podrían utilizar.
![Page 13: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/13.jpg)
GETTING BUSINESSTO
DOWN
Basta de cháchara...
![Page 14: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/14.jpg)
Ejemplo
// Usage: bool copied = CopyFile(textBox1.Text, textBox2.Text, true);
using System.Runtime.InteropServices;
[DllImport("kernel32.dll")]
private static extern bool CopyFile (
string lpExistingFileName,
string lpNewFileName,
bool bFailIfExists
);
![Page 15: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/15.jpg)
● Indica que el método es expuesto por una DLL nativa (class DllImportAttribute).
/- DllImport
● El keyword extern indica que el método está implementado en otro lado.
● En nuestro ejemplo:○ Dll Nativa: kernel32.dll○ Función : CopyFile
![Page 16: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/16.jpg)
Ejemplo[DllImport("kernel32.dll",
CharSet = CharSet.Unicode)]
private static extern bool CopyFile (
[MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName,
[MarshalAs(UnmanagedType.LPWStr)] string lpNewFileName,
bool bFailIfExists
);
// Usage: bool copied = CopyFile(textBox1.Text, textBox2.Text, true);
![Page 17: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/17.jpg)
/- CharSet● Marshalling de strings
○ Charset.Ansi -> char*○ Charset.Unicode -> wchar_t*
● Name matching○ Charset.Ansi -> CopyFileA○ Charset.Unicode -> CopyFileW
![Page 18: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/18.jpg)
Ejemplo[DllImport("kernel32.dll",
CharSet = CharSet.Unicode,
ExactSpelling = true)]
private static extern bool CopyFile (
[MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName,
[MarshalAs(UnmanagedType.LPWStr)] string lpNewFileName,
bool bFailIfExists
);
![Page 19: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/19.jpg)
/- CharSet vs ExactSpelling ● ExactSpelling deshabilita name matching.
● La función CopyFile no existe en kernel32.dll.
● Sí existen CopyFileA y CopyFileW
![Page 20: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/20.jpg)
[DllImport("kernel32.dll",
CharSet = CharSet.Unicode,
ExactSpelling = true)]
private static extern bool CopyFileW (
[MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName,
[MarshalAs(UnmanagedType.LPWStr)] string lpNewFileName,
bool bFailIfExists
);
/- CharSet vs ExactSpelling Posible solución
![Page 21: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/21.jpg)
Ejemplo[DllImport("kernel32.dll",
CharSet = CharSet.Unicode,
SetLastError = true, // This function sets last error
CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CopyFile (
[MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName,
[MarshalAs(UnmanagedType.LPWStr)] string lpNewFileName,
[MarshalAs(UnmanagedType.Bool)] bool bFailIfExists
);
![Page 22: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/22.jpg)
Ejemplo[DllImport("kernel32.dll",
CharSet = CharSet.Unicode,
SetLastError = true, // This function sets last error
CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CopyFile (
[MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName,
[MarshalAs(UnmanagedType.LPWStr)] string lpNewFileName,
[MarshalAs(UnmanagedType.Bool)] bool bFailIfExists
);
![Page 23: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/23.jpg)
/- CallingConvention● Esquema que establece como una función recibe
parámetros y devuelve un resultado.
● El código necesario lo genera el propio compilador (C/C++).
● El default en .Net es Winapi (StdCall).
● “... quien limpia el stack”.
![Page 24: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/24.jpg)
/- CallingConvention
StdCall● El callee limpia el
stack.
● Default en el Windows API.
Cdecl● El caller limpia el
stack.
● Default en Visual C++.
![Page 25: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/25.jpg)
Ejemplo[DllImport("kernel32.dll",
CharSet = CharSet.Unicode,
SetLastError = true,
CallingConvention = CallingConvention.StdCall,
EntryPoint = "CopyFile")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool NativeCopyFile (
[MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName,
[MarshalAs(UnmanagedType.LPWStr)] string lpNewFileName,
[MarshalAs(UnmanagedType.Bool)] bool bFailIfExists
);
![Page 26: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/26.jpg)
/- EntryPoint● Especifica el nombre con el que se va a buscar
la función.
● O el índice de la función dentro de la DLL .○ Ej: EntryPoint = "#167"
● El default es el nombre del método marcado con el DllImport.
![Page 27: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/27.jpg)
Ejemplo
void CopyFile(string from, string to)
{
bool copied = NativeCopyFile(from, to, true);
if (copied == false)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
![Page 28: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/28.jpg)
LOCOSEJEMPLITOSDOS
> git checkout example1
> git checkout example2
![Page 29: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/29.jpg)
● Se trata de una especie de transformación de datos.
● Desde el mundo managed al mundo nativo.
● Desde el mundo nativo al mundo managed.
Data Marshaling
![Page 30: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/30.jpg)
Para cada data type de .net existe un data type unmanaged por defecto.
● int -> int● bool -> BOOL /ojo! son 4 bytes● string -> char * (o TCHAR)● etc
Data Marshaling / Data Types
![Page 31: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/31.jpg)
Data Marshaling / Data Types / System.IntPtr
● Se utiliza para representar punteros (o handles).
● IntPtr es ideal para tipos opacos.
● Es platform-specific (x86, x64).
![Page 32: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/32.jpg)
Ejemplo
[DllImport("kernel32.dll")]
public static extern IntPtr OpenMutex(
uint dwDesiredAccess, bool bInheritHandle, string lpName
);
// typedef void * HANDLE; -> winnt.h
HANDLE WINAPI OpenMutex(
DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName
);
![Page 33: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/33.jpg)
Data Marshaling / Data Types / System.String
● Ya vimos como pasar strings desde el mundo managed al mundo unmanaged.
● Vamos a ver como se hace al revés.
![Page 34: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/34.jpg)
DWORD WINAPI GetModuleFileName(
HMODULE hModule, LPTSTR lpFilename, DWORD nSize
);
[DllImport("kernel32.dll")]
public static extern uint GetModuleFileName(
IntPtr hModule,
// System.String is not mutable StringBuilder lpFilename,
[MarshalAs(UnmanagedType.U4)] int nSize
);
Data Marshaling / Data Types / System.String
![Page 35: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/35.jpg)
Ejemplo
public string StartupPath {
get {
StringBuilder sb = new StringBuilder(260);
GetModuleFileName(IntPtr.Zero, sb, sb.Capacity);
// TODO: Handle error
return sb.ToString();
}
}
![Page 36: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/36.jpg)
EJEMPLITOOTRO
> git checkout example3
![Page 37: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/37.jpg)
● Se debe especificar el layout● Members pueden llevar atributos
Data Marshaling / Structs
[StructLayout(LayoutKind.Sequential)]public class LOGFONT {
// members...
[MarshalAs(UnmanagedType.ByValTStr,
SizeConst = LF_FACESIZE)]
public string lfFaceName;
![Page 38: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/38.jpg)
● El layout se puede configurar explícitamente [StructLayout(LayoutKind.Explicit)]public class LOGFONT {
[FieldOffset(0)] public int lfHeight;
// more members…
}
Data Marshaling / Structs
![Page 39: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/39.jpg)
● Cual es el tamaño de este struct?public struct Test { public byte a;
public int b;
public short c;
public byte d;
}
Data Marshaling / Structs● Ojo con el padding!!
12
![Page 40: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/40.jpg)
JUMP!
> git checkout example4
![Page 41: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/41.jpg)
● Mapean directo con delegates
Data Marshaling / Callbacks
● Mismas reglas que con las demas funciones.
● Una forma de llamar funciones de .Net desde el mundo unmanaged.
![Page 42: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/42.jpg)
Ejemplo
ESME_HANDLE SMPP_API libSMPP_ClientCreate (
OnIncomingMessageCallback onNewMessage
);
![Page 43: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/43.jpg)
Ejemplo
typedef void (*OnIncomingMessageCallback)(
ESME_HANDLE hClient,
const char* from,
const char* to,
const char* content, // UTF-8
unsigned int size
);
![Page 44: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/44.jpg)
Ejemplo
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void IncomingMessageHandler(
IntPtr hClient,
string from,
string to,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)]
byte[] content,
[MarshalAs(UnmanagedType.U4)] int bufferSize
);
![Page 45: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/45.jpg)
Ejemplo
[DllImport("inconcertsmpp.dll"
, CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr libSMPP_ClientCreate(
[MarshalAs(UnmanagedType.FunctionPtr)]
IncomingMessageHandler onNewMessage
);
![Page 46: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/46.jpg)
hClient = libSMPP_ClientCreate(
new IncomingMessageHandler( delegate(
IntPtr hClient, string from, string to,
byte[] content, int bufferSize)
{
string text = Encoding.UTF8.GetString(content);
// ...
});
Ejemplo
![Page 47: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/47.jpg)
Pero...
● Ese código vuela por los aires
● El garbage collector se lleva el delegate
● Hay que mantener vivas las referencias a esos elementos
![Page 48: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/48.jpg)
m_handler = new IncomingMessageHandler( delegate(
IntPtr hClient, string from, string to,
byte[] content, int bufferSize)
{
string text = Encoding.UTF8.GetString(content);
// ...
};
hClient = libSMPP_ClientCreate(m_handler);
Ejemplo
![Page 49: .NET UY Meetup 6 - Integrando con C/C++ por medio de P/Invoke by Juan Ramirez](https://reader034.fdocuments.es/reader034/viewer/2022051313/5482e06cb07959570c8b48c9/html5/thumbnails/49.jpg)
Gracias! Didn’t make it:● Keywords: unsafe, fixed● Custom Marshallers● Punteros