|
|
 |
 |
 |
|
Join the Discussion
|
Post your views and comments to this chapter of the free "raw API programming" Delphi Course
Discuss!
|
|
 |
 |
|
|
 |
 |
|
|
 |
 |
Chapter 1: A guide to developing Delphi programs in raw Windows API
When you read the Win32 API help, you will see that the "C" language syntax is used. This article will help you learn the differences between C langauage types and Delphi language types.
The article is written by Wes Turner, and brought to you by Zarko Gajic
When you read the Win32 API help, you see that the "C" language is used and not Delphi. In a function listing, like CreateWindow( ), the parameters will be listed with the type of variable before the variable name not after it, like in Delphi. The Result of a function is listed before the function not after it as in Delphi. The first HWND in the CreateWindow( ) listing below is the return result type.
HWND CreateWindow(
LPCTSTR lpClassName, // pointer to registered class name
LPCTSTR lpWindowName, // pointer to window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // handle to menu or child-window identifier
HANDLE hInstance, // handle to application instance
LPVOID lpParam // pointer to window-creation data
);
|
In the first parameter the variable name is "lpClassName" and the variable type is "LPCTSTR". A variable naming convention called "Hungarian notation" is used for most variables. In the "lpClassName" the first 2 characters are Hungarian notation for "long pointer". In the variable "dwStyle", the dw tells you it's a "DWORD" type, and in the "nWidth" the n tells you it's a "Short Int" type (actually this "n" is left over from 16 bit windows and is now a 32 bit Integer type). Here is a list of the commonly used Hungarian notation prefixes, in the C language:
C naming conventions
| Prefix | C language Data Type |
| b | Boolean |
| by | Byte |
| c | Char |
| cx/cy | short Int used as size |
| dw | DWORD |
| fn | Function |
| h | Handle |
| i | Integer |
| l | Long |
| n | see below* |
| p | Pointer to address of variable |
| s | string |
| sz | ASCI null terminated string |
| w | Word |
| x, y | Short Int used as coordinates |
You will see the "lp" prefix in most API functions, this is to mark the difference between a "Pointer" (16 bit Word type - 2 bytes) and the "Long Pointer" (32 bit DWord type - 4 bytes), however this has NO significance in the 32 bit windows systems (windows 95 and later), since ALL POINTERS are 32 bit (4 byte - DWord, Cardinal types).
*) The n prefix does not indicate a type. Rather, it designates the purpose of the variable as a count, often a number of characters in a string or a number of elements in an array.
Here is a list of some of the C type compared to Delphi types:
| C type | Delphi type |
| HANDLE | THandle == LongWord (Cardinal) |
| HWND | THandle == LongWord (Cardinal) |
| HMENU, HICON, HBITMAP, HFONT, HBRUSH, HPEN | THandle == LongWord (Cardinal) |
| BYTE | Byte |
| SHORT | SmallInt |
| INT | Integer |
| WORD | Word |
| DWORD | LongWord (Cardinal) |
| LONG | Integer |
| LONGLONG | Int64 |
| UINT | LongWord; Cardinal (see UINT comment below) |
| ASCI null terminated string | PAnsiChar or PChar |
| LPSTR, LPCTSTR | PAnsiChar |
| ULONG | Cardinal (see UINT comment below) |
| BOOLEAN | Boolean |
| BOOL | LongBool |
| LPVOID, PSID | Pointer |
| LCID | DWORD, LongWord (Cardinal) |
| LANGID | Word |
| UCHAR | Byte |
Note: There is no Delphi variable type that is the equivalent of a C code Unsigned Integer, UINT. A C code UINT can have a Cardinal value from 0 to 4,294,967,294 AND a single negative value of -1. Delphi declares a UINT as a LongWord (Cardinal), but if this variable has a Value of (hex) $FFFFFFFF (const MAXDWORD), then it is a -1 to the Windows System.
UINT is defined in C as type unsigned int. Microsoft defines the constant UINT_MAX as 4,294,967,295, which
doesn't leave any room for a -1 value. The reasons Microsoft uses -1 are probably two-fold: First, the C compiler won't care about overflow or underflow on unsigned types, and second, "-1" is easier to write than "4294967295" or "0xffffffff", especially when the actual size of the variable isn't known or is liable to change. "-1" means all-bits-one regardless of the variable's size, whereas 4294967295 won't be the proper value for a 64-bit type.
It may be confusing to understand the data types in the Win32 API help since there are several "C types" for *only* one Delphi variable type, such as "DWORD, UNIT, HWND, ULONG, and LCID" are all Cardinal types in Delphi. And to add to the confusion, many Delphi code examples use "LongWord" for "Cardinal", and a "LongInt" for an "Integer" type. If you have trouble with C data types, you usually can use the "C" type to define your variable in your code, since it was necessary to define these types in the windows.pas unit, for instance, in windows.pas it has:
type
DWORD = LongWord;
LONG = Integer;
LCID = DWORD;
SHORT = Smallint;
|
Since many parameters are declared as a Pointer type, it was necessary to have many pointer types declared in windows.pas, here are some of the most common:
PByte = ^Byte;
PLongint = ^Longint;
PInteger = ^Integer;
PINT = ^Integer;
PLongWord = ^LongWord;
PSmallInt = ^Smallint;
PDWORD = ^DWORD;
LPDWORD = PDWORD;
PDouble = ^Double;
PShortInt = ^ShortInt;
|
BOOLEAN and BOOL, What is a Long Boolean?
In many API functions there is a parameter type of BOOL, a 32-bit "Long" Boolean type (a 4 byte variable to fit a 32-bit memory block). The Delphi compiler will often treat this LongBool as a Normal Boolean type (It is either True or False), and the Windows system will only use a True and False for the value of a Long Boolean. But a Long Boolean type can sometimes have additional information in the other 3 bytes of that variable, although this is not a common practice.
Also, you will see a reference in the API Help for an Integer (usually a Return value) or Cardinal type as a "True" or "False", this just means that the Integer value will be zero (0) for False and one (1) for True. (rarely you might see the reverse, a Boolean type referred to as 1 and 0).
Data structures
In Delphi the term "Record" is used for a collection of data items, in C these collections are called a "Structure". Many API functions will use Pointers to a Structure, like RECT (TRect) or POINT (TPoint) and the API Help will show you what variables are in these structures. Usually the "typedef" and a "struct" precede the structure definition (corresponding roughly to "Type" and "Record" in Delphi)).
// for a POINT
typedef struct tagPOINT {
LONG x;
LONG y;
} POINT;
// for a RECT
typedef struct _RECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT;
Some API Structures:
MSG = TMsg
WNDCLASS = TWndClass
FINDREPLACE = TFindReplace
MENUITEMINFO = TMenuItemInfo
|
For most Windows API data structure types, you add a "T" to the structure data type name to get the Delphi record type, an API, C code "RECT" type becomes a "TRect" for Delphi; a "POINT" becomes a "TPoint". Please notice that for "typedef" names in the C code, like RECT and POINT, they are written in UPPERCASE, which is similar to the way Delphi puts a "T" in front of its type names like TRect or TPoint.
Predefined constants
Many of the API function parameters are numbers (Cardinal, Integer, Word), and to try to make code easier to read, numbers are given constant names to reflect what the number will do as the parameter. There are many predefined values like WM_PAINT, CS_PARENTDC, IDC_ARROW, and COLOR_BTNFACE. The prefix before the "_" sign can tell you the general category of the constant, and what follows the "_" will indicate what the constant value will do. Like IDC_ARROW, is a Cursor ID for the Arrow cursor. Here is a list of a few of the prefixes.
| Prefix | Constant type |
| WM_ | Windows Message |
| WS_ | Window Creation Style |
| CS_ | Class Style |
| CW_ | Create Window |
| IDC_ | Cursor ID |
| IDI_ | Icon ID |
| HKEY_ | Registry Key |
| FW_ | Font Weight |
| BS_ | Window Creation Button Style |
| MB_ | Button Message |
| ES_ | Window Creation Edit Style |
C Text String and Special Characters
In Delphi you can use the ' single quote sign to signify the beginning and end of an array of characters (String, PChar). In C code a " double quote sign is used. And in Delphi the # with the character number, like #9 is used to have special characters in a string, of course if you need a single quote in a string you will need to put two single quotes so the compiler will NOT think the single quote ends the string.
In C code the \ is used as an escape character in text strings enclosed in double quotes. If you wanted to put a double quote in a text string you would put a \" to show that the double quote is in the string and not the end of the character array (string). To have a backslash \ in a C string you would put two backslashes \\ to show that it is not an escape character. The next example shows how to code the string - 'the \ is an "escape" character':
"the \\ is an \"Escape\" character"
|
Some other escape characters are
\t Tab Stop
\' Single Quote
\" Double Quote
\? Question Mark
\\ Backslash
\n New Line
\r Carriage Return
\b Backspace
|
PChar and Delphi String use in API functions
When you use API functions and pass your program's variables as parameters you need to keep in mind that the memory used by the variables is changed by the OS and NOT by your program, so variables that are not a fixed size (usually PChar) will need to be given memory allocations - (Cardinal, Integer, TRect memory size does not change, - PChar and String memory allocation will change as the length of the string changes).
If your program changes a PChar (aPCharVar := 'New text') the compiler changes the memory use to fit the number of characters in the string. But string variables passed to API are changed without your program knowing what's changing so it can NOT change the memory allocation to fit the new text from the OS, you have to do it with code OR make sure there is enough string memory to hold the text. Let's look at the GetWindowText() function.
procedure GetText;
var
Length: Integer;
Text: PChar;
Str1: String;
begin
Length := GetWindowTextLength(hEdit1)+1;
if Length < 2 then Exit;
GetMem(Text,Length);
GetWindowText(hEdit1,Text,Length);
Str1 := String(Text);
FreeMem(Text);
end;
|
The length of the "Text" in hEdit1 is returned by the GetWindowTextLength() function, notice that this is increased by 1 (+1). All "null-terminated" (PChar) strings end with a #0, which is not part of the text, but it is part of the memory for that variable, so you need to add one to the length for this null #0 character. Then the GetMem() function is used to get a memory block allocated for the PChar variable "Text".
Reading example C code
If you try and read some of the C examples you will see code like this:
HANDLE hFile; // variables are listed at the begining
// there is NO declaration like "var"
HANDLE hTempFile;
DWORD dwBytesRead, dwBytesWritten;
char szTempName[MAX_PATH];
// szTempName would be an Array[0..MAX_PATH] of Char
char buffer[4096];
// Open the file.
hFile = CreateFile("ORIGINAL.TXT", // filename
// double quotes are use like single quote for Text
GENERIC_READ, // open for reading
0, // do not share
NULL, // no security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hFile == INVALID_HANDLE_VALUE) {
ErrorHandler("Could not open file."); // process error
}
// Create a temporary file.
GetTempFileName("\\TEMP", // dir. for temp. files
"NEW", // temp. filename prefix
0, // create unique name
szTempName); // buffer for name
hTempFile = CreateFile((LPTSTR) szTempName, // filename
// the (LPTSTR) szTempName is roughly like Typecasting in Pascal
// PChar(szTempName);
GENERIC_READ | GENERIC_WRITE, // open for read-write
0, // do not share
NULL, // no security
CREATE_ALWAYS, // overwrite existing file
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hTempFile == INVALID_HANDLE_VALUE) {
ErrorHandler("Could not create temporary file.");
}
do
{
if (ReadFile(hFile, buffer, 4096,
&dwBytesRead, NULL)) {
// the & above is roughly like the @ in Pascal
CharUpperBuff(buffer, dwBytesRead);
WriteFile(hTempFile, buffer, dwBytesRead,
&dwBytesWritten, NULL);
}
} while (dwBytesRead == 4096);
CloseHandle(hFile);
CloseHandle(hTempFile);
// Move the temporary file to the new text file.
if (!MoveFile(szTempName, "ALLCAPS.TXT")) {
ErrorHandler("Could not move temp. file.");
}
|
In Delphi this would be:
var
hFile, hTempFile: THandle;
BytesRead, BytesWritten: Cardinal;
TempName: Array[0..MAX_PATH-1] of Char;
buffer: Array[0..4095] of Char;
hFile := CreateFile('ORIGINAL.TXT', // filename
GENERIC_READ, // open for reading
0, // do not share
nil, // no security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
0); // no attr. template
if hFile = INVALID_HANDLE_VALUE then
begin
ErrorHandler('Could not open file.'); // process error
Exit;
end;
// Create a temporary file.
GetTempFileName('\\TEMP', // dir. for temp. files
'NEW', // temp. filename prefix
0, // create unique name
TempName); // buffer for name
hTempFile := CreateFile(TempName, // filename
GENERIC_READ or GENERIC_WRITE, // open for read-write
0, // do not share
nil, // no security
CREATE_ALWAYS, // overwrite existing file
FILE_ATTRIBUTE_NORMAL, // normal file
0); // no attr. template
if hTempFile = INVALID_HANDLE_VALUE then
begin
CloseHandle(hFile);
ErrorHandler('Could not create temporary file.');
Exit;
end;
Repeat
if ReadFile(hFile, buffer, SizeOf(buffer),
BytesRead, nil) then
begin
CharUpperBuff(buffer, BytesRead);
WriteFile(hTempFile, buffer, BytesRead,
BytesWritten, nil);
end;
until BytesRead <> SizeOf(buffer); // 4096
CloseHandle(hFile);
CloseHandle(hTempFile);
// Move the temporary file to the new text file.
if not MoveFile(TempName, 'ALLCAPS.TXT') then
ErrorHandler('Could not move temp. file.');
|
The ErrorHandler() function is an application function to show an error message, not a windows API function. In C code there is no variable declaration like var in Pascal, the variables are usually defined at the beginning, look at HANDLE hFile; and HANDLE hTempFile;. The type (HANDLE) is before the variable (hFile). If you look at the CreateFile() function there are 2 NULL parameter values in the C code, and in Delphi there is a nil and a 0 value.
In C code a NULL is translated to a 0 value for numeric (HANDLE) values, in Delphi you will have to use a 0 for numeric values, since nil can only be used for Pointer types. Instead of the Delphi "begin" and "end;" to associate the code with a function (there are no procedures in C, just functions), C uses a "{" and "}". In C an "if" test does not have a "then" (it is implied that an "if" always has a "then" so that then is not written out), but usually wraps the code that would happen in { } after the binary test.
In Delphi a single "=" is used for a binary test (if A = B then). In C a double "==" is used (if A = = B). Delphi binary operator "OR" is the character "|" in C, Delphi's "AND" operator is a "&" in C, the binary operations of "|" and "&", are the same in C and Delphi. In C a "!" would be a "NOT" in Delphi, so that
"if (!MoveFile(" would be, "if not MoveFile(".
To the next chapter: A Guide to raw API programming
If you need any kind of help at this point, please post to the Delphi Programming Forum where all the questions are answered and beginners are treated as experts.
A guide to developing Delphi programs in raw Windows API: Next Chapter >> >>
Your first raw API Delphi program
|