Создание прототипов в управляемом коде
В этом разделе описывается доступ к неуправляемым функциям и представлено несколько полей атрибутов, которые уточняют определение метода в управляемом коде. Примеры, демонстрирующие создание. Объявления на основе 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();
}
См. также
- Использование неуправляемых функций DLL
- Задание точки входа
- Определение кодировки
- Примеры вызовов неуправляемого кода
- Platform Invoke Security Considerations (Вопросы безопасности при вызове неуправляемого кода)
- Идентификация функций в библиотеках DLL
- Создание класса, содержащего функции DLL
- Вызов функции DLL