By Matt Pietrek
While I was preparing to write this article, programmers I work with often asked me “What’s the big deal with Windows NT® 5.0?” In the trade press, articles emphasize the Windows NT 5.0 support for Active Directory, Active Desktop™, and Zero Administration Windows (ZAW). These same articles talk about making Windows NT 5.0 more practical on laptops via its Advanced Power Management (APM) and Plug and Play support. While these improvements are great news to MIS directors, they’re not things that are near and dear to the hearts of most programmers.
I’m going to show what’s new and cool in Windows NT 5.0 from a developer’s perspective. I was skeptical at first, but after digging through countless preliminary SDK header files and doing hundreds of binary file comparisons between Windows NT 4.0 and Windows NT 5.0, I’ve come up with quite a few things that make Windows NT 5.0 compelling to programmers.
Something to keep in mind is that a large amount of the new features fall under Microsoft’s ongoing Internet initiatives. These additions should (for the most part) be in Windows® 98 as well, and include things such as the Active Desktop, channels, the NetMeeting™ API, and so forth. All are worthy of articles in their own right, so I wouldn’t be able to do them justice in this space. Future MSJ and MIND articles should cover them in extensive detail.
Other sets of programming interfaces that are just too big to attempt to cover here include hardware support that will be in both Windows 98 and Windows NT 5.0. Into this category, I’ve put the Win32® Driver Model (WDM), Zero Administration Windows support, and multiple monitor support. There are also major additions to the Advanced Power Management, DirectX®, and Plug and Play APIs. The June 1997 issue of MSJ has already covered multiple monitor programming, and Walter Oney’s article in this issue covers WDM.
Yet a third set of new interfaces that I can’t hope to cover in this space are major additions to Windows NT that (once again) justify their own articles. One such is the Active Directory Services, which present a unified mechanism to locate and use resources (such as printers) across the network. Another is the CryptoAPI 2.0 and Kerberos security. The CryptoAPI 2.0 introduces more than 100 new cryptographic and certificate-based APIs.
You may be wondering “What’s left to cover?” Plenty. I found some of them by digging around in the most basic of the system DLLs such as KERNEL32. There are also some new system DLLs that should very quickly become part of your programming toolbox.
My focus here is the standard system DLLs. I’ll describe all kernel-related things from KERNEL32.DLL before moving on to USER32.DLL, which has new additions to the windowing and messaging systems. In my next article, I’ll move on to other existing system DLLs like OLE32.DLL, OLEAUT32.DLL, and WININET.DLL and then I’ll move on to some of the new system DLLs.
Since this article is about what’s new in Windows NT 5.0, let’s define what I mean by “new.” My baseline for comparison is the original Windows NT 4.0 (no service packs installed), and the Win32 SDK released concurrently with Windows NT 4.0 (dated 8/09/96). I’m certainly aware that Microsoft is now in the habit of slipping new APIs and other functionality into service packs, but watch for this trend to be reversed. Likewise, the release of Microsoft Internet Explorer 3.0 included updates to many of the standard system DLLs. Some of these additions are documented in the March 1997 Platform SDK. What’s more, the ever-changing Internet SDK seems to continually add new APIs and interfaces.
With the advent of “Internet time,” it’s almost impossible to keep up with exactly which technology is shipping, or in beta, and on which platform(s). “New” is context-dependent. However, I had to draw a line in the sand somewhere. I picked the last major revision to the Win32 API, which was the release of Windows NT 4.0 and its corresponding Win32 SDK update. What you’ll see here are new additions that I believe are significant, and which you might otherwise miss in the hurricane of new Windows programming information arriving daily.
Finally, be forewarned that the details of what I describe can (and probably will) change before Windows NT 5.0 ships. I’m writing this article while working on Windows NT Workstation build 1570, which isn’t even a publicly released beta version. Likewise, the updated header files that I’m using haven’t yet been released as part of a beta Win32 SDK. In short, I’m working with some pretty raw material here, and I wouldn’t be surprised if there are significant changes and important additions that occur after this article hits the presses.
Although it didn’t jump out at me at first, the biggest addition to the Windows NT 5.0 kernel is vast enhancements to the file system. For starters, Windows NT 5.0 will support FAT32. Yes, finally a disk format that doesn’t waste vast amounts of space with big clusters, supports huge drives, and most importantly, runs on both Windows NT and the Windows 9x architecture. But from the programming perspective, there’s not much to talk about with FAT32. It’s just there.
Windows NT 5.0 also adds UDF (Universal Disk Format). UDF is an implementation of ISO 13346, a new file system designed for cross-platform data interchange. UDF is destined to replace CDFS (ISO 9660) as the file system of choice for CD and DVD media. The UDF implementations shipping in both Windows 98 (UDF 1.02) and Windows NT 5.0 (UDF 1.50) are read-only. For more information on UDF and some of the organizations defining the new UDF 2.0 specification, see http://www.osta.org.
Beyond FAT32 and UDF, don’t think for a moment that the Windows NT folks have given up on NTFS. Quite the opposite! There are many new NTFS features, all in the new version of NTFS (version 5).
One big addition to NTFS is reparse points. Without getting into too much detail, a reparse point is like a special tag that alters the way the file system does path name resolution. For instance, when opening the file “C:\WINNT\ SYSTEM32\CALC.HLP,” the file system first locates the C: drive. On the C: drive, it then locates the root directory, followed by the WINNT directory, followed by the SYSTEM32 directory, and ending with CALC.HLP. At each sequence along this process, a reparse point can exist. The various file system drivers recognize their particular reparse points and take appropriate action before the open call completes. The action taken is completely defined by the driver. In the works are reparse points that link one directory to another. This feature will enable linking of multiple disk volumes into a single tree, similar to the way DFS (Distributed File System) links together remote network shares. Eventually you’ll be able to have many disk volumes linked together, with only a single drive letter pointing to the root volume. Also in the works are reparse points that act as surrogates for files residing on remote storage.
The reparse point mechanism is extensible; you can define your own reparse point tag that performs special behavior in conjunction with your own driver.
Note: if you are interested in writing new file systems or file system filter drivers, check out the Installable File Systems Kit on http://www.microsoft.com/hwdev/ntifskit.
You can use FSCTL_SET_REPARSE_POINT, FSCTL_ GET_REPARSE_POINT, and FSCTL_DELETE_REPARSE_POINT to create, read, and delete reparse points. You can detect files with associated reparse points by looking for FILE_ATTRIBUTE_REPARSE_POINT in the file attributes structure, for example, during directory enumeration. Finally, you can inhibit the reparse point behavior during path name resolution by using FILE_ FLAG_OPEN_REPARSE_POINT in CreateFile calls. For instance, if you are trying to open a reparse point that links a source directory to a target directory, this new flag ensures that the source is opened rather than the target.
Another new NTFS 5 feature is per-volume, per-user disk quotas. Quotas are typically set by an administrator to prevent users like me from filling up the network drives with hundreds of megabytes of the antics of Beavis and Butt-head. When users exceed their disk quotas, subsequent writes will fail, just as if the disk was physically full. Quota is assigned based on the security identification (SID) of the owner of the file.
The GetDiskFreeSpace and GetDiskFreeSpaceEx APIs work seamlessly with quotas, but now is the time to switch over to these functions if you aren’t already using them. Here’s why:
NTFS 5 now supports native property sets on any file or directory through the IPropertySet and IPropertySetStorage APIs. You can use the native property set support to attach property sets to flat files—for example, attach an “annotation” property set to a bitmap file—in the same way you can create property sets in OLE compound files today. A bonus: Microsoft Index Server (a full-fledged OLE-DB provider), built in to Windows NT 5.0, will index and search the contents and properties of files stored on any supported file system (FAT, NTFS, and so on). It will also index and search native NTFS properties.
NTFS 5 also has a volume-wide change log that tracks all changes to files and directories over long periods of time, across system reboots. You can use the change log to analyze I/O and reliably track a wide variety of changes to file system data. For instance, file creation, renaming, and deletion; data and property writes; changes to security, compression, and encryption.
Other cool new features in NTFS 5 are sparse files and file encryption. I’ll come back to file encryption next month. Sparse files are a way for a program to create huge files (that can even exceed the size of the physical disk) without actually committing disk space for every byte. The basic definition is “allocation on demand.” For example, you may need a file that’s 42GB in size, but you’ll only actually write data to the first 64KB and last 64KB. Using sparse files, NTFS will only allocate physical disk space to the portions of the file that you write to. In this case, the sparse file would only use 128KB of space on disk, but in all other respects, the file would act as if it were 42GB. Some interesting applications include sparse arrays and circular queues.
NTFS sparseness is applied per-file: invoke FSCTL_SET_ SPARSE on the file you wish to make sparse, and then call FSCTL_SET_ZERO_DATA to zero out the desired ranges of the file. You can also find out exactly which ranges are in use with FSCTL_QUERY_ALLOCATED_RANGES. Sparse files can be detected by looking for FILE_ATTRIBUTE_ SPARSE_FILE in the file attributes structure.
NTFS 5 also comes with a number of other smaller goodies. Among others, you can now:
? Find all the files owned by a particular security ID (FSCTL_FIND_FILES_BY_SID)
? Check multiple ACLs simultaneously for file access (FSCTL_SECURITY_ID_CHECK)
? Force a dismount even with files open (FSCTL_DISMOUNT_VOLUME)
You’ll find these and other FSCTLs documented in the Platform SDK. Note that all FSCTLs are now callable from the Win32 layer. There are also a number of new flags returned by GetVolumeInformation and used by other file APIs to indicate support for sparse files, disk quotas, encryption and the like. I’ve listed some of these new flags in Figure 1.
Figure 1: New NTFS File-related Flags
FILE_FLAG_OPEN_REPARSE_POINT | When opening a file, inhibit any effects that may exist due to an associated reparse point |
FILE_ATTRIBUTE_SPARSE_FILE | The file is a sparse file |
FILE_ATTRIBUTE_ENCRYPTED | The file is encrypted |
FILE_ATTRIBUTE_REPARSE_POINT | The file has an associated reparse point |
FILE_VOLUME_QUOTAS | The volume supports disk quotas (NTFS 5 required) |
FILE_SUPPORTS_SPARSE_FILES | The volume supports sparse file (NTFS 5 required) |
FILE_SUPPORTS_REPARSE_POINTS | The volume supports reparse points |
FILE_SUPPORTS_ENCRYPTION | The volume supports encryption |
As a final note on the NTFS additions, some of the new additions require an upgrade to the NTFS on-disk format. Once upgraded to the Windows NT 5.0 format, Windows NT 4.x and earlier won’t recognize the volume. However, Windows NT 5.0 continues to work just fine with earlier NTFS formatting (NTFS 4), and you’re not required to upgrade to the Windows NT 5.0 style format if you don’t need the new features, although that will be the upgrade default.
Moving beyond the NTFS additions, Windows NT 5.0 offers new high-level file system APIs. The GetLongPathName function takes a path or filename using the short format and retrieves the name in its long form. Short file names come from the bad old days of MS-DOS®, and are restricted to the 8.3 format for directory and file names. Additional characters beyond the first 8 are truncated, and replaced with a tilde (~), followed by a number. The new GetLongPathName API isn’t a great new feature. Rather, it’s the overdue complement to the GetShortPathName API from previous versions of Windows NT. To show the GetLongPathName API in action, I wrote the rather trivial LongPathNameDemo program shown in Figure 2.
Figure 2: LongPathNameDemo.cpp
//===============================================================
// Matt Pietrek
// Microsoft Systems Journal, November 1997
// FILE: LongPathNameDemo.CPP
// To compile: CL </DUNICODE=1 /D_UNICODE=1> LongPathNameDemo.CPP
//===============================================================
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x500
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
TCHAR g_szMyLongFilename[] = _T("ThisIsALongFileName.TXT");
int main()
{
HKEY hKey;
// Create a file with a long filename (see above)
HANDLE hFile = CreateFile( g_szMyLongFilename,
GENERIC_WRITE,
0,
0,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
0 );
if ( hFile ) // If the file was opened, close
CloseHandle( hFile );
TCHAR szShortFilename[MAX_PATH];
// Get the short version of the filename
GetShortPathName( g_szMyLongFilename, szShortFilename,
sizeof(szShortFilename)/sizeof(TCHAR) );
_tprintf( _T("Short pathname: %s\n"), szShortFilename );
TCHAR szLongFilename[MAX_PATH];
// Reverse the process, getting the long filename from the
// short filename obtained earlier
GetLongPathName( szShortFilename, szLongFilename,
sizeof(szLongFilename)/sizeof(TCHAR) );
_tprintf( _T("Long pathname: %s\n"), szLongFilename );
if ( hFile ) // If we created a file earlier, delete it now
DeleteFile( g_szMyLongFilename );
return 0;
}
Another pair of new file APIs is ReadFileScatter and WriteFileGather. These APIs are intended for heavy duty disk I/O where multiple page-sized buffers are copied between the file system and memory. WriteFileGather takes pointers to one or more pages in memory, “gathers” them together, and writes them out to the file as one chunk. ReadFileScatter reads in one or more pages from the file system, and “scatters” them to buffers set up beforehand. The advantage of the scatter/gather technique is that the program doesn’t need to work with intermediate buffers that contain the data as a single logical chunk. These APIs were first introduced in an Windows NT 4.0 service pack to enhance the performance of Microsoft’s SQL Server™.
From a purely techie point of view, my favorite addition to KERNEL32 is something that the vast majority of Windows NT users (myself included) won’t be able to take advantage of. The feature is limited support for 64-bit memory addressing. On a Windows NT-compatible CPU that supports 64-bit memory addressing (currently only the DEC Alpha fits this description), it’s now possible to work with memory at addresses greater than 4GB. Why would you want to do this? Industrial strength database servers often have much more than 4GB of RAM installed in order to keep huge chunks of the databases cached in memory. Yes, Windows NT 5.0 is definitely making an effort to convince big IS/IT shops to migrate to it from older legacy minis and mainframes.
Support for memory above 4GB isn’t as seamless as for memory below the 32-bit range. Most significantly, Windows NT doesn’t support demand paging of memory above 4GB. If you want to access this memory, you must have physical RAM committed at those addresses. I’ll have to pass up the opportunity to present a demo program here, since I currently don’t have an Alpha. Even if I did, I couldn’t afford to buy more than 4GB of memory for it. If somebody feels like making a generous donation of the aforementioned hardware, I’m available to write code.
For now, pretend that life really is that good. You’ve got a DEC Alpha, and it’s loaded to the gills with memory. How would you use that memory under Windows NT 5.0? The answer lies in the VLM (Very Large Memory) APIs. These APIs are extensions to the existing APIs that deal with virtual memory. For example, VirtualAllocVlm and VirtualFreeVlm act like their 32-bit equivalents, but use 64-bit addresses and size parameters.
After allocating memory above 4GB, it’s only natural that you’d want to use this memory in conjunction with permanent storage. The MapViewOfFileVlm and UnmapViewOfFileVlm APIs act much like their original 32-bit counterparts. To access individual bytes within the 64-bit region, you simply use a 64-bit pointer and blast away. Windows NT 5.0 also adds the following other 64-bit memory APIs that work much as their 32-bit versions do: VirtualProtectVlm, VirtualQueryVlm, GlobalMemoryStatusVlm, and ReadProcessMemoryVlm /WriteProcessMemoryVlm.
What do the Intel versions of the VLM APIs do? The following APIs all return a failure value, after invoking SetLastError(ERROR_CALL_NOT_IMPLEMENTED):VirtualAllocVLM / VirtualFreeVlm, ReadProcessMemoryVlm/WriteProcessMemoryVlm, and ReadFileVlm/WriteFileVlm.
The remaining VLM APIs in KERNEL32.DLL are actually wrappers around APIs in NTDLL.DLL. What does NTDLL.DLL do in the Intel implementation? It ships the call off to the Ring 0 code in NTOSKRNL.EXE. It’s only at Ring 0 that the Intel version gets around to not implementing the function. It probably would be simpler if KERNEL32 just returned failure in the first place, but who am I to quibble over operating system architecture?
As a final note on 64-bit support, WINNT.H defines two new values. The SEC_VLM flag can be returned by VirtualQueryVlm when used with an address above 4GB. Another flag, IMAGE_FILE_LARGE_ADDRESS_AWARE, lets the system know that a particular executable is 64-bit aware. To use the 64-bit APIs, you need to indicate in the EXE file that the process is 64-bit aware by using the new /LARGEADDRESSAWARE switch in LINK.EXE.
While Intel machines can’t use the 64-bit APIs in Windows NT 5.0, with the right version of Windows NT, it’s possible to add another gigabyte of memory to the user address space. By default, the 4GB address space of Windows NT is divided into two regions: the lower 2GB for each process and the upper 2GB for the system. In the new Enterprise Edition of Windows NT, it’s possible to expand the per-process area to the first 3GB and shrink the system area to the upper 1GB. This capability first appeared in code derived from Windows NT 4.0, service pack 3. The mechanics of accessing 3GB from your program aren’t trivial. In a nutshell, it involves using the /3GB switch in BOOT.INI, and marking your executables as being 3GB-aware.
An additional indicator that Windows NT 5.0 is moving into the enterprise realm is the addition of jobs. Loosely defined, a job is an operation that’s performed by one or more processes. Put another way, one or more processes contribute effort to complete a job. There are several new APIs that work with jobs: CreateJobObject, OpenJobObject, AssignProcessToJobObject, TerminateJobObject, QueryInformationJobObject, and SetInformationJobObject. The common denominator of all these APIs is the job object, which you refer to by a job handle (hJob).
Conceptually, a job handle can be thought of as a process handle, but one that works with multiple processes (that is, the processes assigned to the job). To get a job handle, call CreateJobObject or OpenJobObject. To shut down the job (and all of its associated processes), call TerminateJobObject. To associate a process with a job, there’s the AssignProcessToJobObject API. Job limits can be set on things such as the scheduling priority, CPU time, and working set of the processes that comprise the job. You can also query a job object and get back the limit information as well as a list of processes in the job, how many processes in the job have terminated, and so on.
With service packs now coming out with regularity, many programs need an easy way to tell if a service pack is installed, and if so, which one. To this end, a new data structure, OSVERSIONINFOEX, has been defined. A superset of the original OSVERSIONINFO structure, it includes extra fields at the end that indicate the service pack that’s installed (both major and minor versions.) Luckily, no new API is needed for this structure. GetVersionEx works with either the older OSVERSIONINFO structure or the new EX version. How does GetVersionEx know which structure was passed to it? The first field of both structures should be set to the size of the structure before calling GetVersionEx. (This is one reason why it’s necessary to initialize the size field of so many Win32 structures.)
The Windows NT 5.0 kernel offers several improvements with regard to thread execution. While multiprocessor machines are still relatively rare, they’re growing in popularity, and it’s nice to see explicit support to optimize multiprocessor situations. A problem that Windows NT 5.0 addresses via new APIs is that of a particular process specific memory block or resource that’s constantly being acquired and released. For example, you might have a linked list that you constantly add and delete items from. Usually these resources are guarded by a critical section. However, if a thread blocks on a critical section, under the hood it’s calling WaitForSingleObject, which is relatively expensive.
If you know that the critical section for a resource is usually acquired and released in fairly short order, you can optimize the critical section so that threads won’t spend as much time in an expensive WaitForSingleObject call. The trick is that the dwReserved field of a critical section is now used for a spin count. If the spin count is set, a thread that would normally block while waiting for a critical section will instead enter a loop where it continually checks if the critical section can be acquired. If the loop executes “spin count” number of times, the thread gives up and reverts back to the old behavior by calling WaitForSingleObject. The goal is that the blocking processor should acquire the critical section faster by this method than by using WaitForSingleObject.
The spin count value can be set via two new APIs, InitializeCriticalSectionAndSpinCount and SetCriticalSectionSpinCount. Obviously, on a uniprocessor machine, using spin counts doesn’t buy you anything. However, the spin count APIs can be called on uniprocessor systems with no ill effects.
Another nice new feature of Windows NT 5.0 relating to threading is that if you bring up the Performance tab of the System Properties dialog, you’ll see two groups of radio buttons grouped under “Quantum Type and Length.” With these buttons, you can select fixed versus variable length thread quantums (that is, timeslices). You can also specify that you want the quantum length to be shorter or longer. Shorter quantums give smoother multitasking at the expense of more time spent switching the CPU between threads. I don’t know the programming interface to set or retrieve these values—probably some wacky registry thing!
Speaking of system performance, Windows NT 5.0 has even more goodies in this area. For starters, in the Task Manager Processes tab, it’s possible to see much more than just the process ID (PID), the CPU times, and the memory usage. Go to the View menu, and select “Select columns...”. Within the resulting dialog, you can opt to see many other items that Windows NT 5.0 tracks on a per-process basis, including the number of USER and GDI objects, the number of handles and threads in use by the process, and so forth. While the Task Manager could retrieve this information from the registry performance data, it cheats a little bit and goes straight to the NtQuerySystemInformation API in NTDLL.DLL.
Returning to the subject of the new system level information support in Windows NT 5.0, I was shocked to find that Windows NT 5.0 has finally implemented the Toolhelp32 API. The Toolhelp32 functions, which originated with Windows 95, provide read-only access to the process, thread, and module lists; the list of all heaps in the system; and the ability to enumerate the blocks of any given heap. I did a whole article on the Toolhelp32 APIs in September 1995 issue of MSJ, so I’ll skip a detailed description here. If you’ve never used the Toolhelp32 APIs, the TLHELP32.H header file contains the data structures and API prototypes, while TH32.LIB is the import library. In the SDK documentation, search for functions like CreateToolhelp32Snapshot and Process32First.
The reason I was surprised by the additions of the Toolhelp32 APIs is that long before Windows NT 4.0 came out, I asked the Windows NT development team to add them to Windows NT 4.0. The response was that there wasn’t time, and that anyhow, you can get superior performance information by querying the registry. While the registry performance data certainly is voluminous (see my April 1996 Under the Hood column), it’s much simpler to use the Toolhelp32 APIs, and they provide the most common information that most programmers are after.
To prove to myself that the Toolhelp32 API really was implemented under Windows NT 5.0, I dusted off the source for my TH32DEMO program from my September 1995 Toolhelp32 article. While it didn’t run unmodified the first time, I quickly narrowed the problem down to a specific piece of Windows 95-specific code that translated heap handles to addresses. After removing that code, everything worked great except for the Module32First/Module32Next APIs, which I suspect will be fixed by the time Windows NT 5.0 is in beta. Figure 3 shows TH32DEMO running under Windows NT 5.0. The sources are available on the MSJ site.
Figure 3: TH32DEMO
If you want to use common code for Windows 9x and Windows NT 5.0, then the Toolhelp32 API is your best bet. If you’re only concerned with Windows NT and need more detailed information, then be sure to check out the PSAPI. I described using PSAPI.DLL in two previous Under the Hood columns. (See the August and November 1996 issues of MSJ.)
In the area of debugging, Windows NT 5.0 doesn’t offer any new functionality. On more than one occasion, I’ve been asked if there’s a way for a program to determine if it’s running under a debugger. Typically, these people want to prevent prying eyes from stepping through their code to figure out algorithms or other proprietary information. While there’s no generic Win32 function that indicates if a process is being run under a debugger, KERNEL32.DLL has had a private function called IsDebuggerPresent for quite some time. In Windows NT 5.0, this function moves into the public eye. Be forewarned though: this function only tells you if you’re running under the control of a program using the Win32 debug APIs (such as the Developer Studio debugger). It won’t tell you if a program is being debugged by a system level debugger such as the Windows NT DDK’s i386KD or NuMega’s SoftIce/NT. These debuggers actually run underneath the entire operating system, and no, I can’t divulge how to detect them.
As part of the support in Windows NT 5.0 for APM, applications can inform the system that they’re busy performing particular types of activity. In these instances, the OS should defer putting the hardware into sleep mode. Informing the system is done on a per-thread basis via the SetThreadExecutionState API. A thread can specify ES_ SYSTEM_REQUIRED when it’s doing something that’s important, but that the system wouldn’t recognize as normal activity. ES_DISPLAY_REQUIRED tells Windows NT 5.0 that the thread is emitting information to the display, but in such a way that Windows thinks that the thread is idle. To request that the system simply not bother to put the system into a sleeping state, a thread can specify ES_CONTINUOUS. A final option, ES_USER_PRESENT, tells the system that a user really is actively working with the system, even though the system might not think so.
Another API in the power management realm is RequestWakeupLatency. This API lets you request how long it will take the computer to resume from a sleeping state. The API is called with one of two values: LT_DONT_ CARE or LT_LOWEST_LATENCY. Requesting LT_LOWEST_LATENCY means that you’d like the computer to wake up as soon as possible, assuming the hardware supports such a mode. The quicker wakeup presumably comes at the cost of higher power consumption while sleeping. The default wakeup latency is specified by using LT_DONT_CARE.
Yet another new power management API is GetDevicePowerState, which retrieves the current power state of a device. The device in this case means something like a disk drive or CD. The primary use of GetDevicePowerState is to find out if a disk is fully powered up (for example, spinning and ready to roll). If the device is not powered up, the program may want to avoid using it to prevent additional power consumption.
While on the subject of hardware, the addition of Windows CE systems to the Win32 family has lead to several new additional #defines in WINNT.H. For example, Windows CE doesn’t have the full subsystem functionality as its big brothers, so there’s a subsystem value known as IMAGE_SUBSYSTEM_WINDOWS_CE_GUI. Likewise, Windows CE supports a wider set of CPUs than its desktop counterparts, so the following new processor #defines are listed in WINNT.H:
As a final note on new or extended fields in PE executables, there’s a new value for the DllCharacteristics field called IMAGE_DLLCHARACTERISTICS_WDM_DRIVER. This indicates that the executable is a WDM driver. To indicate that your driver is of the WDM type, a new linker option has been added: /DRIVER:WDM. I won’t go into WDM here, since, again, Walter Oney’s article in this issue covers this subject.
While USER is traditionally considered a single subsystem, I always break its functionality into several major areas. The first area I’ll look at is the messaging system. By messaging, I mean the code that relates to creating, processing, and moving window messages through the system.
The Windows NT 5.0 USER adds several new messages to the hundreds of existing standard window messages. Some of these messages have actually been around for awhile, but are just now being formally documented. I’ve summarized the new messages and what they do in Figure 4. Note that this list is only general window messages below WM_USER, and doesn’t include any control-spe- cific messages.
Figure 4: New Windows NT 5.0 Window Messages
WM_GETOBJECT | Sent to Microsoft Active Accessibility (MSAA) servers to obtain a reference to an object |
WM_SYNCPAINT | Sent to a window that needs to be painted when it’s overlaid by another window of a different thread |
WM_MENURBUTTONUP | Sent when a drop-down menu or |
WM_MENUDRAG | submenu is about to become |
WM_MENUGETOBJECT | active |
WM_UNINITMENUPOPUP | |
WM_MENUCOMMAND | Menu owner receives this when the user makes a selection |
In the past, if you wanted to synthesize keyboard or mouse input, you had to grunge around with the keybd_event and mouse_event functions. These APIs let you insert a single keystroke or mouse action into the input queue Windows NT reads from. But nothing prevents the user from hitting keys or moving the mouse while you’re using these APIs, resulting in potentially wacky and unexpected results. In Windows NT 5.0, these APIs have been superseded by the SendInput API, which lets you send a stream of input events, and guarantees that they’ll all make it to the input queue as a cohesive group. The SendInput API actually first appeared as part of the Microsoft Active Accessibility™ (MSAA) package for Windows 95, including the new NotifyWinEvent API. Complete information can be had from the SDK, downloadable at http://www.microsoft.com/enable (available only for Windows 95 at press time).
You’d think that it would be hard to complicate something as simple as an API like InSendMessage. This long-standing API tells you if your thread is executing in response to receiving a window message that was sent to it. (The alternative would be a posted message.) In recent years, Win32 has offered up a variety of variations on SendMessage such as SendMessageTimeout. In response, Windows NT 5.0 serves up InSendMessageEx, which tells you much more about the messaging system state of the calling thread. The API returns a DWORD with a variety of flags, which I’ve listed in Figure 5.
Figure 5: InSendMessageEx Flags
ISMEX_NOSEND | Message being processed wasn’t sent |
ISMEX_SEND | Message was sent through SendMessage or SendMessageTimeout |
ISMEX_NOTIFY | Message sent through SendNotifyMessage. Sender is not blocked |
ISMEX_CALLBACK | Message sent through SendMessageCallback. Sender is not blocked |
ISMEX_REPLIED | Message has been replied to. Sender is no longer blocked |
Another new API that concerns window messaging is GetMouseMovePoints. Your code provides GetMouseMovePoints with an x,y coordinate where the mouse has been. The API uses this as a starting point for retrieving a specified number of prior mouse coordinates from the mouse history buffer. The system keeps a buffer with the last 64 mouse coordinates and their time stamps.
If the mouse may have been at the same x,y coordinates at different times, you can supply a time stamp to GetMouseMovePoints. The timestamp will be used to differentiate between identical points in the history buffer. The GetMouseMovePoints API returns x,y coordinates that were dispatched to other threads, as well as to the calling thread. When requesting coordinates, you can ask for the information to be in units of display driver pixels, or the native format of the underlying mouse driver. This is useful for Windows CE systems, where the pen drivers have a resolution that exceeds the available pixels.
In case you were wondering when the last time the mouse or keyboard were used, Windows NT 5.0 adds the GetLastInputInfo API, which fills in a LASTINPUTINFO structure. Currently, the dwTime field is the only newsworthy bit of information returned. Its DWORD contains the tick count (a la GetTickCount) when the last activity occurred. What a great new way to tell if the user has fallen asleep at their keyboard! (Or perhaps determine if one of your worker bees has sneaked away from their desk.)
My last bit on new messaging system functionality isn’t even something that you need to program for, but is cool, nonetheless. The Microsoft Intellimouse™, with the scrolling wheel between the two buttons, has been essential to me since the first day I had it. I even wrote a program for my June 1997 column that retrofits existing applications to respond to wheel scrolling. In Windows NT 5.0, my MouseWheel program has been made mostly obsolete. Through sleight of hand, many windows (such as my CodeWright editor windows) now magically just work with the Intellimouse. You can even configure the amount of scrolling to be done with a new registry value: HKEY_CURRENT_ USER\Control Panel\Desktop\WheelScrollLines. The default is three lines of scrolling, but you can change the value to whatever suits your fancy using SystemParametersInfo.
Window hooks are closely related to the messaging system, and Windows NT 5.0 offers two new hook types. They’re WH_KEYBOARD_LL and WH_MOUSE_LL, and they’re lower level (“LL”) versions of the traditional keyboard and mouse hooks. The primary reason for these new hooks is for the accessibility initiative (MSAA, mentioned previously), which make Windows easier to use for people with physical disabilities. By seeing the mouse and keyboard actions earlier, and in a more raw form than normal hooks allow, programs can better anticipate or respond to the user. In a similar vein, the WH_SHELL hook has a new action, HSHELL_ACCESSIBILITYSTATE. This new action notifies the hooker of changes in the states of the StickyKeys, FilterKeys, and MouseKeys features.
Moving on to the improved windowing system in Windows NT 5.0, there’s several new features to look at. First up is active window tracking. The general idea is that moving the cursor to a non-foreground window causes that window to become the foreground window. This feature has been around in other operating systems for awhile, so it’s nice to see it finally in Windows NT. Mind you, it takes a little bit of getting used to, and its primary benefit will be to users with huge monitors running multiple, nonoverlapped applications. If you don’t like this feature, it’s easy enough to turn off via extensions to the SystemParametersInfo API with the new SPI_SETACTIVEWINDOWTRACKING and SPI_SETACTIVEWNDTRKZORDER commands. I’ll have more to say on other new SystemParametersInfo commands later.
If active window tracking weren’t enough for you, you’ll probably be even more weirded out by window animation. I first uncovered the phenomena when I brought up the Task Manager. Rather than simply pop up the familiar window, the window seemed to fade into existence, somewhat like a ghost might appear. Thinking I had been working way too hard, I spent the next 15 minutes starting and shutting down the Task Manager, trying to prove to myself that I wasn’t seeing things.
A little digging eventually revealed that Windows NT 5.0 has a new API called AnimateWindow. It takes an HWND to animate, the time to complete the animation, and a set of animation style flags:
While I won’t describe each flag, the idea is that with AnimateWindow, you can control how a window is shown or hidden. Rather than just popping onto or off the screen, windows can fade in or out (AW_BLEND), slide (AW_ SLIDE), or roll (the default). The direction (top to bottom, left to right) can also be specified where appropriate for a given animation. To show the AnimateWindow API in actual use, I added a call to it in the WM_INITDIALOG handler of the TH32DEMO sample program (see Figure 6). That one call placed at the right location gives TH32DEMO the same cool fade-in effect as the Windows NT 5.0 Task Manager. Since I added that, I found out the Task Manager only does the fade-in animation on displays with a color depth greater than 256 colors, systems with a fast CPU (Pentium Pro or better, Alpha 21164 or better), and when the Task Manager window wasn’t resized to be bigger from the original size. AnimateWindow is also used by the Window Manager to do the combo-box drop-down animation and by comctl32 tooltips for the show animations.
Figure 6: AnimateWindow in TH32DEMO.c
void Handle_WM_INITDIALOG(HWND hWndDlg)
{
DWORD tabStops = 20;
// Set appropriate tab stops in all the listboxes of the main dialog
SendDlgItemMessage( hWndDlg, IDC_LISTBOX_PROCESSES,
LB_SETTABSTOPS, 1, (LPARAM)&tabStops );
SendDlgItemMessage( hWndDlg, IDC_LISTBOX_THREADS,
LB_SETTABSTOPS, 1, (LPARAM)&tabStops );
SendDlgItemMessage( hWndDlg, IDC_LISTBOX_HEAPS,
LB_SETTABSTOPS, 1, (LPARAM)&tabStops );
SendDlgItemMessage( hWndDlg, IDC_LISTBOX_MODULES,
LB_SETTABSTOPS, 1, (LPARAM)&tabStops );
// Fill in the process listbox (and in a cascade reaction, update
// all the other listboxes )
UpdateProcesses( hWndDlg );
// Set focus to the main listbox (the process list)
SetFocus( GetDlgItem(hWndDlg, IDC_LISTBOX_PROCESSES) );
// Cool blend in animation effect
AnimateWindow( hWndDlg, 10, AW_BLEND | AW_ACTIVATE );
}
Given the slickness of AnimateWindow, it’s not surprising that Windows NT 5.0 has even more ways to load up your program with cool behavior previously exclusively available to UI wizards with lots of time on their hands. FlashWindow has expanded to FlashWindowEx, which will take a pointer to a FLASHWINFO structure. This structure contains a DWORD set of flags that let you specify how the window should be flashed (FLASHW_CAPTION, FLASHW_TRAY, and so on).
The new, cool SW_SMOOTHSCROLL flag allows ScrollWindowEx to do the smooth scrolling effect. It’s used by listboxes for smooth-scrolling and by listboxes and edit controls for auto-scrolling/panning. Using SW_SMOOTHSCROLL when calling ScrollWindowEx gives you scrolling similar to that used by Internet Explorer. When this flag is passed in, the HIWORD of the flags parameter can be used to indicate how much time the smooth-scrolling operation should take.
A whole new set of windowing APIs in Windows NT 5.0 fall under the category of “window information.” The GetWindowInfo API is the most general. Given an HWND, it fills in a WINDOWINFO structure, which contains information such as the window’s overall and client area size, its style (WS_XXX) and extended style flags (WS_EX_XXX), and other goodies. Some of this information can be obtained by other APIs, but it’s nice to have it all in one place.
Other new APIs allow you to gather formerly hard-to-obtain information. For example, USER32 takes care of drawing and maintaining the title bars, menu bars, scroll bars, list boxes, and combo boxes, hiding the ugly details from you. But often there’s information or behavior in these controls that you might want to access. The GetTitleBarInfo, GetMenuBarInfo, GetScrollBarInfo, GetListBoxInfo, and GetComboBoxInfo APIs allow unprecedented access to the details of these windows. For instance, for a combobox, you can get its button state and the underlying HWNDs for the item and list portions. Another new Windows NT 5.0 API, GetAltTabInfo, informs you of the contents of the Alt-Tab (task switching) window.
If all this new HWND-related information wasn’t enough, there’s even more miscellaneous new APIs that retrieve information for a given HWND. GetWindowModuleFileName returns the name of a EXE or DLL that created a window. Be forewarned though; this API only works on HWNDs created by the process that called the API. This means it’s only of use for windows that your program created or within a systemwide hook procedure.
The new RealChildWindowFromPoint API digs deeper than ChildWindowFromPoint to find the real underlying child window. For example, it knows about “dead spaces” in group boxes. Likewise, the RealGetWindowClass API knows about things like Visual Basic® controls, where the class of a control isn’t exactly what it appears to be. (Visual Basic wraps existing window classes such as buttons with its own set of classes.)
The new GetAncestor API returns the HWND of the original parent of a window, as opposed to the current parent. (The parent of a window can be changed by the SetParent API.) Finally, the existing GetWindow API adds yet another GW_XXX enum, GW_ENABLEDPOPUP. The retrieved handle identifies the enabled popup window owned by the specified window (the search uses the first such window found using GW_HWNDNEXT); otherwise, if there are no enabled popup windows, the retrieved handle is that of the specified window.
Moving now to the menuing system, there’s just a few things worth mentioning. SetMenuInfo lets you set menu configuration options, such as whether it’s modeless, the background brush used to paint it, and so forth. GetMenuInfo retrieves the same set of information. The EndMenu API shuts down whatever HMENU is passed to it. It’s not new though, just previously private.
No new edition of Windows would be complete without new system metrics and system parameter info, and Windows NT 5.0 doesn’t disappoint. GetSystemMetrics defines at least six new SM_XXX values: SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_CMONITORS, and SM_ SAMEDISPLAYFORMAT. All of these are related to the Windows NT 5.0 support for multiple monitors.
In the SystemParametersInfo arena, Figure 7 shows off the many new SPI_XXX values that affect how the user interface looks and works. All but one of the new SPI_XXX values belongs to a get/set pair. These new SPI_XXX values let you programmatically retrieve and set system options such the mouse speed, whether the active window “tracks” the cursor, and if menus are animated (that is, they slide open, rather than pop up).
Figure 7: New SystemParametersInfo Values
SPI_GETMOUSESPEED SPI_SETMOUSESPEED |
The same value as you'd set via the Mouse applet in the Control Panel. |
SPI_GETSCREENSAVERRUNNING | Is a screen saver currently running? |
SPI_GETACTIVEWINDOWTRACKING SPI_SETACTIVEWINDOWTRACKING |
Makes the active window "track" the mouse. Whatever window the mouse is over becomes the active window. |
SPI_GETMENUANIMATION SPI_SETMENUANIMATION |
If menu animation is enabled, menus will slide open. If disabled, menus pop up fully formed (just as in previous versions). |
SPI_GETCOMBOBOXANIMATION SPI_SETCOMBOBOXANIMATION |
If enabled, combo boxes slide open instead of pop open. |
SPI_GETLISTBOXSMOOTHSCROLLING SPI_SETLISTBOXSMOOTHSCROLLING |
If enabled, list boxes use the smooth-scrolling effect. |
SPI_GETGRADIENTCAPTIONS SPI_SETGRADIENTCAPTIONS |
If enabled, the background color of window title bars blends from the right to the left color. Use GetSysColor with the COLOR_GRADIENTACTIVECAPTION and COLOR_ACTIVECAPTION flags to specify the right and left colors for active windows. Use the COLOR_GRADIENTINACTIVECAPTION and COLOR_INACTIVECAPTION flags for inactive windows. |
SPI_GETMENUUNDERLINES SPI_SETMENUUNDERLINES |
If menu underlines are enabled, the menu shortcut underlines are always drawn. Otherwise, they are only drawn when the menu is activated through the keyboard. |
SPI_GETACTIVEWNDTRKZORDER SPI_SETACTIVEWNDTRKZORDER |
If active window z-order tracking is enabled, the active window will be brought to the top of the z-order. |
SPI_GETHOTTRACKING SPI_SETHOTTRACKING |
If hot tracking is enabled, the system hot tracks and displays tooltips for non-client area buttons and menu bar items. |
SPI_GETFOREGROUNDLOCKTIMEOUT SPI_SETFOREGROUNDLOCKTIMEOUT SPI_GETACTIVEWNDTRKTIMEOUT SPI_SETACTIVEWNDTRKTIMEOUT |
Active window tracking delay, in milliseconds. This happens regardless of the z-ordering mode (see SPI_SETACTIVEWNDTRKZORDER). |
SPI_GETFOREGROUNDFLASHCOUNT SPI_SETFOREGROUNDFLASHCOUNT |
This is the number of times SetForegroundWindow will flash the tray button when rejecting a request by another program to switch to the foreground. |
Where available, Figure 7 includes a short description of what the SPI_XXX values do. In most other cases, the name itself is usually enough of an indicator. To let you easily see what all the new SPI_XXX and SM_XXX values return, I wrote the SystemMetricsParams program listed in Figure 8.
Figure 8: SystemMetricParams.cpp
//=============================================================================
// Matt Pietrek
// Microsoft Systems Journal, November 1997
// FILE: ScheduledTasks.CPP
// To compile: CL </DUNICODE=1 /D_UNICODE=1> SystemMetricParams.CPP user32.lib
//=============================================================================
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x500
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#define WIDTH 40
#define DISPLAY_SM( x ) _tprintf( _T("%-*s%u\n"), WIDTH, \
_T(#x), GetSystemMetrics(x) );
#define DISPLAY_SPI( x ) dwValue = 0xBAADF00D; \
SystemParametersInfo( x, 0, &dwValue, 0 ); \
_tprintf( _T("%-*s%u\n"), WIDTH, _T(#x), \ dwValue);
int main()
{
DISPLAY_SM( SM_XVIRTUALSCREEN )
DISPLAY_SM( SM_YVIRTUALSCREEN )
DISPLAY_SM( SM_CXVIRTUALSCREEN )
DISPLAY_SM( SM_CYVIRTUALSCREEN )
DISPLAY_SM( SM_CMONITORS )
DISPLAY_SM( SM_SAMEDISPLAYFORMAT )
_tprintf( _T("\n") ); // separator
DWORD dwValue;
DISPLAY_SPI( SPI_GETMOUSESPEED )
DISPLAY_SPI( SPI_GETSCREENSAVERRUNNING )
DISPLAY_SPI( SPI_GETACTIVEWINDOWTRACKING )
DISPLAY_SPI( SPI_GETMENUANIMATION )
DISPLAY_SPI( SPI_GETCOMBOBOXANIMATION )
DISPLAY_SPI( SPI_GETLISTBOXSMOOTHSCROLLING )
DISPLAY_SPI( SPI_GETGRADIENTCAPTIONS )
DISPLAY_SPI( SPI_GETMENUUNDERLINES )
DISPLAY_SPI( SPI_GETACTIVEWNDTRKZORDER )
DISPLAY_SPI( SPI_GETHOTTRACKING )
DISPLAY_SPI( SPI_GETFOREGROUNDLOCKTIMEOUT )
return 0;
}
To finish up with the USER subsystem, I found a couple of random yet interesting tidbits. Another MSAA API, GetGUIThreadInfo returns information for a thread’s local input state. This includes the thread’s active, capture, and focus windows. GetGuiResources gives you a count of how many USER or GDI objects a process is using, which is useful if you’re trying to track down resource leaks. The GetCursorInfo API tells you if the cursor is currently visible, and where it is on the screen. The Internet/intranet invasion continues with the introduction of a new resource type, RT_HTML, which means that HTML and the dialog box template can duke it out for ownership of the form layout world.
While experimenting with how the Task Manager blends its window into view, I came across some new functions in GDI32.DLL, which are used by AnimateWindow. Later, I discovered a new system DLL that wraps these calls with official Win32 APIs. This new DLL is named MSIMG32.DLL, and as of this writing, it has just a handful of APIs. The first is AlphaBlend, which blends a rectangular region of one device context into a different device context. The raster op used to do the blending is under your control, but currently only one raster op is defined. AlphaBlend can be used to specify both a per-pixel alpha channel for 32BPP bitmaps, and a global Alpha value to apply to the whole bitmap.
Also included in MSIMG32.DLL is the TransparentBlt API, whose name pretty much says what it does. The new GradientFill API is used to implement caption bars with gradient shading (as in Microsoft Office apps). In addition it can be used for rectangles, and it is also possible to use it for triangles—specifying a distinct color for each vertex.
All of the MSIMG32 APIs are prototyped in WINGDI.H, but you’ll need to use the new MSIMG32.LIB import library when linking.
I’ve briefly led you through changes to NTFS, the kernel, USER, and GDI, but I’ve barely scratched the surfaces of each. Next month I’ll dig deeper when I examine changes to ADVAPI32.DLL, new shell features, common controls, the Task Scheduler interfaces, and much, much more. Stay tuned.
To obtain complete source code listings, see the MSJ Web site at http://www.microsoft.com/msj.