The Microsoft Win32 Programmers Reference was written for programmers who use the C programming language. An understanding of some of the concepts and conventions of C can make using the Programmers Reference and the Win32 API significantly easier. This tutorial is part of a series of tutorials about the Win32 API. It introduces C to the Delphi programmer who seeks a greater understanding of the Win32 API and Windows API functions in general.
|
Tutorial Objectives |
Tutorial Table of Contents |
|
Working through this tutorial can be a long haul. Hopefully though, understanding a little C programming may help you to better understand function declarations in the Win32 Programmers reference. It may even help you understand some of those C code snippets that are readily available on the internet and in Microsoft documentation. Obviously this tutorial will not turn you into a C programmer. Those seeking to become C programmers may wish to consider one of the books or tutorials in the references section. |
|
A traditional non Windows C program begins by calling the function main(). Main is the first function to be executed when a C program begins.
Windows 32-bit programs begin by calling the function winmain().
There seems to be an unwritten law that all C tutorials provide the hello world program. For completion, here it is in traditional C.
|
main() { printf("hello, world"); } |
There is not much that can be said about this program that has not been said by others a thousands times. All this program does is displays the string “hello world”. The main() function is a throwback to original non windows (and non OOP) C versions when structured programming was king. It was always the first function executed in the C program.
A declaration defines an identifier and may allocate memory. The following are some sample declarations used in Object Pascal.
|
var R : Real; |
// declares a variable called R that holds a real value and allocates memory. |
|
function Myfunc(s: string): Integer; |
// declares a function that takes a string as an argument and returns an integer |
|
Procedure MyProc(s : String); |
// declares a procedure that takes a string as an argument but does not return anything. |
C uses different conventions. It is not possible to explain all of the C identifiers, functions and references here but we can look at some of the most important concepts and perhaps, gain enough information to understand those C declarations in the Win32 Programmers Reference.
Variable declarations in C take the form
type
variable;
Unlike Pascal, the variable type comes first and the variable name comes second. The following table lists the common C data types and the Object Pascal equivalent.
|
C declaration |
Pascal |
Description |
|
int x; |
x : Integer; |
// declare a 32-bit signed integer variable called x // (16-bit in older C implementations on 16-bit // platforms |
|
signed int x; |
x : Integer; |
// signed 32-bit integer called x |
|
unsigned int x; |
x : LongWord |
// unsigned 32-bit integer called x |
|
long l; |
l : LongInt |
// signed 32-bit integer |
|
unsigned long l; |
l : LongWord; |
// unsigned 32-bit integer |
|
char c; |
c : Char; |
// signed 8-bit variable that can be used to hold // numeric data or character data. |
|
unsigned char c; |
c : byte; |
// unsigned 8-bit integer or character |
|
small s; |
s : Shortint; |
// signed 8-bit integer |
|
short s; short int s; |
s : Smallint; |
// signed 16-bit integer |
|
unsigned short u; |
u : word; |
// unsigned 16-bit integer |
|
float f; |
f : single; |
// 32-bit floating point variable |
|
double d; |
d : Double; |
// 64-bit floating point variable |
|
long double l; |
l : Extended |
// 80-bit floating point variable |
|
bool b; |
b : Boolean; |
// 8-bit Boolean. A zero or null value is considered // false. Any non-zero value is considered true. |
|
DWORD dwrd; |
dwrd : DWORD; dwrd : LongWord; |
// unsigned four byte (32-bit) integer used by many // Windows API functions |
|
int a,b,c; |
a, b, c : Integer; |
// declares multiple variables. */ |
C also allows you to initialize variables when they are created. For example
|
int I=25; |
/* declare an integer variable called I and initializes it to 25. */ |
Once again, to the Pascal (Delphi) programmer function declarations may appear to be written backwards. They take the form
return-type function-name(parameter-list,...).
|
|
return-type |
the variable type that the function returns. |
|
|
function-name |
the name of the function. |
|
|
parameter-list |
the list of parameters that the function takes separated by commas. |
The return variable type is specified before the actual function definition.
|
The C function: |
int calcnumb(int n) |
|
Would look like this in Pascal: |
Function calcnum(n : Integer) : Integer; |
It declares a function of called calcnum that returns an integer result. The function accepts a single argument n, which is an integer value.
|
The C function: |
int addup(int i, int j, int k) |
|
Corresponds to |
Function addup(i, j, k : Integer) : Integer; |
This function declaration declares a function called addup that returns an integer result. The function accepts three arguments: i, j and k which are integer values.
In C, the term Void is used for functions that do not have any parameters. That is instead of declaring your function as myfunc(); you must declare it as myfunc(void);
|
The C function: |
int sayhello(void); |
|
Corresponds to |
Function sayhello : Integer; |
If you look closely at the function declarations in C you may notice that the word function does not appear anywhere in the declaration. C does not require that you explicitly differentiate functions from procedures. In C you simply declare a function that does not return any value. In other words, the function is void of any return value. However, you can’t just leave the entry blank, you have to declare that the function returns void.
Procedure declarations in C therefore take the form
void function-name(parameter-list,...).
|
|
Void |
indicates that this function does not return anything. |
|
|
function-name |
the name of the function (procedure). |
|
|
parameter-list |
the list of parameters that the function takes separated by commas. |
|
The C function: |
void addup(int i, int j, int k); |
|
Corresponds to |
Procedure addup(i, j, k : Integer); |
|
The C function: |
void sayhello(void); |
|
Corresponds to |
Procedure sayhello; |
Arrays create single or multidimensional matrices. They are defined using the form
type variable[size];
Indexing begins at 0 and ends at 1 less than the size of the array. An array declared as int x[4]; would be accessed as x[0], x[1], x[2] and x[3].
Examples are:
|
C |
Object Pascal |
Comment |
|
char s[20]; int x[5] |
s : Array[0..19] of Char; x : Array[0..5] of Integer; |
declares a 20 byte character array declares an array of five integers |
Pointers don’t hold information. Instead they contain the address of a location in computer memory that does hold some information. This can be confusing, for the new C programmer and for the Object Pascal programmer who seldom has to worry about pointers. Once you understand the concept though, you will find that accessing API functions becomes significantly easier. For more information about pointers see the tutorial: Using Pointers in Delphi.
The general form for declaring a pointer in C is:
type *variable
The following declarations declare a variable called pnumber that is a pointer to the address in computer memory where a32-bit floating point number will be stored.
|
long *pnumber; // declares a pointer to a memory location containing a 32 bit integer number |
.
In Object Pascal the declaration would appear as
|
pnumber : ^LongInt; // declares a pointer to a memory location containing a 32 bit integer number |
In C the * is used to declare a pointer, much in the way that ^ is used in Object Pascal.
The & operator returns the memory address of its operand. It is functionally equivalent to the @ operator used in Object Pascal.
The * operator returns the value of the variable at the address referenced by the pointer. It de-references the pointer in the same way that ^ at the end of the variable de-references a pointer in Object Pascal.
To illustrate we will convert a small C code snippet into Object Pascal.
|
C |
Object Pascal |
Comment |
|
int myfunc(void) { int i, j; int *p i = 25; p = &i; j = *p; return j; } |
function myfunc : Integer; var i, j : Integer; p : ^Integer; begin i := 25; p := @i; j := p^; result := j; end; |
// declare the function // declare integers i and j // declare p as a pointer to an integer // set i equal to 25 // p receives the address of i // j receives the data pointed to by p // the result of the function is j // end of function |
There are two arithmetic operators that can be used on pointers in C. The ++ operator increments the variable by the size of the variable type and the -- operator decrements it. The following code snippet is designed to illustrate the concept.
|
char *p1; /* declares p1 as a pointer to a single-byte character */ int *p2; /* declares p2 as a pointer to a four-byte integer */ p1 = 5000; /* assigns the address 5000 to pointer p1 */ p1 ++; /* increments p1 by 1. p1 will contain the address 5004. */ p1 –-; /* decrements p1 by 1. p1 will contain the address 5000 again. */ p2 = 5000; /* assigns the address 5000 to pointer p2 */ p2 ++; /* increments p2 by 4. p2 will contain the address 5004. */ p2 –-; /* decrements p2 by 4. p2 will contain the address 5000 again. */ |
Of course, this will not work. You cannot arbitrarily assign addresses like 5000 to a pointer. The code snippet is for illustrative purposes only.
You may have noticed that C, at least in it’s original form, did not have a dedicated string data type that corresponds to Object Pascal’s string type. C has no explicit string type so a string must be defined as an array of single characters.
To declare a text string, declare an array of type char.
|
char newstring[254]; /* declares an array of 254 characters and allocates memory */ |
|
char mystring[15] = “HELLO WORLD”; /* declares an array of 15 characters and initializes it. */ |
It is worthwhile looking at the data that ends up in mystring.
mystring[0] contains H
mystring[1] contains E
mystring[2] contains L
mystring[3] contains L
mystring[4] contains O
mystring[5] contains a space (“ “)
mystring[6] contains W
mystring[7] contains O
mystring[8] contains R
mystring[9]contains L
mystring[10] contains D
mystring[11] contains #NUL (binary zeros);
mystring[12] contains unknown data
mystring[13] contains unknown data
mystring[14] contains unknown data
mystring[15] contains unknown data
Many C functions identify the length of the string to be the length between the first character and the first #NUL (binary zero), not the declared array size. Such functions assume that text strings will be delimited by a null terminator. The C function strcpy() copies the contents of one string to another. The operation terminates when it encounters the #Null character. When copying mystring above, the strcpy would copy only 11 characters and the #NUL
C programmers love to use pointers to work with arrays. Well actually, sometimes they hate pointers because, when used incorrectly, pointers can yield all types of unexpected results. However pointers can be a very powerful method of working with arrays. The pointer is often initialized to the address of the first item in the array and incremented (see pointer arithmetic) through the array. Since arrays are stored contiguously in memory, this pointer arithmetic functions well.
The following example illustrates the use of pointers to access elements in an array of single-byte characters.
|
main(); /* C function called main */ { /* begin function char *p1, s[6]; /* declares two variables. */ /* A character pointer */ /* and an array of six characters. */ strcpy(s,“HELLO”); /* place HELLO followed by #NUL into the character array. p1 := s; /* assigns p1 the starting address of s */ putc(*p1); /* prints the first character of s (H) */ p1++; /* increment p1 by 1 so it will point to the */ /* address of the second character of s */ putc(*p1); /* prints the second character of s (E) */ } /* end function main */ |
Notice in the above example that we have only allocated 6 bytes of contiguous memory for the character array s. However we are accessing individual characters in the string using a pointer to a char. C would not stop us from increasing p1 until is pointed to a character past the end of the array. We have no real way of knowing exactly what exists at that address and any attempt to manipulate data there may cause a lot of problems in our program.
We have seen that C does not have an explicit string data type. The lack of a standard string data type in C means that strings are generally passed between functions as arrays of type char. When one C function calls another and must pass a string parameter, there is no explicit way for the function to directly send the string data. The calling function must therefore pass the address of the first character of the string, not the string itself. By convention, the parameter is a pointer to the first character in an array of characters.
Windows API functions are C friendly. You cannot pass strings directly to API functions. Rather you must pass the address of the string.
Since we are only passing the address of the address of the first character in an array of type char, it follows that the called (target) function, whether it be another C function or a Windows API function must have some way to figure out how much memory has been set aside. There are two ways that this can be accomplished:
1) If the string has been initialized and contains a #NUL terminator, the called function can identify the length of the string to be the length from the first character to the first #NUL.
2) You can tell the called function how much memory has been set aside for the string.
This bears repeating. API functions requiring strings need the following:
1) The address of the string, not the string itself.
2) The size of the string or, in those few cases where the API function does not require you to pass the size of the string, a #NUL terminated string.
A Structure is a grouping of different data types. A C structure is the same as Object Pascal (Delphi) record. Here is an example
|
C |
Object Pascal |
Comment |
|
typedef struct { char name[50]; int age; float key1, key2, key3; } myrecord ; |
Tmyrecord = Record name : array[0..49] of char; age : Integer; key1, key2, key3 : Real; end; |
// 50 byte string // integer // 3 real numbers |
A C Union is a memory location that is used to represent more than one data type. In other words, a single location in memory is used to hold two or more variables of different types. The programmer must know which format is relevant at any point in time. This concept is best explained through an example.
|
union myunion /* declare a union called myunion */ { short int i; /* 16-bit (2-byte) integer */ char ch[2]; /* 2 byte character array */ } ; union myunion myvariable; /* declare a variable myvariable that is of type myunion */ |
In this example, we have declared a union type called myunion. The length of the union is two bytes long. The data held within any variable of type myunion can hold two bytes that can be of either the short int (16-bit integer) type, or 2 characters. It is possible to declare unions in which the data types have different sizes. In that case the size of the union is the size of the largest data type in the union.
The equivalent declaration in Object Pascal is to declare a variable of type variant.
C is one of the oldest and most widely used programming languages. There are numerous references on the Internet and in print form. Since this is a primer for Object Pascal programmers we will not look at each operator or statement in detail. The following table provides the C operator, the corresponding Object Pascal operator and a brief description.
|
C |
Pascal |
Comment |
|
|
|
|
|
++ |
inc |
Increments the variable by 1. In C, special attention needs to be paid when incrementing pointers. |
|
-- |
dec |
Decrements the variable by 1. |