Сервисы вызова платформы - Platform Invocation Services
Эта статья может быть слишком техническим для большинства читателей, чтобы понять. Пожалуйста помогите улучшить это к Сделайте это понятным для неспециалистов, не снимая технических деталей. (Март 2015 г.) (Узнайте, как и когда удалить этот шаблон сообщения) |
Сервисы вызова платформы, обычно называемый 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 #
Инструменты
В этом разделе использование внешняя ссылка может не следовать политикам или рекомендациям Википедии.Ноябрь 2013) (Узнайте, как и когда удалить этот шаблон сообщения) ( |
Существует ряд инструментов, предназначенных для помощи в создании подписей 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.
Смотрите также
- Blittable типы
- Собственный интерфейс Java, стандартный способ для программ Java доступа к машинному коду
- Собственный доступ Java, Java-эквивалент P / Invoke
- Файлы библиотеки Windows
- J / Direct, более не поддерживаемый эквивалентный API для Виртуальная машина Microsoft Java
Рекомендации
- ^ Маршалинг параметров не следует путать с общим термином сортировка, смысл Сериализация. Маршалированные параметры копируются в CLR стек после их преобразования в CTS типы, но не сериализованы.
- ^ https://docs.microsoft.com/en-us/cpp/dotnet/double-thunking-cpp
- ^ https://docs.microsoft.com/en-us/cpp/dotnet/initialization-of-mixed-assemblies
внешняя ссылка
- Сайт, посвященный P / Invoke
- J / Invoke Доступ Java к Win32 API или разделяемым библиотекам Linux / Mac OS X, аналогично P / Invoke
- [2] Неявный P / Invoke с особым упором на методы расширения на шаблон маршалинга
- 3 статьи от Microsoft, противопоставляющие эти методы, Использование явного вызова PInvoke, неявного взаимодействия C ++ и «Более пристальный взгляд на вызов платформы»
- Помощник Microsoft Interop Главная страница Microsoft Interop Assistant.
- P / Invoke Wizard Домашняя страница P / Invoke Wizard.
- Пинвокер Главная страница ПИнвокера.
- xInterop C ++ .NET Bridge xInterop C ++ .NET Bridge главная страница