Прототипирование вызовов API для "чайников"


Спросите дюжину Clarion программистов делать прототипы вызовов функций Windows API и, вероятно, вы получите 20 разных ответов. Каждый имеет свое собственное мнение и теорию о том как это нужно делать, и почему это должно быть сделано его способом а не вашим. В соответствии с этой традицией, это мой список из 10 простых правил, которых нужно придерживаться при прототипировании вызовов Windows API.

1) Не используйте утилиту Clarion-овскую утилиту конвертирования WINAPI.EXE, которая находится в папке .\Bin. В ней я нашел прототипы, которые просто неверны. Допускаю, что их можно исправить, но я больше ей не доверяю. К тому же они не соответствуют моим персональным правилам прототипирования!

Намного проще идти прямо от C-шных заголовочных файлов. В идеале, вы должны использовать библиотеку MSDN (онлайновую или на CDROM/DVD)

3) Используйте атрибут PASCAL. Вызовы Windows API используют соглашение о вызовах языка Pascal, которое означает что параметры помещаются на стек и передаются слева направо. Имеются две других альтернативы, соглашение о вызовах языка C (через стек, справа налево)  и, по умолчанию, собственное соглашение JPI (через регистры, использует стек только при необходимости).

Sleep(DWord),Pascal
GetActiveWindow(),Handle,Pascal
GetLastError(),DWord,Pascal

4) Атрибут DLL не нужен. Как минимум, он не для вызовов API. Не забываете его в ваших собственных прототипах.

5) Если в качестве параметра нужно использовать строку, используйте CSTRINGs а не обычные Clarion STRINGs. Если у вас есть процедура, которая принимает указатель на буфер (как ReadFile() или WriteFile()), то можно использовать обычную STRING, но для процедур, которые принимают текст или заголовок окна, нужно использовать Clarion-овские CSTRING.

6) Для процедур, принимающих строковые параметры, не забывайте использовать атрибут NAME. Процедуры, вероятно, имеют две версии: ANSI и Unicode - MSDN скажет точно. Нужно использовать атрибут NAME, чтобы сообщить линкеру какую именно версию в хотите использовать. Clarion (пока) не поддерживает Unicode, так что вероятно вы должны использовать версию ANSI если не хотите потом вручную конвертировать в или с Unicode.

7) Для параметров, которые указывают на что-либо, используйте ULONG.

Когда вам нужно передать указатель на  что-либо, у вас в основном есть 2 варианта:

Лично я всегда предпочитаю делать первым способом, по паре причин:

- для вещей отличных от строк, наподобие групп или других структур, я не должен иметь группу, объявленную глобально перед MAP. Например, посмотрите на SetCommTimeouts():

BOOL SetCommTimeouts( 
 HANDLE hFile,
 LPCOMMTIMEOUTS lpCommTimeouts
 };

Я могу прототипировать это как:

SetCommTimeouts(Handle,ulong),Bool,Pascal
SetCommTimeouts(Handle,*COMMTIMEOUTS),Bool,Pascal,Raw

Во втором прототипе, я должен иметь структуру COMMTIMEOUTS, объявленную глобально перед MAP. По каким-либо причинам я могу не захотеть этого делать.

- Легче передать нулевой указатель, когда он определен как ULONG, чем если он определен как *THING. Да, я знаю, что это легко когда мы имеем дело с *CSTRINGs (передать пустую строку), но становится намного сложнее если это не строка - посмотрите, например, на первый параметр CreateMutex().

8) Учимся использовать memcopy()

Clarion является языком не поддерживающим указателей.

Взгляните на GetHostByName() в качестве примера - она возвращает указатель на структуру HOSTENT.  Получение отдельных полей этой структуры сложно без использования memcpy()

Map
 GetHostByName(ULong),ULong,Pascal
End
IPAddress  CString(20)
HostBuffer ULong
HostInfo   Like(HostEnt)
Code
 IPAddress = '127.0.0.1'
 HostBuffer = GetHostByName( Address(IPAddress ) )
 If HostBuffer = 0
  ! Call failed - we got a null pointer back
 Else
  ! Call worked
  memcpy( Address(HostInfo), HostBuffer, Size(HostInfo) )
  ! Now we can look at the fields in the HostEnt structure
  ! It just so happens that most of them all pointers anyway, so you have to use
  ! memcpy() again on each individual field
 End

memcpy() является стандартным вызовом runtime-библиотеки C, который используется для копирования содержимого памяти из одного места в другое. Три параметра - это адрес назначения, адрес источника и число байт для копирования. Runtime библиотека Clarion содержит большую часть runtime библиотеки C, поэтому вам не надо беспокоится об использовании другой DLL - все уже есть в RTL Clarion.

9) Учимся использовать LibMaker

Clarion поставляется с файлом библиотеки (.LIB) называемой WIN32.LIB.  Она включает информацию, которая нужна линкеру для линковки вашего приложения, когда оно использует вызов API. К сожалению, она включает информацию только о тех  вызовах  API, которые имеются во всех версиях Windows. Если нужно использовать вызов API, которого нет  в Win95 (наподобие IsDebuggerPresent(), появившейся только в Win98, или GetModuleBaseName(), которая есть только в Windows NT, Win2000 и WinXP), то вам не повезло.

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

Нужно иметь в виду, что если вы будете использовать вызов API, который не поддерживается в других ОС, то ваша программа не сможет даже загрузиться под этой ОС.  Например, под Win95 невозможно запустить программу, которая использует вызов IsDebuggerPresent(). 

10) Если не можете заставить работать, спросите...

Не спрашивайте у меня.  Спросите в конференции Clarion на news.softvelocity.com. Имеется шанс, что кто-либо уже сделал то что вы пытаетесь сделать, или знает того кто сделал...