P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP [email protected] MVP...

36
P Invoke e COM P Invoke e COM Interoperability Interoperability Raffaele Rialdi Raffaele Rialdi Visual Developer Security Visual Developer Security MVP MVP [email protected] [email protected] MVP Profile: http://snipurl.com/f0cv MVP Profile: http://snipurl.com/f0cv http:// http:// mvp.support.microsoft.com mvp.support.microsoft.com latform latform

Transcript of P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP [email protected] MVP...

Page 1: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

P Invoke e COM P Invoke e COM InteroperabilityInteroperability

Raffaele RialdiRaffaele RialdiVisual Developer Security MVPVisual Developer Security [email protected]@vevy.comMVP Profile: http://snipurl.com/f0cvMVP Profile: http://snipurl.com/f0cv

http://mvp.support.microsoft.comhttp://mvp.support.microsoft.com

latformlatform

Page 2: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

AgendaAgenda

• Platform Invocation ServicesPlatform Invocation Services

• COMCOM• Usare oggetti COM in dotnetUsare oggetti COM in dotnet

• Usare Activex in dotnetUsare Activex in dotnet

• Esporre oggetti COM da dotnetEsporre oggetti COM da dotnet

• Esporre Activex da dotnetEsporre Activex da dotnet

• Reverse P/InvokeReverse P/Invoke

Page 3: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Platform InvokePlatform Invoke

• Servizio del framework che permette di chiamare le API Servizio del framework che permette di chiamare le API esportate in una DLL "C-style"esportate in una DLL "C-style"• Gli attrezzi del mestiere sono dentro Gli attrezzi del mestiere sono dentro System.Runtime.InteropServicesSystem.Runtime.InteropServices

• Il protagonista è l'attributo DllImportAttribute che svolge (tra le Il protagonista è l'attributo DllImportAttribute che svolge (tra le altre cose) le classiche operazioni di:altre cose) le classiche operazioni di:• LoadLibrary (carica la dll in memoria) – Free (scarica la dll)LoadLibrary (carica la dll in memoria) – Free (scarica la dll)• GetProcAddress (ottiene un puntatore alla funzione richiesta)GetProcAddress (ottiene un puntatore alla funzione richiesta)• Marshalling (mapping e passaggio parametri)Marshalling (mapping e passaggio parametri)

• Molto aiuto viene dal wiki Molto aiuto viene dal wiki www.pinvoke.netwww.pinvoke.net con molte con molte dichiarazioni già pronte per la API di Windowsdichiarazioni già pronte per la API di Windows

Page 4: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

DllImportAttributeDllImportAttributestep-by-stepstep-by-step

• Ottenere la Ottenere la documentazione documentazione (MSDN, ...)(MSDN, ...)

• Cercare la Cercare la funzione funzione prototipoprototipo

• Cercare il nome Cercare il nome della dll dove è della dll dove è implementataimplementata

[DllImport("PowrProf.dll")][DllImport("PowrProf.dll")]extern static bool IsPwrHibernateAllowed();extern static bool IsPwrHibernateAllowed();

<DllImport("PowrProf.dll")> _<DllImport("PowrProf.dll")> _Function IsPwrHibernateAllowed() As BooleanFunction IsPwrHibernateAllowed() As BooleanEnd FunctionEnd Function

Page 5: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

DllImportAttributeDllImportAttributestep-by-stepstep-by-step[DllImport("User32.dll")][DllImport("User32.dll")]extern static int MessageBox(IntPtr hWnd, extern static int MessageBox(IntPtr hWnd,

string Text, string Caption, string Text, string Caption, int uTypeint uType););

MSDNMSDN

public enum MessageBoxUType{

MB_OK = 0x00000000,MB_OKCANCEL = 0x00000001,MB_ABORTRETRYIGNORE = 0x00000002,MB_YESNOCANCEL = 0x00000003,MB_YESNO = 0x00000004,MB_RETRYCANCEL = 0x00000005,MB_CANCELTRYCONTINUE = 0x00000006

}

A MANOA MANO

ma a volte su:ma a volte su:www.pinvoke.netwww.pinvoke.net

dichiarare con const oppure enum (meglio)dichiarare con const oppure enum (meglio)

Page 6: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

DllImportAttributeDllImportAttributeNomi delle APINomi delle API• Le API di Windows che usano stringhe esistono in due versioni: Le API di Windows che usano stringhe esistono in due versioni:

MessageBoxA (MessageBoxA (AAnsi), MessageBoxW (nsi), MessageBoxW (WWide = Unicode)ide = Unicode)• ExactSpelling = falseExactSpelling = false aggiunge automaticamente questo suffisso (A o W) aggiunge automaticamente questo suffisso (A o W)

• Il default che è DllImport("MessageBox") cerca in sequenza:Il default che è DllImport("MessageBox") cerca in sequenza:• MessageBox, MessageBoxA, MessageBoxW, _MessageBox@N (N=numero MessageBox, MessageBoxA, MessageBoxW, _MessageBox@N (N=numero

che rappresenta la dimensione dei parametri del metodo)che rappresenta la dimensione dei parametri del metodo)

• È possibile cambiare il nome esatto tramite "EntryPoint"È possibile cambiare il nome esatto tramite "EntryPoint"

• A volte aiuta ilA volte aiuta il"dependancy walker""dependancy walker"(http://www.dependencywalker.com/)(http://www.dependencywalker.com/)

[DllImport("User32.dll", [DllImport("User32.dll", EntryPoint="MessageBoxW"EntryPoint="MessageBoxW", , ExactSpelling=trueExactSpelling=true, , CharSet=CharSet.AutoCharSet=CharSet.Auto)])]

extern static int extern static int MBoxMBox(IntPtr hWnd, string Text, string Caption, int uType);(IntPtr hWnd, string Text, string Caption, int uType);

Page 7: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

DllImportAttributeDllImportAttributeCalling ConventionCalling Convention• Le calling convention sono una sorta di contratto alla Le calling convention sono una sorta di contratto alla

compilazione che prevede:compilazione che prevede:• come passare gli argomenti delle funzioni ed il valore di ritornocome passare gli argomenti delle funzioni ed il valore di ritorno• quali registri della CPU devono essere salvatiquali registri della CPU devono essere salvati

• Le quattro convenzioni più usate oggi sono:Le quattro convenzioni più usate oggi sono:• __cdecl__cdecl usato dalle librerie C e numerose APIusato dalle librerie C e numerose API• __stdcall__stdcall conosciuta anche come "pascal", usata dalle conosciuta anche come "pascal", usata dalle Win32 Win32

APIAPI• __fastcall__fastcall usa i registri per passare gli argomentiusa i registri per passare gli argomenti• __thiscall__thiscall default per le chiamate a funzioni membro in C++default per le chiamate a funzioni membro in C++

• Perché chiamata a funzione abbia successo la calling Perché chiamata a funzione abbia successo la calling convention deve essere corretta (StdCall è il default)convention deve essere corretta (StdCall è il default)[DllImport("User32.dll", [DllImport("User32.dll", CallingConvention=CallingConvention.StdCallCallingConvention=CallingConvention.StdCall)])]

extern static int MessageBox(IntPtr hWnd, string Text, string Caption, int uType);extern static int MessageBox(IntPtr hWnd, string Text, string Caption, int uType);

Page 8: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

DllImportAttributeDllImportAttributeGestione erroriGestione errori• Le Win32 mantengono una variabile di stato per gli erroriLe Win32 mantengono una variabile di stato per gli errori

• Problema: se dopo l'API con errore ne vengono eseguite altre senza Problema: se dopo l'API con errore ne vengono eseguite altre senza errore, questo va persoerrore, questo va perso

• Usare via PInvoke la funzione GetLastError() è Usare via PInvoke la funzione GetLastError() è sbagliatosbagliato

• DLLImport provvede alla proprietà SetLastErrorDLLImport provvede alla proprietà SetLastError• L'errore viene letto subito dal CLR e messo da parteL'errore viene letto subito dal CLR e messo da parte• Può essere letto con il metodo Marshal.GetLastWin32Error()Può essere letto con il metodo Marshal.GetLastWin32Error()

[DllImport("User32.dll", [DllImport("User32.dll", SetLastError=trueSetLastError=true)])]

extern static int MessageBox(IntPtr hWnd, string Text, string Caption, int uType);extern static int MessageBox(IntPtr hWnd, string Text, string Caption, int uType);

Page 9: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Uno sguardo in memoriaUno sguardo in memoria• Gli oggetti del CLR Gli oggetti del CLR

devono stare nel devono stare nel managed heapmanaged heap

• I buffer, blob, strutture I buffer, blob, strutture dati 'classiche' devono dati 'classiche' devono stare nell'unmanaged stare nell'unmanaged heapheap

• Il problema è quindi il Il problema è quindi il marshalling dei datimarshalling dei dati, cioè , cioè copiare e/o mappare i copiare e/o mappare i dati da un mondo all'altrodati da un mondo all'altro

• La bella notizia è che le La bella notizia è che le tecniche di marshalling tecniche di marshalling valgono anche per valgono anche per l'interoperabilità COMl'interoperabilità COM

Managed heapManaged heapManaged heapManaged heap

Managed StackManaged StackManaged StackManaged Stack

Bitmap

String

DateTime

Int32

ref String

ref Bitmap

Unmanaged heapUnmanaged heapUnmanaged heapUnmanaged heap

char[...]

byte[...]

BSTR

int

FILETIME

Il problema del marshallingIl problema del marshalling"Come mappare i tipi?""Come mappare i tipi?"

Page 10: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Marshalling: blittable typesMarshalling: blittable types

• I blittable types sono i tipi che hanno una rappresentazione I blittable types sono i tipi che hanno una rappresentazione identica sia nel mondo managed che unmanaged:identica sia nel mondo managed che unmanaged:• Byte, SByteByte, SByte• Int16, UInt16Int16, UInt16• Int32, UInt32Int32, UInt32• Int64, UInt64Int64, UInt64• IntPtr, UIntPtrIntPtr, UIntPtr• SingleSingle• DoubleDouble• Array monodimensionali (UInt32[], Byte[], ...)Array monodimensionali (UInt32[], Byte[], ...)• Value types (strutture) contenenti i tipi appena citatiValue types (strutture) contenenti i tipi appena citati

• Array e classi contenenti blittable types vengono "pinned" in memoria invece di Array e classi contenenti blittable types vengono "pinned" in memoria invece di copiare il loro valore nel mondo unmanaged.copiare il loro valore nel mondo unmanaged.

• Anche se "pinned" richiedono comunque l'applicazione degli attributi direzionali [in] Anche se "pinned" richiedono comunque l'applicazione degli attributi direzionali [in] o [out] quando richiestio [out] quando richiesti

Page 11: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Marshalling: le stringheMarshalling: le stringhe

• CharSet influenza la scelta Ansi / Unicode di tutti i parametriCharSet influenza la scelta Ansi / Unicode di tutti i parametri• Si può applicare [MarshalAs(...)] al parametroSi può applicare [MarshalAs(...)] al parametro

• La direzione del parametro è fondamentaleLa direzione del parametro è fondamentale• Se la stringa è [in] nella API, si usa StringSe la stringa è [in] nella API, si usa String• Se la stringa è [out] o [in, out] si usa StringBuilderSe la stringa è [out] o [in, out] si usa StringBuilder

preallocando lo spaziopreallocando lo spazio

• Da MSDN:Da MSDN:

MSDNMSDN[DllImport("Kernel32.dll")][DllImport("Kernel32.dll")]

extern static uint GetCurrentDirectory(uint Size, extern static uint GetCurrentDirectory(uint Size, StringBuilder StringBuilder Buffer);Buffer);

[DllImport("User32.dll)][DllImport("User32.dll)]

extern static int MessageBox(IntPtr hWnd, string Text, string Caption, int uType);extern static int MessageBox(IntPtr hWnd, string Text, string Caption, int uType);

Normalmente Win9xNormalmente Win9x

Normalmente WinNT +Normalmente WinNT +

Page 12: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Marshalling: strutture di datiMarshalling: strutture di dati

• Il CLR non garantisce l'ordine e dimensione dei dati nella strutturaIl CLR non garantisce l'ordine e dimensione dei dati nella struttura• il CLR ottimizza l'allineamento dei dati il CLR ottimizza l'allineamento dei dati guadagnando spazio e performanceguadagnando spazio e performance

• la dimensione di un oggetto managed è sempre e solo presunta la dimensione di un oggetto managed è sempre e solo presunta (Marshal.SizeOf indica la dimensione dell'oggetto dopo il marshalling)(Marshal.SizeOf indica la dimensione dell'oggetto dopo il marshalling)

• a questo proposito: a questo proposito: httphttp://forum.ugidotnet.org/b.asp?m=13367://forum.ugidotnet.org/b.asp?m=13367

• Per ovviare a questo "problema" esiste Per ovviare a questo "problema" esiste StructLayoutAttributeStructLayoutAttribute• Pack consente di imporre l'allineamentoPack consente di imporre l'allineamento

(il default è 8)(il default è 8)

• Size consente di imporre la dimensioneSize consente di imporre la dimensionedella struttura (lasciare spazio, ...)della struttura (lasciare spazio, ...)

• Si possono anche realizzare le "Union"Si possono anche realizzare le "Union"grazie a "Explicit"grazie a "Explicit"

• Si usa ref o out (ByRef) quando si vuoleSi usa ref o out (ByRef) quando si vuolepassare il puntatore all'oggetto.passare il puntatore all'oggetto.Diversamente viene passato by valueDiversamente viene passato by value

[StructLayout([StructLayout(LayoutKind.SequentialLayoutKind.Sequential)])]struct MyStructstruct MyStruct{{

char c;char c;int i;int i;short s;short s;

}}

union MyUnionunion MyUnion{{

int x;int x;float f;float f;

}}

[StructLayout [StructLayout ((LayoutKind.ExplicitLayoutKind.Explicit)])]struct MyUnionstruct MyUnion{{

[[FieldOffset( 0 )FieldOffset( 0 )]]int x;int x;[[FieldOffset( 0 )FieldOffset( 0 )]]float f;float f;

}}

Page 13: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Marshalling di arrayMarshalling di array• Possono esistere più dichiarazioni PInvoke valide per una stessa chiamataPossono esistere più dichiarazioni PInvoke valide per una stessa chiamata

• Usare la falsa riga di dichiarazioni come quelle di www.pinvoke.netUsare la falsa riga di dichiarazioni come quelle di www.pinvoke.net

• Array di stringheArray di stringhe

• Array di struttureArray di strutture

• L'attributo MarshalAs permette di informare il CLR quando la dimensione è L'attributo MarshalAs permette di informare il CLR quando la dimensione è prefissataprefissata

• A volte è necessario fare manualmenteA volte è necessario fare manualmente• Il parametro viene dichiarato IntPtr (puntatore)Il parametro viene dichiarato IntPtr (puntatore)• Si usano i metodi StructureToPtr e PtrToStructure della classe MarshalSi usano i metodi StructureToPtr e PtrToStructure della classe Marshal• Alla fine li vedremo in un esempioAlla fine li vedremo in un esempio

• Tip: In caso di NullReferenceException, provare a imporre il parametro ref (ByRef)Tip: In caso di NullReferenceException, provare a imporre il parametro ref (ByRef)

int f(char **ppArray, int size);int f(char **ppArray, int size);

[ DllImport("...")][ DllImport("...")]public static extern int f([in, out]string[] strings, int size);public static extern int f([in, out]string[] strings, int size);

int f(POINT *pPOINT , int size);int f(POINT *pPOINT , int size);

[ DllImport( "..." )][ DllImport( "..." )]public static extern int f([In, Out] MyPoint[] points, int size);public static extern int f([In, Out] MyPoint[] points, int size);

[MarshalAs(UnmanagedType.ByValArray, SizeConst=128)] [MarshalAs(UnmanagedType.ByValArray, SizeConst=128)]

Page 14: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Marshalling di callbackMarshalling di callback

• Le Callback usate nelle Win32 API sono puntatori a funzioniLe Callback usate nelle Win32 API sono puntatori a funzioni• .NET non ha puntatori a funzioni, ma i delegate che sono type-safe.NET non ha puntatori a funzioni, ma i delegate che sono type-safe

• Esempio EnumDesktopWindowsEsempio EnumDesktopWindows[DllImport("User32.dll")][DllImport("User32.dll")]public extern static bool EnumDesktopWindows(IntPtr hDesktop,public extern static bool EnumDesktopWindows(IntPtr hDesktop,

EnumWindowsProcDelegate lpfnEnumWindowsProcDelegate lpfn, uint lParam);, uint lParam);

public public delegatedelegate bool EnumWindowsProcDelegate(IntPtr hwnd, uint lParam); bool EnumWindowsProcDelegate(IntPtr hwnd, uint lParam);

Page 15: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Pinning della memoria managedPinning della memoria managed

• Quando si passa un reference type viene copiato il reference Quando si passa un reference type viene copiato il reference e fatto il pinning della memoria nel managed heape fatto il pinning della memoria nel managed heap• il pinning automatico funziona spesso ...il pinning automatico funziona spesso ...• ... ad esempio non può funzionare per le chiamate asincrone... ad esempio non può funzionare per le chiamate asincrone

• Quando si passa un value type il valore viene copiato nella Quando si passa un value type il valore viene copiato nella memoria unmanagedmemoria unmanaged• Se si può usare un value type, questo risolve il problema di cui sopraSe si può usare un value type, questo risolve il problema di cui sopra

• Per eseguire il pinning da C# e VB si usa GCHandlePer eseguire il pinning da C# e VB si usa GCHandle• Free rilascia l'handle e permette al GC di muovere il blocco di memoriaFree rilascia l'handle e permette al GC di muovere il blocco di memoria

string s = "blah blah";string s = "blah blah";GCHandle pin = GCHandle.Alloc(s, GCHandleType.Pinned);GCHandle pin = GCHandle.Alloc(s, GCHandleType.Pinned);

try { ... }try { ... }finally { pin.Free(); } finally { pin.Free(); }

Page 16: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

AgendaAgenda

• Platform Invocation ServicesPlatform Invocation Services

• COMCOM• Usare oggetti COM in dotnetUsare oggetti COM in dotnet

• Usare Activex in dotnetUsare Activex in dotnet

• Esporre oggetti COM da dotnetEsporre oggetti COM da dotnet

• Esporre Activex da dotnetEsporre Activex da dotnet

• Reverse P/InvokeReverse P/Invoke

Page 17: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Il mondo COMIl mondo COM

• Ci sono molte differenzeCi sono molte differenze• Serve quindi un wrapperServe quindi un wrapper• I wrapper sono diversi a seconda se si espone un oggetto COM I wrapper sono diversi a seconda se si espone un oggetto COM

a .NET o viceversaa .NET o viceversa

COMCOM .NET.NET

CreateInstanceCreateInstance newnew

Reference countReference count Garbage CollectorGarbage Collector

QueryInterfaceQueryInterface castcast

Errori via HRESULTErrori via HRESULT EccezioniEccezioni

GuidGuid Strong nameStrong name

Type libraryType library MetadatiMetadati

ApartmentsApartments MTA-styleMTA-style

Page 18: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Usare oggetti COM da .NETUsare oggetti COM da .NET• Visual Studio.NETVisual Studio.NET

• TLBimpTLBimp

• Una volta disponibile l'assembly di interopUna volta disponibile l'assembly di interop• Gli oggetti COM si usano come fossero managed (si usa il wrapper)Gli oggetti COM si usano come fossero managed (si usa il wrapper)• Si istanziano con new e si chiamano proprietà e metodiSi istanziano con new e si chiamano proprietà e metodi• addref/release sono gestiti dall'infrastruttura (RCW)addref/release sono gestiti dall'infrastruttura (RCW)

Page 19: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Dietro le quinteDietro le quinte

• RCW = Runtime Callable WrapperRCW = Runtime Callable Wrapper• creato al runtime per parlare con il mondo comcreato al runtime per parlare con il mondo com• si occupa di eseguire il marshalling dei tipisi occupa di eseguire il marshalling dei tipi• esiste un RCW per ogni oggetto (non interfaccia) COMesiste un RCW per ogni oggetto (non interfaccia) COM• si occupa di chiamare Addref/Release/QueryInterfacesi occupa di chiamare Addref/Release/QueryInterface• L'oggetto referenziato da RCW è scaricato dalla memoria se iL'oggetto referenziato da RCW è scaricato dalla memoria se i l suo l suo

reference counter è 0 e cioè se:reference counter è 0 e cioè se:• RCW viene scaricato dal Garbage CollectorRCW viene scaricato dal Garbage Collector oppure oppure

• viene fatto un esplicito Marshal.ReleaseComObjectviene fatto un esplicito Marshal.ReleaseComObject

• RCW wrappa IEnumVariant, IDispatch, IUnknown, RCW wrappa IEnumVariant, IDispatch, IUnknown, IErrorInfoIErrorInfo

.NET.NET COMCOMRCWRCW

Page 20: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Come customizzare RCWCome customizzare RCW

• Si può customizzare RCW in due modiSi può customizzare RCW in due modi• Modificando la type libraryModificando la type library

• si ricava l'IDL (se non è dispobile si può importare con il typelib viewer di si ricava l'IDL (se non è dispobile si può importare con il typelib viewer di Oleview.exe)Oleview.exe)

• si aggiungono gli attributi IDL grazie alla keyword custom che permette di si aggiungono gli attributi IDL grazie alla keyword custom che permette di modificare namespace e il nome dotnet del wrapper:modificare namespace e il nome dotnet del wrapper:

• si ricompila con midl /Oicfsi ricompila con midl /Oicf

• si importa con TLBImp oppure VS.netsi importa con TLBImp oppure VS.net

• Modificando l'RCWModificando l'RCW• si decompila l'assembly generato da TLBImpsi decompila l'assembly generato da TLBImp

• si modifica l'ILsi modifica l'IL

• si ricompilasi ricompila

Page 21: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

COM da .NET: TLBImpCOM da .NET: TLBImp

[ComImport, ClassInterface((short) 0), Guid("EF70C490-E7FE-11D2-AB0D-00A02457BBE9"), TypeLibType((short) 2), ComSourceInterfaces("mailctl.interop._IMailerEvents\0")]public class MailerClass : IMailer, Mailer, _IMailerEvents_Event

{ // Methods [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(3)] public virtual extern void IsValidName([In, MarshalAs(UnmanagedType.BStr)] string Recipient);

[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(2)] public virtual extern void Logoff();

[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(1)] public virtual extern void Logon([In, MarshalAs(UnmanagedType.BStr)] string Profile, [In] int bUI);

[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(4)] public virtual extern void SendMail([In, MarshalAs(UnmanagedType.BStr)] string Subject,

[In, MarshalAs(UnmanagedType.BStr)] string Message, [In, MarshalAs(UnmanagedType.BStr)] string OriginatorAddress, [In, MarshalAs(UnmanagedType.BStr)] string AddrTo, [In, MarshalAs(UnmanagedType.BStr)] string AddrCc, [In, MarshalAs(UnmanagedType.BStr)] string AddrCcn, [In, MarshalAs(UnmanagedType.BStr)] string PathNames, [In] int bReceipt, [In] int bUI);

// Properties [DispId(5)] public virtual int MapiIsInstalled {

[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(5)] get; }

}

TLBImp usaTLBImp usa la classela classe

TypeLibConverterTypeLibConverter

Page 22: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Usare ActiveX in dotnetUsare ActiveX in dotnet

• Winform può solo usare controlli che derivano da ControlWinform può solo usare controlli che derivano da Control• Ci vuole quindi un wrapper Ci vuole quindi un wrapper • Il Il controllocontrollo wrapper è AxHost che deriva da Control e ospita l'ActiveX wrapper è AxHost che deriva da Control e ospita l'ActiveX

• Come si genera il wrapper?Come si genera il wrapper?• Aggiungendo alla toolbar dei controlli l'ActiveXAggiungendo alla toolbar dei controlli l'ActiveX• Usando da command prompt l'utility AxImp.exeUsando da command prompt l'utility AxImp.exe

• Si può customizzare il comportamento?Si può customizzare il comportamento?• si, anche se serve raramentesi, anche se serve raramente• il punto di partenza è generare il wrapper e guardarlo con reflectoril punto di partenza è generare il wrapper e guardarlo con reflector

• Si può usare un ActiveX facendo Add-Reference?Si può usare un ActiveX facendo Add-Reference?• dipende dal controllo ma nella maggior parte dei casi no perchè dipende dal controllo ma nella maggior parte dei casi no perchè

l'ActiveX esegue il dispatch dei messaggi tramite message pump della l'ActiveX esegue il dispatch dei messaggi tramite message pump della finestra che viene creata solo se è 'visuale'finestra che viene creata solo se è 'visuale'

Page 23: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Primary Interop AssemblyPrimary Interop AssemblyPIAPIA• Devono essere fornite dall'autore del componente COMDevono essere fornite dall'autore del componente COM

• Deve esistere una sola PIA per ogni oggetto COMDeve esistere una sola PIA per ogni oggetto COM• Una sola PIA può gestire più versioni dello stesso oggetto COMUna sola PIA può gestire più versioni dello stesso oggetto COM• Devono essere strong-namedDevono essere strong-named• Devono esporre tutti gli oggetti COM originali con gli stessi guidDevono esporre tutti gli oggetti COM originali con gli stessi guid

• In caso di componenti esterni referenziatiIn caso di componenti esterni referenziati• Non li devono ridefinireNon li devono ridefinire• Devono referenziare le loro PIADevono referenziare le loro PIA

• Devono essere marcati con l'attributo PrimaryInteropAssemblyAttributeDevono essere marcati con l'attributo PrimaryInteropAssemblyAttribute1.1. Usare TLBImpUsare TLBImp2.2. Creare il wrapper manualmente assegnandogli:Creare il wrapper manualmente assegnandogli:

• strong namestrong name• GuidAttribute (LIBID guid)GuidAttribute (LIBID guid)• attributo PrimaryInteropAssemblyAttributeattributo PrimaryInteropAssemblyAttribute

Page 24: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

AgendaAgenda

• Platform Invocation ServicesPlatform Invocation Services

• COMCOM• Usare oggetti COM in dotnetUsare oggetti COM in dotnet

• Usare Activex in dotnetUsare Activex in dotnet

• Esporre oggetti COM da dotnetEsporre oggetti COM da dotnet

• Esporre Activex da dotnetEsporre Activex da dotnet

• Reverse P/InvokeReverse P/Invoke

Page 25: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Esporre oggetti COM da .NETEsporre oggetti COM da .NET

• CCW = COM Callable WrapperCCW = COM Callable Wrapper• creato al runtimecreato al runtime• si occupa di eseguire il marshalling dei tipisi occupa di eseguire il marshalling dei tipi• esiste un CCW per ogni oggetto dotnetesiste un CCW per ogni oggetto dotnet• l'oggetto dotnet come sempre sarà dato al GC solo se tutti i reference l'oggetto dotnet come sempre sarà dato al GC solo se tutti i reference

(del CCW ed altri) vengono rilasciati(del CCW ed altri) vengono rilasciati• CCW espone sempre una "dual interface" (IUnknown + IDispatch)CCW espone sempre una "dual interface" (IUnknown + IDispatch)• CCW espone IEnumVariant (for each di vb6) per i tipi dotnet che CCW espone IEnumVariant (for each di vb6) per i tipi dotnet che

implementano IEnumerable, IErrorInfo/HRESULT per le eccezioni e implementano IEnumerable, IErrorInfo/HRESULT per le eccezioni e ovviamente IDispatch/IUnknownovviamente IDispatch/IUnknown

.NET.NET COMCOMCCWCCW

Page 26: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

COM object step-by-stepCOM object step-by-step

• Creare una class library con strong nameCreare una class library con strong name• Dare una versione custom all'oggettoDare una versione custom all'oggetto

• La differenza di versioning è un problema rilevante con COMLa differenza di versioning è un problema rilevante con COM

• Scrivere la classe dotnetScrivere la classe dotnet• Decorarla con gli attributiDecorarla con gli attributi

di interop che customizzanodi interop che customizzanoil comportamento del CCWil comportamento del CCW

• Registrarla con RegasmRegistrarla con Regasm• Registrarla nella GACRegistrarla nella GAC• Esportare la Type LibraryEsportare la Type Library

[ClassInterface(ClassInterfaceType.AutoDual)][ClassInterface(ClassInterfaceType.AutoDual)][ComVisible(true)][ComVisible(true)][Guid("D0C1F90F-59B2-4e06-B36F-CC835B14C2EC")][Guid("D0C1F90F-59B2-4e06-B36F-CC835B14C2EC")]public class Adderpublic class Adder{{

public Adder()public Adder(){{}}public int Add(int x, int y)public int Add(int x, int y){{

return x + y;return x + y;}}public int Mul(int x, int y)public int Mul(int x, int y){{

return x * y;return x * y;}}

}}

regasm addercomserver.dllregasm addercomserver.dll

gacutil -i addercomserver.dllgacutil -i addercomserver.dll

tlbexp addercomserver.dll /out:addercomserver.tlbtlbexp addercomserver.dll /out:addercomserver.tlb

Page 27: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Registration-freeRegistration-free

• Disponibile solo in Windows XP - SP2, e Windows 2003Disponibile solo in Windows XP - SP2, e Windows 2003• Evita la registrazione nel registry dei componenti COMEvita la registrazione nel registry dei componenti COM

• È necessario scrivere un manifest Win32 È necessario scrivere un manifest Win32

• Vantaggi:Vantaggi:• più versioni dello stesso componente nello stesso PCpiù versioni dello stesso componente nello stesso PC• xcopy deploymentxcopy deployment

• httphttp://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconCon://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconConfiguringNET-BasedComponentsForRegistration-FreeActivation.aspfiguringNET-BasedComponentsForRegistration-FreeActivation.asp

Page 28: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Realizzare ActiveX in dotnet Realizzare ActiveX in dotnet

• Activex Activex specifiche OC96 + libro "Inside OLE" specifiche OC96 + libro "Inside OLE"• Per ActiveX si intende quindi oggetti 'visuali' e non i comuni oggetti Per ActiveX si intende quindi oggetti 'visuali' e non i comuni oggetti

COMCOM

• Task NON supportato ufficialmente da MicrosoftTask NON supportato ufficialmente da Microsoft• Fattibile ma con non poche difficoltà. Strada sconsigliata.Fattibile ma con non poche difficoltà. Strada sconsigliata.

• Per IE esiste la possibilità di mostrare controlli Winform in una Per IE esiste la possibilità di mostrare controlli Winform in una pagina htmlpagina html• migliore sicurezzamigliore sicurezza• nessuna dialog di richiesta certificatonessuna dialog di richiesta certificato• tecnica poco diffusa in attesa della diffusione del frameworktecnica poco diffusa in attesa della diffusione del framework

• Per le migrazioni di UI conviene il contrarioPer le migrazioni di UI conviene il contrario• Fare l'hosting di Winform dentro MFC, etc.Fare l'hosting di Winform dentro MFC, etc.

Page 29: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

PerformancePerformance

• PInvoke PInvoke 10 istruzioni x86 10 istruzioni x86• COM COM 50 istruzioni x86 50 istruzioni x86• Ma poi c'è il marshalling (che è il più) dei parametriMa poi c'è il marshalling (che è il più) dei parametri

• Riduzione del numero di parametriRiduzione del numero di parametri• Riduzione del numero di funzioni da chiamareRiduzione del numero di funzioni da chiamare• Usare [in] e [out] per ridurre il numero di marshallingUsare [in] e [out] per ridurre il numero di marshalling• In DllImport usare SetLastError=true solo se poi si chiama In DllImport usare SetLastError=true solo se poi si chiama

Marshal.GetLastWin32Error()Marshal.GetLastWin32Error()

Page 30: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

AgendaAgenda

• Platform Invocation ServicesPlatform Invocation Services

• COMCOM• Usare oggetti COM in dotnetUsare oggetti COM in dotnet

• Usare Activex in dotnetUsare Activex in dotnet

• Esporre oggetti COM in dotnetEsporre oggetti COM in dotnet

• Esporre Activex in dotnetEsporre Activex in dotnet

• Reverse P/InvokeReverse P/Invoke

Page 31: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Reverse P/InvokeReverse P/Invoke

• MSDN KB 318804 dice:MSDN KB 318804 dice:.... This requires a .... This requires a DLL export, which .NET Framework does not supportDLL export, which .NET Framework does not support. .

Managed code has no concept of a consistent value for a function pointer Managed code has no concept of a consistent value for a function pointer because these function pointers are proxies that are built dynamically.because these function pointers are proxies that are built dynamically.

• A che servono "DLL Export"?A che servono "DLL Export"?• Global hookGlobal hook• Control Panel AppletsControl Panel Applets• ......• Applicazione unmanaged chiama una Applicazione unmanaged chiama una APIAPI esposta da un assembly esposta da un assembly

• Si, si può fare! MA .... (c'è sempre un "ma")Si, si può fare! MA .... (c'è sempre un "ma")• Certamente con C++, ma è troppo scontato Certamente con C++, ma è troppo scontato • Non con VB.NET, neppure con C# ... ok, neanche con J#Non con VB.NET, neppure con C# ... ok, neanche con J#

Page 32: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Reverse P/InvokeReverse P/Invoke1.1. Creare un progetto Class Library (C# o VB.NET)Creare un progetto Class Library (C# o VB.NET)2.2. Scrivere la funzione da esportare come APIScrivere la funzione da esportare come API3.3. Compilarla e disassemblarla con ILDasm (Compilarla e disassemblarla con ILDasm (ildasm /out=revsimple.il revsimple.dllildasm /out=revsimple.il revsimple.dll))4.4. Modificare l'IL come segue:Modificare l'IL come segue:

• .corflags 0x00000002.corflags 0x00000002 invece di invece di .corflags 0x00000001.corflags 0x00000001• Aggiungere in cima:Aggiungere in cima:

.vtfixup [1] int32 fromunmanaged at VT_01.vtfixup [1] int32 fromunmanaged at VT_01

.data VT_01 = int32(0).data VT_01 = int32(0)• Dentro la funzione da esportare aggiungere:Dentro la funzione da esportare aggiungere:

.method public hidebysig static int32.method public hidebysig static int32modopt([mscorlib]System.Runtime.InteropServices.CallConvStdCall)modopt([mscorlib]System.Runtime.InteropServices.CallConvStdCall)

• CPlApplet(native int hwndCPl, unsigned int32 uMsg,CPlApplet(native int hwndCPl, unsigned int32 uMsg,[in][out] native int lParam1, [in][out] native int lParam2) cil [in][out] native int lParam1, [in][out] native int lParam2) cil

managedmanaged{{ .vtentry 1 : 1 .vtentry 1 : 1 .export [1] as CPlApplet .export [1] as CPlApplet......................

5.5. Riassemblare: ilasm /out=hello.dll revsimple.il /dllRiassemblare: ilasm /out=hello.dll revsimple.il /dll

Rinomina la funzione esportata

CallingConvention

Page 33: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Reverse P/InvokeReverse P/InvokeControl panel managed appletControl panel managed applet1.1. Class library con una funzione chiamata:Class library con una funzione chiamata:

publicpublic static static int CPlApplet( int CPlApplet(IntPtr hwndCPl, uint uMsg, [In,Out] IntPtr lParam1, [In,Out] IntPtr lParam2)IntPtr hwndCPl, uint uMsg, [In,Out] IntPtr lParam1, [In,Out] IntPtr lParam2)

2.2. Dichiarazione di CPLINFO, NEWCPLINFO, CPL_...Dichiarazione di CPLINFO, NEWCPLINFO, CPL_...

3.3. Marshalling manuale dei parametri perché la lParam1 e Marshalling manuale dei parametri perché la lParam1 e lParam2 possono puntare a strutture diverse (CPLINFO o lParam2 possono puntare a strutture diverse (CPLINFO o NEWCPLINFO)NEWCPLINFO)

case CPLMessages.CPL_INQUIRE:AppletNumber = lParam1.ToInt32();

Info = (CPLINFO)Marshal.PtrToStructure(lParam2, typeof(CPLINFO));

Info.idIcon = 101;Info.idName = 201;Info.idInfo = 202;Info.lData = (IntPtr)1;

Marshal.StructureToPtr(Info, lParam2, true);

return 0;

numeri delle risorse (prox slide)

Page 34: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Control panel managed appletControl panel managed appletIl problema delle risorseIl problema delle risorse4.4. Preparazione del file di risorse "resources.rc":Preparazione del file di risorse "resources.rc":

5.5. Compilazione delle risorse in resCompilazione delle risorse in res(command prompt di Visual Studio):(command prompt di Visual Studio):rc resources.rcrc resources.rc

6.6. Eventualmente aggiungere al file resources.rc le informazioni Eventualmente aggiungere al file resources.rc le informazioni di versione perché quelle di vs.net vanno persedi versione perché quelle di vs.net vanno perse

101 ICON DISCARDABLE "Italy.ico"

STRINGTABLE DISCARDABLE BEGIN

201 "Managed Applet"202 "Mostra le opzioni del managed applet"

END

icona applet

nome sotto l'icona

descrizione a fianco all'icona

Page 35: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Reverse P/InvokeReverse P/InvokeControl panel managed appletControl panel managed applet7.7. Embedding di risorse Win32 nell'assembly:Embedding di risorse Win32 nell'assembly:

c:\dev.net\Demo\Interop\RevPInvoke>c:\dev.net\Demo\Interop\RevPInvoke>csccsc /resource/resource:obj\Debug\:obj\Debug\RevPInvoke.InteropWorkshop.resourcesRevPInvoke.InteropWorkshop.resources/win32res/win32res:resources.res :resources.res /target/target:library :library /out/out:bin\debug\RevPInvoke.dll :bin\debug\RevPInvoke.dll *.cs*.cs

8.8. In alternativa usare "Resource Hacker" importando il file .res:In alternativa usare "Resource Hacker" importando il file .res:http://www.download.com/3000-2352-10178588.htmlhttp://www.download.com/3000-2352-10178588.html

9.9. Registrarlo:Registrarlo:HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\CplsHKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\CplsManagedApplet = path del .cplManagedApplet = path del .cpl

risorse unmanaged (win32)dllnome dllcompilare tutti i sorgenti

risorsemanaged

Page 36: P Invoke e COM Interoperability Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile:  .

Domande?Domande?