Использование WinApi в определенных ЯП

Привет, читатель! В данной статье пойдет речь о том, как разобраться в странных записях с msdn’а, вроде такой:


HANDLE WINAPI CreateFile(
  _In_      LPCTSTR lpFileName,
  _In_      DWORD dwDesiredAccess,
  _In_      DWORD dwShareMode,
  _In_opt_  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  _In_      DWORD dwCreationDisposition,
  _In_      DWORD dwFlagsAndAttributes,
  _In_opt_  HANDLE hTemplateFile
);

 

 

Это не сложно! Статью я решил разбить на:

1) Общий обзор — буду рассказывать о том, как microsoft записывает прототипы функций у себя на сайте ( msdn ).

2) обзор для Delphi, C++, Assembler — буду рассказывать о том, как использовать это в определенных языках программирования.

Начнем.

Общий обзор

Для начала, ссылку на сам msdn: https://msdn.microsoft.com/

Нас интересует эта страница: https://msdn.microsoft.com/en-us/library/windows/desktop/ff818516%28v=vs.85%29.aspx

На этой странице вы сможете найти нужные вам структуры и функции. А я сейчас вам расскажу, как в них разобраться. Для примера возьмем функцию, которую использовали в прошлой статье: MessageBox: https://msdn.microsoft.com/en-us/library/windows/desktop/ms645505%28v=vs.85%29.aspx


int WINAPI MessageBox(
  _In_opt_  HWND hWnd,
  _In_opt_  LPCTSTR lpText,
  _In_opt_  LPCTSTR lpCaption,
  _In_      UINT uType
);

 

 

Итак, первая строка: int WINAPI MessageBox

Означает, что MessageBox возвращает значение типа int. Значение int — целочисленное значение. Аналог integer’а в Delphi. Занимает 4 или 2 байта ( зависит от системы ), очень часто именно 4 байта. В современных компах именно 4 байта, так что про 2 можете забыть.

имеет конвенцию вызовов stdcall (winapi). Это важно для ассемблерщиков, кодеры на высокоуровневых языках могут не въезжать в это. Ассемблерщики поймут.

А если все же не поймут, то конвенция вызовов ( информация для тех, кто кодит на ассемблере ) — это правило, по которым в функцию передают параметры. stdcall означает, что аргументы передаются через стек в обратном порядке, стек очищает вызываемая функция. Существует еще множество: pascal, cdecl, stdcall самые распространенные. Конвенцию pascal в WinAPI не встречал, а вот cdecl есть одна часто используемая функция: wsprintf. cdecl означает, что параметры в функцию передаются через стек, стек очищает вызывающая функция. Это что касается x86 системы, в x64 системе распространен fastcall, где аргументы передаются через регистры, а если регистров не хватает — через стек.

MessageBox — имя функции.

Далее: _In_opt_  HWND hWnd

_In_opt_ означает, что данный аргумент принимается на вход.

Бывают еще следующие:

_out_opt_ и _in_out_opt_

Первый означает, что это аргумент на выход, т.е в переданную переменную будет что-то положено, именно поэтому, если у нас _out_opt_ передаем АДРЕС переменной. Если вы не знаете, как получить адрес переменной, то почитайте про указатели в своем языке программирования.

_in_out_opt_ означает, что значение из переданной переменной используется в работе функции, а также в него сохраняется что-либо. В этих случаях, следует создать отдельную переменную с нужным нам значением, и передавать адрес переменной.

Следующее слово: HWND — означает, какой тип нужно передать. HWND — это хэндл окна, сокращенно от Handle of WiNDow ( ладно, забудьте, это я сам придумал, но, скорее всего, отсюда :D ).

Хэндл — это уникальный идентификатор какого-либо объекта.

hWnd — имя переменной, но не обращайте внимание, оно никакой роли не играет и используется во внутренней работе функции.

Теперь вы знаете, как понять прототип функции.

Где взять подробную информацию?

Подробную информацию можно взять в описании функции, в том же MSDN’е, давайте посмотрим страницу описания MessageBox: https://msdn.microsoft.com/en-us/library/windows/desktop/ms645505%28v=vs.85%29.aspx

Внизу, после прототипа функции мы можем найти подробное описание всех аргументов функции и возвращаемого значения.

Итак, давайте теперь подробно о популярных типах:

Что такое LPCTSTR

LPCTSTR — LP означает, что это указатель, т.е передаем мы адрес. константа, т.е строка не изменится. T — ASCII кодировка, почему T — не спрашивайте, не знаю, бывают еще Unicode строки, тогда будет LPCWSTR.

Str — указывает на то,  что у нас строка.

Что такое HANDLE

Handle — хэндл, я уже говорил о нем, является уникальным описателем объекта. Используя хэндл мы можем обращаться к ранее открытым файлам, процессам, созданным потоком и так далее.

Что такое UINT

UINT — сокращенно от Unsigned int, т.е беззнаковое целое.

Как понять приставку LP

LP означает, что передается указатель на что либо, другими словами, адрес переменной.

Например, LPHANDLE — указатель на переменную типа handle

Или LPINT — указатель на переменную типа int

О другом

Также бывают структуры: какие-либо структуированные данные: например, PROCESSENTRY32, содержит большое количество инфы о процессе.

Их, обычно, можно определить по длинному названию из нескольких англ слов.

Обзор для Delphi

Итак, приступим к делфям. Хоть я их не люблю, но…

Итак, в делфи LPCTSTR — это pansichar, или по другому, pchar. Чтобы передать строку какую-либо функции из переменной типа string, нужно это привести к типу pchar.

pchar(str);

НЕПРАВИЛЬНЫЙ пример на MessageBox

program hello;

uses Windows;

var a:string = 'Hello, World!';

begin

MessageBox(0, a, a, 0);

end.


 

 

Выдаст ошибку:

[Error] Project1.dpr(10): Incompatible types: ‘String’ and ‘PAnsiChar’

Как надо:

program hello;

uses
  Windows;

var a:string = 'Hello, World!';

begin

MessageBox(0, pchar(a), pchar(a), 0);

end.

Обзор для C/C++


#include <windows.h>

#pragma comment(linker, "/ENTRY:main /SUBSYSTEM:WINDOWS")

int main()
{
  char Hello[] = "Hello, World!";
  MessageBox(0, Hello, Hello, 0);
  return 0;
}

Ведь строка — это массив символов!

#pragma comment(linker, «/ENTRY:main /SUBSYSTEM:WINDOWS») означает, что точка входа у нас будет в main, а подсистема будет WINDOWS. Это нужно, чтобы не создавалось лишних окон, вроде консольного окна.

Обзор для Assembler

Юзать буду, как всегда, FASM. Хоть я больше люблю MASM, но FASM более доступен новичку.


include 'win32ax.inc'

section '.code' code readable executable

body db 'Hello, World!',0

title db 'Hi! I am a title',0

start:

xor ebx, ebx

push ebx

push title

push body

push ebx

call [MessageBoxA]

push 0

call [ExitProcess]

.end start

xor ebx, ebx — аннулируем ebx, в нем будет 0. Это быстрее чем mov ebx, 0 и оптимальнее заносить push ebx, нежели push 0 ( что равносильно push 00000000 )

В примере я специально привел еще и создание заголовка отдельного, чтобы было видно, что в стек аргументы передаются в обратном порядке.

Но! В FASM’е и MASM’е есть специальный макрос: invoke.

Он позволяет вызывать функции привычно, давайте посмотрим:


include 'win32ax.inc'

section '.code' code readable executable

body db 'Hello, World!',0

title db 'Hi! I am a title',0

start:

invoke MessageBoxA, 0, body, title, 0

invoke ExitProcess, 0

.end start

макрос invoke сам передаст аргументы, как того требует функция, если надо очистить стек — очистит. При компиляции это все заменится на привычные нам push и call

 

Что такое постфиксы A и W к функциям?

В примерах на ассемблере я использую MessageBoxA, почему?

Дело в том, что нет такой функции MessageBox, есть только MessageBoxA и MessageBoxW

Отличие в том, что первая принимает ASCII строку, а другая Unicode строку.

Те функции, которые не требуют себе строку будут выглядить обычно: CreateThread, например. А вот требующие, например, wsprintf иначе: wsprintfA/wsprintfW

Компилятор C/C++ и Delphi сам подбирает нужную, исходя из настроек. По умолчанию обычно стоит A, поэтому я их и не прописывал. Но, вы можете попробовать — компилятор все равно скомпилирует все верно.

 

 

Оставьте комментарий