this color and font
refers to VdB code,
this color and
font
refers to code written in the C programming language and
this color and font
refers to definitions
found in the WinAPI interface (which is also written in C).
(You will see colors only if you are using a CSS1-capable browser.)
A quick example
Using strings
Using nested structs
Using arrays
WinAPI Data Types
Function reference
Copyright notice and disclaimer
RECT
and call
the GetClientRect()
function.
BOOL GetClientRect ( HWND hWnd, // handle of window LPRECT lpRect // address of structure for client coordinates ); typedef struct _RECT { // rc LONG left; LONG top; LONG right; LONG bottom; } RECT;(You'll find a Windows-Help version of the Win32 Programmer's Reference in the HELP directory of your VdB 7.01 installation: WIN32.HLP. Use the Index to search for
GetClientRect
and RECT
. )
LPSTRUCT.
LPSTRUCT
is defined in hlstruct.cch.
#include "hlstruct.cch" extern CLOGICAL GetClientRect (CHANDLE, LPSTRUCT) USER32
HLStruct
and use the HLS-macros:
CLASS RECT OF HLStruct HLS_LONG("left") HLS_LONG("top") HLS_LONG("right") HLS_LONG("bottom") ENDCLASSFor most C types you can find the appropriate HLS-macro just by prepending
HLS_
to the C type name. Always translate the entire C
prototype, don't skip the members you don't need. If you don't know
how to translate a member, try with HLS_INT
first.
HLSRECT
object and initialize its members. (In
the case of GetClientRect()
you really don't need to
initialize members, but we do it here to illustrate the concept.)
Call the put()
method of the class. Pass the result of
put()
to the LPSTRUCT
parameter of the WinAPI
function.
Call the get()
method of the class. Access the class
properties to gather what information you need.
Release the object.
LOCAL rect rect = new RECT () rect.left = 0 rect.top = 0 rect.right = 0 rect.bottom = 0 GetClientRect (form.hwnd, rect.put ()) rect.get () ? "Form size in pixels: ", rect.right, rect.bottom rect.release ()Always pair calls to
put()
and
get()
even when you need no answer from the function.
The class needs this to do its housekeeping.
Always release the object when you are done. If you don't release you will leak memory.
This is the more common type. These strings have a C type of LP?STR
. All you have to do is to use the
corresponding HLS-macro.
A typical struct using strings referred to by pointers is the
DOCINFO
struct:
typedef struct { // di int cbSize; LPCTSTR lpszDocName; LPCTSTR lpszOutput; LPCTSTR lpszDatatype; // Windows 95 only; ignored on Windows NT DWORD fwType; // Windows 95 only; ignored on Windows NT } DOCINFO;This would translate to the VdB class:
#include "hlstruct.cch" CLASS DOCINFO OF HLStruct HLS_INT("cbSize") HLS_LPCTSTR("lpszDocName") HLS_LPCTSTR("lpszOutput") HLS_LPCTSTR("lpszDatatype") HLS_DWORD("fwType") ENDCLASS
Inlined strings have a C type of ?CHAR [size]
. Use the
corresponding HLS-macro passing the size as second
argument.
A typical struct containing an inlined string is the
DEVMODE
struct. CCHDEVICENAME
is a constant
defined in a Windows header file
.
#define CCHDEVICENAME 32 typedef struct _devicemode { // dvmd BCHAR dmDeviceName[CCHDEVICENAME]; WORD dmSpecVersion; WORD dmDriverVersion; WORD dmSize; ... more members ... } DEVMODE;This would translate to the VdB class:
#include "hlstruct.cch" #define CCHDEVICENAME 32 CLASS DEVMODE OF HLStruct HLS_BCHAR("dmDeviceName", CCHDEVICENAME) HLS_WORD("dmSpecVersion") HLS_WORD("dmDriverVersion") HLS_WORD("dmSize") ... more members ... ENDCLASS
VdB 7 internally uses Unicode strings. The HLStruct class translates transparently from the internal Unicode to the external ANSI representation where appropriate.
Windows NT provides two versions of every WinAPI function that uses strings or structs containing strings. The "W" version takes Unicode strings whereas the "A" version takes ANSI strings. Win95 provides both versions too, but the "W" functions are just a fake, they simply don't work.
If you want your programs to run on Win95 and NT you should always call the "A" function (and skip to Using Nested Structs).
If you support NT only you may call the "W" version. If you
are going to call only "W" functions,
#define UNICODE
at the top of
HLSTRUCT.CCH
(and skip to Using Nested Structs).
If you are going to mix calls to "A" and "W" functions, don't
#define UNICODE
but use the HLS_LPWSTR
and HLS_WCHAR
macros for structs you pass to "W" functions, and HLS_LPTSTR
and
HLS_TCHAR
macros for structs you pass to "A" functions. If you are
going to pass the same WinAPI struct to both a "W" and an "A" function,
you must define two different classes one for each function.
HLS_LPSTRUCT
macro.
HLS_INLINEDSTRUCT
macro.
HLS_HGLOBALSTRUCT
macro.
The second parameter to each of these macros is an object of the nested class.
Access the members of the nested struct with:
oStruct.oNestedStruct.Member
CHOOSEFONT
struct contains a pointer to a LOGFONT
struct:
typedef struct { // cf DWORD lStructSize; HWND hwndOwner; HDC hDC; LPLOGFONT lpLogFont; INT iPointSize; ... more members ... } CHOOSEFONT;This translates to the VdB class:
#include "hlstruct.cch" CLASS CHOOSEFONT OF HLStruct HLS_DWORD("lStructSize") HLS_HWND("hwndOwner") HLS_HDC("hDC") HLS_LPSTRUCT("lpLogFont", new LOGFONT ()) && pointer to LOGFONT struct HLS_INT("iPointSize") ... more members ... this.lStructSize = this.GetSize () ENDCLASS(You also see how you can conveniently initialize a struct member with the struct size. This is required for many WinAPI structs.)
To create an object of class CHOOSEFONT
and initialize
some LOGFONT
members:
oChooseFont = new CHOOSEFONT () oChooseFont.hwndOwner = _app.framewin.hwnd oChooseFont.lpLogFont.lfFaceName = "Arial" oChooseFont.lpLogFont.lfHeight = 12
PAINSTRUCT
struct is a typical example of a struct
containing an inlined (RECT) struct.
typedef struct tagPAINTSTRUCT { // ps HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT;This translates to the VdB class:
#include "hlstruct.cch" CLASS PAINTSTRUCT OF HLStruct HLS_HDC("hdc") HLS_BOOL("fErase") HLS_INLINEDSTRUCT("rcPaint", new RECT ()) HLS_BOOL("fRestore") HLS_BOOL("fIncUpdate") HLS_BYTES("rgbReserved", 16) ENDCLASSUse the
HLS_BYTES
macro for unstructured data that is not characters.
typedef struct tagPD { // pd DWORD lStructSize; HWND hwndOwner; HANDLE hDevMode; HANDLE hDevNames; HDC hDC; ... more members ... } PRINTDLG;
#include "hlstruct.cch" CLASS PRINTDLG (oDevMode, oDevNames) OF HLStruct HLS_DWORD("lStructSize") HLS_HWND("hwndOwner") HLS_HGLOBALSTRUCT("hDevMode", oDevMode) && global memory handle HLS_HGLOBALSTRUCT("hDevNames", oDevNames) && global memory handle HLS_HDC("hDC") ... more members ... this.lStructSize = this.GetSize () ENDCLASS
oDevMode
and oDevNames
are pre-instantiated
objects of class DEVMODE
and DEVNAMES
.
oStruct [nName]To build an array of integers use:
#include "hlstruct.cch" CLASS INTARRAY (nElements) OF HLStruct LOCAL i FOR i = 0 TO nElements - 1 HLS_INT(i) NEXT ENDCLASS LOCAL a ; a = new INTARRAY (3) a [0] = 1 a [1] = 3 a [2] = 5You can number your elements starting by 0 or 1 (or any other number) just as you like it, or even number them backwards. A number is just a name for an element. In the WinAPI struct all members come in the order of their definition.
The Win32 Programmer's Reference is included with the VdB 7 product. The Windows 3.1 SDK help file is not included with the VdB 5.6 product. The original header files are not included with either product. VdB 7 includes a VdB-ified version of the header files, but much information was lost in the conversion, so they are near to worthless. To get the Windows 3.1 SDK and the original header files you have to find a copy of Borland C++ or Borland Delphi or MS Visual C++.
"There are only a few basic data types in C:
char
- a single byte, capable of holding one character in the local character set.
int
- an integer, typically reflecting the natural size of integers on the host machine.
float
- single-precision floating point.
double
- double-precision floating point.
In addition, there are a number of qualifiers that can be applied to these basic types.
short
andlong
apply to integers:short int sh; long int counter;The wordint
can be omitted in such declarations and typically is.The intent is that
short
andlong
should provide different lengths of integers where practical;int
will normally be the natural size for a particular machine.short
is often 16 bits,long
32 bits, andint
either 16 or 32 bits. Each compiler is free to choose appropriate sizes for its own hardware, subject only to the restriction thatshort
s andint
s are at least 16 bits,long
s are at least 32 bits, andshort
is no longer thanint
, which is no longer thanlong
.The qualifier
signed
orunsigned
may be applied tochar
or any integer.unsigned
numbers are always positive or zero, and obey the laws of arithmetic modulo 2n, where n is the number of bits in the type. So, for instance, ifchar
s are 8 bits,unsigned char
variables have values between 0 and 255, whilesigned char
s have values between -128 and 127 (in a two´s complement machine). Whether plainchar
s are signed or unsigned is machine-dependent, but printable characters are always positive.The type
long double
specifies extended-precision floating point. As with integers, the sizes of floating-point objects are implementation-defined;float
,double
andlong double
could represent one, two or three distinct sizes."
In C you must declare a variable before you can use it. Once declared the variable doesn't change type. You declare a variable like this:
int i;Note the characteristic notation of C; you first say what you are going to declare and then name it: "I declare an
int
that I will call i
".
The advantage of declaring a variable, which by far outweighs the
trouble of doing it, is that the compiler will catch the most common
errors for you. If you misspell CustonerId
, the compiler
will tell you that there ain't no CustonerId
.
If you code something like this:
int i; int length; i = 1; lenght = strlen (i);The compiler will tell you that you are trying to take the length of a string but are not passing a string as argument to the function and that there is no variable
lenght
. The compiler will not
produce an executable file before all these errors are fixed.
On the other hand, VdB will happily compile:
replace custom->id with CustonerId i = 1 length = len (i)and not until you run the program and trigger these program lines, you will be told, that there is no
CustonerId
and there is a
data type mismatch. So you must make sure you run every line of your
code before deploying. A tedious task.
int
or a pointer to a double
.
You declare a pointer with the unary operator *
.
int *ip; double *dp;We just declared two variables:
ip
as a pointer
to an int
type, dp
as a
pointer to a double
.
typedef
for
creating new data type names. For example, the declaration
typedef int Length;makes the name
Length
a synonym for int
. The
type Length
can be used in declarations in exactly the same
way as the type int
can be:" [KR88]
int len1; Length len2;Again, note the characteristic C notation: "I want to
typedef
the data
type int
to be called Length
".
Typedef
s can be found in great abundance in the WinAPI header
files
. This is just an excerpt from the WinAPI 16 windows.h
file:
typedef int BOOL; typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; typedef unsigned short USHORT; typedef unsigned int UINT; typedef signed long LONG; typedef char * LPSTR; typedef const char * LPCSTR;We see that the WinAPI data type
WORD
is an
alias for the C type unsigned short
and DWORD
is just an unsigned
long
.
Like simple variables, C structures must be declared before using
them. This declares a structure point
, with two
elements of type int
:
struct point { int x; int y; };C compilers handle structures in a way completely distinct from how an interpreter like VdB handles them. A VdB object has a list of member names attached to it that contains the address where VdB can find each member in memory. Members of one and the same object can be stored very far away from each other. VdB does a lookup of the properties name against the object's member list at runtime. This design is flexible in that it allows you to add members at runtime: simply add another entry to the member list. It is also very slow compared to the approach of a compiler.
A compiler stores all members of a struct in adiacent memory. This
means: the x member of struct point
will start
at struct offset 0. Assuming an int
is 4 bytes
long, the y member will start at struct offset 4 and the whole struct point
will have a size of 8 bytes. The compiler
computes the relative offsets of the members and only those offsets are
stored in the executable code. You will find no structure member names
in compiled C code but you will find plenty of them in a VdB "compiled"
program.
This is the reason why you cannot leave out any member from a C structure declaration. If you do, all following members will change their offset. Because the WinAPI function to which you pass the structure accesses its members through offsets it will not find the right ones any more.
Data type
name | Description | Win 16 API size | Win 32 API size | HLS-macro |
---|---|---|---|---|
char | signed integer | 8 | 8 | HLS_CHAR
|
short | signed integer | 16 | 16 | HLS_SHORT
|
int | signed integer | 16 | 32 | HLS_INT
|
long | signed integer | 32 | 32 | HLS_LONG
|
LONG | signed integer | 32 | 32 | HLS_LONG
|
UCHAR | unsigned integer | 8 | 8 | HLS_UCHAR
|
USHORT | unsigned integer | 16 | 16 | HLS_USHORT
|
UINT | unsigned integer | 16 | 32 | HLS_UINT
|
ULONG | unsigned integer | 32 | 32 | HLS_ULONG
|
BYTE | unsigned integer | 8 | 8 | HLS_BYTE
|
WORD | unsigned integer | 16 | 16 | HLS_WORD
|
DWORD | unsigned integer | 32 | 32 | HLS_DWORD
|
HANDLE | unsigned integer | 16 | 32 | HLS_HANDLE
|
HWND | unsigned integer | 16 | 32 | HLS_HWND
|
HDC | unsigned integer | 16 | 32 | HLS_HDC
|
HGLOBAL | unsigned integer | 16 | 32 | HLS_HGLOBAL
|
HINSTANCE | unsigned integer | 16 | 32 | HLS_HINSTANCE
|
HMODULE | unsigned integer | 16 | 32 | HLS_HMODULE
|
BOOLEAN | unsigned char | 8 | 8 | HLS_BOOLEAN
|
BOOL | signed integer | 16 | 32 | HLS_BOOL
|
WPARAM | unsigned integer | 16 | 32 | HLS_WPARAM
|
LPARAM | signed integer | 32 | 32 | HLS_LPARAM
|
LRESULT | signed integer | 32 | 32 | HLS_LRESULT
|
LPFN? | long pointer to function | 32 | 32 | HLS_LPFN
|
LP?STR | long pointer to string | 32 | 32 | HLS_LP?STR |
?CHAR [number] | inlined string | number | number | HLS_?CHARS |
? is a joker for zero or more characters. Note the plural in the
HLS_?CHARS
macros. You have to specify number as second
parameter to the HLS?_CHARS macros.
Data types like LP?
but not LP?STR
or LPFN?
are
long pointers to some data type. eg. LPDWORD
is a long pointer to a DWORD
. LPSOMENAME
is most likely a long pointer
to a SOMENAME
structure. You have
to lookup and convert that structure first, then use the HLS_LPSTRUCT
macro to insert a pointer to the struct.
Data types like SOMENAME FAR *
are long pointers to structures.
See above.
For so-called Hook-Functions use HLS_LPHOOK
or
HLS_LPFN
. You cannot access hook functions from VdB but must
still declare them in your structs.
If you still dont find your data type here you must scan the Windows
header files
. Most likely you will find a
typedef
to a known data type. Use the HLS-macro for that
data type then.
Some data type sizes change between WinAPI 16 and
WinAPI 32, notably the int
type and the
ubiquitous HANDLE
types.
Where the declaration of a structure member differs, use these macros. They are declared in a way that will get you the right sizes when compilig for either OS.
Win 32 API type | Win 16 API type | HLS-macro |
---|---|---|
LONG | int | HLS_INT
|
LONG | short | HLS_INT
|
ULONG | UINT | HLS_UINT
|
ULONG | USHORT | HLS_UINT
|
DWORD | UINT | HLS_UINT
|
DWORD | USHORT | HLS_UINT
|
LPCSTR | LPSTR | HLS_LPCSTR
|
LPTSTR | LPSTR | HLS_LPTSTR
|
LPCTSTR | LPSTR | HLS_LPCTSTR
|
TCHAR [] | CHAR [] | HLS_TCHAR
|
BCHAR [] | CHAR [] | HLS_BCHAR
|
HLS_SHORT
HLS_USHORT
HLS_LONG
HLS_ULONG
HLS_WORD
HLS_DWORD
HLS_CHARS
Entries are in the form
FunctionName [(Param1 [, Param2, [...]])] [ : ReturnValue ]Functions not mentioned here are reserved. Do not call them.
get()
after you call put()
.
fwrite()
if you have to write a struct to a file.
fread()
to read a struct from a file.
ToStatus()
.
ToStatus()
.
.t.
if the entry
was found. Does nothing and returns .f.
if the entry was not found. Uses
FromStatus()
.
DEVMODE
, DEVNAMES
, PRINTER_INFO_?
).
© 1998 Marcello Perathoner
This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software.
The author is not obliged to fix bugs, write upgrades nor to provide any upgraded version of this software under these same terms.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
I wrote this software; it does not include third-party code.
If you use this software in a product, I would appreciate not receiving lengthy legal documents to sign.
Visual dBASE and Borland are registered trademarks of Inprise Corp.
Microsoft and Windows are either registered
trademarks or trademarks of Microsoft Corporation
Marcello Perathoner / JLSeagull@csi.com / revised June 22, 1998