Site hosted by Angelfire.com: Build your free website today!

Home
DelphiZeus
4. PChar, Pointers in Using API functions

Home



You have seen the API functions called to interact with the OS. Functions like CreateWindow( ) have parameters that are pointers (like PChar type), and the API function definitions in the Windows.pas unit set the variable types used in these functions when you write code. You may have noticed that the parameter types given in the Win32 API Help are in C language and can seem different than the variable types set by the windows.pas unit. In Win32 API Help, the second parameter of the GetClientRect( ) function is listed as a LPRECT, a Long Pointer to a Rect structure (record). But when we used this function - GetClientRect(hMainForm, FormRect); - the second parameter was a TRect. Keep in mind that the windows.pas unit defined the function from the Win system User.dll as -
function GetClientRect(hWnd: HWND; var lpRect: TRect): BOOL;
And that the lpRect (FormRect in this case) is a pointer to the record's memory location. So the GetClientRect( ) function works with a TRect variable type without having to change it to a Pointer type, because Delphi Pascal has the var parameter designation and C-Code does not. The strict type checking done by the Delphi compiler and the windows.pas API function definitions can be confusing when using some of the generalized functions like SendMessage( ) where the WParam and LParam parameters are an Integer type and you have to Typecast any non numeric variables to an Integer Type.

On this page you will get some help in using the "Pointers" in the API functions. We'll start with the PChar since it is used in so many of them. You may not need the information on this page to use API functions, but you should at least read through this and get an overview.

Delphi uses Strict Type Checking
When coding in the delphi-pascal Form Unit, it is commom practice to place everything (variables and procedures) in an Object Oriented TObject Type Declaration. You will rarely need to use Pointers or Memory address references, since all of the Type Object's variable names will be used as pointers (converted to assembly laguage as a memory location) by the compiler. However, in the C code of Windows API, some of the parameters are a "reference" to a position in the memory where the information is located. Many of these API parameters will be used with the var parameter designation (Integer, Cardinal, TRect, TPoint), or a Typed Pointer like PChar. (These parameter variable types are based on the Windows System API, but are Type-Checked against the Pascal function "conversion" type definitions in the windows.pas unit) But many API function parameters will be a Cardinal or Integer Type that will be used to get or send a Pointer, like the WParam and LParam of the Window's Proc for messages. And in other API functions some of the parameters are a Pointer (Typed and UnTyped), so it will help you to have a basic understanding of pointers and using them. Since Delphi Pascal uses a very strict Type Checking, you will need to do TypeCasting. You will see many examples in the programs here of TypeCasting the Variable's address (@) as Pointers (or integer reference), and Pointers as another Variable Type, so the Delphi Compiler will accept that variable in an API function. Strict type checking is in delphi to help prevent you from making coding errors, like placing the wrong variable name or wrong variable type as parameters.
It may be helpful to keep in mind that the Code you are writting is set up to be "Human" readable, and help us Humans use References that are not binary numbers, but the code you write will NOT look like the assembly code accually used to compile your program.

Pointers
Window's API memory is used and referenced as an array of bytes. A Pointer is a variable that stores a memory-address (a numeric value, like an integer, memory array byte number). In the case of a Record pointer (PRect for example) or a Static Array pointer (Dynamic Arrays are different), the pointer holds the address of the first element in that variable. Pointers can be Typed to indicate the kind of data stored at the memory address of the pointer (like PChar and PInteger). The general-purpose "Pointer" type can represent a pointer to any data, while more specialized pointer types reference only specific types of data. Pointers occupy four bytes of memory, the same as the Integer or Cardinal type. First, understanding pointers will help you to understand Object Pascal, since pointers often operate behind the scenes in code where they don’t appear explicitly. Any data type that requires large, dynamically allocated blocks of memory uses pointers. Long-string variables, for instance, are implicitly pointers, as are class variables. The reserved word nil is a special constant that can be assigned to any pointer (called NULL in C language). When nil is assigned to a pointer, the pointer doesn’t reference anything (the numeric value of nil is Zero).

You can declare a pointer to any type, using the syntax
type pointerTypeName = ^type
When you define a record or other data type, it’s a common practice also to define a pointer to that type. This makes it easy to manipulate instances of the type without copying large blocks of memory. When a pointer holds the address of "another variable", we say that it points to the location of that variable in memory, but NOT to the data of that variable. Many Standard pointer types exist (like PInterger and PRect) to allow the pointer data size and use to be defined. The most versatile is Pointer, which can point to data of any kind, and has NO memory assigned to it, so you must always assign a memory block for it to use (usually with GetMem or AllocMem).

There are some pascal Pointer operators that you will need to use with API function parameters, the   @   and   ^   operators. The   @   operator, is used to get the memory address of a variable (this address is a Pointer Type). The   @   operator returns the address of a variable, or of a function, procedure, or method, which means,   @   returns a pointer to its operand variable.
var
X: Integer;
P: Pointer;

begin
X := 22;
P := @X; // Now P has the memory address for X
end;
The operator   ^   has two purposes. When it appears before a type identifier —
^typeName
It denotes a Type that represents pointers to variables of Type typeName.

When it appears after a pointer variable (used in some API Parameters) -
Pointer^
It dereferences the pointer, which means, it returns the value (data) stored at the memory address held by the pointer, this is nessary because the Delphi compiler will not allow you to assign ( := ) a Pointer to a non-Pointer variable. Derefencing tells the compiler that you want the data stored in the Pointer's memory location, instead of the Pointers memory location address. .
var
X, Y: Integer;
Pnt1: Pointer;
  {Pnt1 is a NonTyped pointer}
P: ^Integer; 
{P is now a Pointer Type to 4 bytes of Memory, which
will treated as an Integer variable in assignments, := }

{or you could use}
{P: PInteger;}

begin
X := 22;
P := @X; // assign the address of X to P
Pnt1 := @X; // assign the address of X to Pnt1

Y := P^ // dereference P, assign the result to Y
    {this is NOT the same as
     Y := Integer(P);}

Y := PInteger(Pnt1)^; 
    // a non-typed pointer will have to be Type Cast
end;


Since a pointer is the numeric value of a memory location, you can TypeCast a numeric type variable like Integer or Cardinal to a Pointer Type, and TypeCast a Pointer Type to an Integer or Cardinal type. But remember, this TypeCast will get the numeric memory address, NOT the data in that memory, , Like This -
var
PStr1: PChar;
Integer1, Integer2: Integer;
Rect1: TRect;

begin
PStr1 := 'A Pchar string';
Integer1 := Integer(PStr1);
  {now Integer1 has the memory address of the PStr1 pointer.
   unlike some TypeCastes, this Integer Typecaste has Nothing to
   do with the Characters in the PStr1, only it's memory address}

PStr1 := PChar(Integer1);
  {this is the same as
   PStr1 := PStr1
   again, this typeCaste has Nothing to do with the Data (Characters)
   in the PStr1 character string}
Rect1.Top := 44;
Integer2 := Integer(@Rect1);
// since Rect1 is NOT a Pointer type, you need the  @

Integer1 := PRect(Integer2)^.Top;
{to get the data values from a memory location number like Integer2, you
will need to typecast the integer to the "Pointer Type" for the data type
that the memory location is formated for, in this case a 16 byte TRect}
end;

Int1 := Integer(Pstr1);
Pt1 := Pointer(Int1);
//Char(Pointer(Cardinal(Pt1)+4)^) := 'B';//Byte(Ord('B'));
//PChar(Pt1)[4] := 'B';
PChar(Int1)[4] := 'B';
{if you TypeCast the Integer to a Pchar, then you can use the [4]
like a PChar}
if Pstr1[4] = Char(Pointer(Cardinal(Pt1)+4)^){PChar(Int1+4)^} then
MessageBox(0,'They Match',
     'Is True',  MB_OK or MB_ICONQUESTION or MB_DEFBUTTON2
     or MB_SETFOREGROUND);

The WParam and LParam in the Window Proc
In the More Messages Program you saw how to TypeCast the Windows Proc LParam to a PWindowPos pointer type for the WM_WINDOWPOSCHANGING message. And the LParam was TypeCast as a PMinMaxInfo for the WM_GETMINMAXINFO message. Since the LParam was TypeCast to a Typed Pointer, we can use this Typed Pointer as the Variable type of this Pointer. When the LParam is TypeCast as a PWindowPos,
if   PWINDOWPOS(lParam).cx > 450 then
we can use it as a TWindowPos variable and access the .cx and .cy elements of the TWindowPos Record. In recent versions of Delphi, if you add the period for Object access to a Typed Pointer, the compiler will automaticlly dereference the Pointer. The code above would be more correctly written as
if   PWINDOWPOS(lParam)^.cx > 450 then
You also could declare a Typed Pointer variable and then assign the memory address of the lParam to that Typed Pointer.
function MessageProc(hWnd, Msg, WParam, LParam: Integer): Integer; stdcall;
var
PWinPos1: PWindowPos;

begin
// code
WM_WINDOWPOSCHANGING: begin
                      PWinPos1 := Pointer(LParam);
                      if PWinPos1.cx > 450 then PWinPos1.cx := 450;
                      if PWinPos1^.cy > 450 then PWinPos1^.cy := 400;
                      end;
// code
end;
This second method may save some typing if you reference the PWinPos1 several times.

    • NOTE: You can Typecast any 4-byte numeric variable (Integer, Cardinal) to a Pointer type, but that does NOT make it a Pointer (memory location).


the PChar Variable type, it's a Pointer

String Type Pointer, PChar
PChar is the Type declaration for "Null Terminated" charater strings and is different than the Pascal "String" type. A PChar is a Pointer to a null-terminated string (array) of characters. PChars are, with short strings, one of the original Object Pascal string types. They were used primarily as a C language and Windows API compatibility type. The Widows System, using the C language, does not have a dedicated string data type. The Windows system using the C language, rely on null-terminated strings. A Null-Terminated string is a zero-based array of characters that ends with a NULL (#0), since the array has no length indicator (like Pascal Strings), the first NULL character marks the end of the string. You can use special functions to handle null-terminated strings when you need to share data with the Windows system. A Pascal Long-String is also null-terminated (has a #0 at the end of the string), but the term "null-terminated" is refering to PChar types. The pascal Short-String does NOT always have a NULL terminator, and uses the first byte as the valid text length.

Most that use Delphi know the Pascal long "String" type used for charater string variables. This "String" type is easy to use, you can just assign the charaters to the variable without haveing to allocate memory for the variable or add a #0 to the end, and join two strings together with a + operator like this
var
  Str1, Str2: String;

begin
Str1 := 'New String 1';
{all memory allocation for the Str1 var is done automaticaly}
Str2 := 'New String 2';
Str1 := Str1+ ' '+ Str2+ ' joined';
{memory allocation and charater assignment is 
 done with just a + operator}
end;
With the PChar type, memory allocation and adding a #0 to the end is also automatic, when the compiler is aware of the number of charaters to allocate memory to the variable. If the PChar variable is used as a parameter in a .DLL function (like API calls) then it can't adjust the memory because it is unaware of what the memory requirements are.
var
  Pstr1: PChar;
begin
Pstr1 := 'New Pchar 1';
{memory allocation for the Pstr1 var is also done automaticaly IF
the compiler can determine the memory needed, as in this example.
The compiler can count the charaters in 'New Pchar 1' and get
the memory needed for these, 12 bytes with null}
end;
Pstr1 points to an area of memory that contains a null-terminated copy of “New Pchar 1” This is like using an Array of Char in the following code
const
  ArrayString: array[0..11] of Char = 'New Pchar 1'#0;
{notice that a null char #0 was placed as the last Char}

var
  Pstr1: PChar;

begin
Pstr1 := @ArrayString;
{which is the same as
Pstr1 := @ArrayString[0]}
end;
For a PChar variable passed to a windows OS function to read, the OS Starts reading charaters at the memory address of the Pointer and then continues to read the next byte (Char) from the memory byte array until it reads a zero-value byte (#0) and it stops. (This is why it is called a Null Terminated string) When using a PChar as a function parameter, the windows OS can Not determine the "type" of variable for this pointer or even if it is a variable. It can not know the variable's memory size or anything else, all it knows is the 4 bytes of memory that it reads to get a memory address number.
You can TypeCaste a Pointer to an Integer or Cardinal type and you can TypeCast an Integer to a Pointer type.
var
PStr1: PChar;
Pnt1: Pointer;
Int1: Integer;

begin
PStr1 := 'A Pchar variable';
Int1 := Integer(PStr1);
Pnt1 := Pointer(Int1);
{Now PStr1 and Pnt1 are pointing to the same memory location.
Even though you have two Variable names, PStr1 and Pnt1, string
operations (changing) on either one will effect the same Memory}
end;
Since Pointers are numbers representing a location in the memory array, you can do "Pointer Arithmatic" to change the pointer value. If you have a PChar variable, it is the memory location of the first charater in the PChar string's array of charaters, so if Pstr1 is a PChar variable and has charaters assigned to it then Integer( Pstr1 ) is equal to Integer( @Pstr1[0] ). And Integer( @Pstr1[4] ) is the same as Integer( Pstr1 ) + 4.
var
Pstr1, Pstr2: Pchar;
Int1: Integer;

begin
Pstr1 := 'Six Words In This Pchar String';
Pstr2 := 'Three in This';
Int1 := Integer(Pstr1);
{typecasting the Pstr1 to an Integer gets the memory address
of Pstr1 into the Int1 integer variable}

if MessageBox(0, Pstr1,
     Pstr2,  MB_YESNO or MB_ICONQUESTION) = ID_YES then
  begin
  {the first messagebox displays the entire text in
  the Pstr1 and Pstr2 variables}

  MessageBox(0, @Pstr1[4],
     @Pstr2[6],  MB_OK or MB_ICONQUESTION);
  {this second messagebox displays shorter strings because the
  Pointers are set to the address of the second word in the 
  char array. For the Delphi compiler the Pstr1[4] is a single 
  Char but for this MessageBox function the OS just uses that 
  memory address as the first Char in a null terminated string}

  MessageBox(0, Pointer(Int1 + 4),
     Pointer(Pstr2 + 6),  MB_YESNO or MB_ICONError);
  {this third messagebox has the same shorter strings as the
  second messagebox, here pointer arithmatic is used to change
  it to the second word in the P string. Notice that the Pointer
  typcasting are for integer values, take care because there is 
  no Range checking by the Delphi compiler or the Windows OS for
  these Pointer values. In the second pointer typecast
  "Pointer(Pstr2+6)" the compiler will add 6 to a PChar variable
  without an invalid operation error}
end;

You can Typecaste a long Pascal String to a Pchar, and a PChar to a Pascal String, which can make using PChar strings easier to code with the more familiar "+" string joining method. But you may want to use the PChar without typecasting to a String, so I want to mention some of the API functions for PChar strings (null-terminated). With PChar-Strings the + operator does not work, to manipulate null-terminated strings, it is often necessary to use fuctions and pointers. Here are 6 API PChar string functions which can be used to get the length, copy, join, or compare null-terminated strings. But remember that for the lstrcpy, lstrcpyn, and lstrcat functions, you have to do the memory allocation for the lpString1 variables, it is not "Automatic" like using pascal Strings.
  1. function   lstrlen (lpString: PAnsiChar): Integer;
    - - gets the number of Char before the #0 (length) . . . MSDN- lstrlen
  2. function   lstrcpy (lpString1, lpString2: PChar): PChar;
    - - Copy a Pchar string to another . . . MSDN- lstrcpy
  3. function   lstrcpyn (lpString1, lpString2: PChar; iMaxLength: Integer): PChar;
    - - Copy and limit length . . . MSDN- lstrcpyn
  4. function   lstrcat (lpString1, lpString2: PChar): PChar;
    - - joins 2 Pchar together . . . MSDN- lstrcat
  5. function   lstrcmp (lpString1, lpString2: PChar): Integer; stdcall;
    - - compairs 2 Pchar, case sensitive . . . MSDN- lstrcmp
  6. function   lstrcmpi (lpString1, lpString2: PChar): Integer;
    - - compairs 2 Pchar, case insensitive . . . MSDN- lstrcmpi
Here are several more API PChar String Manipulation Functions
  1. function   CharLower (lpsz: PChar): PChar;
    - - Single Char to lower case
  2. function   CharLowerBuff (lpsz: PChar; cchLength: DWORD): DWORD;
    - - PChar string to lower case
  3. function   CharUpper (lpsz: PChar): PChar;
    - - Single Char to upper case
  4. function   CharUpperBuff (lpsz: PChar; cchLength: DWORD): DWORD;
    - - PChar string to upper case
  5. function   CompareString (Locale: LCID; dwCmpFlags: DWORD; lpString1: PChar; cchCount1: Integer; lpString2: PChar; cchCount2: Integer): Integer;
    - - Compairs 2 PChar strings acording to the language Locale
  6. function   IsCharLower (ch: Char): BOOL;
    - - Tests Char for Lower case
  7. function   IsCharUpper (ch: Char): BOOL;
    - - Tests Char for Upper case
  8. function   IsCharAlpha ((ch: Char): BOOL;
    - - Tests for Char in the alphabet (a..Z)
  9. function   IsCharAlphaNumeric ((ch: Char): BOOL;
    - - Tests for Char in the alphabet (a..Z) and numbers (0..9)
There are similar null-terminated string manipulating function in Delphi's SysUtils unit which I have copied in my SmallUtil unit. Functions Like StrLen, StrCopy, StrCat and PCharUpper, can be used the same way the API functions are. I wanted to show how to use some of the API functions, but you can substitute the Delphi functions, which are efficient assembly code.
Some examples for changing PChar strings
var
Pstr1, Pstr2, Pstr3: Pchar;
Pt1: Pointer;
Int1: Integer;

begin
Pstr1 := 'Six Words In This Pchar String';
Pstr2 := 'Three in This';

{get enough memory to copy the Pstr1}
GetMem(Pt1, lstrlen(Pstr1)+1);
  {add 1 to the length for the Null #0 char}
  {Pt1 is a Pointer type, not a PChar type}
lstrcpy(Pt1, Pstr1);
{lstrcpy is a window's OS fuction to Copy a null-Terminated string.
  But is does NOT do any range checking, AND it DOES NOT assign 
  any memory, so you have to GetMem for Pt1.
  You can use a PChar or Pointer type variable in this string copy 
  function without a compiler error.}
Pstr3 := PChar(String(Pstr1)+' '+String(Pstr2));
{typecasting seems easier to me, but this creates 2 new un-named 
variables, 2 new String, String(Pstr1), String(Pstr2). 
Although these are unNamed, unlike Pstr3, the compiler creates the 
variables just the same}

Pstr1 := ''; 
  {free the Pstr1 memory, the next call to GetMem will create
  a new Memory Location for the Pstr1 variable, but it does not
  check the Pstr1 variable or free it's memory}
GetMem(Pstr1, lstrlen(Pt1)+lstrlen(Pstr2)+1);
{you need to get enough memory for Pstr1 to hold both
 and add 1 for the #0}
lstrcpy(Pstr1, Pt1);
  {lstrcpy copies Pt1 to the Pstr1, but it does NOT
   adjust the memory for Pstr1, so it still has enough 
   memory for both strings}
lstrcat(Pstr1, Pstr2);
  {lstrcat will join 2 PChar strings together, but it can not
  check to see if there is memory in the Pstr1 memory allocation 
  for  the new chars, if there is not enough memory allocated to 
  Pstr1 for the new chars then it will overwrite the memory of 
  other variables, functions or unused memory past the end of 
  Pstr1 memory block, this type of error can be difficult 
  to find}

MessageBox(0, Pstr1,
     Pstr2,  MB_OK or MB_ICONQUESTION
     or MB_SETFOREGROUND);
FreeMem(Pstr1);
  {since we're finished with the Pstr1, free it's memory}

MessageBox(0, Pt1,
     'Pointer',  MB_OK or MB_ICONQUESTION
     or MB_SETFOREGROUND);
FreeMem(Pt1);

CharUpperBuff(@Pstr3[13], 4);
{CharUpperBuff sets a number of charaters to UpperCase.
If you are used to using the Delphi String variable, 
you might need to adjust to some of the API functions that
require a "Length" or "Number of Charaters" parameter.
But this parameter may be very useful as in this example
where only one word is UpperCased}
MessageBox(0, Pstr3,
     'Pointer',  MB_OK or MB_ICONQUESTION
     or MB_SETFOREGROUND);
end;

A powerful and useful, feature is accessing variables by the use of indirect addressing through pointers and pointer arithmatic, but it can cause problems, if there is not enough memory allocated to the variable. (A common misteak is to forget to add 1 to the memory allocation for the Null #0 char in PChar) Bugs introduced by misusing pointers can be difficult to detect and isolate because the error often corrupts memory unpredictably. Most of the time, without any thing seeming to be wrong, at least with no error or warning messages. Remember, the OS does not know and can not check the memory allocation for a variable passed to it as a function parameter. Be sure you do not make specific assumptions when Typecasting variables to different types especially to pointers that may not work if used outside of that specific way. The SizeOf( ) function is very useful to get the memory size of a "fixed-size" variable. If you use "Pointer Arithmatic" be very carefull when adding or subtrating amounts that you do not go past the begining or end of the memory allocation of that variable.

Dereferencing a PChar
You can use the ^ operator on a PChar, which dereferences a pointer; that is, it returns the value stored at the memory address held by the pointer. (Derefencing tells the compiler that you want the data stored in the Pointer's memory location, instead of the Pointers memory location value). When you dereference a PChar the compiler will use that as a Char variable, that's right, a single Character, not a null-terminated string or array of charaters. So if you use the code -

var
PStr1: Pchar
Got1: Boolean;
begin
PStr1 := 'Some Text';
if PStr1^ = 'S' then
  Got1 := True
  else
  Got1 := False;
end;
Got1 will be True, because PStr1^  will be used as a Char variable, which will be the first charater of the PStr1 array, an 'S'. Be aware, that in some API functions you will need to Dereference a PChar variable, and ALL of the charaters in that memory array will be used by that function, not just the first character.



All Pointers are 4 bytes of memory (32 bits) and so are the following variable types in Delphi 4 and above - Pointer, Cardinal, Integer, LongWord, DWORD, THandle. All of these can be Typecast as a Pointer.

?? warning ?? You may be more confused by this Page than helped, if you have not used pointers before and are not familar with the "Pointer" programming methods, which Delphi hides so well. Don't worry, it's not an easy step from RAD delphi programming to pointers, dereferencing, and memory allocation. You do not need a total understanding of pointers to use API functions.
Pointer Arithmatic will not be used in the following pages, and was included here to help you see that pointers represent a memory address. But there will be alot of Typecasting to and from Pointers and the occasional dereferencing of a Pointer.

Bypassing Delphi Type-Checking of Function Parameters
This section is for those developers who do understand some about pointers. But this "Method" is not used or needed in these lessons.

API Function Parameters
Most of the API functions that are defined in the Windows.pas unit, , have typed parameters, that are based on the "C-Code" variable type used by the window's system. However, if you try to use a variable in the API function that is different than the parameter type declaration, it will not compile with a "Fatal Error" of - "Incompatible types: HWND and TRect" (with your incorrect types), , , or sometimes you might get a error message like "Types of actual and formal var parameters must be identical"

So if you know enough to correctly use a variable of a different type, what can you do to make it accept your code? You can use the memory-address of your variable (place a @ in frount of it), and Typecast that as a typed-Pointer for the required variable (example - PRect for a TRect), and you must Dereference that pointer typecast with a ^. Below is an example, I will use the API function SetRect( ), with a parameter variable that is not a TRect. I define my own variable type TRectPlus with more than 4 integers. Code -

type
// the first 4 members of TRectPlus record are the same as a TRect
  TRectPlus = record
    Side,Above,Extent,Below: Integer; // like a TRect
    hWnd, Color: Cardinal;
    end;

procedure aTest;
var
RectPlus1: TRectPlus;

begin
// SetRect(RectPlus1, 100, 20, 160, 58); // will NOT compile
// SetRect(TRect(RectPlus1), 100, 20, 160, 58); // will NOT compile

SetRect(PRect(@RectPlus1)^, 100, 20, 160, 58);
// typecast to typed-pointer and dereference, this will work
RectPlus1.hWnd := 0; // only changes first 4 members
RectPlus1.Color := $FFFFFF; // you need to set the other two
end;
Please notice that I have the   PRect(@RectPlus1)^   in place for the var TRect function parameter. This SetRect( ) will change the first 4 integer members of the RectPlus1 variable, but nothing is done to the hWnd , Color members.
Delphi-pascal will not allow typecasting two "record types" if the types do not have the same members, so you could write -
  TRect(RectPlus1)
as the parameter, but it will not compile with a fatal-error of "Invalid Typcast". However, you can typecast almost anything to a typed-pointer by using a memory-address, as I did here.

The example above was used with the default compilier directive of {$TYPEDADDRESS OFF} ($T-}, , which controls the types of pointer values generated by the @ operator. In the (T-) , the result of the @ operator is always an untyped pointer. If you get a fatal error when trying to compile, you may can use the {$T-} to turn it off , but I would recomend trying this -

procedure aTest;
var
RectPlus1: TRectPlus;
memAddy: Cardinal;

begin
memAddy := Cardinal(@RectPlus1);
// get a numeric Cardinal or Integer value
SetRect(PRect(memAddy)^, 100, 20, 160, 58);
// typecast number to typed-pointer and dereference
end;
Numeric values (Integer and Cardinal) can not be type checked, and the code will be compiled.

Below is some more example code, this time I use the API function CreatePalette( ) and have a variable with the type of TMaxLogPalette.
* I will NOT try and explain about palettes, just about typecasting. *
The function CreatePalette( ) requires a parameter type of TLogPalette, but this TLogPalette type is useless for this, since it's palPalEntry member is defined as an array with one member (array[0..0]), and you can NOT have a palette with one color definition. So you can never use the TLogPalette as a normal variable would be used. Delphi added the TMaxLogPalette type which could be used, HOWEVER the CreatePalette( ) function will NOT accept a TMaxLogPalette variable as the parameter. In "C-Code" using a LOGPALETTE structure, you would assign whatever memory block to the palPalEntry needed for the color defintions, but in delphi you can not do that with a TLogPalette without some hack type run-arounds or use pointer types like PLogPalette. But you can typecast a TMaxLogPalette to get it to work. Example code -

function BWPalette: Cardinal;
var
maxPal: TMaxLogPalette;

begin
maxPal.palVersion := $300;
maxPal.palNumEntries := 2;
maxPal.palPalEntry[0] := 0;
maxPal.palPalEntry[1] := $FFFFFF;
Result := CreatePalette(PLogPalette(@maxPal)^); // this works
// typecast @maxPal to a PLogPalette and dereference
end;
You may find there are a few other API functions that you will have trouble using the parameter types as the windows.pas unit defines them, and will have to find some other way to code. Or if you do much API programming you will need to use your own type definitions in some function parameter, you can use this method to get the code to compile. But be careful and check that the memory block (variable data) that you typecast is large enough for the amount of memory used in reading or writing.


                           
Next
We have made a program that creates windows, and has a Message Loop to interact with Windows OS. There are many more things about the display of a windows GUI that you need to know. Now we are using a Graphical Interface and have to be able to paint and draw on it, and use colors, fonts, and Device Contexts. So the next program will show some basic graphical device interface functions.
  Lesson 5, Fonts and using Device Contexts


       

Lesson -     One  Two  Three  Four  Five  Six  Seven  Eight  Nine  Ten  Eleven  Twelve  Thirteen  Fourteen




H O M E