Службы вызова платформы

редактировать

Службы вызова платформы, обычно называемые P / Invoke - это функция реализации Common Language Infrastructure. такие возможности, как Microsoft Common Language Runtime, которые позволяют управляемому коду вызывать собственный код.

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

Содержание
  • 1 Архитектура
    • 1.1 Обзор
      • 1.1.1 Явный
      • 1.1.2 Неявный
  • 2 Подробности
  • 3 Ловушки
  • 4 Примеры
    • 4.1 Базовые примеры
    • 4.2 Более сложный пример
  • 5 Инструменты
    • 5.1 PInvoke.net
    • 5.2 PInvoker
    • 5.3 Microsoft Interop Assistant
    • 5.4 Мастер P / Invoke
    • 5.5 xInterop C ++.NET Bridge
  • 6 См. Также
  • 7 Ссылки
  • 8 Внешние ссылки
Архитектура

Обзор

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

Явный

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

Неявного

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

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

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

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

Ловушки

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

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

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

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

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

  • Код подвержен двойному преобразованию, если не решен специально
  • Проблема блокировки загрузчика

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

Примеры

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

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

DllGetVersion в Windows. API :

HRESULT DllGetVersion (DLLVERSIONINFO * pdvi)

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

[DllImport ("shell32.dll")] static extern int DllGetVersion (ref DLLVERSIONINFO pdvi);

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

Сигнатура функции ExtractIcon в Windows API:

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

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

[DllImport ("shell32.dll")] static extern IntPtr ExtractIcon (IntPtr hInst, [MarshalAs (UnmanagedType.LPStr)] string lpszExeFileName, uint nIconIndex);

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

сигнатура функции CreateEvent:

HANDLE CreateEvent (LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);

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

[DllImport ("kernel32.dll", SetLastError = true)] static extern IntPtr CreateEvent (IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, [MarshalyAs (Unmanaged. LPStr)] строка lpName);

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

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

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

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

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

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

__declspec (dllexport) void MyFunction (char * params);

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

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

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

PInvoke.net

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

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

PInvoker

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

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

Microsoft Interop Assistant

Microsoft Interop Assistant - это бесплатный инструмент, доступный с двоичными файлами и исходным кодом, доступным для загрузки на CodePlex. Он находится под лицензией Microsoft Limited Public License (Ms-LPL).

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

  • Конвертер, который принимает небольшие участки исходного кода файла заголовка 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.

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

См. Также
Ссылки
Внешние ссылки
Последняя правка сделана 2021-06-02 07:49:31
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте