win32API.pdf

(209 KB) Pobierz
win32API.fm
Calling Win32 API Routines from HLA
by Randy Hyde
The win32 libraries are probably second only to the HLA Standard Library it terms of frequency
of use in an HLA program. Although the HLA Standard Library shields you from much of the
details concerning win32 API calling convensions when writing console applications, the Stan-
dard Library does not provide a buffer layer between you and all the possible win32 calls. Fur-
thermore, for GUI and other non-console applications, the HLA Standard Library provides very
little help, you will have no choice but to interface directly with the win32 Application Program-
mer’s Interface. This document will describe how this is done from within an HLA program.
The first, and perhaps most important point to note is that HLA uses the “Pascal” calling conven-
tion while the Windows API uses the “STDCALL” calling convention. Pascal/HLA procedures
push the parameters on the stack in the order they are encountered in the actual parameter list;
Pascal/HLA procedures automatically pop all parameters off the stack upon return. The “STD-
CALL” calling convention also pops all actual parameters off the stack upon return; however, the
“STDCALL” convention pushes parameters on the stack in the reverse order the compiler
encounters them in the actual parameter list. Since both calling mechanisms expect the called
procedure to remove all parameters from the stack, HLA is compatible with the STDCALL con-
vention on this point. As for the order of parameters, the win32 API expects the HLA caller to
push the parameters in the reverse order than HLA actually pushes them. So the two calling
mechanisms are incompatible on this point. Fortunately, it’s easy to work around this problem,
just specify the @stdcall procedure option when creating the prototype for a Win32 API function.
The last difference between the two calling mechanisms is that the Pascal interface simply uses
the name of the procedure as the external name while the STDCALL prepends an underscore to
the function name and uses that combination as the external name. Fortunately, HLA’s external
procedure declaration syntax provides an easy solution to this problem.
First consider the “C” declaration for the win32 API
WriteFile
function call:
int WriteFile( dword Handle, byte *buffer, dword len, dword *BytesWritten, dword Overlapped );
As it turns out, “WriteFile” isn’t the true name of this procedure. Microsoft appends “@20” to the
end of the name to denote that this procedure has 20 bytes of parameters. Along with the STD-
CALL convention of prepending an underscore, the actual external name is “_WriteFile@20”.
Therefore, this is the name we’ll have to supply as the HLA external name. Since HLA does not
allow at-signs (“@”) in the middle of an identifier, we’ll have to use the auxiliary external declara-
tion syntax that HLA supports when declaring this function. Specifically, the HLA declaration
will look something like:
procedure WriteFile(
parameters
); @stdcall; external( "_WriteFile@20" );
procedure WriteFile
(
Handle: dword
var buffer: var;
len: dword;
var BytesWritten: dword;
overlapped: dword;
); external( "_WriteFile@20" );
This declaration will work but there a slight problem with it. The problem with calling the win32
API in this fashion is that it’s slightly inefficient; this inefficiency is not specific to HLA, most
win32 API calls suffer from this problem, but since this is assembly language it is possible to
improve the efficicency of the win32 API call without much effort.
One problem that affects all win32 API calls is the actual machine language interface to the DLLs
that process the system calls. Whenever you have an external procedure declaration like the one
above, HLA emits a CALL machine instruction whose operand field the linker must fill in with
the address of the actual subroutine. Unfortunately, win32 API calls are made indirectly through a
variable rather than directly to the actual routine. The direct call that HLA (and other languages)
emits will not jump indirect through this pointer variable. To solve this problem, external symbols
like "_WriteFile@20" in the kernel32.lib file do not hold the address of the actual API routine or
even the indirect jump address. Instead, these names specify the address of a JMP instruction that
jumps indirectly through the pointer variable. Therefore, your win32 API calls wind up calling a
JMP instruction that jumps indirect through the pointer variable.
It turns out that with a very small change, you can avoid the overhead of this extra JMP instruction
on each win32 API call. In addition to exporting the names of the win32 API entry points (which
are the addresses of these JMP instructions), the win32 LIB files also export the names of the
pointer variables. These pointer variables have the same name as the API routines except they
have "__imp__" prepended to the name rather than a single underscore (by the way, that’s two
underscores followed by "imp" followed by two more underscores). One really slick feature of
HLA is that it uses the same syntax for indirect procedure calls as it does for direct procedure
calls. This means that by simply changing the external declaration, we can tell HLA to generate
an indirect jump rather than a direct jump to the API routines; nothing else changes. Therefore,
to remove all the calls to JMPs, all you need to is declare your API routines as follows:
static
win32_WriteFile: procedure
(
Handle: dword
var buffer: var;
len: dword;
var BytesWritten: dword;
overlapped: dword;
); @external( "__imp__WriteFile@20" );
This declares win32_WriteFile as a procedure pointer rather than a procedure. The external
name is the name of the variable containing the address of the actual WriteFile@20 routine.
Whenever HLA encounters a call to win32_WriteFile, it emits an indirect CALL instruction that
transfers control directly to the WriteFile routine rather than transferring control to the indirect
JMP instruction. This saves a small amount of execution time on each API call.
The only real catch is figuring out the API names. Unfortunately, Microsoft’s documentation
doesn’t tell you the (external) name of each API routine. Fortunately, you can use the Microsoft
DUMPBIN.EXE utility to extract this information directly from the LIB files. To do this, use the
following command syntax:
dumpbin /symbols kernel32.lib
(Obviously, you should supply the appropriate name of the library whose symbols you want to
see).
The dumpbin utiltity, with the "/symbols" option displays all the symbols that the specified library
module exports. If you’re looking for a symbol like "WriteFile" you will probably find a few
symbols that have the "WriteFile" substring present. It’s a good bet that one of these names (par-
ticularly, with the "__imp__" prefix) is the name you want to use in the HLA external declaration.
) suffix deal with 16-bit UNI-
CODE characters. Feel free to call whichever form is appropriate for your application. Do keep
in mind, though, that the HLA Standard Library routines always expect ANSI/ASCII characters
and generally don’t support UNICODE characters. So if you’re working with wide characters,
you may not be able to use routines in the HLA Standard Library that work with character and
string data.
wide
The following tables list the names that the "kernel32.lib" file exports. This will give you a basic
idea of the symbol types that appear in a typical LIB file. This list only presents those symbols
found in the "kernel32.lib" file, there are many hundreds of additional names found in other win-
dows LIB files. Here is a list of some of the LIB files that accompany Microsoft’s Visual C++ sys-
tem that may contain useful API routines you can call:
ADVAPI32.LIB
COMCTL32.LIB
COMDLG32.LIB
CRYPT32.LIB
CTL3D32.LIB
CTLFWD32.LIB
CTLFWR32.LIB
DMAPIW32.LIB
Many Win32 API export names will have two varieties: one form containing an "A" before the at-
sign and one containing a "W" before the at-sign. For example, the "CreateFile" function has two
forms: "_CreateFileA@28" and "_CreateFileW@28". The difference between functions whose
names end in these two characters is that those with an "A" suffix deal with 8-bit ASCII characters
(or, more correctly, ANSI characters). Those with a "W" (for
FMISTR32.LIB
GDI32.LIB
GLU32.LIB
GTRTST32.LIB
IMM32.LIB
KERNEL32.LIB
LSAPI32.LIB
LZ32.LIB
MAPI32.LIB
MFCUIA32.LIB
MSACM32.LIB
MSIMG32.LIB
MSLSP32.LIB
NETAPI32.LIB
ODBC32.LIB
ODBCCP32.LIB
OLE32.LIB
OLEAUT32.LIB
OLEPRO32.LIB
OPENGL32.LIB
PENWIN32.LIB
PKPD32.LIB
RASAPI32.LIB
RTFLIB32.LIB
SHELL32.LIB
TAPI32.LIB
TH32.LIB
THUNK32.LIB
USER32.LIB
VFW32.LIB
WAPPC32.LIB
WCPIC32.LIB
WINCSV32.LIB
WINRUI32.LIB
WINSLI32.LIB
WLDAP32.LIB
WOW32.LIB
WS2_32.LIB
WSNMP32.LIB
WSOCK32.LIB
Table 1: Kernel32.lib API Routine Export Names
_AddAtomA@4
_AddAtomW@4
_AddConsoleAliasA@12
527774613.001.png
Table 1: Kernel32.lib API Routine Export Names
_AddConsoleAliasW@12
_AllocConsole@0
_AreFileApisANSI@0
_BackupRead@28
_BackupSeek@24
_BackupWrite@28
_BaseAttachCompleteThunk@0
_Beep@8
_BeginUpdateResourceA@8
_BeginUpdateResourceW@8
_BuildCommDCBA@8
_BuildCommDCBAndTimeoutsA@1
2
_BuildCommDCBAndTimeoutsW@
12
_BuildCommDCBW@8
_CallNamedPipeA@28
_CallNamedPipeW@28
_CancelIo@4
_CancelWaitableTimer@4
_ClearCommBreak@4
_ClearCommError@12
_CloseConsoleHandle@4
_CloseHandle@4
_CloseProfileUserMapping@0
_CmdBatNotification@4
_CommConfigDialogA@12
_CommConfigDialogW@12
_CompareFileTime@8
_CompareStringA@24
_CompareStringW@24
_ConnectNamedPipe@8
_ConsoleMenuControl@12
_ContinueDebugEvent@12
_ConvertDefaultLocale@4
_ConvertThreadToFiber@4
_CopyFileA@12
_CopyFileExA@24
_CopyFileExW@24
_CopyFileW@12
_CreateConsoleScreenBuffer@20
_CreateDirectoryA@8
_CreateDirectoryExA@12
_CreateDirectoryExW@12
_CreateDirectoryW@8
_CreateEventA@16
_CreateEventW@16
_CreateFiber@12
_CreateFileA@28
_CreateFileMappingA@24
_CreateFileMappingW@24
_CreateFileW@28
_CreateIoCompletionPort@16
_CreateMailslotA@16
_CreateMailslotW@16
_CreateMutexA@12
_CreateMutexW@12
_CreateNamedPipeA@32
_CreateNamedPipeW@32
_CreatePipe@16
_CreateProcessA@40
_CreateProcessW@40
_CreateRemoteThread@28
_CreateSemaphoreA@16
_CreateSemaphoreW@16
_CreateTapePartition@16
_CreateThread@24
_CreateVirtualBuffer@12
_CreateWaitableTimerA@12
_CreateWaitableTimerW@12
_DebugActiveProcess@4
_DebugBreak@0
_DefineDosDeviceA@12
_DefineDosDeviceW@12
_DeleteAtom@4
_DeleteCriticalSection@4
_DeleteFiber@4
_DeleteFileA@4
_DeleteFileW@4
_DeviceIoControl@32
_DisableThreadLibraryCalls@4
_DisconnectNamedPipe@4
_DosDateTimeToFileTime@12
_DuplicateConsoleHandle@16
_DuplicateHandle@28
_EndUpdateResourceA@8
_EndUpdateResourceW@8
_EnterCriticalSection@4
_EnumCalendarInfoA@16
_EnumCalendarInfoW@16
_EnumDateFormatsA@12
_EnumDateFormatsW@12
527774613.002.png
Zgłoś jeśli naruszono regulamin