Привет, читатель! В данной статье пойдет речь о том, как разобраться в странных записях с 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, поэтому я их и не прописывал. Но, вы можете попробовать — компилятор все равно скомпилирует все верно.