Delphi Programming

  1. Home
  2. Computing & Technology
  3. Delphi Programming
Reading C code in Win32 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 the C language types and the Delphi language types.
 Win prizes by sharing code!
Do you have some Delphi code you want to share? Are you interested in winning a prize for your work?
Delphi Programming Quickies Contest
 Join the Discussion
Post your views and comments to this chapter of the free "raw API programming" Delphi Course
Discuss!
 Related Resources
• A Guide to raw API programming.TOC
 From Other Guides
• About C/C++ programming

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
PrefixC language Data Type
bBoolean
byByte
cChar
cx/cyshort Int used as size
dwDWORD
fnFunction
hHandle
iInteger
lLong
nsee below*
pPointer to address of variable
sstring
szASCI null terminated string
wWord
x, yShort 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 typeDelphi type
HANDLETHandle == LongWord (Cardinal)
HWNDTHandle == LongWord (Cardinal)
HMENU, HICON, HBITMAP, HFONT, HBRUSH, HPENTHandle == LongWord (Cardinal)
BYTEByte
SHORTSmallInt
INTInteger
WORDWord
DWORDLongWord (Cardinal)
LONGInteger
LONGLONGInt64
UINTLongWord; Cardinal (see UINT comment below)
ASCI null terminated stringPAnsiChar or PChar
LPSTR, LPCTSTRPAnsiChar
ULONGCardinal (see UINT comment below)
BOOLEANBoolean
BOOLLongBool
LPVOID, PSIDPointer
LCIDDWORD, LongWord (Cardinal)
LANGIDWord
UCHARByte
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.
PrefixConstant 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

Explore Delphi Programming

About.com Special Features

Delphi Programming

  1. Home
  2. Computing & Technology
  3. Delphi Programming

©2009 About.com, a part of The New York Times Company.

All rights reserved.