Win32
Windows was originally a 16-bit graphical layer for MS-DOS that was written by Microsoft. As it grew, it gained the ability to handle 32-bit programs and eventually became totally 32-bit when Windows NT and 2000 came out. After Windows 95, Microsoft began to remove dependencies on DOS and finally fully implemented the separation in Windows 2000. Windows has many advanced features as well as many platform specific problems. It possesses an Application Programming Interface that consists of thousands of mostly undocumented GUI functions as well as having varying degrees of MS-DOS compatibility. Additionally, with the advent of NT (New Technology), Windows relies completely on the NT kernel instead of its MS-DOS subsystem, the NT kernel emulating the necessary DOS functionality. In addition to the NT kernel, Microsoft has also introduced many API wrappers, such as the MFCs (Microsoft Foundation Classes), COM (Component Object Model), and .NET technologies. The most popular languages for use on Windows include Visual Basic/VB6 and C/C++, although C++ is quickly being replaced by the .NET platform, specificially C# (C Sharp).
Windows Kernels
Windows 1.0, 2.0, and 3.11 are considered to be an older generation of windows systems that were built as simply a graphical layer over the MS-DOS operating system. Windows 95, Windows 98, and Windows ME were designed to bypass MS-DOS (although DOS was still present), and were all based off the same code structure known as the "9x Kernel". Windows NT 4.0, Windows 2000, Windows XP, and Windows Server are all based off a bunch of code known as the "NT Kernel".
System Architecture
The Windows NT Kernel is divided up into several sections, here we will briefly discuss how the windows operating system is put together. At the most basic level is the file NTOSKRNL.EXE, the kernel of the windows operating system, and the most important file on your computer. If you are interested in seeing this for yourself, you can find it in the C:\Windows\System32 folder (this can also be found using the following path %systemroot%\system32 ) on your own windows NT machines.
NTOSKRNL.EXE provides some of the basic functionality of windows, but one file alone cannot make the whole system work. NTOSKRNL relies heavily on a Dynamic Link Library (DLL) known as HAL.DLL. HAL stands for "Hardware Abstraction Layer", and is the portion of code that allows the Kernel (NTOSKRNL.EXE) to communicate with the computer hardware (ports, monitors, keyboards, etc.).
If we consider that Windows architecture is a layered architecture, with NTOSKRNL.EXE and HAL.DLL on the bottom layer, the next layer up contains 2 important files, NTDLL.DLL, and WIN32K.SYS. NTDLL contains a number of kernel-mode functions that implement much of the functionality of the Windows API, but NTDLL does not handle any of the graphical components. NTDLL is instead responsible for things like File I/O functions, threading and synchronization, timing, messages, etc...
WIN32K.SYS is similar to NTDLL.DLL, in that it contains a lot of primative kernel-mode code that performs most of the low-level tasks in windows. However, WIN32K.SYS is primarily concerned with graphics and user interface tasks, while NTDLL.DLL is mostly concerned with system tasks. The Functions in NTDLL.DLL are undocumented by microsoft, and are known popularly as the "Native Windows API".
The next layer up from our kernel-mode functions contains a number of libraries that will be of primary interest to us. This layer comprises what is called the Win32 API, and contains (almost) all the functions that a user will need to program in windows. The Win32 API is divided into 3 component parts, each one a .DLL:
Kernel32.DLL
This is the user-mode equivalent of the NTDLL.DLL code we discussed already. Kernel32 contains a number of high-level functions for performing a variety of system tasks. Most of the Kernel32 functions simply call into NTDLL.DLL, which has lead many people to believe that programs could be made faster by using the NTDLL.DLL functions directly, and not going through Kernel32.DLL at all. However, NTDLL functions are very complex, and can be more of a pain to use than the benefit they bring.
gdi32.DLL
WinGDI.DLL contains a number of basic functions for drawing. These functions are all relatively simple, and allow the user to draw shapes (circles, rectangles, etc.) on the screen, to display and manipulate bitmaps, etc.
user32.DLL
The User32 DLL contains a number of functions that implement the familar user-interface of windows. Programs, message boxes, prompts, etc are all implemented using the User32 functions. User32 performs its tasks by calling on WinGDI.DLL and WIN32K.SYS functions to do the "dirty work".
In addition to the 3 primary libraries in the Win32 API, there are a number of other important libraries that a windows programmer should become familiar with:
MSVCRT.DLL
MSVCRT.DLL is the dynamic link library that contains the implementations of the C standard libary (stdlib) functions that C programmers should be familiar with. These are the functions defined in the common header files stdio.h, string.h, stdlib.h, etc.
WS2_32.DLL
This is the Winsock2 library, that contains the standard Berkeley socket API for communicating on the internet. We will talk about winsock programming later in this book.
Windows Does It
The Windows system, it might be surprising for some people to learn, is a very hands-on system. This is not a familiar concept for people who are just beginning C programming using the standard library. In a normal software project, there is typically a main function, and the main function in turn calls other functions that are defined in your project. In a Windows function, typically the programmer provides function pointers to the system, and Windows will make calls into your program. Also, in a Windows program, your code will sit idle when there is nothing to be done. Using the message loop architecture, Windows will send messages to your program when an event needs to be handled, and the program responds to the messages. If the program doesnt respond, the message is ignored.
For each program, Windows sets up a message queue structure to handle the message transmission process. Windows will maintain a listing of all the objects and system resources in use by a program, and will assign each one a handle. These handles are useless by themselves, but they can be passed to the system to reference particular objects and resources.
User Mode vs Kernel Mode
In Windows, there is a distinction between code that is running in "user mode", and code is that running in "kernel mode". This chapter is going to point out some of the differences.
User Mode Code
User Mode code is just about everything that you will see running on your computer. Programs written in user mode will utilize the standard Win32 API, and won't interact with the kernel, the memory, or the hardware directly. This is the safest mode of operation, because user-mode is heavily monitored and restricted by the system. Programs running in user mode may not be able to perform all the same tasks as kernel mode code can (not directly, anyway), but user mode programs have a limited ability to hurt or "crash" the system.
Kernel Mode Code
Code operating in kernel mode has fewer restrictions than user-mode code, but with the greater freedom comes a much larger risk of damaging the system. For instance, a poorly constructed device driver can crash the system, and since it runs outside of user control, there will be no good way to fix it! It is important, then, that kernel mode code be written only when absolutely necessary, and that kernel mode code is extensively tested and debugged before shipping it to the user.
C and Win32 API
C and Windows
Many of the low-level functions in Windows were created using the C programming language. C code tends to be relatively small and fast compared to C++ code or even VB code, and has a lower development time compared to raw assembly code. All the DLLs in the Win32 API, and most of the kernel-level structures are implemented in C code.
Visual Basic and Windows
Visual Basic is essentially a "Windows-only" programming language, although some ports either exist or are in development for other systems. However, VB is not nearly as popular off Windows as it is on Windows. The VB runtime libraries tap into the Win32 API and the MFC libraries, to implement its functionality. The new version of VB, "VB.NET", uses the .NET platform, and therefore will not be covered in this wikibook. Old versions of VB, or "VB Classic" utilize the same Win32 API and MFC interfaces that C and C++ use to communicate to windows. However, not all the functions are identical, because VB uses different data types, and stores data differently from C or C++. As we discuss the Win32 API and MFC, we will attempt to show how the same functionality is implemented in VB.
Visual Basic uses the STDCALL calling convention for implementing function calls. Most of the Win32 API uses the STDCALL calling convention as well, so much of the hard work for interfacing the two is done by the compiler. C and C++, on the other hand, do not use STDCALL by default, and if you want to manually link a VB object code file with a C object file, you will need to explicitly make the C interface an STDCALL one.
COM and Windows
Microsoft implemented a technology known as the Component Object Model for Windows. COM essentially takes the object-oriented programming paradigm to the next level, by standardizing the class interface, and allowing classes to be written in different languages (C++, VB, etc.) and interfaced together seemlessly. COM programs (or "COM clients") can be written in most languages that allow for object-orientation.
Other Languages
Many other programming languages have been implemented on Windows systems, and many of them have some sort of method for interfacing the Win32 API, or the MFC libraries. These interfaces are known collectively as "Wrappers", because they wrap the functionality of the Win32 API in another programming language. Common wrappers are provided for Perl, Ada, Python, PHP, etc..
Windows.H
The Win32 API can be added to a C programming project by linking to the appropriate libraries (kernel32.lib, user32.lib and wingdi.lib), and by including the header file in your code. Windows.h contains all the function declarations in the API, as well as declarations for all the common macros used by windows programmers, and all the data types used by the various functions and subsystems. Windows.h is available in the platform SDK, and is also available as part of the Visual Studio package.
Child Header Files
There are a number of header files that are automatically included with windows.h. Many of these files cannot simply be included by themselves, because of dependancies
Additional Header Files
This section will talk about some other interesting header files and libraries that can be included with your project, but which are not a default include with windows.h.
Windows.h Macros
This section will briefly detail some of the changes that the user can make in the windows.h structure at compile time using macros.
Handles and Data Types
From Wikibooks, the open-content textbooks collection
One of the first things that is going to strike many first-time programmers of the Win32 API is that there are tons and tons of new data types to deal with. Sometimes, just keeping all the correct data types in order can be more difficult than writing a nice program. This page will talk a little bit about some of the data types that a programmer will come in contact with.
Hungarian Notation
First, let's make a quick note about the naming convention used for some data types, and some variables. The Win32 API uses the so-called "Hungarian Notation" for naming variables.
Hungarian Notation requires that a variable be prefixed with an abbreviation of its data type, so that when you are reading the code, you know exactly what type of variable it is. This practice is especially useful in the Win32 API because there are so many different data types that it can be hard to keep them all straight. Also, there are a number of different data types that are essentially defined the same way, and therefore some compilers will not pick up on errors when they are used incorrectly. As we discuss each data type, we will also note the common prefixes for that data type.
Putting the letter "P" in front of a data type, or "p" in front of a variable usually indicates that the variable is a pointer. The letters "LP" or the prefix "lp" stands for "Long Pointer", which is exactly the same as a regular pointer on 32 bit machines. LP data objects are simply legacy objects that were carried over from Windows 3.1 or later, when pointers and long pointers needed to be differentiated. On modern 32-bit systems, these prefixes can be used interchangably.
LPVOID
LPVOID data types are defined as being a "pointer to a void object". This may seem strange to some people, but the ANSI-C standard allows for generic pointers to be defined as "void*" types. This means that LPVOID pointers can be used to point to different types of objects, without creating a compiler error. However, the burden is on the programmer to keep track of what type of object is being pointed to.
Also, some functions in the Win32 API may have an argument (or more than 1) labled as "LPVOID reserved". These reserved data members are never to be used in your program, because they either depend on functionality that hasn't yet been implemented by Microsoft, or else they are only used in certain applications. If you see a function with an "LPVOID reserved" argument, you must always pass a NULL value for that parameter. Some functions will fail if you do not pass a NULL in that argument.
LPVOID objects frequently do not have prefixes, although it is relatively common to prefix an LPVOID variable with the letter "p" because it's a pointer.
STR, LPSTR
STR data types are string data types, with storage already allocated. This data type is less common than the LPSTR. STR data types are used when the string is supposed to be treated as an immediate array, and not as a simple character pointer. The prefix to STR data types is "sz" because it's a "String, Zero-terminated".
Most programmers will not define a variable as an STR, opting instead to define it as a character array, because defining it as an array allows the size of the array to be set explicitly. Also, creating a large string on the stack can allow for stack-overflow problems, which nobody wants.
LPSTR stands for "Long Pointer to an STR", and is essentially defined as such:
typedef STR* LPSTR;
LPSTR can be used exactly like other string objects, except that LPSTR is explicitly defined as being ASCII, not unicode, and this definition will hold on all platforms. LPSTR variables will usually be prefixed with the letters "lpsz" to denote a "Long Pointer to a String that is Zero-terminated". The "sz" part of the prefix is important, because some strings in the windows world (especially when talking about the DDK) are not zero-terminated. LPSTR data types, and variables prefixed with the "lpsz" prefix can all be used seamlessly with the standard library functions.
TCHAR
TCHAR data types, as will be explained in the section on unicode, are generic character data types. TCHAR can hold either standard 1-byte ASCII characters, or wide 2-byte Unicode characters. Because this data type is defined by a macro and is not set in stone, only character data should be used with this type.
TSTR, LPTSTR
TSTR data types are essentially just generic string data types
LPTSTR data types are long pointers to generic strings, and may contain either ASCII strings or Unicode strings, depending on the environment being used. LPTSTR data types are also prefixed with the letters "lpsz".
DWORD, PDWORD
DWORD is defined as being an unsigned 32-bit quantity. This is important, because the C type int is implementation-dependant. On some machines an int is 16 bits, and on some it is 32 bits. Likewise, on some machines a long int is 32 bits, and on some machines it is 64 bits. To avoid the confusion, and to make code portable, DWORDs are defined on each different machine in order to be exactly 32 bits. DWORD variables generally do not have a prefix, although occasionally some of the following will be used:
1. "i" because the variable is being used as a general integer quantity
2. "c" because the variable is being used to count a quantity, or manage a loop
3. "f" because the variable is being used to hold a series of bit-flags.
4. "d" because that's the letter DWORD starts with.
a PDWORD, or an LPDWORD are both pointers to DWORDS, which equally could be an array of DWORDs. PPDWORDS are "pointers to pointers to DWORDS", and so on and so forth.
WORD, BYTE, etc
WORDs, like DWORDs, are defined strictly as being unsigned 16-bit values, regardless of what machine you are programming on. BYTEs are defined strictly as being unsigned 8-bit values. QWORDs, although rare, are defined as being unsigned 64-bit quantities. Putting a "P" in front of any of these identifiers indicates that the variable is a pointer. putting two "P"s in front indicates it's a pointer to a pointer. These variables may be unprefixed, or they may use any of the prefixes common with DWORDs.
UINT, LONG, etc...
the Win32 API also defines a number of other similar data types for integer values. These declarations are usually exactly the same as DWORD, WORD, or BYTE declarations, but using a different identifier can occasionally make code more readable.
UINT
UINT is an "Unsigned Integer", and is defined as being "unsigned int". This value can be 16-bits on a 16-bit machine, or 32-bits on a 32-bit machine. UINT variables are typically prefixed with an "i" or a "ui" to indicate that it is an integer, and that it is unsigned.
LONG
LONG is defined as being a "unsigned long int", and is dependant on the host machine to determine exactly how long a LONG is. LONG variables are typically prefixed with an "l".
CHAR, UCHAR
similarly, CHAR is defined as being a regular C char, and UCHAR is defined as being an "unsigned char". These variables are usually prefixed with a "c" or a "uc" respectively.
If the size of the variable doesn't matter, you can use some of these integer types. However, if you want to exactly specify the size of a variable, use the BYTE, WORD, DWORD, or QWORD identifiers, because their length's never change.
HANDLE
HANDLE data types are some of the most important data objects in Win32 programming, and also some of the hardest for new programmers to understand. Inside the kernel, windows maintains a table of all the different objects that the kernel is responsible for. Windows, buttons, icons, mouse pointers, menus, etc all get an entry in the table, and each entry is assigned a unique identifier known as a HANDLE. If you want to pick a particular entry out of that table, you need to give Windows the HANDLE value, and Windows will return the corresponding table entry.
HANDLES are defined as being unsigned 32-bit quantities in , but HANDLES should never be used like integers. They are unique identifiers, and if you edit them, or use them in arithmetic, then they can never be used to get the table entry that they correspond to. In other words, HANDLES should be stored, but they should never be used by the programmer.
HANDLES are generally prefixed with an "h", although there are a few special handles that are worth discussing:
HWND
HWND data types are "Handles to a Window", and are used to keep track of the various objects that appear on the screen. To communicate with a particular window, you need to have a copy of the window's handle. HWND variables are usually prefixed with the letters "hwnd", just so the programmer knows they are important.
Cannonically, main windows are defined as:
HWND hwnd;
Child windows are defined as:
HWND hwndChild1, hwndChild2...
and Dialog Box handles are defined as:
HWND hDlg;
Although, you are free to name these variables whatever you want in your own program.
HINSTANCE
HINSTANCE variables are handles to a program instance. Each program get's a single instance variable, and this is important so that the kernel can communicate with the program. If you want to create a new window, for instance, you need to pass your programs HINSTANCE variable to the kernel, so that the kernel knows who the new window belongs to. If you want to communicate with another program, it is frequently very useful to have a copy of that program's HINSTANCE variable. HINSTANCE variables are usually prefixed with an "h", and further more, since there is frequently only one HINSTANCE variable in a program, it is cannonical to declare that variable as such:
HINSTANCE hInstance;
it is usually a benefit to make this HINSTANCE variable a global value, so that all your functions can access it when needed.
HMENU
If your program has a drop-down menu available (like most windows programs do), that menu will have a HMENU handle associated. To display the menu, or to alter it's contents, you need to have access to this HMENU handle. HMENU handles are frequently prefixed with simply an "h".
WPARAM, LPARAM
In the olden days of Microsoft Windows, parameters were passed to a window in one of two formats: WORD-length (16-bit) parameters, and LONG-length (32-bit) parameters. These parameter types were defined as being WPARAM (16-bit) and LPARAM (32-bit). However, in modern 32-bit systems, WPARAM and LPARAM are both 32 bits long. The names however have not changed, for legacy reasons.
WPARAM and LPARAM variables are generic function parameters, and are frequently type-cast to other data types including pointers and DWORDs.
Unicode
Introduction to Unicode
Unicode is an industry standard whose goal is to provide the means by which text of all forms and languages can be encoded for use by computers. Originally, text-characters were represented in computers using byte-wide data: each printable character (and many non-printing, or "control" characters) were implemented using a single byte each, which allowed for 256 characters total. However, globalization has created a need for computers to be able to accomodate many different alphabets from around the world.
The old codes were known as ASCII or EBCDIC, but it was apparent that neither of these codes were capable of handling all the different characters and alphabets from around the world. The solution to this problem created a set of "wide" 16-bit characters known as Unicode. Windows NT implements many of it's core functions in unicode, although it provides a series of functions that are compatable with the standard ASCII characters as well.
Windows Implementation
Windows breaks all it's functions that require text input into 2 functions. Some of the functions have an "A" suffix (for ASCII), and some have a "W" prefix (for Wide characters, or Unicode). These functions are differentiated using the macro "UNICODE":
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif
Unicode Environment
All windows functions that require character strings are defined in this manner. If you want to use unicode in your program, you need to explicitly define the UNICODE macro before you include the windows.h file:
#define UNICODE
#include
Also, some functions in other libraries require you to define the macro _UNICODE also. The standard library functions can be provided in unicode by including the file as well. So, to use unicode in your project, you need to make the following
declarations in your project:
#define UNICODE
#define _UNICODE
#include
#include
TEXT macro
In C, to make a string of wide characters, you need to prefix the string with the letter "L". Here is an example:
tchar *asciimessage = "This is an ASCII string.";
tchar *unicodemessage = L"This is a Wide Unicode string.";
The data type "tchar" is defined as being a char type if unicode is not defined, and is defined as being a wide type if UNICODE is defined (in tchar.h). To make strings portable between unicode and non-unicode, we can use the TEXT() macro to automatically define a string as being unicode or not:
tchar *automessage = TEXT("This message can be either ASCII or UNICODE!");
Using tchar data types, and the TEXT macro are important steps in making your code portable between different environments.
Also, the TEXT macro can be written as:
TEXT("This is a generic string");
_T("This is also a generic string");
T("This is also a generic string");
All 3 of these statments are equivalent.
Unicode Reference
• Unicode Character Reference
Control Characters
Unicode characters 0 to 31 (U+0000 to U+001F) are part of the C0 Controls and Basic Latin block. They are all control characters. This characters correspond to the first 32 characters of the ASCII set.
Code point Decimal equivalent Name
U+0000 0 null character
U+0001 1 start of header
U+0002 2 start of text
U+0003 3 end of text
U+0004 4 end of transmission
U+0005 5 enquiry
U+0006 6 acknowledgement
U+0007 7 bell
U+0008 8 backspace
U+0009 9 horizontal tab
U+000A 10 line feed
U+000B 11 vertical tab
U+000C 12 form feed
U+000D 13 carriage return
U+000E 14 shift out
U+000F 15 shift in
U+0010 16 data link escape
U+0011 17 device control 1
U+0012 18 device control 2
U+0013 19 device control 3
U+0014 20 device control 4
U+0015 21 negative acknowledgement
U+0016 22 synchronous idle
U+0017 23 end of transmision block
U+0018 24 cancel
U+0019 25 end of medium
U+001A 26 substitute
U+001B 27 escape
U+001C 28 file separator
U+001D 29 group separator
U+001E 30 record seprator
U+001F 31 unit separator
Dynamic Link Libraries
Dynamic Link Libraries (DLL) are an important structural component of Microsoft Windows. DLLs allow certain code fragments to be compiled to a single library, and to be linked to by multiple programs. This means that only one copy of the library needs to exist, and multiple programs can share the functions and the data between them. The difference between a DLL and a static library is that when you compile your programs, the DLL is not compiled into your executable, but instead remains a separate module. This feature helps to keep executable size low, and also allows for a DLL to be loaded into memory only when it is needed.
The exact method of building a DLL file is dependant on the compiler you are using. However, the way in which DLLs are programmed is universal. We will talk about how to program DLL files in this chapter.
_declspec
the _declspec keyword is a strange new keyword that is not part of the ANSI C standard, but that most compilers will understand anyway. _declspec allows a variety of non-standard options to be specified, that will affect the way a program runs. specifically, there are two _declspec identifiers that we want to discuss:
• _declspec(dllexport)
• _declspec(dllimport)
When writing a DLL, we need to use the dllexport keyword to denote functions that are going to be available to other programs. Functions without this keyword will only be available for use from inside the library itself. Here is an example:
_declspec(dllexport) int MyFunc1(int foo)
The declspec identifier for a function needs to be specified both in the function prototype and the function declaration, when building a dll.
To "Import" a DLL function from a regular program, we need to link to the DLL, and we need to prototype the function with the dllimport keyword as such:
_declspec(dllimport) int MyFunct1(int foo);
now the program can use the function like normal, even though the function exists in an external library. Don't worry, Windows will handle all the details for you.
Many people find it useful to define a single header file for their DLL, instead of maintaining 1 header file for building a DLL, and 1 header file for importing a DLL. Here is a macro that is common in DLL creation:
#ifdef BUILDING_DLL
#define DLL_FUNCTION _declspec(dllexport)
#else
#define DLL_FUNCTION _declspec(dllimport)
#end
Now, to build the DLL, we need to define the BUILDING_DLL macro, and when we are importing the DLL, we don't need to use that macro. Functions then can be prototyped as such:
DLL_FUNCTION int MyFunc1(void);
DLL_FUNCTION int MyFunc2(void);
...
DllMain
When Windows links a DLL to a program, Windows calls the libraries' DllMain function. This means that every DLL needs to have a DllMain function. The DllMain function needs to be defined as such:
BOOL APIENTRY DllMain (HINSTANCE hInstance, DWORD reason, LPVOID reserved)
The keywords "BOOL", "APIENTRY", "HINSTANCE", et cetera, are all defined in so you must include that file (even if you don't use any Win32 API functions in your library).
APIENTRY is just a keyword that windows uses internally, so you don't need to worry about it. the variable "hInstance" is the HINSTANCE handle for the library, and you can keep this and use it, or you can trash it. reason will be 1 of 4 different values:
Open" command, we will use an id called "IDC_FILE_OPEN". We will define all these ID tags in a resource header script later. Here is our menu with all the ID's in place:
IDM_MY_MENU MENU DISCARDABLE
BEGIN
POPUP "File"
BEGIN
MENUITEM "Open", IDC_FILE_OPEN
MENUITEM "Save", IDC_FILE_SAVE
MENUITEM "Close", IDC_FILE_CLOSE
END
POPUP "Edit"
BEGIN
MENUITEM "Cut", IDC_EDIT_CUT
MENUITEM "Copy", IDC_EDIT_COPY
MENUITEM "Paste", IDC_EDIT_PASTE
END
POPUP "View"
BEGIN
POPUP "Toolbars"
BEGIN
"Standard", IDC_VIEW_STANDARD
"Custom", IDC_VIEW_CUSTOM
END
END
MENUITEM "Help", IDC_HELP
END
When we click on one of these entries in our window, the message loop will receive a WM_COMMAND message, with the identifier in the WPARAM parameter.
We will define all our identifiers in a header file to be numerical values in an arbitrary range that does not overlap with the command identifiers of our other input sources (accelerator tables, push-buttons, etc):
//resource.h
#define IDC_FILE_OPEN 200
#define IDC_FILE_SAVE 201
#define IDC_FILE_CLOSE 202
#define IDC_EDIT_COPY 203
#define IDC_EDIT_CUT 204
#define IDC_EDIT_PASTE 205
#define IDC_VIEW_STANDARD 206
#define IDC_VIEW_CUSTOM 207
#define IDC_HELP 208
And we will then include this resource header both into our main program code file, and our resource script. When we want to load a menu into our program, we need to create a handle to a menu, or an HMENU. HMENU data items are identical in size and shape to other handle types, except they are used specifically for pointing to menus.
When we start our program, usually in the WinMain function, we will obtain a handle to this menu using an HMENU data item, with the LoadMenu function:
HMENU hmenu;
hmenu = LoadMenu(hInst, MAKEINTRESOURCE(IDM_MY_MENU));
We will discuss how to use this handle to make the menu appear in another section, below.
Menu Bar: From API Calls
Menu Bar: Loading a Menu
To load associate a menu with a window class, we need to include the name of the menu into the WNDCLASS structure. Remember the WNDCLASS structure:
typedef struct {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;
It has a data field called "lpszMenuName". This is where we will include the ID of our menu:
WNDCLASS wnd;
wnd.lpszMenuName = MAKEINTRESOURCE(IDM_MY_MENU);
Remember, we need to use the MAKEINTRESOURCE keyword to convert the numerical identifier (IDM_MY_MENU) into an appropriate string pointer.
Next, after we have associated the menu with the window class, we need to obtain our handle to the menu:
HMENU hmenu;
hmenu = LoadMenu(hInst, MAKEINTRESOURCE(IDM_MY_MENU));
And once we have the HMENU handle to the menu, we can supply it to our CreateWindow function, so that the menu is created when the window is created:
HWND CreateWindow(
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
We pass our HMENU handle to the hMenu parameter of the CreateWindow function call. Here is a simple example:
HWND hwnd;
hwnd = CreateWindow(szClassName, "Menu Test Window!",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, hMenu,
hInstance, 0);
As a quick refresher, notice that we are using default values for all the position and size attributes. We are defining the new window to be a WS_OVERLAPPEDWINDOW, which is a common, ordinary window type. Also the title bar of the window will say "Menu Test Window!". We also need to pass in the HINSTANCE parameter as well, which is the second-to-last parameter.
Right-Click Menus
Dialog Boxes
People are familiar with dialog boxes. They are the grey windows that pop up on Windows systems to display messages, and allow the user to set parameters. There are 3 types of dialog boxes: modeless, modal, and system modal.
Modal
Modal dialog boxes are generally used inside a program, to display messages, and to set program parameters. Modal dialog boxes come to the front of the screen, and you may not use the program while the modal dialog box is open. to continue using the program, the modal dialog box must be closed.
System Modal
System modal dialog boxes are like modal boxes, except that they superceed the entire desktop area. When a system modal dialog box is open, nothing else on the screen can be clicked or selected.
Modeless
Modeless dialog boxes are able to be deselected, and control can be taken away from a modeless dialog box and transferred to some other window. Modeless dialog boxes are frequently used as a fast and easy way to create a window, without having to register a window class. Modeless dialog boxes are common in the Windows control panel.
MessageBox
The most simple type of dialog box is the MessageBox function. The MessageBox function takes 4 parameters: a handle to a parent, a message, a title, and an option. If the parent handle is NULL, the message box is modeless. If you provide a handle for a parent window, the MessageBox can become Modal to the parent window.
MessageBox dialog boxes have a number of different options that can be specified: Button types, Icons, modality (modal/modeless), and text justification. These options are specified as bit flags, that can be bitwise ORing them together.
Buttons
Message boxes can have standard OK or Cancel buttons, or they can have a "Yes, No, Cancel" configuration, or a number of derivatives. Only one primary button scheme can be used per message box:
MB_ABORTRETRYIGNORE
The message box contains three push buttons: Abort, Retry, and Ignore.
MB_CANCELTRYCONTINUE
Same as MB_ABORTRETRYIGNORE, but preferred on Windows 2000/XP.
MB_OK
The message box contains an "OK" button. This is the default.
MB_OKCANCEL
The message box contains two push buttons: OK and Cancel.
MB_RETRYCANCEL
The message box contains two push buttons: Retry and Cancel.
MB_YESNO
The message box contains two push buttons: Yes and No.
MB_YESNOCANCEL
The message box contains three push buttons: Yes, No, and Cancel. To display an icon in the message box, specify one of the following values.
In addition, a message box can add an additional "Help" button by specifying the "MB_HELP" flag.
A "Default Button", a concept that we will see frequently in this chapter, is the button that is automatically selected when a dialog box is opened. Windows provides the ability to set the default button to any of the buttons on a message box, by using the MB_DEFBUTTONx macro. Here is an example:
MessageBox(NULL, "This is a Test", "Test", MB_OKCANCEL|MB_HELP|MB_DEFBUTTON2);
This will have a message box with an "OK" a "Cancel" and a "Help" button, and the "Cancel" button will be automatically selected.
Icons
A message box may have no icons, or it may have one. You shouldn't specify a message box to have multiple icons. The different icons, according to MSDN are:
MB_ICONEXCLAMATION
An exclamation-point icon appears in the message box.
MB_ICONWARNING
An exclamation-point icon appears in the message box.
MB_ICONINFORMATION
An icon consisting of a lowercase letter i in a circle appears in the message box.
MB_ICONASTERISK
An icon consisting of a lowercase letter i in a circle appears in the message box.
MB_ICONQUESTION
A question-mark icon appears in the message box. The question-mark message icon is no longer
recommended because it does not clearly represent a specific type of message and because the
phrasing of a message as a question could apply to any message type. In addition, users can
confuse the message symbol question mark with Help information. Therefore, do not use this
question mark message symbol in your message boxes. The system continues to support its
inclusion only for backward compatibility.
MB_ICONSTOP
A stop-sign icon appears in the message box.
MB_ICONERROR
A stop-sign icon appears in the message box.
MB_ICONHAND
A stop-sign icon appears in the message box.
Modality
Finally, the MessageBox can be defined as being Modal, Modeless, or System Modal, by using another identifier: MB_APPLMODAL, MB_SYSTEMMODAL, or MB_TASKMODAL. MB_APPLMODAL is the default value, and only works if a parent window handle was specified to the function. There are a number of other options available, check out MSDN for more details.
Dialog Box Procedures
Dialog box procedures are slightly different from window procedures. Specifically, they return BOOL values, instead of LRESULT values. Also, dialog boxes do not have a default message processing function, because messages don't always need to be handled. Specifically, Windows manages dialog boxes, and Windows will handle the unused messages. If a dialog box processes a certain message, it should return TRUE. If the message is not processed, the function should return FALSE. Also, Dialog boxes do not get a WM_CREATE message, but instead get a WM_INITDIALOG message. Also, when a dialog box is finished it's business, it should call the EndDialog function.
Here is an example of a skeleton dialog box function:
BOOL CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
return TRUE;
case WM_DESTROY:
EndDialog(0);
return TRUE;
}
return FALSE;
}
Creating Modal Dialog Boxes
Once a dialog box procedure has been defined, a dialog box can be created by calling either the DialogBox or DialogBoxParam function. These functions return an NRESULT value, that is the integer number passed to the EndDialog function in the dialog box procedure.
The DialogBox function will not return until the dialog box is closed. This means, essentially, that the program is frozen in time until we close the dialog box. The DialogBox function requires 2 handles: the module instance handle and the handle of the parent window. Also, the DialogBox function requires that a string be passed naming the resource where the dialog box is defined. The last argument to DialogBox is a pointer to the dialog box procedure function, that you have already defined.
To pass a parameter to a dialog box, the function DialogBoxParam can be used. DialogBoxParam has all the same parameters as the regular version, except it takes a fifth argument as a 32-bit pointer. This 32 bit value will be passed as the LPARAM element of the WM_INITDIALOG message.
Indirect Dialog Boxes
DialogBox and DialogBoxParam both require that the dialog box be defined in a resource. However, if you want to make the dialog box on the fly, you can use the DialogBoxIndirect or the DialogBoxIndirectParam functions. When defining a dialog box indirectly, we need to fill out a DLGTEMPLATE structure, and pass a pointer to that structure to the function, in place of a resource identifier. The DLGTEMPLATE contains fields for determining some of the characteristics of the dialog box, such as the dimensions and screen location.
The DLGITEMTEMPLATE structure is used to define individual dialog box items. For more information on this subject, search MSDN.
Creating Modeless Dialog Boxes
Modeless dialog boxes are a breed of a different color, and are more like windows than dialog boxes. First, we need to modify the message loop, to ensure that dialog box messages are routed correctly:
while(GetMessage(&msg, NULL, 0, 0))
{
if(!IsDialogMessage(hDlg, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Now, there are 2 ways we can define a message box in a resource script, with a class or without. We will discuss each in turn.
Without Class
We can define a dialog box in a resource script with the DIALOG keyword. The resource will have an ID associated with it (either a number or a string), and this ID can be passed directly to the CreateDialog function.
With Class
If we want to define a modeless dialog box in terms of a window class, we can use a few additions to make the job easier. First, we create a WNDCLASS structure with the information about our dialog box. However, there is one difference, in that we must set the cbWndExtra field to the value DLGWINDOWEXTRA value:
wnd.cbWndExtra = DLGWINDOWEXTRA;
Then, we register the class like normal. Since we are registering our window classes like normal windows, it shouldnt come as a surprise that Modeless dialog boxes use a regular window procedure, and not a dialog box procedure. Now, Windows identifies classes by name, so we should remember the name of our class. Let's say we named our class "MyDlgClass". We could create a dialog box resource as such:
MYDLGCLASS DIALOG DISCARDABLE 100, 100, 200, 200
TITLE "My Dialog Box"
CLASS "MyDlgClass"
FONT "MS Sans Serif"
BEGIN
...
END
notice the field that says "CLASS"? this is the same string that we used in our WNDCLASS structure to name the class. It is important that these two strings be identical, because windows needs this string to link the WNDCLASS and the dialog box resource together. Notice also that we used the string "MYDLGCLASS" to identify the dialog resource. This isn't mandatory, but it does make things convenient later on.
Now, instead of calling CreateWindow, we will call the easier-to-use function CreateDialog. We do not use the DialogBox function, because CreateDialog returns immediately, and doesnt halt program execution.
Here is an example:
HWND hDlg;
hDlg = CreateDialog(hInst, "MyDlgClass", hwndParent, MyDlgProc);
Here, we are saying that "hInst" is the instance handle of the application, and "hwndParent" is the handle to the parent window of our dialog box. If the hwndParent parameter is NULL, the dialog box won't have a parent. When the modeless dialog box is finished, it calls "DestroyWindow", not "EndDialog", like a modal dialog box would.
Common Controls Dialog Boxes
The Common Controls is a library of functions that automatically produce some of the most common dialog boxes in Windows. This is an effort to make some amount of continuity between different programs, so that each different program doesn't create it's own proprietary "File Open" dialog, for instance.
Each Common Control generally has a single function, that takes a pointer to a structure. This structure is defined specifically for each different control. The common controls can be added to a project by including the header file, and linking to the comctls32.dll library.
Some of the common controls available through this library are the "Choose Font" dialog box, the "File open" and "File save" boxes, and the "Color Palette" dialog box.
ChooseColor
The ChooseColor function brings up the color palette window, and returns a 32-bit color value to your program.
BOOL ChooseColor(LPCHOOSECOLOR lpcc);
ChooseColor takes a single argument, in the form of a pointer to a CHOOSECOLOR structure. This structure is initialized with various values, and when the function returns, the CHOOSECOLOR structure contains the color value code.
GetOpenFileName and GetSaveFileName
These two functions bring up the familiar file open and file save dialog boxes that are found in nearly every Windows application.
BOOL GetOpenFileName(LPOPENFILENAME lpofn);
BOOL GetSaveFileName(LPOPENFILENAME lpofn);
both of these functions take a pointer to an OPENFILENAME structure. This structure controls such things as the file extensions that may be loaded, and the starting path to look in. When the function returns, the structure will contain the name of the file selected. Once your program has this information, you can use the File I/O API to access the file.
ChooseFont
The ChooseFont function brings up a familiar dialog box that allows the user to select a font and various font attributes such as size, underline/bold/italics, color, etc. This function takes a pointer to a CHOOSEFONT structure.
BOOL ChooseFont(LPCHOOSEFONT lpcf);
Dialog Box Resources
Dialog boxes can be specified in a resource script, to handle the tricky task of creating all the various child windows (buttons and editboxes, etc) that a dialog box may contain. This process is described in detail in the Resource Script Reference in the appendix. Here, we will discuss some of the basics of using a resource script to define a dialog box.
The DIALOG keyword
A dialog box resource is specified with the DIALOG (must be all caps) keyword. The DIALOG keyword is preceeded by the resource identifier, and is followed by a series of dimension values:
ID_DLGBOX DIALOG X, Y, CX, CY
X and Y are the location coordinates of the upper-left corner of the dialog box, in relation to the upper-left corner of the screen. Remember, all the coordinates start at (0,0) in the upper-left hand corner. The next set of numbers, CX and CY, are the dimensions of the dialog box. These dimensions do not include the title bar (if any), so setting your Y value to 0 will make a dialog box that is only a title bar.
if you have any dobut about win32
please send me mails : kaspar1984@gmail.com
+---------------> X
["DialogBox" [_][O][x]]
+ | |
| | |
| | |
| | |
| | |
| | |
v | |
Y | |
| |
| |
+-----------------------+
After the DIALOG declaration, there are a number of other fields that can be filled in, to provide information about your dialog box:
ID_DLGBOX DIALOG 100, 100, 200, 150
STYLE WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE
CAPTION "Title Bar Text"
FONT 8, "MS Sans Serif"
The STYLE declaration contains all the window styles, bitwise OR'd, that you would have used in the WNDCLASS structure, or in the style field of the CreateWindow function. All the same values are available. The CAPTION is the title of the dialog box. The FONT is the point-size and the TrueType font to be used on all the surfaces of the dialog box. Any font and size can be specified, although if the font is too big, your dialog box will be very annoying.
Now, once we have our dialog box sized and shaped the way we want it, we can start to fill it with control buttons and edit boxes, and all sorts of other goodies. First, we use the BEGIN and END tags:
ID_DLGBOX DIALOG 100, 100, 200, 150
STYLE WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE
CAPTION "Title Bar Text"
FONT 8, "MS Sans Serif"
BEGIN
...
END
Next, we can start to fill in the dialog box with buttons, checkboxes, or whatever we want, using the following format:
ID_DLGBOX DIALOG 100, 100, 200, 150
STYLE WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE
CAPTION "Title Bar Text"
FONT 8, "MS Sans Serif"
BEGIN
PUSHBUTTON "OK", IDOK, 10, 10, 50, 15, WS_TABSTOP
CHECKBOX "Box 1", IDC_CB1, 10, 30, 50, 15, WS_TABSTOP
EDITTEXT "", IDC_EDIT1, 10, 50, 100, 100
END
After the declaration, you may optionally include one or more style flags, to specify how you want a particular control to appear. The WS_TABSTOP identifier specifies which controls can be selected when you press the TAB key on the keyboard. When you press the TAB key, control switches among the dialog box controls in the same order that they are specified in the resource script (top to bottom).
Input-Output
Many of the previous chapters have attempted to shed some light on the Windows graphical interface, but this chapter is going to start a detour into the inner-workings of the Windows operating system foundations. In this chapter, we are going to talk about Input and Output routines. This includes (but is not limited to) File I/O, Console I/O, and even device I/O.
File API
Files, like everything else in a windows platform, are managed by handles. When you want to read a file or write to one, you must first open a handle to that file. Once the handle is open, you may use the handle in read/write operations. In fact, this is the same with all I/O, including console I/O and device I/O: you must open a handle for reading/writing, and you must use the handle to perform your operations.
CreateFile
We will start with a function that we will see frequently in this chapter: CreateFile. CreateFile is the generic function used to open I/O handles in your system. Even though the name doesn't indicated it, CreateFile is used to open Console Handles and Device Handles as well. As the MSDN documentation says:
The CreateFile function creates or opens a file, file stream, directory,
physical disk, volume, console buffer, tape drive, communications resource,
mailslot, or named pipe. The function returns a handle that can be used to
access an object.
Now, this is a powerful function, and with the power comes a certain amount of difficulty in using the function. Needless to say, CreateFile is a little more involved than the standard C STDLIB fopen.
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
As can be guessed, the "lpFileName" parameter is the name of the file to be opened. "dwDesiredAccess" specifies the desired access permissions for the file handle. In the most basic sense, for a file, this parameter can specify a read operation, a write operation, or an execute operation. However, don't be fooled, there are many many different options that can be used here, for different applications. The most common operations are GENERIC_READ, GENERIC_WRITE, and GENERIC_EXECUTE. These can be bitwise-OR'd to have read+write access, if needed.
File handles can be optionally shared or locked. A shared file can be simultaneously opened and accessed by other processes. If a file is not shared, then other programs attempting to access the file will fail. The "dwShareMode" specifies whether or not the file can be accessed by other applications. Setting dwShareMode to zero means that the file access cannot be shared, and other applications attempting to access the file, while the file handle is open, will fail. Other common values are FILE_SHARE_READ and FILE_SHARE_WRITE which allow other programs to open read handles and write handles, respectfully.
The lpSecurityAttributes is a pointer to a SECURITY_ATTRIBUTES structure. This structure can help to secure the file against unwanted accesses. We will discuss security attributes in a later chapter. For now, you can always set this field to NULL.
The dwCreationDisposition member would be better named "dwCreateMode" or something similar. This bit flag allows you to determine how the file is to be opened, according to different flag values:
CREATE_ALWAYS
Always creates a new file. If the file exists already, it will be deleted, and overwritten. If the file does not exist, it is created.
CREATE_NEW
If the file exists, the function fails. Otherwise, creates a new file.
OPEN_ALWAYS
Opens the file, without erasing the contents, if the file exists. Creates a new file if the file does not exist.
OPEN_EXISTING
Opens the file, without erasing the contents, only if the file exists already. If the file does not exist, the function fails.
TRUNCATE_EXISTING
Opens the file, only if the file exists. When the file is opened, all the contents are deleted, and the file is set to 0 bytes long. If the file does not exist, the function fails. When opening with TRUNCATE_EXISTING, you must specify a GENERIC_WRITE flag as the access mode, or the function will fail.
The dwFileAttributes member specifies a series of flags for controlling File I/O. If the CreateFile function is being used to create something that isn't a File handle, this parameter is not used and may be set to 0. For accessing a normal file, the flag FILE_ATTRIBUTE_NORMAL should be used. However, there are also options for FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE, etc.
Finally, the hTemplateFile member can be specified if you want the new file handle to mimic the properties of an existing file handle. This can be set to NULL if not used.
ReadFile WriteFile
Once a file handle is opened, ideally we would like to interact with the specified file. We can do this most directly by using the ReadFile and WriteFile functions. Both of them take similar parameters:
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped);
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped);
In both, the hFile parameter is the handle to the file that we obtained with CreateFile. The lpOverlapped parameter is used only for a special I/O mode known as "Overlapped I/O Mode", which we will discuss later. For simple I/O, the lpOverlapped parameter can be set to NULL.
In ReadFile, the lpBuffer is a pointer to a generic buffer to receive the data. This data may not be character data, so we don't call it a LPSTR type. "nNumberofBytesToRead" is the number of bytes that should be read, and "lpNumberOfBytesRead" is the actual number of bytes that were read. If lpNumberOfBytesRead is zero, the file has no more data in it.
In WriteFile, the lpBuffer parameter points to the data that should be written into the file. Again, it isn't specifcally character data. nNumberOfBytesToWrite is the maximum number of bytes to write, and the lpNumberOfBytesWritten returns the number of bytes that were actually written to the file.
CloseHandle
When you are done with a file handle, you should close it with the CloseHandle function. CloseHandle only takes one parameter, the file handle you wish to close. If you do not close your handle, Windows will automatically close the handle when the program closes. However, it is a more expensive operation for Windows to do it for you, and can waste time on your system. It is a good idea to always explicitly close all your handles before you exit your program.
Memory-Mapped Files
seems it is already daunting..
Overlapped IO
Console API
Allocating a Console
Getting a Console Handle
High Level I/O
Low Level I/O
Colors and Features
Device IO API
Getting a Device Handle
Device IO Functions
Warnings about Device IO
Completion Ports
File Managment
File Management Functions
The following functions are used to manage files.
Function Description
AreFileApisANSI
Determines whether the file I/O functions are using the ANSI or OEM character set code page.
CheckNameLegalDOS8Dot3
Determines whether a specified name can be used to create a file on a FAT file system.
CloseHandle
Closes an open object handle.
CopyFile
Copies an existing file to a new file.
CopyFileEx
Copies an existing file to a new file, and notifies an application of the progress through a callback function.
CopyFileTransacted
Copies an existing file to a new file as a transacted operation, notifying the application of its progress through a callback function.
CreateFile
Creates or opens a file, directory, physical disk, volume, console buffer, tape drive, communications resource, mailslot, or named pipe.
CreateFileTransacted
Creates or opens a file, file stream, directory, physical disk, volume, console buffer, tape drive, communications resource, mailslot, or named pipe as a transacted operation.
CreateHardLink
Establishes a hard link between an existing file and a new file.
CreateHardLinkTransacted
Establishes a hard link between an existing file and a new file as a transacted operation.
CreateSymbolicLink
Creates a symbolic link.
CreateSymbolicLinkTransacted
Creates a symbolic link as a transacted operation.
DeleteFile
Deletes an existing file.
DeleteFileTransacted
Deletes an existing file as a transacted operation.
FindClose
Closes a file search handle that the FindFirstFile, FindFirstFileEx, or FindFirstStreamW function opens.
FindFirstFile
Searches a directory for a file or subdirectory name that matches a specified name.
FindFirstFileEx
Searches a directory for a file or subdirectory name and attributes that match those that are specified.
FindFirstFileNameTransactedW
Creates an enumeration of all the hard links to the specified file as a transacted operation. The function returns a handle to the enumeration that can be used on subsequent calls to the FindNextFileNameW function.
FindFirstFileNameW
Creates an enumeration of all the hard links to the specified file. The FindFirstFileNameW function returns a handle to the enumeration that can be used on subsequent calls to the FindNextFileNameW function.
FindFirstFileTransacted
Searches a directory for a file or subdirectory with a name that matches a specific name as a transacted operation.
FindFirstStreamTransactedW
Enumerates the first stream in the specified file or directory as a transacted operation.
FindFirstStreamW
Enumerates the first stream in a specified file or directory.
FindNextFile
Continues a file search.
FindNextStreamW
Continues a stream search.
GetBinaryType
Determines whether a file is executable. If the file is executable, this function determines the type of executable file.
GetCompressedFileSize
Retrieves the actual number of disk storage bytes that are used to store a specified file.
GetCompressedFileSizeTransacted
Retrieves the actual number of bytes of disk storage used to store a specified file as a transacted operation.
GetFileAttributes
Retrieves a set of FAT file system attributes for a specified file or directory.
GetFileAttributesEx
Retrieves attributes for a specified file or directory.
GetFileAttributesTransacted
Retrieves a set of FAT file system attributes for a specified file or directory as a transacted operation.
GetFileBandwidthReservation
Retrieves the bandwidth reservation properties of the volume on which the specified file resides.
GetFileInformationByHandle
Retrieves file information for a specified file.
GetFileInformationByHandleEx
Retrieves file information for the specified file.
GetFileSize
Retrieves the size of a specified file. The file size that can be reported by this function is limited to a DWORD value.
GetFileSizeEx
Retrieves the size of a specified file.
GetFileType
Retrieves the file type of a specified file.
GetFinalPathNameByHandle
Retrieves the final filesystem path for the specified file.
GetFullPathName
Retrieves the full path and file name of a specified file.
GetFullPathNameTransacted
Retrieves the full path and file name of a specified file as a transacted operation.
GetLongPathName
Converts a specified path to its long form.
GetLongPathNameTransacted
Converts the specified path to its long form as a transacted operation.
GetShortPathName
Retrieves the short path form of a specified path.
GetTempFileName
Creates a name for a temporary file.
GetTempPath
Retrieves the path of the directory that is designated for temporary files.
MoveFile
Moves an existing file or directory and its children.
MoveFileEx
Moves an existing file or directory.
MoveFileTransacted
Moves an existing file or a directory, including its children, as a transacted operation.
MoveFileWithProgress
Moves a file or directory. You can provide a callback function that receives progress notifications.
OpenFile
Creates, opens, reopens, or deletes a file.
OpenFileById
Opens the file that matches the specified identifier.
ReOpenFile
Reopens a specified file system object with different access rights, a different sharing mode, and different flags than it was previously opened with.
ReplaceFile
Replaces one file with a different file, and optionally creates a backup copy of the original file.
RtlIsNameLegalDOS8Dot3
Determines whether or not a specified name can be used to create a file on the FAT file system.
SearchPath
Searches for a specified file in a specified path.
SetFileApisToANSI
Indicates that the file I/O functions must use the ANSI character set code page.
SetFileApisToOEM
Causes the file I/O functions to use the OEM character set code page.
SetFileAttributes
Sets the attributes of a file.
SetFileAttributesTransacted
Sets the attributes for a file or directory as a transacted operation.
SetFileBandwidthReservation
Requests that bandwidth for the specified file stream be reserved.
SetFileInformationByHandle
Sets the information for the specified file.
SetFileShortName
Sets the short name for a specified file.
SetFileValidData
Sets the valid data length of a specified file.
The following functions are used with file I/O.
Function Description
CancelIo
Cancels all pending I/O operations that are issued by the calling thread for a specified file handle.
CancelIoEx
Marks all pending I/O operations for the specified file handle in the current process as canceled, regardless of which thread created the I/O operation.
CancelSynchronousIo
Marks pending synchronous I/O operations that are issued by the specified thread as canceled.
CreateIoCompletionPort
Associates an I/O completion port with one or more file handles, or creates an I/O completion port that is not associated with a file handle.
FlushFileBuffers
Flushes the buffers for a specified file, and causes all buffered data to be written to the file.
GetQueuedCompletionStatus
Attempts to dequeue an I/O completion packet from a specified I/O completion port.
GetQueuedCompletionStatusEx
Retrieves multiple completion port entries simultaneously.
LockFile
Locks a specified file for exclusive access by the calling process.
LockFileEx
Locks a specified file for exclusive access by the calling process. This function can operate synchronously or asynchronously.
PostQueuedCompletionStatus
Posts an I/O completion packet to an I/O completion port.
ReadFile
Reads data from a file, starting at the position that is indicated by a file pointer. This function can operate synchronously and asynchronously.
ReadFileEx
Reads data from a file asynchronously.
ReadFileScatter
Reads data from a file and stores it in an array of buffers.
SetEndOfFile
Moves the end-of-file position for a specified file to the current position of a file pointer.
SetFileCompletionNotificationModes
Sets the notification modes for a file handle.
SetFileIoOverlappedRange
Associates a virtual address range with a file handle.
SetFilePointer
Moves the file pointer of an open file.
SetFilePointerEx
Moves the file pointer of a specified file.
UnlockFile
Unlocks a region in an open file.
UnlockFileEx
Unlocks a region in an open file. This function can operate synchronously or asynchronously.
WriteFile
Writes data to a file at a position that a file pointer specifies. This function can operate synchronously and asynchronously.
WriteFileEx
Writes data to a file. This function reports the completion status asynchronously by calling a specified completion routine when writing is completed or canceled and when the calling thread is in an alertable wait state.
WriteFileGather
Retrieves data from an array of buffers, and then writes the data to a file.
The following functions are used with the encrypted file system.
Function Description
AddUsersToEncryptedFile
Adds user keys to a specified encrypted file.
CloseEncryptedFileRaw
Closes an encrypted file after a backup or restore operation, and frees the associated system resources.
DecryptFile
Decrypts an encrypted file or directory.
DuplicateEncryptionInfoFile
Copies the EFS metadata from one file or directory to another.
EncryptFile
Encrypts a file or directory.
EncryptionDisable
Disables or enables encryption of a specified directory and the files in the directory.
FileEncryptionStatus
Retrieves the encryption status of a specified file.
FreeEncryptionCertificateHashList
Frees a certificate hash list.
OpenEncryptedFileRaw
Opens an encrypted file to backup (export) or restore (import) the file.
QueryRecoveryAgentsOnEncryptedFile
Retrieves a list of recovery agents for a specified file.
QueryUsersOnEncryptedFile
Retrieves a list of users for a specified file.
ReadEncryptedFileRaw
Backs up (exports) encrypted files.
RemoveUsersFromEncryptedFile
Removes specified certificate hashes from a specified file.
SetUserFileEncryptionKey
Sets a current user key to a specified certificate.
WriteEncryptedFileRaw
Restores (imports) encrypted files.
The following functions are used with the file system redirector.
Function Description
Wow64DisableWow64FsRedirection
Disables file system redirection for the calling thread.
Wow64EnableWow64FsRedirection
Enables or disables file system redirection for the calling thread.
Wow64RevertWow64FsRedirection
Restores file system redirection for the calling thread.
The following functions are used to decompress files that are compressed by the Lempel-Ziv algorithm.
Function Description
GetExpandedName
Retrieves the original name of a compressed file, only if the file is compressed by the Lempel-Ziv algorithm.
LZClose
Closes a file that was opened by using the LZOpenFile function.
LZCopy
Copies a source file to a destination file.
LZInit
Allocates memory for the internal data structures that are required to decompress files, and then creates and initializes the files.
LZOpenFile
Creates, opens, reopens, or deletes a specified file.
LZRead
Reads a specified number of bytes from a file and copies them into a buffer.
LZSeek
Moves a file pointer a specified number of bytes from a starting position.
The following callback functions are used in file I/O.
Function Description
CopyProgressRoutine
Callback function used with the CopyFileEx and MoveFileWithProgress functions, called when a portion of a copy or move operation is completed.
ExportCallback
Callback function used with ReadEncryptedFileRaw, called one or more times, each time with a block of the encrypted file's data, until it has received all of the file data.
FileIOCompletionRoutine
Callback function used with the ReadFileEx and WriteFileEx functions, called when the asynchronous input and output (I/O) operation is completed or canceled.
ImportCallback
Callback function used with WriteEncryptedFileRaw, called one or more times, each time to retrieve a portion of a backup file's data.
Memory Subsystem
From Wikibooks, the open-content textbooks collection
C programmers will undoubtably be familiar with the stdlib memory allocation functions, malloc, realloc, calloc, etc. These functions are based off a number of other functions in the Win32 API that deal with memory segments.
Windows Memory
When talking about the memory subsystem, there are 4 distinct types of memory that Windows manages, and each type of memory has a number of different functions to allocated and free that memory.
Virtual Memory
The Virtual Memory subsystem allocates and manages memory by pages. This means that memory can only be allocated in blocks of 4096Kbytes at a time. This is a fantastically large amount of memory for most applications, and using the virtual memory functions in most programs is overkill. However, some programs do need use of entire pages for storage, so the Virtual Memory subsystem can be used for that.
Heap Memory
The heap is an area of memory that usually takes up a whole page, or a fraction of a page. Each process is allocated a heap immediately by windows, called the process heap. The stdlib functions such as malloc will allocate memory from this memory region. The heap can be divided up into small memory segments, for use with variables and dynamic storage.
Global Memory
Windows maintains at least 1 page of memory for use as general-purpose global memory. This memory can be read or written by any process running on the computer, using the global memory functions. Data in the global memory space can be shared among various programs, and items sent to the windows clipboard are generally stored in global memory (so it can be "pasted" into any program). Global memory is limited, so it should not be used without a specific need.
LocalMemory
Local memory has similarities to both global memory and heap memory. It is local to the process, but the memory is managed by the Windows memory manager, and not by the program. We will discuss this later.
Virtual Memory Subsystem
The virtual memory functions, as explained above, allocate memory in terms of pages. Pages are generally 4096Kbytes of memory, so most applications won't need to allocate an entire page (much less more then 1 page). The Virtual memory system is essentially the primitive function base that the other memory functions utilize to perform their tasks. For instance, the heap is comprised of 1 or more pages, and the heap functions will allocate pages using the virtual memory system when needed.
When virtual memory blocks are allocated, they are not actually being utilized, they are simply reserved by the system for future use. Other functions need to be used to segment the virtual memory pages into useful segments. Since virtual memory is allocated by pages, a number of special paging features can be used on virtual memory that can not be used on other types of memory. For instance, pages can be locked (to prevent read/write access), or they can be protected from any particular access mode (read, write, execute).
That said, there are a number of functions in the virtual memory subsystem that can be used:
VirtualAlloc
VirtualFree
VirtualProtect
VirtualLock
VirtualQuery
Heap Memory
Each program is provided with a default process heap, but a process may optionally allocate any number of additional heaps, if more storage is needed. The heap functions will manage their virtual memory usage automatically, and therefore heaps can be set to grow if they are being filled up with data. If a heap is allowed to grow automatically, the heap functions will automatically allocate additional pages as needed. On the x86 architecture the heap grows in size towards higher memory addresses.
To use heap memory, a heap must first be allocated (or a handle must be obtained to the default heap). Once you have obtained a handle to a heap, you can pass that handle to the memory allocation functions, to allocate memory from that particular heap.
The stdlib memory functions (malloc, realloc, calloc, free) are all used very similarly to the heap functions, so programmers familiar with the stdlib functions may be able to figure out what many of the heap functions are doing, by examining their names:
HeapCreate
HeapDestroy
GetProcessHeap
HeapAlloc
HeapReAlloc
HeapFree
Global Memory
When data has been written to the global memory, you don't get a pointer to that data, but instead you get a handle for that data. Once you give your data to the global memory manager, the system is in charge of it. Remember, a handle is not a pointer, and should never be used as one. The system will manage the memory in the global memory section, moving it between pages, and defragmenting it, et cetera.
GlobalAlloc
GlobalFree
GlobalDiscard
GlobalLock
GlobalUnlock
GlobalFlags
GlobalSize
GlobalHandle
Local Memory
Local memory, in this sense, is not the kind of storage that programs utilize internally, on the stack and otherwise. Instead, Windows manages a special section of memory that it dubs to be "Local Memory", and it provides a number of functions to allocate and manage this special memory. Local memory is similar to global memory in the sense that data is written to the local location, and the system returns a handle to that data. The system will manage the data, just like in global memory. The local functions are named very similarly to the global memory functions. However, the global memory and local memory functions should never be mixed. For instance, a global memory handle should never be closed with the LocalFree function.
LocalAlloc
LocalFree
LocalDiscard
LocalFlags
LocalLock
LocalReAlloc
LocalUnlock
LocalHandle
Multitasking
Current versions of Windows are multitasking operating systems. In this chapter, we will discuss some of the tools and API functions that are involved in multitasking, threading, and synchronization for use in Windows.
Processes and Threads
First, let's explain a little bit of terminology. A process is a single program, with a single entry point, and a single exit point. A Thread, on the other hand is only a part of a process. A process has at least 1 thread, and some processes have more then 1 thread. Threads and processes, when created, run automatically, and are alloted time slices of execution time by the scheduler in a round-robin fashion. The operating system may activate and deactivate any thread or process at any time. For this reason, we will need to control access to program resources such as global memory and output devices.
If the volatility of threads is disconcerting, Windows also provides an execution object known as a fiber that only runs when activated by the parent thread.
If more than one process are working together, the resulting grouping is known as a job. Jobs can also be managed by Windows.
Managing Processes
• CreateProcess, etc
Creating Threads
• CreateThread
• beginthread
MDI Programs
Multiple Document Interface (MDI) applications are a very common and popular type of application. MDI applications allow a single program window to contain multiple open workspaces simultaneously. Creating them isn't particularly tricky, and this chapter will attempt to explain the process in detail.
Steps to Making an MDI
These are the steps to making an MDI application. We will explain each of them.
1. Register the Frame and Child window classes.
2. Modify the message loop.
3. Create the frame window.
4. Create the MDI Client
5. Create MDI child windows with the client.
Register the Window Classes
Like any other windows application, we need to register the window classes. The main window (the frame, or the "parent" window) needs to be created. Frame windows are just like any other window, but they generally have a background color of COLOR_APPWORKSPACE. Also, the child windows in an MDI application are not allowed to have their own menus, so the Frame window must manage the application menu. Remember, that different child windows usually require different menu choices (or different menus all together), so the Frame window needs to maintain a current record of the active child window, and set the menu accordingly.
The child window class (all of them) needs to be created with WS_CHILD class. An MDI application may have any number of different types of child windows (a famous example is the use of spreadsheets and charts in Microsoft Excel), and they all need to be registered before use.
Modify the Message Loop
MDI applications use an almost-normal message loop, but if you want to use accelerators in your program, you need to add a new step:
while(GetMessage(&msg, hwndFrame, 0, 0))
{
if(!TranslateMDISysAccel(hwndClient, &msg) &&
!TranslateAccelerator(hwndFrame, hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
This way the accelerators get routed to the correct destination among all the different child windows. If you are not using accelerators, you don't need to worry about this step.
The Frame Window
Frame Windows get created like any other windows. Remember, however, that frame windows are just a backdrop for the action that is happening in the child windows, so you shouldn't get to crazy or fancy with your background artwork.
The Client Window
You need to create a window of type "MDICLIENT". MDICLIENT windows are defined internally in Windows, so you don't need to worry about what it is or what it does. When you create an MDI client window, you first need to fill out the fields in the CLIENTCREATESTRUCT structure. You pass a pointer to this structure as the LPARAM data field in the CreateWindow function.
MDI Child Windows
Creating MDI child windows is a little bit different from creating normal windows. To create an MDI child window, you must fill out the MDICREATESTRUCT data structure. The MDICREATESTRUCT is very similar to the WNDCLASS structure, except it is more limited. After creating this structure, you must send it as a message to the MDICLIENT window:
hwndChild = (HWND)SendMessage(hwndClient, WM_MDICREATE, 0, (LPARAM)mdicreatestruct);
The message function will return a handle to the newly created child window.
The "Window" Menu
Many MDI applications will offer a "Window" popup menu on the menu bar, to manage the MDI child windows. This menu frequently has options to "Tile" or "Cascade" the child windows, and it frequently maintains a listing of the currently available child windows in the application. It turns out that the MDI Client window will manage this menu for you, if you just pass it the correct handle to the menu.
Winsock
Winsock is the name of the library in Windows that handles the Berkely Socket API. Technically, this library is not part of the Win32 API, although there are some windows-specific issues that need to be considered when programming a Winsock application.
Making a Winsock Project
You can add Winsock to your programming project by including the header file. This header file is for the 32-bit version of the library. For the 16-bit version, include the file .
winsock.dll is the 16-bit version of the library, and ws2_32.dll is the 32-bit version. You must instruct your linker to link to the appropriate version of the library.
Initializing Winsock
Before calling any of the Winsock routines, the library must first be initialized by calling the WSAStartup function. This function requires a pointer to the WSADATA structure. You do not need to initialize this structure, because the call to WSAStartup will fill in all the fields of the structure. You may, optionally, read the values from this structure, and use the results in your program. This is not necessary, however. WSAStartup also requires that you specify the version of winsock that you wish to use. The most current version of winsock is version 1.1, although the newest version of the library is version 2.0. To specify this parameter, pass the major and minor versions to the MAKEWORD macro. Here is an example:
WSADATA wd;
WSAStartup(MAKEWORD(2, 0), &wd);
Here is the definition of the WSADATA structure. From this data structure, you can determine some important system metrics, including the version of your library, the maximum number of simultaneous sockets available, etc.
typedef struct WSAData {
WORD wVersion;
WORD wHighVersion;
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYS_STATUS_LEN+1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR* lpVendorInfo;
} WSADATA, *LPWSADATA;
The definition of WSAStartup is as follows:
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
This function returns zero on success, and will return non-zero on failure. These error codes can be handled, or the program can abort.
Exiting Winsock
After a program has completed, it must call WSACleanup, to unregister itself from the listing, and to free any resources in use by the library. WSACleanup takes no parameters, and returns zero on success. A non-zero return value signifies an error in the cleanup process.
Sockets as Handles
It has been said that unlike UNIX, Win32 does not allow sockets to be read/written using the file I/O functions. This is only partially true. Sockets may not be accessed using the standard-library functions such as fread, fwrite, fprintf, etc. However, if we cast our SOCKET structures to HANDLE structures, we can use the Win32 File I/O API to interface with the sockets. For instance, we can now use ReadFile and WriteFile to write to sockets, and any routines that we have written around these APIs can be used when writing to the network.
Under Win32, do not attempt to cast a SOCKET to a FILE type, and use the stdio.h file functions. This will result in some sort of error (most likely a bad error).
Advanced Win32 Sockets
Win32 has a full compliment of socket functions, including bind, accept, socket, listen and recv. However, Win32 also provides a number of advanced function varieties that allow for advanced operation modes. For instance, using the advanced socket functions allow Overlapped I/O mode, asynchronous modes, events, etc. These functions can be explored on MSDN.
Window Classes
This topic describes the types of window classes, how the system locates them, and the elements that define the default behavior of windows that belong to them.
A window class is a set of attributes that the system uses as a template to create a window. Every window is a member of a window class. All window classes are process specific.
Overviews
About Window Classes
Each window class has an associated window procedure shared by all windows of the same class. The window procedure processes messages for all windows of that class and therefore controls their behavior and appearance.
Using Window Classes
This topic has a code example that shows how to register a local window and use it to create a main window.
Functions
GetClassInfo
The GetClassInfo function retrieves information about a window class.
Note The GetClassInfo function has been superseded by the GetClassInfoEx function. You can still use GetClassInfo, however, if you do not need information about the class small icon.
GetClassInfoEx
The GetClassInfoEx function retrieves information about a window class, including a handle to the small icon associated with the window class. The GetClassInfo function does not retrieve a handle to the small icon.
GetClassLong
The GetClassLong function retrieves the specified 32-bit (long) value from the WNDCLASSEX structure associated with the specified window.
GetClassLongPtr
The GetClassLongPtr function retrieves the specified value from the WNDCLASSEX structure associated with the specified window.
If you are retrieving a pointer or a handle, this function supersedes the GetClassLong function. (Pointers and handles are 32 bits on 32-bit Microsoft Windows and 64 bits on 64-bit Windows.) To write code that is compatible with both 32-bit and 64-bit versions of Windows, use GetClassLongPtr.
GetClassName
The GetClassName function retrieves the name of the class to which the specified window belongs.
GetClassWord
The GetClassWord function retrieves the 16-bit (WORD) value at the specified offset into the extra class memory for the window class to which the specified window belongs.
Note This function is provided only for compatibility with 16-bit versions of Windows. Applications should use the GetClassLong function.
GetWindowLong
The GetWindowLong function retrieves information about the specified window. The function also retrieves the 32-bit (long) value at the specified offset into the extra window memory.
If you are retrieving a pointer or a handle, this function has been superseded by the GetWindowLongPtr function. (Pointers and handles are 32 bits on 32-bit Windows and 64 bits on 64-bit Windows.) To write code that is compatible with both 32-bit and 64-bit versions of Windows, use GetWindowLongPtr.
GetWindowLongPtr
The GetWindowLongPtr function retrieves information about the specified window. The function also retrieves the value at a specified offset into the extra window memory.
If you are retrieving a pointer or a handle, this function supersedes the GetWindowLong function. (Pointers and handles are 32 bits on 32-bit Windows and 64 bits on 64-bit Windows.) To write code that is compatible with both 32-bit and 64-bit versions of Windows, use GetWindowLongPtr.
RegisterClass
The RegisterClass function registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
The RegisterClass function has been superseded by the RegisterClassEx function. You can still use RegisterClass, however, if you do not need to set the class small icon.
RegisterClassEx
The RegisterClassEx function registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
SetClassLong
The SetClassLong function replaces the specified 32-bit (long) value at the specified offset into the extra class memory or the WNDCLASSEX structure for the class to which the specified window belongs.
Note This function has been superseded by the SetClassLongPtr function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use SetClassLongPtr.
SetClassLongPtr
The SetClassLongPtr function replaces the specified value at the specified offset in the extra class memory or the WNDCLASSEX structure for the class to which the specified window belongs.
This function supersedes the SetClassLong function. To write code that is compatible with both 32-bit and 64-bit Windows, use SetClassLongPtr.
SetClassWord
The SetClassWord function replaces the 16-bit (WORD) value at the specified offset into the extra class memory for the window class to which the specified window belongs.
Note This function is provided only for compatibility with 16-bit versions of Windows. Applications should use the SetClassLong function.
SetWindowLong
The SetWindowLong function changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
Note This function has been superseded by the SetWindowLongPtr function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
SetWindowLongPtr
The SetWindowLongPtr function changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory.
This function supersedes the SetWindowLong function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use SetWindowLongPtr.
UnregisterClass
The UnregisterClass function unregisters a window class, freeing the memory required for the class.
Structures
WNDCLASS
The WNDCLASS structure contains the window class attributes that are registered by the RegisterClass function.
This structure has been superseded by the WNDCLASSEX structure used with the RegisterClassEx function. You can still use WNDCLASS and RegisterClass if you do not need to set the small icon associated with the window class.
WNDCLASSEX
The WNDCLASSEX structure contains window class information. It is used with the RegisterClassEx and GetClassInfoEx
WNDCLASS Structure
The WNDCLASS structure contains the window class attributes that are registered by the RegisterClass function.
This structure has been superseded by the WNDCLASSEX structure used with the RegisterClassEx function. You can still use WNDCLASS and RegisterClass if you do not need to set the small icon associated with the window class.
Syntax
typedef struct {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;
Members
style
Specifies the class style(s). This member can be any combination of the Class Styles.
lpfnWndProc
Pointer to the window procedure. You must use the CallWindowProc function to call the window procedure. For more information, see WindowProc.
cbClsExtra
Specifies the number of extra bytes to allocate following the window-class structure. The system initializes the bytes to zero.
cbWndExtra
Specifies the number of extra bytes to allocate following the window instance. The system initializes the bytes to zero. If an application uses WNDCLASS to register a dialog box created by using the CLASS directive in the resource file, it must set this member to DLGWINDOWEXTRA.
hInstance
Handle to the instance that contains the window procedure for the class.
hIcon
Handle to the class icon. This member must be a handle to an icon resource. If this member is NULL, the system provides a default icon.
hCursor
Handle to the class cursor. This member must be a handle to a cursor resource. If this member is NULL, an application must explicitly set the cursor shape whenever the mouse moves into the application's window.
hbrBackground
Handle to the class background brush. This member can be a handle to the physical brush to be used for painting the background, or it can be a color value. A color value must be one of the following standard system colors (the value 1 must be added to the chosen color). If a color value is given, you must convert it to one of the following HBRUSH types:
COLOR_ACTIVEBORDER
COLOR_ACTIVECAPTION
COLOR_APPWORKSPACE
COLOR_BACKGROUND
COLOR_BTNFACE
COLOR_BTNSHADOW
COLOR_BTNTEXT
COLOR_CAPTIONTEXT
COLOR_GRAYTEXT
COLOR_HIGHLIGHT
COLOR_HIGHLIGHTTEXT
COLOR_INACTIVEBORDER
COLOR_INACTIVECAPTION
COLOR_MENU
COLOR_MENUTEXT
COLOR_SCROLLBAR
COLOR_WINDOW
COLOR_WINDOWFRAME
COLOR_WINDOWTEXT
The system automatically deletes class background brushes when the class is unregistered by using UnregisterClass. An application should not delete these brushes.
When this member is NULL, an application must paint its own background whenever it is requested to paint in its client area. To determine whether the background must be painted, an application can either process the WM_ERASEBKGND message or test the fErase member of the PAINTSTRUCT structure filled by the BeginPaint function.
lpszMenuName
Pointer to a null-terminated character string that specifies the resource name of the class menu, as the name appears in the resource file. If you use an integer to identify the menu, use the MAKEINTRESOURCE macro. If this member is NULL, windows belonging to this class have no default menu.
lpszClassName
Pointer to a null-terminated string or is an atom. If this parameter is an atom, it must be a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be in the low-order word of lpszClassName; the high-order word must be zero.
If lpszClassName is a string, it specifies the window class name. The class name can be any name registered with RegisterClass or RegisterClassEx, or any of the predefined control-class names.
Structure Information
Header Declared in Winuser.h, include Windows.h
Minimum operating systems Windows 95, Windows NT 3.1
Unicode Implemented as ANSI and Unicode versions.
Hal.dll
Hal.dll is the core file of the Windows NT family of operating systems that provides and handles the interaction of software and hardware via the Hardware Abstraction Layer. Without hal.dll being present, any machine running a Windows NT based operating system will fail to function, if it even boots.
Windows includes several HALs to support different kinds of hardware; the appropriate HAL is chosen during the initial installation of Windows. Generally speaking, the determining factors for HAL selection are uni- vs. multi-processor CPU, ACPI vs. non-ACPI, and APIC vs. PIC.
Msvcrt.dll
Msvcrt.dll is a DLL that contains the C Run-Time Library for programs compiled with Visual C++, versions 4.2 to 6.
In newer Windows operating systems (e.g., Windows XP) this file is included as part of the operating system and should only be updated by a service pack or hotfix (although it is also used for compatibility with Visual C++ 4.2 to 6 programs). The debug version of this file is called msvcrtd.dll.
Ntdll.dll (Native API)
The Native API (with capitalized N) is the publicly mostly undocumented application programming interface used internally by the Windows NT family of operating systems produced by Microsoft. Most of them are in ntdll.dll and ntoskrnl.exe (and it's variants).
User32.dll
user32.dll is a DLL that implements the Windows User API Client Library. It is a core file for several versions of the Microsoft Windows operating system. If this file is damaged or deleted, the operating system will not work.
Device Driver Introduction
Types of Drivers
Windows device drivers generally come in 2 flavors: Virtual Device Drivers (VXD) and Windows Driver Model (WDM). VXD style drivers are older, and are less compatable, while WMD drivers are supposed to be fully code-compatable all the way back to Windows 95.
Driver History
In the old days of DOS, the computer was free land where anything goes. To that end, developers wrote their own hardware drivers, conforming to no specific specification or interface, using real-mode assembly code. With the advent of Windows 3.0, the operating system began to take a more hands-on approach to application management, by creating and maintaining a variety of virtual machines, to execute different programs in different processor contexts. Drivers could no longer exist as non-conformist real-mode DOS drivers, but instead had to mitigate access between multiple programs, running more or less in parallel with each other. Windows 3.0 changed the "real devices" into managed resources known as "virtual devices", and replaced the real-mode drivers with new virtual device drivers (VDD).
The Windows NT product line existed as a separate entity from the "regular" windows brand. These two operating systems were completely different in almost every imaginable way, except perhaps that the shells looked similar. Windows NT was a fully-managed operating system, and unauthorized resource accesses were blocked by the NT kernel. This meant that in Windows NT, device drivers needed to interface with the computer through specific methods, while standard windows drivers (Windows 3.0, 3.1, 3.11, 95, 98, Me) could access hardware directly, without any sort of management. The drivers for both systems at this point, were generally written in assembly language, as well.
Realizing that the market was split between Windows and Windows NT, Microsoft saw a need to introduce a single driver model, so that device drivers could be portable between Windows and Windows NT. In addition, Microsoft knew that drivers had to writable in a higher-level language, like C, in order to be code-compatable for different hardware systems. To meet these needs, Microsoft created the Windows Driver Model (WDM). WDM drivers are compiled using the DDK, they are written in C, and they follow exacting specifications that ensure they can be executed on any windows system. This book will attempt to focus on WDM drivers, but will include notes on writing DOS TSR drivers, and VDDs as well.
Driver Issues
Device Drivers operate in kernel mode so writing, testing, and debugging drivers can be a tricky task. Drivers should always be well tested before they are installed.
Since device drivers do not operate in user mode, the user mode libraries (kernel32.dll, user32.dll, wingdi.dll, msvcrt.dll) are not available to a device driver. Instead, a device driver must link directly to the Native API functions in ntdll.dll, and even lower-level functions in the kernel itself.
Writing a Driver
Device drivers are typically written in C, using the Driver Development Kit (DDK). There are functional and object-oriented ways to program drivers, depending on the language chosen to write in. It is generally not possible to program a driver in VB.
Because drivers operate in kernel mode, there are no restrictions on the actions that a driver may take. A driver may read and write to protected areas of memory, it may access I/O ports directly, and can generally do all sorts of very powerful things. Great power, of course, comes with great responsibility, and drivers are exceptionally capable of crashing an otherwise stable system.
The Windows platform DDK comes with header files, library files, and a command-line compiler that can be used to write device drivers in C or C++. There is no graphical interface to the DDK compiler.
Device Driver Stack
Windows implements device drivers in a highly-modular fashion, and it is important that we discuss some vocabulary before we continue the discussion of driver programming any further. The drivers necessary for any particular device are arranged in a driver stack, and are connected together internally by a singly-linked list, that starts at the bottom of the stack (the root driver), and terminates at the highest level driver. Each driver must contain at least 2 modules, a root driver, and a function driver. This combination, with some optional additions, constitute the whole of what people generally call a complete "device driver". Function Drivers will be the most common type of driver to be written, and will be of a primary focus in this wikibook.
Microsoft realized that certain classes of devices all behave similarly, and it would be a gigantic waste of time for every hardware manufacturer to have to write the entire driver code from scratch. To this end, Windows allows for a type of driver known as a class driver. Class drivers are themselves not complete function drivers, but class drivers can be dynamically linked to a regular function driver, and can simplify the development process quite a bit. It is possible to write your own class driver, but 3rd party programmers generally don't worry about it. In general, Microsoft will supply the class drivers, and driver developers will tap into those class drivers. This ensures that class drivers are fully microsoft tested and certified, and that they are very versatile.
Another classification of driver is the filter driver. There are two general types of filter driver, an upper filter driver, and a lower filter driver. Upper filter drivers exist in the stack above the function driver, and--as their name implies--they filter the incoming I/O requests. Lower filter drivers are placed in the stack between the function driver and the root driver. Filter drivers are generally implemented as bug fixes, or as quick hack extensions for preexisting drivers.
Here is a general diagram of a driver stack:
Upper filter driver
|
|
Function Driver Class Driver
|
|
Lower Filter Driver
|
|
Root driver
|
|
Hardware
Buses and Physical Devices
For simplification, let us use the term "bus" to refer to any place on your computer where information can travel from one place to another. This is a very broad definition, and rightfully so: the term "bus" needs to account for everything from USB, Serial ports, PCI cards, Video outputs, etc. Each bus is controlled by its own root driver. There is a USB root driver, a PCI root driver, and so on.
Let's now consider a mythical construct known as the root bus, a structure that all other buses connect into. A root bus object doesn't actually physically exist in your computer, but it is handy to think about it. Plus, the root bus has its own driver. The root bus driver object is responsible for keeping track of the devices connected on any bus in your entire computer, and ensuring that the data gets to where it is all going.
PnP
Plug-n-Play (PnP) is a technology that allows for the hardware on the computer to be changed dynamically, and the PnP software will automatically detect changes, and allocate important system resources. PnP get's it's own root driver, that communicates closely with the Root bus driver, to keep track of the devices in your system.
Device Namespace, and Named Devices
"Arbitrary Context"
Drivers execute in the context of whatever thread was running when windows accessed the driver. To this end, we say that drivers execute in an "arbitrary context". Therefore, it is not good practice for a driver programmer to make any assumptions about the state of the processor at the entry point to a driver. There are a few issues that arise with this, so we will discuss them here.
Floating Point Arithmetic
Drivers that want to use MMX or floating point arithmetic may find they are in for some undue difficulty. Because a driver may be entered in any context, at any time, the floating point unit may contain partial results and unhandled exceptions from the user mode program that was interrupted to call the driver. It is not enough to simply save the context and then to restore it, because any unhandled exceptions may become "unhandleable", and raise a system error or a bug check. There are only certain times when microsoft recommends using floating point arithmetic, and we will discuss them later.
Dynamic-link library
Dynamic-link library (DLL), also known as dynamic link library (without the hyphen), is Microsoft's implementation of the shared library concept in the Microsoft Windows operating systems. These libraries usually have the file extension DLL, OCX (for libraries containing ActiveX controls), or DRV (for legacy system drivers).
The file formats for DLLs are the same as for Windows EXE files — that is, Portable Executable (PE) for 32-bit Windows, and New Executable (NE) for 16-bit Windows. As with EXEs, DLLs can contain code, data, and resources, in any combination.
In the broader sense of the term, any data file with the same file format can be called a resource DLL. Examples of such DLLs include icon libraries, sometimes having the extension ICL, and font files, having the extensions FON and FOT.
Background
The original purpose for DLLs was saving both disk space and memory required for applications by storing it locally on the hard drive. In a conventional non-shared library, sections of code are simply added to the calling program. If two programs call the same routine, that code would be duplicated. Instead, any code which many applications share could be separated into a DLL which only exists as a single disk file and a single instance in memory. Extensive use of DLLs allowed early versions of Windows to work under tight memory conditions.
DLLs provide the standard benefits of shared libraries, such as modularity. Modularity allows changes to be made to code and data in a single self-contained DLL shared by several applications without any change to the applications themselves. This basic form of modularity allows for relatively compact patches and service packs for large applications, such as Microsoft Office, Microsoft Visual Studio, and even Microsoft Windows itself.
Another benefit of the modularity is the use of generic interfaces for plug-ins. A single interface may be developed which allows old as well as new modules to be integrated seamlessly at run-time into pre-existing applications, without any modification to the application itself. This concept of dynamic extensibility is taken to the extreme with ActiveX.
With this many benefits, using DLLs also has a drawback: the DLL hell, when several applications conflict on which version of a shared DLL library is to be used. Such conflicts can usually be resolved by placing the different versions of the problem DLL into the applications' folders, rather than a system-wide folder; however, this also nullifies the savings provided by using shared DLLs. Currently, Microsoft .NET is targeted as a solution to the problem of DLL hell by allowing side-by-side coexistence of different versions of a same shared library. With modern computers which have plenty of disk space and memory, it can be a reasonable approach.
Features
Memory management
In Win32, the DLL files are organized into sections. Each section has its own set of attributes, such as being writable or read-only, executable (for code) or non-executable (for data), and so on.
The code in a DLL is usually shared among all the processes that use the DLL; that is, they occupy a single place in physical memory, and do not take up space in the page file. If the physical memory occupied by a code section is to be reclaimed, its contents are discarded, and later reloaded directly from the DLL file as necessary.
In contrast to code sections, the data sections of a DLL are usually private; that is, each process using the DLL has its own copy of all the DLL's data. Optionally, data sections can be made shared, allowing inter-process communication via this shared memory area. However, because user restrictions do not apply to the use of shared DLL memory, this creates a security hole; namely, one process can corrupt the shared data, which will likely cause all other sharing processes to behave undesirably. For example, a process running under a guest account can in this way corrupt another process running under a privileged account. This is an important reason to avoid the use of shared sections in DLLs.
If a DLL is compressed by an executable packer, such as UPX, all of its code sections are marked as read-and-write, and will be unshared. Read-and-write code sections, much like private data sections, are private to each process and backed up by the page file. Thus, compressing DLLs increases both their memory and disk space consumption, and should be generally avoided for shared DLLs.
Symbol resolution and binding
Each function exported by a DLL is identified by a numeric ordinal and optionally a name. Likewise, functions can be imported from a DLL either by ordinal or by name. It is common for internal functions to be exported by ordinal only. For most Windows API functions only the names are preserved across different Windows releases; the ordinals are subject to change. So, one cannot reliably import Windows API functions by their ordinals.
Importing functions by ordinal does not necessarily provide better performance than importing them by name: export tables of DLLs are ordered by name, so binary search can be used to find a function in this table by its name. On the other hand, only linear search can be used to find a function by its ordinal.
It is also possible to bind an executable to a specific version of DLL, that is, to resolve the addresses of imported functions at compile-time. For bound imports, the linker saves the timestamp and checksum of the DLL to which the import is bound. At run-time Windows checks to see if the same version of library is being used, and if so, Windows bypasses processing the imports. Otherwise, if the library is different from the one which was bound to, Windows processes the imports in a normal way.
Bound executables load somewhat faster if they are run in the same environment that they were compiled for, and exactly the same time if they are run in a different environment, so there's no drawback for binding the imports. For example, all the standard Windows applications are bound to the system DLLs of their respective Windows release. A good opportunity to bind an application's imports to its target environment is during the application's installation.
Explicit run-time linking
DLL files may be explicitly loaded at run-time, a process referred to simply as run-time dynamic linking by Microsoft, by using the LoadLibrary (or LoadLibraryEx) API function. The GetProcAddress API function is used to lookup exported symbols by name, and FreeLibrary — to unload the DLL. These functions are analogous to dlopen, dlsym, and dlclose in the POSIX standard API.
Note that with implicit run-time linking, referred to simply as load-time dynamic linking by Microsoft, if the linked DLL file cannot be found, Windows will display an error message and fail to load the application. The application developer cannot handle the absence of DLL files linked implicitly by the compile-time linker. On the other hand, with explicit run-time linking, developers have the opportunity to provide a graceful fall-back facility.
The procedure for explicit run-time linking is the same in any language, since it depends on the Windows API rather than language constructs.
