Сервисы вызова платформы - Platform Invocation Services

Сервисы вызова платформы, обычно называемый P / Invoke, это особенность Инфраструктура общего языка реализации, такие как Microsoft с общеязыковая среда выполнения, что позволяет управляемый код звонить собственный код.

Управляемый код, например C # или VB.NET, обеспечивает собственный доступ к классам, методам и типам, определенным в библиотеках, составляющих .NET Framework. Хотя .NET Framework предоставляет обширный набор функций, ему может не хватать доступа ко многим библиотекам операционной системы нижнего уровня, обычно написанным в неуправляемом коде, или сторонним библиотекам, также написанным в неуправляемом коде. P / Invoke - это метод, который программист может использовать для доступа к функциям в этих библиотеках. Вызов функций в этих библиотеках происходит путем объявления сигнатуры неуправляемой функции в управляемом коде, которая служит фактической функцией, которую можно вызывать, как и любой другой управляемый метод. Объявление ссылается на путь к файлу библиотеки и определяет параметры функции и возвращаемые данные в управляемых типах, которые, скорее всего, будут неявно маршалироваться в и из неуправляемых типов средой CLR. Когда неуправляемые типы данных становятся слишком сложными для простого неявного преобразования из и в управляемые типы, инфраструктура позволяет пользователю определять атрибуты функции, возврата и / или параметров для явного уточнения того, как данные должны быть маршалированы, чтобы приводить к исключениям при попытке сделать это неявно. Программистам с управляемым кодом доступно множество абстракций концепций программирования нижнего уровня по сравнению с программированием на неуправляемых языках. В результате программист, имеющий только опыт работы с управляемым кодом, должен будет освежить в памяти такие концепции программирования, как указатели, структуры и передача по ссылке, чтобы преодолеть некоторые из более простых, но общих препятствий при использовании P / Invoke.

Архитектура

Обзор

В настоящее время используются два варианта P / Invoke:

Явный

  • Собственный код импортируется через динамически подключаемые библиотеки (DLL)
  • Метаданные встроенный в сборку вызывающего абонента определяет, как должен вызываться собственный код и получать доступ к данным (обычно требует спецификаторов источника с атрибутами, чтобы помочь компилятору в создании маршал-клея)
    • Это определение является «явной» частью

Скрытый

  • Используя C ++ / CLI, приложение может одновременно использовать управляемую кучу (посредством указателей отслеживания) и любую область собственной памяти без явного объявления. (Скрытый)
  • Основным преимуществом в этом случае является то, что если базовые собственные структуры данных изменяются, при условии совместимости именования, нарушение изменения избегается.
    • т.е. добавление / удаление / изменение порядка структур в собственном заголовке будет прозрачно поддерживаться до тех пор, пока имена членов структуры также не изменились.

Подробности

При использовании P / Invoke CLR ручки DLL загрузка и преобразование неуправляемый предыдущие типы CTS типы (также называемые сортировка параметров).[1][нужна цитата ]Для этого CLR:

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

P / Invoke полезен для использования стандартных (неуправляемых) C или же C ++ DLL. Его можно использовать, когда программисту нужен доступ к обширным Windows API, как и многие функции, предоставляемые Библиотеки Windows недостаток в наличии обертки. Когда Win32 API не подвергается .NET Framework обертка к этому API надо писать вручную.

Ловушки

Написание оболочек P / Invoke может быть трудным и подверженным ошибкам. Использование собственных DLL означает, что программист больше не может извлечь выгоду из безопасность типа и вывоз мусора как обычно предоставляется в среде .NET. При их неправильном использовании могут возникнуть такие проблемы, как ошибки сегментации или же утечки памяти. Получение точных подписей унаследованных функций для использования в .СЕТЬ окружающая среда может быть сложной, что может привести к таким проблемам. Для этого существуют инструменты и веб-сайты для получения таких подписей, помогающие предотвратить проблемы с подписями. [1]

К другим подводным камням относятся:

  • Неверно выравнивание данных определяемых пользователем типы на управляемом языке: существуют разные способы выравнивания данных в зависимости от компиляторов или директив компилятора в C, и необходимо проявлять осторожность, чтобы явно указать CLR как выровнять данные для непреобразуемые типы. Типичный пример этого - попытка определить тип данных в .NET для представления союз в C. Две разные переменные перекрываются в памяти, и определение этих двух переменных в типе в .NET приведет к тому, что они будут находиться в разных местах в памяти, поэтому для устранения проблемы необходимо использовать специальные атрибуты.
  • Вмешательство в расположение данных сборщиком мусора управляемого языка: если ссылка является локальной для метода в .NET и передается встроенной функции, когда управляемый метод возвращается, сборщик мусора может вернуть эту ссылку. Следует позаботиться о том, чтобы ссылка на объект приколот, предотвращая его сбор или перемещение сборщиком мусора, что привело бы к недопустимому доступу для собственного модуля.

При использовании C ++ / CLI испущенный CIL может свободно взаимодействовать с объектами, расположенными в управляемой куче, и одновременно с любой адресуемой собственной ячейкой памяти. Резидентный объект управляемой кучи может быть вызван, изменен или сконструирован с использованием простого "объект-> поле;" нотация для присвоения значений или указания вызовов методов. Значительный прирост производительности достигается за счет устранения ненужного переключения контекста, уменьшения требований к памяти (более короткие стеки).

Это связано с новыми проблемами:

  • Код подвержен двойному преобразованию[2] если не указано иное
  • В Проблема с блокировкой загрузчика [3]

В этих справочниках указаны решения для каждой из этих проблем, если они встречаются. Основным преимуществом является устранение объявления структуры, порядка объявления полей и проблем с выравниванием, которые отсутствуют в контексте взаимодействия C ++.

Примеры

Основные примеры

Этот первый простой пример показывает, как получить версию определенного DLL:

DllGetVersion подпись функции в Windows API:

HRESULT DllGetVersion(    DLLVERSIONINFO* pdvi)

P / Invoke C # код для вызова DllGetVersion функция:

[DllImport ("shell32.dll")]статический внешний int DllGetVersion(ссылка DLLVERSIONINFO pdvi);

Во втором примере показано, как извлечь значок в файл:

ExtractIcon подпись функции в Windows API:

HICON ExtractIcon(          HINSTANCE hInst,    LPCTSTR lpszExeFileName,    UINT nIconIndex);

P / Invoke код C # для вызова ExtractIcon функция:

[DllImport ("shell32.dll")]статический внешний IntPtr ExtractIcon(    IntPtr hInst,     [MarshalAs (UnmanagedType.LPStr)] нить lpszExeFileName,     uint nIconIndex);

В следующем сложном примере показано, как разделить событие между двумя процессами в Платформа Windows:

Создать событие сигнатура функции:

 РУЧКА Создать событие(     LPSECURITY_ATTRIBUTES lpEventAttributes,     BOOL bManualReset,     BOOL bInitialState,     LPCTSTR lpName );

P / Invoke код C # для вызова Создать событие функция:

[DllImport ("kernel32.dll", SetLastError = true)]статический внешний IntPtr Создать событие(    IntPtr lpEventAttributes,     bool bManualReset,    bool bInitialState,     [MarshalAs (UnmanagedType.LPStr)] нить lpName);

Более сложный пример

// собственное объявлениеtypedef структура _ПАРА { 	DWORD Val1; 	DWORD Val2; } ПАРА, *PPAIR;
// Скомпилировано с помощью / clr; использование #pragma managed / unmanaged может привести к двойному преобразованию;// Избегайте использования автономного .cpp с .h includes.// Это будет находиться в файле .h.шаблон<>в соответствии CLR_PAIR^ marshal_as<CLR_PAIR^, ПАРА> (const ПАРА&Src) {    // Обратите внимание на использование de / referencing. Он должен соответствовать вашему использованию.	CLR_PAIR^ Dest = gcnew CLR_PAIR;	Dest->Val1 = Src.Val1;	Dest->Val2 = Src.Val2;	возвращаться Dest;};
CLR_PAIR^ mgd_pair1;CLR_PAIR^ mgd_pair2;ПАРА родной0,*родной1=&родной0;родной0 = NativeCallGetRefToMemory();// Использование marshal_as. Это имеет смысл для больших или часто используемых типов.mgd_pair1 = marshal_as<CLR_PAIR^>(*родной1);// Прямое использование поляmgd_pair2->Val1 = родной0.Val1;mgd_pair2->val2 = родной0.val2;возвращаться(mgd_pair1); // Вернуться на C #

Инструменты

Существует ряд инструментов, предназначенных для помощи в создании подписей P / Invoke.

Написание служебного приложения, которое будет импортировать файлы заголовков C ++ и собственные DLL файлов и произвести сборку интерфейса автоматически оказывается довольно сложно. Основная проблема при создании такого импортера / экспортера для сигнатур P / Invoke - неоднозначность некоторых типов параметров вызова функций C ++.

Брэд Абрамс сказал по этому поводу следующее: Проблема P / Invoke.

Проблема заключается в следующих функциях C ++:

__declspec(dllexport) пустота Моя функция(char *параметры);

Какой тип использовать для параметра параметры в нашей подписи P / Invoke? Это может быть либо строка C ++ с завершающим нулем, либо char массив или может быть выходом char параметр. Так следует ли нам использовать нить, StringBuilder, char [] или же ref char ?

Независимо от этой проблемы, существует несколько инструментов, позволяющих упростить создание подписей P / Invoke.

Один из инструментов, перечисленных ниже, xInterop C ++ .NET Bridge решил эту проблему, реализовав несколько переопределений одного и того же метода C ++ в мире .NET, после чего разработчики могут выбрать правильный метод для вызова.

PInvoke.net

PInvoke.net - это вики, содержащая подписи P / Invoke для большого количества стандартных API Windows. Он принадлежит Программное обеспечение Redgate и имеет около 50000 посещений в месяц.

Подписи создаются вручную пользователями вики. Их можно искать с помощью бесплатное дополнение к Microsoft Visual Studio.

Пинвокер

Пинвокер это приложение, которое импортирует собственные библиотеки DLL и файлы .h C ++ и экспортирует полностью сформированные и скомпилированные P / Invoke DLL взаимодействия. Он преодолевает проблему двусмысленности, оборачивая параметры функций собственных указателей в классы интерфейса .NET, специфичные для PInvoker. Вместо использования стандартных типов параметров .NET в определениях методов P / Invoke (char [], нитьи т. д.) он использует эти классы интерфейса в вызовах функций P / Invoke.

Например, если мы рассмотрим приведенный выше пример кода, PInvoker создаст .NET P / Invoke функция, принимающая класс интерфейса .NET, обертывающий собственный символ * указатель. Конструкция этого класса могла быть из нить или из char [] множество. Фактическая структура собственной памяти для обоих одинакова, но соответствующие конструкторы класса интерфейса для каждого типа будут заполнять память по-разному. Таким образом, ответственность за принятие решения о том, какой тип .NET необходимо передать функции, передается разработчику.

Помощник Microsoft Interop

Помощник Microsoft Interop это бесплатный инструмент с двоичными файлами и исходным кодом, доступный для загрузки на CodePlex. Он лицензирован под Ограниченная общественная лицензия Microsoft (Г-жа-ЛПЛ).

Он состоит из двух частей:

  • Конвертер, который принимает небольшие фрагменты исходного кода файла заголовка C ++, содержащего структура и определения методов. Затем он создает код C # P / Invoke, который вы можете скопировать и вставить в свои приложения.
  • База данных с возможностью поиска преобразованных определений констант, методов и структур Windows API.

Поскольку этот инструмент создает исходный код C #, а не скомпилированную dll, пользователь может вносить в код любые изменения перед использованием. Таким образом, проблема неоднозначности решается тем, что приложение выбирает один конкретный тип .NET для использования в сигнатуре метода P / Invoke, и при необходимости пользователь может изменить его на требуемый тип.

P / Invoke Wizard

В P / Invoke Wizard использует метод, аналогичный Microsoft Interop Assistant, в котором он принимает собственный код файла C ++ .h и создает код C # (или VB.NET) для вставки в код приложения .NET.

У него также есть варианты, для какой платформы вы хотите использовать: .NET Framework для настольных ПК или .NET Compact Framework для интеллектуальных устройств Windows Mobile (и Windows CE).

xInterop C ++ .NET Bridge

xInterop C ++ .NET Bridge - это приложение Windows для создания оболочки C # для собственных библиотек DLL C ++ и моста C ++ для доступа к сборкам .NET, оно поставляется с библиотекой C # /. NET, которая обертывает стандартные классы C ++, такие как string, iostream и т. д., классы и объекты C ++ можно получить из .NET.

Этот инструмент генерирует библиотеки DLL-оболочки C # с исходным кодом из существующих собственных библиотек DLL C ++ и связанных файлов заголовков, которые требуются инструменту для создания DLL-оболочки C #. Подписи P / Invoke и маршалинг данных генерируются приложением. Полученная оболочка C # имеет интерфейс, аналогичный интерфейсу C ++, с типом параметра, преобразованным в код .NET.

Этот инструмент распознает классы шаблонов, которые не экспортируются из библиотеки DLL C ++, создает экземпляр класса шаблона и экспортирует его в дополнительную DLL, а соответствующий интерфейс C ++ может использоваться в .NET.

Смотрите также

Рекомендации

  1. ^ Маршалинг параметров не следует путать с общим термином сортировка, смысл Сериализация. Маршалированные параметры копируются в CLR стек после их преобразования в CTS типы, но не сериализованы.
  2. ^ https://docs.microsoft.com/en-us/cpp/dotnet/double-thunking-cpp
  3. ^ https://docs.microsoft.com/en-us/cpp/dotnet/initialization-of-mixed-assemblies

внешняя ссылка