Partager via


Création de prototypes dans le code managé

Cette rubrique explique comment accéder aux fonctions non managées et présente plusieurs champs d’attribut qui annotent la définition de méthode dans le code managé. Pour obtenir des exemples montrant comment construire des déclarations .NET à utiliser avec un appel de code non managé, consultez Marshaling de données avec un appel de code non managé.

Avant de pouvoir accéder à une fonction DLL non managée à partir du code managé, vous devez connaître le nom de la fonction et le nom de la DLL qui l’exporte. Avec ces informations, vous pouvez commencer à écrire la définition managée pour une fonction non managée implémentée dans une DLL. En outre, vous pouvez ajuster la façon dont l'appel de code non managé crée la fonction et marshale les données vers et depuis la fonction.

Remarque

Les fonctions d’API Windows qui allouent une chaîne vous permettent de libérer la chaîne à l’aide d’une méthode telle que LocalFree. L’appel de plateforme gère ces paramètres différemment. Pour les appels de plateforme, définissez le paramètre comme un type IntPtr au lieu d’un type String. Utilisez des méthodes fournies par la System.Runtime.InteropServices.Marshal classe pour convertir le type en chaîne manuellement et le libérer manuellement.

Principes de base de la déclaration

Les définitions managées pour les fonctions non managées dépendent du langage, comme vous pouvez le voir dans les exemples suivants. Pour obtenir des exemples de code plus complets, consultez Exemples d’appel de plateforme.

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

Pour appliquer les champs DllImportAttribute.BestFitMapping, DllImportAttribute.CallingConvention, DllImportAttribute.ExactSpelling, DllImportAttribute.PreserveSig, DllImportAttribute.SetLastError ou DllImportAttribute.ThrowOnUnmappableChar à une déclaration Visual Basic, vous devez utiliser l’attribut DllImportAttribute au lieu de l’instruction 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);

Ajustement de la définition

Que vous les définissez explicitement ou non, les champs d’attribut sont au travail définissant le comportement du code managé. L’appel de plateforme fonctionne en fonction des valeurs par défaut définies sur différents champs qui existent en tant que métadonnées dans un assembly. Vous pouvez modifier ce comportement par défaut en ajustant les valeurs d’un ou plusieurs champs. Dans de nombreux cas, vous utilisez le DllImportAttribute pour définir une valeur.

Le tableau suivant répertorie l’ensemble complet de champs d’attributs relatifs à l’appel de plateforme. Pour chaque champ, la table inclut la valeur par défaut et un lien vers des informations sur l’utilisation de ces champs pour définir des fonctions DLL non managées.

Terrain Descriptif
BestFitMapping Active ou désactive le mappage le mieux adapté.
CallingConvention Spécifie la convention d’appel à utiliser pour passer des arguments de méthode. La valeur par défaut est WinAPI, qui correspond à __stdcall pour les plateformes 32 bits à base d'Intel.
CharSet Contrôle la décoration des noms et la façon dont les arguments de chaîne doivent être marshalés vers la fonction. La valeur par défaut est CharSet.Ansi.
EntryPoint Spécifie le point d’entrée DLL à appeler.
ExactSpelling Contrôle si un point d’entrée doit être modifié pour correspondre au jeu de caractères. La valeur par défaut varie selon le langage de programmation.
PreserveSig Contrôle si la signature de méthode managée doit être transformée en une signature non managée qui retourne un HRESULT et a un argument supplémentaire [out, retval] pour la valeur de retour.

La valeur par défaut est true (la signature ne doit pas être transformée).
SetLastError Permet à l’appelant d’utiliser la Marshal.GetLastWin32Error fonction API pour déterminer si une erreur s’est produite lors de l’exécution de la méthode. En Visual Basic, la valeur par défaut est true; en C# et C++, la valeur par défaut est false.
ThrowOnUnmappableChar Contrôle la levée d'une exception sur un caractère Unicode non mappable converti en un caractère ANSI "?".

Pour obtenir des informations de référence détaillées, consultez DllImportAttribute.

Considérations relatives à la sécurité des appels de code non managé

Les membres Assert, Deny et PermitOnly de l’énumération SecurityAction sont appelés modificateurs de parcours de pile. Ces membres sont ignorés s'ils sont utilisés comme des attributs déclaratifs sur des déclarations d'appel de code non managé et des instructions Interface Definition Language (IDL) COM.

Exemples d'appel de code non managé

Les exemples d'appel de code non managé de cette section illustrent l'utilisation de l'attribut RegistryPermission avec les modificateurs de parcours de pile.

Dans l’exemple suivant, les modificateurs SecurityActionAssert, Deny, et PermitOnly sont ignorés.

[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();  

Toutefois, le Demand modificateur de l’exemple suivant est accepté.

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

SecurityAction Les modificateurs fonctionnent correctement s’ils sont placés sur une classe qui contient (encapsule) l'appel de plateforme.

      [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();  
}  

Les modificateurs SecurityAction fonctionnent également correctement dans un scénario d’imbrication dans lequel ils sont placés sur l’appelant de l’appel de code non managé :

      {  
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();  
    }  
}  

Exemples d’interopérabilité COM

Les exemples COM Interop de cette section illustrent l'utilisation de l'attribut RegistryPermission avec les modificateurs de parcours de pile.

Les déclarations d’interface COM interop suivantes ignorent les modificateurs Assert, Deny, et PermitOnly, de la même façon que les exemples d'appel de plateforme dans la section précédente.

[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();  
}  

En outre, le Demand modificateur n’est pas accepté dans les scénarios de déclaration d’interface COM Interop, comme illustré dans l’exemple suivant.

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

Voir aussi