Виклик функцій windows api

З книги C #. Поради програмістам (в скороченні)

Програмний код, який виконується під управлінням CLR (Common Language Runtime, т. Е. Загальна Виконавча мов), називається керованим (managed) кодом. Програмний код, що виконується поза середовищем виконання CLR, називається некерованим (unmanaged) кодом. Прикладом некерованого програмного коду служать функції Win32 API, компоненти COM, інтерфейси ActiveX. Незважаючи на велику кількість класів .NET Framework, що містять безліч методів, програмісту все одно доводиться іноді вдаватися до некерованого коду. Треба сказати, що число викликів некерованого коду зменшується з виходом кожної нової версії .NET Framework. Microsoft сподівається, що настане такий час, коли весь код можна буде зробити керованим і безпечним. Але поки реальність така, що без викликів функцій Windows API нам поки не обійтися. Але спочатку трохи теорії.

Керований код .NET Framework може викликати некеровану функцію з DLL (функцію Windows API) за допомогою спеціального механізму Platform Invoke (скор. P / Invoke). Для того щоб звернутися до якої-небудь неуправлямойнеуправляемой бібліотеці DLL, ви повинні перетворити .NET-об'єкти в набори struct, char * і покажчиків на функції, як того вимагає мову C. Як сказали б програмісти на своєму жаргоні - вам потрібно маршаліровать параметри. Більш докладно про маршалинга (Marshalling) вам слід почитати в документації. Щоб викликати DLL-функцію з C #, спочатку її необхідно оголосити (програмісти, які мають досвід роботи з Visual Basic 6.0, вже знайомі з цим способом). Для цього використовується атрибут DllImport:

Іноді в прикладах ви можете також зустріти такий спосіб (довгий і незручний): [System.Runtime.InteropServices.DllImport ( "User32.Dll")]. . але це на любителя.

Атрибут DllImport повідомляє компілятору, де знаходиться точка входу, що дозволяє далі викликати функцію з потрібного місця. Ви повинні завжди використовувати тип IntPtr для HWND. HMENU і будь-яких інших описателей. Для LPCTSTR використовуйте String. а сервіси взаємодії (interop services) виконають автоматичний маршаллінг System.String в LPCTSTR до передачі в Windows. Компілятор шукає зазначену вище функцію SetWindowText в файлі User32.dll і перед її викликом автоматично перетворює вашу рядок в LPTSTR (TCHAR *). Чому це відбувається? Для кожного типу в C # визначено свій тип, який використовується при маршалинга за замовчуванням (default marshaling type). Для рядків це LPTSTR.

Виклик функцій Windows API, що мають вихідний строковий параметр char *

Припустимо, нам необхідно викликати функцію GetWindowText. у якій є строковий вихідний параметр char *. За замовчуванням, для рядків використовується LPTSTR. але якщо ми будемо використовувати System.String. як було сказано вище, то нічого не станеться, так як клас System.String не дозволяє модифікувати рядок. Вам необхідно використовувати клас StringBuilder. який дозволяє змінювати рядки.

Тип, який використовується для маршашлінга StringBuilder за замовчуванням, - теж LPTSTR. зате тепер GetWindowText може модифікувати саму вашу рядок:

Таким чином, відповіддю на питання, як викликати функцію, у якій є вихідний строковий параметр, буде - використовуйте клас StringBuilder.

Зміна типу, застосовуваного для маршалинга за замовчуванням

Наприклад, ми хочемо викликати функцію GetClassName. який приймає параметр LPSTR (char *) навіть в Unicode-версіях. Якщо ви передасте рядок, загальномовне виконуюча середовище (CLR) перетворює її в серію TCHAR. Але за допомогою атрибута MarshalAs можна перевизначити те, що пропонується за замовчуванням:

Тепер, коли ви викличете GetClassName. NET передасть вашу рядок у вигляді символів ANSI, а не "широких символів".

Виклик функцій, що вимагають struct

Візьмемо для прикладу функцію GetWindowRect. яка записує в структуру RECT екранні координати вікна. Щоб викликати функцію GetWindowRect і передати їй структуру RECT потрібно використовувати тип struct в поєднанні з атрибутом StructLayout:

Робота з функціями зворотного виклику в C #

Для використання функцій, написаних на C #, як функцій зворотного виклику Windows, потрібно використовувати делегати (delegate).

Оголосивши свій тип делегата, можна написати оболонку для функції Windows API:

Так як в рядку з delegate просто оголошується тип делегата (delegate type), сам делегат потрібно надати в класі:

а потім передати оболонці:

Проникливі читачі помітять, що я промовчав про проблему з lparam.

У мові C, якщо в EnumWindows дається LPARAM, Windows повідомлятиме вашу функцію зворотного виклику цим LPARAM. Зазвичай lparam - покажчик на якусь структуру або клас, який містить контекстну інформацію, потрібну вам для виконання своїх операцій. Але запам'ятайте: в .NET слово «покажчик» вимовляти не можна! Так що ж робити? Можна оголосити ваш lparam як IntPtr і використовувати GCHandle як його оболонки:

Не забудьте викликати Free, коли закінчите свої справи! Іноді в C # доводиться самому звільняти пам'ять. Щоб отримати доступ до «вказівником» lparam всередині перечіслітеля, використовуйте GCHandle.Target.

Нижче показаний написаний мною клас, який інкапсулює EnumWindows в масив. Замість того щоб возитися з усіма цими делегатами і зворотними викликами, ви пишете:

Невелика програма ListWin (Додаток ListWin.cs), яку я написав для перерахування вікон верхнього рівня, дозволяє переглядати списки HWND, імен класів, заголовків і / або прямокутників вікон, використовуючи RECT або Rectangle. Вихідний код ListWin показаний не повністю; весь вихідний код можна завантажити за наведеним в кінці статті.

Створення власної керованої бібліотеки

Можна створити власну керовану бібліотеку, з якої можна буде викликати функції Windows API. Для цього в Visual Studio передбачені спеціальні опції. Новий проект створюється як бібліотека класів (Class Library). Збірка при цьому автоматично отримує розширення dll. Використовувати керовану бібліотеку в керованому коді просто. Для цього треба додати посилання (використовуючи меню Project | Add Reference ...) на бібліотечну збірку, вказавши місце розташування збірки у відповідному діалоговому вікні. Після цього Visual Studio копіює збірку в директорію, в якій розташовується розробляється код. Далі в коді програми використовується або оператор using. або повне ім'я бібліотечного модуля з точковою нотацією. Всі бібліотечні класи і методи готові до використання в коді програми.

Також можна скористатися командним рядком. Щоб скомпілювати клас Win32API.cs, введіть:

В результаті у вас буде створений файл Win32API.dll, доступний для будь-якого проекту на C #.

Приклад створення бібліотеки та використання функцій Windows API знаходиться в папці Win32 на прикладеному до книги диску.

Приклади використання функцій API

Коротенько ознайомившись з теорією, перейдемо до конкретних прикладів. У попередніх розділах я вже неодноразово наводив приклад використання функцій Windows API для вирішення різних проблем. Розглянемо ще кілька корисних порад, які не ввійшли в інші розділи.

висновок

Незважаючи на величезну кількість наявних класів .NET Framework, програмісту як і раніше доводиться вдаватися до викликів системних функцій Windows API. В папці Win32Help на прикладеному до книги компакт-диску ви знайдете демо-версію довідника по функціях Windows API для .NET Framework. Якщо вам сподобається цей довідник, то ви можете придбати його повну версію на моєму сайті.

додаток

Win32API.cs

ListWin.cs

додаткова інформація

Схожі статті