转自 Delphi and the Windows API - Win32 Shell API - Delphi Power
Delphi and the Windows API
Last Updated on Wed, 31 Mar 2021 | Win32 Shell APIWhen Delphi was introduced, it brought a new era to Windows programming. Never before was it so easy to create robust, full-featured applications for the Windows environment with such short development times. Now in its sixth incarnation, Delphi has been the development tool for innumerable shareware and freeware applications, internal business and proprietary system applications, several well-known commercial applications, and even a commercial game or two. Delphi's power and ease of use make it a wonderful choice for a development platform that can stand up to C++ and Visual Basic in almost every situation.
One of Delphi's strengths is the Visual Component Library, Borland's object model. This object model has allowed the Delphi development team to encapsulate the vast majority of Windows programming tedium into easy-to-use components. Earlier Windows programming languages required the developer to write large amounts of code just to squeeze a minimal amount of functionality out of Windows. The mere act of creating a window and accepting menu selections could take pages of code to create. Delphi's excellent encapsulation of this dreary requirement of Windows programming has turned what once was a chore into a fun, exciting experience.
Windows Data Types
Windows API functions use a number of data types that may be unfamiliar to the casual Delphi programmer. These data types are all taken from the original C header files that define the Windows API function syntax. For the most part, these new data types are simply Pascal data types that have been renamed to make them similar to the original data types used in legacy Windows programming languages. This was done so that experienced Windows programmers would understand the parameter types and function return values, and the function prototypes would match the syntax shown in existing Windows API documentation to avoid confusion. The following table outlines the most common Windows data types and their correlating Object Pascal data type.
Table 1-1: Windows data types |
||
Windows |
Object Pascal |
Description |
Data Type |
Data Type |
|
LPSTR |
PAnsiChar |
String pointer |
LPCSTR |
PAnsiChar |
String pointer |
DWORD |
LongWord |
Whole numbers |
BOOL |
LongBool |
Boolean values |
PBOOL |
~BOOL |
Pointer to a Boolean value |
PByte |
~Byte |
Pointer to a byte value |
PINT |
^Integer |
Pointer to an integer value |
PSingle |
^Single |
Pointer to a single (floating-point) value |
PWORD |
~Word |
Pointer to a 16-bit value |
PDWORD |
^DWORD |
Pointer to a 32-bit value |
LPDWORD |
PDWORD |
Pointer to a 32-bit value |
UCHAR |
Byte |
8-bit values (can represent characters) |
PUCHAR |
~Byte |
Pointer to 8-bit values |
SHORT |
Smallint |
Signed 16-bit whole numbers |
UINT |
LongWord |
Unsigned 32-bit whole numbers |
PUINT |
~UINT |
Pointer to unsigned 32-bit whole numbers |
ULONG |
Cardinal |
Unsigned 32-bit whole numbers |
PULONG |
^ULONG |
Pointer to unsigned 32-bit whole numbers |
PLongint |
^Longint |
Pointer to 32-bit values |
PInteger |
^Integer |
Pointer to 32-bit values |
PSmallInt |
^Smallint |
Pointer to 16-bit values |
PDouble |
^Double |
Pointer to double (floating-point) values |
LCID |
DWORD |
A local identifier |
LANGID |
Word |
A language identifier |
THandle |
LongWord |
An object handle. Many Windows API functions return a value of type THandle, which identifies that object within Window's internal object tracking tables. |
PHandle |
^THandle |
A pointer to a handle |
WPARAM |
Longint |
A 32-bit message parameter. Under earlier versions of Windows, this was a 16-bit data type. |
LPARAM |
Longint |
A 32-bit message parameter |
LRESULT |
Longint |
A 32-bit function return value |
HWND |
LongWord |
A handle to a window. All windowed controls, child windows, main windows, etc., have a corresponding window handle that identifies them within Windows' internal tracking tables. |
HHOOK |
LongWord |
A handle to an installed Windows system hook |
Windows |
Object Pascal |
Description |
Data Type |
Data Type |
|
ATOM |
Word |
An index into the local or global atom table for a string |
HGLOBAL |
THandle |
A handle identifying a globally allocated dynamic memory object. Under 32-bit Windows, there is no distinction between globally and locally allocated memory. |
HLOCAL |
THandle |
A handle identifying a locally allocated dynamic memory object. Under 32-bit Windows, there is no distinction between globally and locally allocated memory. |
FARPROC |
Pointer |
A pointer to a procedure, usually used as a parameter type in functions that require a callback function |
HGDIOBJ |
LongWord |
A handle to a GDI object. Pens, device contexts, brushes, etc., all have a handle of this type that identifies them within Window's internal tracking tables. |
HBITMAP |
LongWord |
A handle to a Windows bitmap object |
HBRUSH |
LongWord |
A handle to a Windows brush object |
HDC |
LongWord |
A handle to a device context |
HENHMETAFILE |
LongWord |
A handle to a Windows enhanced metafile object |
HFONT |
LongWord |
A handle to a Windows logical font object |
HICON |
LongWord |
A handle to a Windows icon object |
HMENU |
LongWord |
A handle to a Windows menu object |
HMETAFILE |
LongWord |
A handle to a Windows metafile object |
HINST |
THandle |
A handle to an instance object |
HMODULE |
HINST |
A handle to a module |
HPALETTE |
LongWord |
A handle to a Windows color palette |
HPEN |
LongWord |
A handle to a Windows pen object |
HRGN |
LongWord |
A handle to a Windows region object |
HRSRC |
THandle |
A handle to a Windows resource object |
HKL |
LongWord |
A handle to a keyboard layout |
HFILE |
LongWord |
A handle to an open file |
HCURSOR |
HICON; |
A handle to a Windows mouse cursor object |
COLORREF |
DWORD; |
A Windows color reference value, containing values for the red, green, and blue components of a color |
Handles
An important concept in Windows programming is the concept of an object handle. Many functions return a handle to an object that the function created or loaded from a resource. Functions like CreateWindowEx return a window handle. Other functions, like CreateFile, return a handle to an open file or, like HeapCreate, return a handle to a newly allocated heap. Internally, Windows keeps track of all these handles, and the handle serves as the link through the operating system between the object and the application. Using these handles, an application can easily refer to any of these objects, and the operating system instantly knows which object a piece of code wants to manipulate.
Constants
The Windows API functions declare literally thousands upon thousands of different constants to be used as parameter values. Constants for everything from color values to return values have been defined in the Windows.pas, Types.pas, ActiveX.pas, ShlObj.pas, ComObj.pas, and System.pas files. The constants that are defined for each API function are listed with that function within the text. However, the Windows.pas file may yield more information concerning the constants for any particular function, and it is a good rule of thumb to check this Delphi source code file when using complicated functions.
Strings
All Windows API functions that use strings require a pointer to a null-terminated string type. Windows is written in C, which does not have the Pascal string type. Earlier versions of Delphi required the application to allocate a string buffer and convert the string type to a PChar. However, since Delphi 3, we have a string conversion mechanism that allows a string to be used as a PChar by simply typecasting it (i.e., PChar(MyString), where MyString is declared as MyString: string). For the most part, this conversion will work with almost all Windows API functions that require a string parameter.
Importing Windows Functions
The Windows API is huge. It defines functions for almost every kind of utility or comparison or action that a programmer could think of. Due to the sheer volume of Windows API functions, some functions simply fell through the cracks and were not imported by the Delphi source code. Since all Windows API functions are simply functions exported from a DLL, importing a new Windows API function is a relatively simple process, if the function parameters are known.
Importing a new Windows API function is exactly like importing any other function from a DLL. For example, in earlier versions of Delphi, the BroadcastSystemMessage function was not imported by the Delphi source code. (It is now imported and available for use, but we'll use this function as an example.) In order to import this function for use within an application, it is simply declared as a function from within a DLL as:
Note: Use the stdcall directive, appended to the end of the function prototype, when importing Windows API functions.
Incorrectly Imported Functions
Some functions have been incorrectly imported by the Delphi source code. These exceptions are noted in the individual function descriptions. For the most part, the functions that have been imported incorrectly deal with the ability to pass NIL as a value to a pointer parameter, usually to retrieve the required size of a buffer so the buffer can be dynamically allocated to the exact length before calling the function to retrieve the real data. In Delphi, some of these functions have been imported with parameters defined as VAR or CONST. These types of parameters can accept a pointer to a buffer but can never be set to NIL, thus limiting the use of the function within the Delphi environment. As is the case with almost anything in Delphi, it is a simple matter to fix. Simply reimport the function as if it did not exist, as outlined above. Functions that have been imported incorrectly are identified in their individual function descriptions throughout this book.
Callback Functions
Another very important concept in Windows programming is that of a callback function. A callback function is a function within the developer's application that is never called directly by any other function or procedure within that application. Instead, it is called by the Windows operating system. This allows Windows to communicate directly with the application, passing it various parameters as defined by the individual callback function. Most of the enumeration functions require some form of application-defined callback function that receives the enumerated information.
Individual callback functions have specific parameters that must be declared exactly by the application. This is required so that Windows passes the correct information to the application in the correct order. A good example of a function that uses a callback function is EnumWindows. The EnumWindows function parses through all top-level windows on the screen, passing the handle of each window to an application-defined callback function. This continues until all top-level windows have been enumerated or function BroadcastSystemMessage(Flags: DWORD; Recipients: PDWORD;
uiMessage: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
implementation function BroadcastSystemMessage; external user32 name 'BroadcastSystemMessage';
As long as the parameters required by the function and the DLL containing the function are known, any Windows API function can be imported and used by a Delphi application. It is important to note that the stdcall directive must be appended to the prototype for the function, as this defines the standard mechanism by which Windows passes parameters to a function on the stack.
the callback function returns FALSE. The callback function used by EnumWindows is defined as:
EnumWindowsProc(
hWnd: HWND; {a handle to a top-level window}
lParam: LPARAM {the application-defined data}
): BOOL; {returns TRUE or FALSE}
A function matching this function prototype is created within the application, and a pointer to the function is passed as one of the parameters to the EnumWindows function. The Windows operating system calls this callback function for each top-level window, passing the window's handle in one of the callback function's parameters. It is important to note that the stdcall directive must be appended to the prototype for the callback function, as this defines the standard mechanism by which Windows passes parameters to a function on the stack. For example, the above callback function would be prototyped as:
EnumWindowsProc(hWnd: HWND; lParam: LPARAM); stdcall;
Without the stdcall directive, Windows will not be able to access the callback function. This powerful software mechanism, in many cases, allows an application to retrieve information about the system that is only stored internally by Windows and would otherwise be unreachable. For a complete example of callback function usage, see the EnumWindows function and many other functions throughout the book.
Function Parameters
The vast majority of Windows API functions simply take the static parameters handed to them and perform some function based on the value of the parameters. However, certain functions return values that must be stored in a buffer, and that buffer is passed to the function in the form of a pointer. In most cases, when the function description specifies that it returns some value in a buffer, null-terminated string buffer, or pointer to a data structure, these buffers and data structures must be allocated by the application before the function is called.
In many cases, a parameter may state that it can contain one or more values from some table. These values are defined as constants, and they are combined using the Boolean OR operator. The actual value passed to the function usually identifies a bitmask, where the state of each bit has some significance to the function. This is why the constants can be combined using Boolean operations. For example, the CreateWindowEx function has a parameter called dwStyle which can accept a number of constants combined with the Boolean OR operator. To pass more than one constant to the function, the parameter would be set to something like "WS_CAPTION or WS_CHILD or WS_CLIPCHILDREN." This would create a child window that includes a caption bar and would clip around its child windows during painting.
Conversely, when a function states that it returns one or more values that are defined as specific constants, the return value can be combined with one of the constants using the Boolean AND operator to determine if that constant is contained within the return value. If the result of the combination equals the value of the constant, then that constant is included in the return value.
Originally, software only needed a single byte to define a character within a character set. This allowed for up to 256 characters, which was more than plenty for the entire alphabet, numbers, punctuation symbols, and common mathematical symbols. However, due to the shrinking of the global community and the subsequent internationalization of Windows and Windows software, a new method of identifying characters was needed. Many languages have well over 256 characters used for writing, much more than a single byte can describe. Therefore, Unicode was invented. A Unicode character is 16 bits long and can therefore identify 65,535 characters within a language's alphabet. To accommodate the new character set type, many Windows API functions come in two flavors: ANSI and Unicode. When browsing the Windows.pas source code, many functions are defined with an A or W appended to the end of the function name, identifying them as an ANSI function or Wide character (Unicode) function. The functions within this book cover only the ANSI functions. However, the Unicode functions usually differ only in the type of string information passed to a function, and the text within this book should adequately describe the Unicode function's behavior.
Unicode £
Delphi vs. the Windows API
The Delphi development team did a world-class job of encapsulating the vast majority of important Windows API functionality into the VCL. However, due to the vastness of the Windows API, it would be impossible and impractical to wrap every API function in an Object Pascal object. To achieve certain goals or solve specific problems, a developer may be forced to use lower-level Windows API functions that are simply not encapsulated by a Delphi object. It may also be necessary to extend the functionality of a Delphi object, and if this object encapsulates some part of the Windows API, it will be the API that the developer will likely have to use to extend the functionality by any great amount.
Indeed, there are literally hundreds of APIs out there that dramatically extend Windows' functionality, and due to the sheer numbers of API functions and the ever changing, ever expanding functionality being introduced by Microsoft, it would be nearly impossible to actively import every last function from every available API. Therefore, it is important that the well-prepared and capable Delphi programmer is familiar with hardcore Windows programming, as it is highly likely that you'll be called upon sometime in your Delphi career to make use of some Windows API functionality that is not encapsulated by the VCL.
There may even be situations where it is impractical to use the Delphi components that encapsulate Windows functionality. The VCL makes Windows programming easy, but by its very nature, Delphi applications tend to be a minimum 350 KB in size. Bypassing the VCL and using direct Windows API calls, on the other hand, can yield a
Delphi application as small as 10 KB. Every situation is different, and fortunately, as Delphi programmers, we have a lot of flexibility in this area. Using direct Windows API calls may not always be necessary, but when it is, it's good to know that we have that option available to us.
Chapter 2
Was this article helpful?
Related Posts