Создание прототипов в управляемом коде

В этом разделе описывается доступ к неуправляемым функциям и представлено несколько полей атрибутов, которые уточняют определение метода в управляемом коде. Примеры, демонстрирующие создание. Объявления на основе NET для использования с вызовом платформы см. в разделе "Маршаллирование данных с помощью вызова платформы".

Чтобы обратиться к неуправляемой функции DLL из управляемого кода, нужно знать ее имя и имя библиотеки DLL, которая ее экспортирует. Имея эту информацию, можно приступать к написанию управляемого определения для неуправляемой функции, реализованной в библиотеке DLL. Кроме того, можно настроить способ, которым вызов неуправляемого кода создает функцию и маршалирует данные в функцию и обратно.

Примечание.

Функции API Windows, которые распределяют строку и позволяют освободить строку с помощью такого метода, как LocalFree. Вызов неуправляемого кода обрабатывает эти параметры иначе. Для вызовов неуправляемого кода следует использовать параметр типа IntPtr, а не String. Чтобы вручную преобразовать тип в строку и вручную освободить его, можно использовать методы, предоставленные классом System.Runtime.InteropServices.Marshal.

Основы объявления

Как показано в примерах ниже, управляемые определения неуправляемых функций зависят от используемого языка. Более полные примеры кода представлены в разделе Примеры вызовов неуправляемого кода.

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

Для применения полей DllImportAttribute.BestFitMapping, DllImportAttribute.CallingConvention, DllImportAttribute.ExactSpelling, DllImportAttribute.PreserveSig, DllImportAttribute.SetLastError или DllImportAttribute.ThrowOnUnmappableChar к объявлению Visual Basic необходимо использовать атрибут DllImportAttribute вместо оператора 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);

Настройка определения

Поля атрибутов, заданные явно или неявно, определяют выполнение управляемого кода. Вызов неуправляемого кода выполняется в соответствии с набором значений по умолчанию различных полей, хранящихся в сборке как метаданные. Это поведение по умолчанию можно изменить, настроив значения одного или нескольких полей. Во многих случаях для установки значения используется DllImportAttribute.

Ниже приведен полный набор полей атрибутов, относящихся к вызову неуправляемого кода. Для каждого поля в таблице указано значение по умолчанию и дана ссылка на инструкцию по использованию поля для определения неуправляемых функций DLL.

Поле Description
BestFitMapping Включает или отключает наилучшее сопоставление.
CallingConvention Задает соглашение о вызовах, которое должно использоваться при передаче аргументов методов. Значение по умолчанию — WinAPI, что соответствует режиму __stdcall для 32-разрядных платформ на базе процессора Intel.
CharSet Управляет изменением имени и способом маршаллинга строковых аргументов в функцию. Значение по умолчанию — CharSet.Ansi.
EntryPoint Указывает точку входа DLL для вызова.
ExactSpelling Определяет, должна ли изменяться точка входа в соответствии с кодировкой. Значение по умолчанию зависит от языка программирования.
PreserveSig Определяет, должна ли сигнатура управляемого метода преобразовываться в неуправляемую сигнатуру, которая возвращает значение HRESULT и имеет дополнительный аргумент [out, retval] для возвращаемого значения.

Значение по умолчанию — true (сигнатура не должна преобразовываться).
SetLastError Позволяет вызывающему объекту использовать функцию интерфейса API Marshal.GetLastWin32Error для определения факта ошибки при выполнении метода. В Visual Basic значение по умолчанию — true, а в C# и C++ — false.
ThrowOnUnmappableChar Управляет возникновением исключения при появлении несопоставимого символа Юникода, который преобразуется в символ ANSI "?".

Дополнительные справочные сведения см. в разделе DllImportAttribute.

Вопросы безопасности при вызове неуправляемого кода

Члены Assert, Deny и PermitOnly перечисления SecurityAction называются модификаторами обхода стека. Эти члены игнорируются, если они используются как декларативные атрибуты в объявлениях вызова неуправляемого кода и операторах COM языка IDL.

Примеры вызовов неуправляемого кода

Примеры вызовов неуправляемого кода в этом разделе иллюстрируют использование атрибута RegistryPermission с модификаторами обхода стека.

В приведенном ниже примере кода модификаторы SecurityActionAssert, Deny и PermitOnly не учитываются.

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

Однако модификатор Demand в следующем примере принимается.

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

Модификаторы SecurityAction действительно правильно работают, если они заданы для класса, содержащего (инкапсулирующего) вызов неуправляемого кода.

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

Модификаторы SecurityAction также правильно работают в сценарии с вложением, когда они задаются для объекта, выполняющего вызов неуправляемого кода.

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

Примеры COM-взаимодействия

Примеры COM-взаимодействия в этом разделе иллюстрируют использование атрибута RegistryPermission с модификаторами обхода стека.

В приведенных ниже объявлениях интерфейса COM-взаимодействия модификаторы Assert, Deny и PermitOnly не учитываются по аналогии с примерами вызовов неуправляемого кода в предыдущем разделе.

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

Кроме того, модификатор Demand недопустим в сценариях объявления интерфейса COM-взаимодействия, как показано ниже.

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

См. также