Creazione di prototipi nel codice gestito

Questo argomento descrive come accedere alle funzioni non gestite e introduce diversi campi attributo che annotano la definizione di metodo nel codice gestito. Per alcuni esempi che mostrano come costruire dichiarazioni basate su .NET da usare con platform invoke, vedere Marshalling dei dati con platform invoke.

Per accedere a una funzione DLL non gestita dal codice gestito, è necessario conoscere il nome della funzione e il nome della DLL che la esporta. Con queste informazioni, è possibile iniziare a scrivere la definizione gestita per una funzione non gestita implementata in una DLL. È anche possibile modificare il modo in cui platform invoke crea la funzione ed effettua il marshalling dei dati da e verso la funzione.

Nota

Le funzioni API Windows che allocano una stringa consentono di liberare la stringa usando un metodo come LocalFree. Platform invoke gestisce tali parametri in modo diverso. Per le chiamate di platform invoke, il parametro deve essere di tipo IntPtr anziché di tipo String. Usare i metodi forniti dalla classe System.Runtime.InteropServices.Marshal per convertire manualmente il tipo in una stringa e liberarlo manualmente.

Nozioni di base sulle dichiarazioni

Le definizioni gestite di funzioni non gestite sono dipendenti dal linguaggio, come è possibile vedere negli esempi seguenti. Per esempi di codice più completi, vedere Esempi di platform invoke.

Friend Class NativeMethods
    Friend Declare Auto Function MessageBox Lib "user32.dll" (
        ByVal hWnd As IntPtr,
        ByVal lpText As String,
        ByVal lpCaption As String,
        ByVal uType As UInteger) As Integer
End Class

Per applicare i campi DllImportAttribute.BestFitMapping, DllImportAttribute.CallingConvention, DllImportAttribute.ExactSpelling, DllImportAttribute.PreserveSig, DllImportAttribute.SetLastError o DllImportAttribute.ThrowOnUnmappableChar a una dichiarazione Visual Basic, è necessario usare l'attributo DllImportAttribute anziché l'istruzione Declare.

Imports System.Runtime.InteropServices

Friend Class NativeMethods
    <DllImport("user32.dll", CharSet:=CharSet.Auto)>
    Friend Shared Function MessageBox(
        ByVal hWnd As IntPtr,
        ByVal lpText As String,
        ByVal lpCaption As String,
        ByVal uType As UInteger) As Integer
    End Function
End Class
using System;
using System.Runtime.InteropServices;

internal static class NativeMethods
{
    [DllImport("user32.dll")]
    internal static extern int MessageBox(
        IntPtr hWnd, string lpText, string lpCaption, uint uType);
}
using namespace System;
using namespace System::Runtime::InteropServices;

[DllImport("user32.dll")]
extern "C" int MessageBox(
    IntPtr hWnd, String* lpText, String* lpCaption, unsigned int uType);

Modifica della definizione

Indipendentemente dal fatto che vengano impostati in modo esplicito o meno, i campi attributo definiscono il comportamento del codice gestito. Platform invoke opera in base ai valori predefiniti impostati in diversi campi presenti come metadati in un assembly. È possibile modificare questo comportamento predefinito modificando i valori di uno o più campi. In molti casi, è possibile usare DllImportAttribute per impostare un valore.

La tabella seguente elenca il set completo di campi attributo relativi a platform invoke. Per ogni campo, la tabella include il valore predefinito e un collegamento a informazioni su come usare questi campi per definire le funzioni DLL non gestite.

Campo Descrizione
BestFitMapping Abilita o disabilita il mapping più appropriato.
CallingConvention Specifica la convenzione di chiamata da usare per passare gli argomenti del metodo. Il valore predefinito è WinAPI, che corrisponde a __stdcall per le piattaforme Intel a 32 bit.
CharSet Controlla la modifica dei nomi e il modo in cui deve essere effettuato il marshalling degli argomenti stringa alla funzione. Il valore predefinito è CharSet.Ansi.
EntryPoint Specifica il punto di ingresso della DLL da chiamare.
ExactSpelling Controlla se un punto di ingresso deve essere modificato per corrispondere al set di caratteri. Il valore predefinito varia in base al linguaggio di programmazione.
PreserveSig Controlla se la firma del metodo gestito deve essere trasformata in una firma non gestita che restituisce un oggetto HRESULT e dispone di un argomento aggiuntivo [out, retval] per il valore restituito.

Il valore predefinito è true (la firma non deve essere trasformata).
SetLastError Consente al chiamante di usare la funzione API Marshal.GetLastWin32Error per determinare se si è verificato un errore durante l'esecuzione del metodo. In Visual Basic il valore predefinito è true, in C# e C++ il valore predefinito è false.
ThrowOnUnmappableChar Controlla la generazione di un'eccezione per un carattere Unicode di cui non è possibile eseguire il mapping convertito in un carattere ANSI "?".

Per informazioni di riferimento dettagliate, vedere DllImportAttribute.

Considerazioni sulla sicurezza di platform invoke

I membri Assert, Deny e PermitOnly dell'enumerazione SecurityAction sono detti modificatori di percorso stack. Questi membri vengono ignorati se vengono usati come attributi dichiarativi in dichiarazioni platform invoke e istruzioni IDL (Interface Definition Language, linguaggio di definizione dell'interfaccia) COM.

Esempi di platform invoke

Gli esempi di platform invoke in questa sezione illustrano l'uso dell'attributo RegistryPermission con i modificatori di percorso stack.

Nell'esempio seguente i modificatori SecurityActionAssert, Deny e PermitOnly vengono ignorati.

[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
[RegistryPermission(SecurityAction.Assert, Unrestricted = true)]  
    private static extern bool CallRegistryPermissionAssert();  
  
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
[RegistryPermission(SecurityAction.Deny, Unrestricted = true)]  
    private static extern bool CallRegistryPermissionDeny();  
  
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
[RegistryPermission(SecurityAction.PermitOnly, Unrestricted = true)]  
    private static extern bool CallRegistryPermissionDeny();  

Il modificatore Demand nell'esempio seguente viene tuttavia accettato.

[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]  
    private static extern bool CallRegistryPermissionDeny();  

I modificatori SecurityAction funzionano correttamente se vengono posizionati in una classe che contiene (esegue il wrapping) la chiamata di platform invoke.

      [RegistryPermission(SecurityAction.Demand, Unrestricted = true)]  
public ref class PInvokeWrapper  
{  
public:  
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
    private static extern bool CallRegistryPermissionDeny();  
};  
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]  
class PInvokeWrapper  
{  
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
    private static extern bool CallRegistryPermissionDeny();  
}  

I modificatori SecurityAction funzionano correttamente anche in uno scenario annidato in cui vengono posizionati nel chiamante della chiamata di platform invoke:

      {  
public ref class PInvokeWrapper  
public:  
    [DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
    private static extern bool CallRegistryPermissionDeny();  
  
    [RegistryPermission(SecurityAction.Demand, Unrestricted = true)]  
    public static bool CallRegistryPermission()  
    {  
     return CallRegistryPermissionInternal();  
    }  
};  
class PInvokeScenario  
{  
    [DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
    private static extern bool CallRegistryPermissionInternal();  
  
    [RegistryPermission(SecurityAction.Assert, Unrestricted = true)]  
    public static bool CallRegistryPermission()  
    {  
     return CallRegistryPermissionInternal();  
    }  
}  

Esempi di interoperabilità COM

Gli esempi di interoperabilità COM in questa sezione illustrano l'uso dell'attributo RegistryPermission con i modificatori di percorso stack.

Le dichiarazioni di interfacce di interoperabilità COM seguenti ignorano i modificatori Assert, Deny e PermitOnly, analogamente agli esempi di platform invoke nella sezione precedente.

[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]  
interface IAssertStubsItf  
{  
[RegistryPermission(SecurityAction.Assert, Unrestricted = true)]  
    bool CallRegistryPermission();  
[FileIOPermission(SecurityAction.Assert, Unrestricted = true)]  
    bool CallFileIoPermission();  
}  
  
[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]  
interface IDenyStubsItf  
{  
[RegistryPermission(SecurityAction.Deny, Unrestricted = true)]  
    bool CallRegistryPermission();  
[FileIOPermission(SecurityAction.Deny, Unrestricted = true)]  
    bool CallFileIoPermission();  
}  
  
[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]  
interface IAssertStubsItf  
{  
[RegistryPermission(SecurityAction.PermitOnly, Unrestricted = true)]  
    bool CallRegistryPermission();  
[FileIOPermission(SecurityAction.PermitOnly, Unrestricted = true)]  
    bool CallFileIoPermission();  
}  

Inoltre, il modificatore Demand non viene accettato negli scenari di dichiarazione di interfacce di interoperabilità COM, come illustrato nell'esempio seguente.

[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]  
interface IDemandStubsItf  
{  
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]  
    bool CallRegistryPermission();  
[FileIOPermission(SecurityAction.Demand, Unrestricted = true)]  
    bool CallFileIoPermission();  
}  

Vedi anche