Jump to content

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

(Перенаправлено с P/Invoke )

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

Управляемый код, такой как 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 . Его можно использовать, когда программисту необходим доступ к обширному API Windows , поскольку для многих функций, предоставляемых библиотеками Windows, отсутствуют доступные оболочки . Если API Win32 не предоставляется .NET Framework, оболочку этого API необходимо написать вручную.

Подводные камни

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

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

Другие подводные камни включают в себя:

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

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

Это сопряжено с новыми задачами:

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

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

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

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

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

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

HRESULT DllGetVersion
(
    DLLVERSIONINFO* pdvi
)

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

[StructLayout(LayoutKind.Sequential)]
private struct DLLVERSIONINFO {
    public int cbSize;
    public int dwMajorVersion;
    public int dwMinorVersion;
    public int dwBuildNumber;
    public int dwPlatformID;
}
[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, 
    [MarshalAs(UnmanagedType.LPStr)] string lpName);

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

[ редактировать ]
// native declaration
typedef struct _PAIR 
{ 
	DWORD Val1; 
	DWORD Val2; 
} PAIR, *PPAIR;
// Compiled with /clr; use of #pragma managed/unmanaged can lead to double thunking;
// avoid by using a stand-alone .cpp with .h includes.
// This would be located in a .h file.

template<>
inline CLR_PAIR^ marshal_as<CLR_PAIR^, PAIR> (const PAIR&Src) {    // Note use of de/referencing. It must match your use.
	CLR_PAIR^ Dest = gcnew CLR_PAIR;
	Dest->Val1 = Src.Val1;
	Dest->Val2 = Src.Val2;
	return Dest;
};
CLR_PAIR^ mgd_pair1;
CLR_PAIR^ mgd_pair2;
PAIR native0,*native1=&native0;

native0 = NativeCallGetRefToMemory();

// Using marshal_as. It makes sense for large or frequently used types.
mgd_pair1 = marshal_as<CLR_PAIR^>(*native1);

// Direct field use
mgd_pair2->Val1 = native0.Val1;
mgd_pair2->val2 = native0.val2;

return(mgd_pair1); // Return to C#

Инструменты

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

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

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

Брэд Абрамс говорит по этому поводу следующее: [ 4 ]

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

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

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

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

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

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

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

ПИнвокер

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

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

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

Помощник по взаимодействию Microsoft

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

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

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

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

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

Мастер P/вызова

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

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

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

xInterop C++ .NET мост

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

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. ^ «Двойное преобразование (C++)» .
  3. ^ «Инициализация смешанных сборок» .
  4. ^ «Проблема PInvoke» . Learn.microsoft.com . 6 февраля 2004 г.
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: a76dda30c5aaf9189c69d37a6fdbecce__1687023240
URL1:https://arc.ask3.ru/arc/aa/a7/ce/a76dda30c5aaf9189c69d37a6fdbecce.html
Заголовок, (Title) документа по адресу, URL1:
Platform Invocation Services - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)