Поделиться через


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

В этом разделе описывается, как получить доступ к неуправляемым функциям и ввести несколько полей атрибутов, которые аннотируют определение метода в управляемом коде. Примеры, демонстрирующие, как создавать объявления на основе .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.

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

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

Подробные справочные сведения см. в разделе DllImportAttribute.

Рекомендации по обеспечению безопасности при вызове процедур на платформе

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

Примеры вызова функций платформы

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

В следующем примере модификаторы SecurityAction, Assert, 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();  
}  

См. также