A Programmer's Perspective
on New System DLL Features
in Windows NT 5.0, Part II
| The Windows NT 5.0 registry editor, RegEdit.EXE, has an awesome new feature for anybody who's laboriously copied the name of a deeply nested registry key. Simply right-click on the desired registry key and the context menu gives you the opportunity to Copy Key Name.|
The information contained herein is based on prerelease software and is published without the endorsement of Microsoft Corporation. Microsoft Corporation makes no warranty as to the accuracy or completeness of the information. Since anything is subject to change, readers are advised that they use this information at their own risk.
This article assumes you're familiar with Win32
Code for this article: NT51297.exe (2KB)
Matt Pietrek is the author of Windows 95 System Programming Secrets (IDG Books, 1995). He works at NuMega Technologies Inc., and can be reached at email@example.com or at http://www.wheaty.net. |
When MSJ asked me
to write about Microsoft® Windows NT® 5.0, I decided to focus on the core portions of the operating system that have changed since Microsoft Windows NT 4. Last month, I covered what's new in the kernel, USER, and GDI components. This month, I'll look at additions to ADVAPI32, the shell, the common controls, and COM. Although technically not core items, I'll also look at additions to WININET.DLL and IMAGEHLP.DLL, as well as the new Shell Light Weight API and task scheduling interfaces. Before you dive in, be aware that much of this was written prior to the release of Windows NT 5.0 Beta 1. Any and all of what I describe here is subject to change.
ADVAPI32 is best known for its ownership of the registry APIs. It does a lot more than that, though. Windows NT 5.0 adds quite a few new APIs to ADVAPI32, especially in the areas of security and cryptography. I'll mention that the only new registry function (so far) is RegOverridePredefKey. It lets you substitute one of the predefined keys with a different key. The predefined keys are the keys you see when you first start RegEdit, and include HKEY_
CLASSES_ROOT, HKEY_CURRENT_USER, and HKEY_
The Windows NT 5.0 registry editor, RegEdit.EXE, has an awesome new feature for anybody who's laboriously copied the name of a deeply nested registry key. Simply right-click on the desired registry key, and the context menu gives you the opportunity to Copy Key Name. This copies the key's complete path into the clipboard, ready to paste wherever you please.
New Security APIs
There are lots of new Windows NT 5.0 security APIs. I won't pretend I'm an expert on Windows NT security, so I'll just give a high-level rendition of the groups of new APIs. The first set of security APIs is the AccessCheck group, which determines whether a security descriptor grants a specified set of access rights to a client.
Next up is the Add functions, which add new Access Control Entries (ACEs) to an Access Control List (ACL). Some of these new APIs are just extended versions of existing APIs.
Windows NT 5.0 provides APIs for converting between ACLs (ACTRL_ACCESS) and security descriptors (SECURITY_DESCRIPTOR).
Next up in the hit parade of new security APIs are the SecurityInfoEx APIs. These APIs differ from their non-Ex versions in that they allow you to retrieve access-control information for the properties on an object, as well as for the object itself.
A new API, SetEntriesInAccessList, creates a new ACL by merging new access-control information into an existing ACL. The SetEntriesInAuditList API does the same thing, but for audit lists (ACTRL_AUDIT).
The CreatePrivateObjectSecurityEx and SetPrivateObjectSecurityEx functions are extensions to existing APIs that create and modify security descriptors. The primary difference in the Ex functions is that they can specify flags that control how ACEs are inherited from the security descriptor of the parent container.
Although they sound like part of the CryptoAPI 2.0,
the new EncryptFile and DecryptFile APIs belong to ADVAPI32.DLL. They do file encryption and decryption at the file system level. The encryption is implemented as a filter driver, and only works on NTFS formatted volumes. To put a file into the encrypted state, you call EncryptFile, and to decrypt it, you use DecryptFile. (Duh!)
From the user's perspective, encryption is an attribute of a file, much like compression. It's worth noting that a file that's encrypted cannot be compressed, and vice versa. A file that's encrypted by one user appears completely unchanged to the user. However, it isn't accessible to someone logged in as a different user. To test this, I logged in as the Administrator, right-clicked on a file in a Microsoft Internet Explorer window, and selected Encrypt. I then logged out, and logged in as Guest. When I attempted to access the same file, I received the message "A device attached to the system is not functioning." This actually makes sense, as the file system Encryption filter driver simply disallows access to the file by someone without the proper security attributes.
New Windows NT 5.0 Shell Additions
One of the top new features in Windows NT 5.0 is its support for the Active Desktop (which Windows® 98 also supports). The topic of Active Desktop programming is way beyond the scope of this article, so I'll defer an in-depth discussion to some future MSJ article. I'll go over a few of the more important new shell APIs and ActiveX interfaces that are defined in SHELLAPI.H and SHLOBJ.H.
On the API side of things, there's a relative handful of new tricks. SHQueryRecycleBin tells you (in 64-bit integer format, no less!) how many items are in the recycle bin, and how much space it takes up. Information can be retrieved for a particular drive, or for all bins on all drives. To empty out a particular recycle bin (or all bins), there's the SHEmptyRecycleBin API. I've put together a tiny cheesy sample, RecycleBin.EXE, the source for which can be found in Figure 1.
Other new shell APIs include SHGetNewLinkInfo, which can help you create a new link. It will return information such as whether the target is also a linkif so, it should just be copiedand what the new name should be. There's also the SHInvokePrinterCommand, which lets you easily query the printer, install drivers for a network printer, print a test page, and get properties for a document in its print queue. The existing ShellExecute API extends the SHELLEXECUTEINFO structure to let you specify which monitor an application should be initially be displayed on. The last new shell API to mention here is DoEnvironmentSubst. You pass it a string with environment variables embedded (such as %windir%), and the API will replace them with the current environment settings.
The most interesting new shell functionality in Windows NT is the new ActiveX interfaces. The flagship interface here is IActiveDesktop. Its methods are listed below and are mostly self-explanatory.
You obtain an Active Desktop pointer by calling CoCreateInstance and specifying CLSID_ActiveDesktop and IID_IActiveDesktop as the CLSID and IID. Note that the GUID for IActiveDesktop changed between IE 4.0 Preview 2 and the final release of IE 4.0. This means that anyone targeting the Windows NT 5.0 interface on Windows NT 5.0 Beta 1 will need to use the old version, and any binary compiled for one release will not work with the other. The Platform SDK ships both versions, defaulting to the IE 4.0 final GUID and definitions.
GetWallpaper / SetWallpaper
GetWallpaperOptions / SetWallpaperOptions
GetPattern / SetPattern
GetComponentsOptions / SetComponentsOptions
AddComponent / ModifyComponent / RemoveComponent
GetComponent / GetComponentByID
To use the Windows NT 5.0 Beta 1 version, you will need to copy the mssdk\include\nt5b1 directory to mssdk\include (or put it earlier on the path) and the mssdk\lib\nt5b1 directory to mssdk\lib. (Further information is found in the Platform SDK release notes.) The Internet Client SDK (aka Internet Explorer 4.0 Toolkit) can't be used to target Windows NT 5.0 Beta 1 since it has the later definitions.
Another interesting new ActiveX interface is IURLSearchHook. IURLSearchHook, which is called by Internet Explorer to translate URLs using unknown strings into something it can use. When attempting to browse to a URL address that does not contain a protocol, Internet Explorer first attempts to determine the correct protocol using the unmodified address. If this fails, Internet Explorer creates URL Search Hook objects that have been registered, and calls each object's Translate method until the URL has been translated or until all hooks have been called.
New Common Controls
Ever since the Win32® common controls first appeared in Windows 95, they've been constantly tweaked and extended. The changes between the Windows NT 5.0 common controls and those in Windows NT 4.0 are huge, and normally you'd expect them to go through all the beta testing that a major operating system upgrade entails. The Internet wars haven't let things play out this way, however.
In between Windows NT 4.0 and Windows NT 5.0, several releases of Internet Explorer 3.x have been released, with new common controls coming as part of the deal. As if that weren't enough, the Internet Explorer 4.0 preview has added even more functionality to the common controls corral. In comparing the current COMMCTRL.H with the version released for Windows NT 4.0, my head spun when I saw all the differences. The newer COMMCTRL.H is more than double the size of the original.
I'm not going to even attempt to describe all the differences in COMM-CTRL.H. Many changes are simply additional custom window messages (or pseudo-APIs that are really just macros around a call to Send-Message). These new messages, along with many new #defines, let you extend the functionality of the common controls beyond what was available in Windows NT 4.0 or Windows 95. Many of these additions have already been covered extensively in Strohm Armstrong's October and November 1996 MSJ articles ("Previewing the Common Controls DLL for Microsoft Internet Explorer 4.0"). What I will briefly describe here are the
additional common controls that
weren't available in the Windows NT 4.0 timeframe.
Before I get to the new controls, there is one new API that definitely requires mentioning. InitCommonControlsEx is a superset of the existing InitCommon-Controls API. The primary job of InitCommonControls is to register the appropriate window class for each common control. The flaw with Init-Common-Controls is that if you don't want to use every common control, you waste time and space registering classes you don't care about. InitCommonCon-trols-Ex remedies this by allowing you to select which controls to register. Simply fill in the INITCOMMON-CONTROLSEX structure with the ICC_XXX flags representing the controls that you'll use, then pass the structure to InitCommon-ControlsEx.
The ComboBoxEx control is an extended combo box that supports images alongside the combo box's items. To make the images easily accessible, the control uses image lists. Image lists are one of the original common control components. In addition to the standard combo box styles, the Combo-BoxEx control supports new styles such as CBES_EX_
CASESENSITIVE, which enables case sensitive searching.
The calendar control provides an easy way for the user to select a date from a control resembling a monthly calendar (see Figure 2). The left and right arrows at the top let you scroll to previous and subsequent months. Clicking on the month name at the top brings up a menu with all the months. Clicking on the year creates an up-down control that quickly moves you forwards and backwards in year increments.
Figure 2 Calendar Control
The date and time picker (DTP) control displays time information and acts as the interface through which users modify time information (see Figure 3). The control's display consists of fields defined by the control's format string. Each field in the control displays a portion of the time information that the control stores internally. The user can click a field to set keyboard focus, and then provide keyboard input to change the time information represented by that field.
Figure 3 DTP Control
Rebar controls act as containers for child windows. An application assigns child windows, which are often other common controls, to a rebar control band. Rebar controls contain one or more bands. Each band can have any combination of a gripper bar, a bitmap, a text label, and a child window. You've probably seen rebar controls in Internet Explorer 3.0 and 4.0, as well as in Visual Basic® 5.0. You may also have heard rebar controls referred to as CoolBars. One of the best parts about rebar controls is that users can move the bands and bars around to their heart's content, practically guaranteeing that no two users will have the exact same layout in their program. Yep, a technical support staffer's dream.
The Internet Protocol (IP) address control is similar to an edit control, but only allows you to enter numerical addresses in IP format. This format consists of four three-digit fields. Each field is treated like a separate edit control. Tab and Shift-Tab let you navigate between the fields, as shown in Figure 4. It's possible to configure the control so that it forces the IP address fields to fall within ranges you define (for example, your company's subnet mask).
Figure 4 IP Address Control
Flat scrollbars behave identically to normal scrollbars. The only difference is in how they are displayed to the user. Perhaps the UI team at Microsoft waxes nostalgic for those pre-Windows 3.x days, when everything in the UI was flat, rather than three dimensional. Unfortunately, the normal scrollbar APIs don't work with flat scrollbars. Instead, you'll need to call a new set of APIs (all prefixed with FlatSB_). It's also necessary to call InitializeFlatSB before using any of the other FlatSB APIs.
The pager control is a parent window class that can be used to scroll any child window. It works as a viewport, avoiding the system scrollbars that require the child to build in scrolling knowledge and how to draw at specific offsets. You can see examples of pagers in the IE 4.0 channels explorer bar and the quicklinks desktop toolbar. The little left/right scrollers and top/bottom scrollers are pagers. Spy++ will also show this window hierarchy.
Finally, there's custom draw, which isn't a common control; it is a service that many common controls provide. Custom draw services give you greater flexibility over a control's appearance. Your application can use custom draw notifications to change the font used to display items or manually draw an item without having to do a full-blown owner draw.
COM/OLE Additions to Windows NT 5.0
I won't even attempt to cover everything that's new in COM and ActiveX for Windows NT 5.0. COM+ is being covered by Mary Kirtland in this issue. But there are a few COM/OLE highlights that are worth going over.
A new concept in COM under Windows NT 5.0 is surrogates. A surrogate is a special process into which DLL servers can be loaded. This gives the DLL server the advantages of an EXE server without all the additional coding. It also allows different DLL servers to be loaded together within a single process, reducing the number of server processes needed. Among the benefits of surrogate processes is fault isolation. In other words, a buggy DLL server won't bring down your entire program. Surrogates also allow servers to serve multiple clients simultaneously, and let the DLL server provide services to clients on other machines (à la DCOM). Of course, the disadvantages of EXE processes (such as greater execution overhead) are also present with surrogates.
To use the system-supplied surrogate, you need to make one change to the registry on the server machine: under the AppID key for your server, add "DllSurrogate" as a named value. Assuming you have a valid InprocServer32 key for this server, the system will now respond to a request for a local (EXE) server using the default surrogate. For most situations, the system-supplied surrogate should be sufficient. If you need detailed control over activation, security, or threading, you can create your own custom surrogate. For more information about custom surrogates, see the latest Platform SDK documentation.
Another new COM addition in Windows NT 5.0 is IGlobal-InterfaceTable. This interface lets any apartment in a process get access to an interface implemented on an object in any other apartment within the process. The IGlobalInter-faceTable::RegisterInterfaceInGlobal method lets you register interfaces as being "global" throughout the process. IGlobalInterfaceTable::GetInterfaceFromGlobal retrieves a pointer to that interface from any other apartment through a token. Finally, IGlobalInterface-Table::Revoke-Interface-FromGlobal revokes a previously registered global interface.
Using IGlobalInterfaceTable can be an efficient way for a process to store an interface pointer in memory that may be accessed from multiple apartments within the processfor instance, process global variables and freethreaded objects containing interface pointers to non-freethreaded objects. The IGlobalInterfaceTable capabilities are not portable across process or machine boundaries, so cannot be used as a substitute parameter passing mechanism.
New WinInet Fun with Windows NT 5.0
As Internet technologies continue to multiply at an amazing rate, Microsoft operating system gurus somehow keep up with it, adding new features and APIs. The focus point for Internet-related APIs is WININET.DLL; quite a bit has been added since WinInet first appeared for Internet Explorer 3.0, including support for HTTP 1.1 headers. I'll just go over some of the new APIs. There are many new #defines and enumerations in WININET.H that I won't describe because they're for preexisting APIs.
To start out with, the WinInet folks have discovered that not everybody is continuously hooked up to the Internet via a T3 line to their desktop. A whole new set of APIs shown in Figure 5 enable you to programmatically fire up your modem to hook up to your ISP, hang up the connection, and otherwise query what sort of IP connection you have. Quick quiz: without looking, what's the difference between InternetGoOnline, InternetDial, and InternetAutodial?
From its first release, WinInet has cached the network data it receives. The APIs that let you access the cached data are known as the URL Cache APIs. The Windows NT 5.0 WININET.DLL adds several new caching APIs (see Figure 6). Without going into too much detail, the new APIs are intended primarily for use by applications such as offline browsers. An offline browser downloads multiple related HTML pages and stores them in the file system. Later, when the computer isn't connected to the net (for instance, when you're at 30,000 feet), you can still "browse" the portions of the Web that were retrieved earlier. To help organize related cached data, WinInet adds the notion of a GROUPID. Check the documentation for CreateUrl-Cache-Group and friends if you're interested in this sort of thing.
Shell Light Weight API
One of the coolest new system DLLs in Windows NT 5.0 is SHLWAPI.DLL. (Wow, I sure sound like a geek!) Anyhow, SHLWAPI (pronounced "shell-wappi") stands for Shell Light Weight API. Even though SHLWAPI.DLL is only 90KB (so far), it packs a ton of useful "Why didn't they do this three years ago?" functionality into its APIs. The APIs are in four categories: C/C++ string functions, path manipulation, registry access, and miscellaneous.
Many SHLWAPI string functions are C++ runtime library functions that were long overdue in the Win32 API. Examples include StrDup, StrStr, and StrCmpN. Other APIs (which end with I) are case-insensitive versions of a standard C++ function, for instance StrStrI. Other new string APIs (see Figure 7) provide capabilities that aren't in ANSI C++, but are useful nonetheless. Examples of this are StrTrim, StrToInt, and StrFromTimeInterval.
With the SHLWAPI Path APIs (see Figure 8) you get unified methods for working with files and file paths. How often have you written code that extracts just the directory from a complete filespec? You can now have the Path-Remove-FileSpec API. Likewise, how often have you written code that finds just the filename portion from a complete filespec? Now there's Path-Find--File-Name to do this potentially error-prone work for you (especially help-ful because of the naming minefield that long file-names create).
Most of the SHLWAPI Path APIs names are self explanatory if you mentally strip off the "Path" part of the name. For example, PathAdd-Back-Slash adds a backslash. The "Path" part of the name is just a common prefix. In the few cases where the API name doesn't give its intended use away, the API probably does something really worthwhile. For example, Path-Com-pactPath strips out enough characters from the middle of a path so that the resultant string will fit into a given screen space. (The stripped out characters are replaced by ellipsis.) All in all, the SHLWAPI Path APIs are very cool stuff.
Although the Win32 registry is very flexible, most of us only use a few of the numerous registry APIs and we use them in fairly standard ways. The SHLWAPI registry APIs reduce much of the tedium if you're using the registry in standard ways:
SHDeleteEmptyKeyTo read a value of a particular subkey, it gets tedious to open the registry subkey, call Reg-QueryValue-Ex, and then close the reg-istry key. SHGet-Value does everything in one step. Enumerating and delet-ing sub-keys is also a hassle using the standard registry APIs. SHLWAPI.DLL has easy solutions for these scenarios as well.
There are only two miscellaneous SHLWAPI APIs. SH-Open-RegStream opens up a registry value for reading using the IStream interface. Yes, I think it's a bit weird, but it sort of make sense if you're reading from a value containing gobs of data. Think of it this way: configuration and settings of COM objects are usually saved with IPersist-Stream, and an appropriate place to save is HKEY_CUR-RENT_USER. So SHOpen-RegStream bridges that gap. SHCreateShellPalette takes an HDC and creates an HPALETTE using the palette associated with the DC. This is the system's shell palette. The halftone palettes on Windows 95 and on Windows NT differ, so writing an application that works well on both for all your bitmaps is sometimes hard. This can be used on both platforms.
New Stuff in IMAGEHLP.DLL
While IMAGEHLP.DLL hasn't undergone any dramatic changes for Windows NT 5.0, there is one new group of APIs that a few developers should be overjoyed with. Ever since I wrote about IMAGEHLP's symbol table capabilities, developers have told me "That's great, but how can I get the source line associated with an address?" This glaring omission in the IMAGEHLP API has finally been corrected in the Windows NT 5.0 version.
All of the new IMAGEHLP line number APIs work with a new data structure called an IMAGEHLP_LINE, which contains both an address and a source file/line number pair. The API most developers will use is SymGetLineFromAddr, which takes a process handle, an address, and fills in an IMAGEHLP_LINE from them. In reverse, SymGetLine-From-Name uses a source file/line number to look up the corresponding address, again returning the whole ball of wax in an IMAGEHLP_LINE struct. This API will be primarily of use to debugger writers, who need to set source level breakpoints. The last two APIs in this set are Sym-GetLinePrev and SymGetLineNext, which provide first/next enumeration through the set of IMAGEHLP_LINE structures associated with the process.
The New Task Scheduling Interfaces
One of the first things I noticed about Windows NT 5.0 is that it added the ability to schedule tasks. If you've used the CRON utility with Unix, you're familiar with the concept. To the user, this feature is seen as the Scheduled Tasks application. This program is invoked via the Start menu, under Programs | Admin-istrative Tools | Scheduled Tasks. There's even a Scheduled Tasks Wizard to guide the user through creating a new task.
A task can be run once at a particular time, or can be set up to execute on a repeating basis. Scheduled tasks can even be used to start programs on other machines. Imagine the ability to automatically reboot the boss's computer 10 minutes before the weekly status meeting. Of course, it wouldn't really be this easy, since scheduled tasks are subject to all the standard Windows NT security requirements.
The Scheduled Tasks capability is built atop standard operating system functionality. A program called MS-TASK.EXE runs at startup, and is responsible for firing off tasks at the appropriate time. The properties of each individual task are stored in a file with the .JOB extension that MSTASK reads. All the .JOB files are kept in a common directory. The location of the .JOB directory can be found in the registry value: HKEY_LOCAL_MACHINE\
Of course, we programmers always want programmatic access to system goodies like this, and Windows NT doesn't disappoint. A new header file, MSTASK.H, provides the definition for these new COM interfaces:
The ITaskScheduler interface is the most fundamental of the Task Scheduler interfaces. ITaskScheduler lets you add, delete, change, and enumerate tasks, as well as
administer tasks on other machines. Obtaining an ITask-Scheduler instance is easy. Simply call CoCreate-Instance, specifying CLSID_CTaskScheduler as the CLSID and IID_ITask--Scheduler as the IID. Here are the ITask-Scheduler interfaces.
SetTargetComputer / GetTargetComputer
The next important interface to understand is ITask. ITask is what you use to control a task object. You can set and retrieve all of the task's information, execute and terminate the task, add and delete triggers, and allow the user to modify the task via a property page. I'll get to trig-gers momentarily.
NewWorkItem / AddWorkItem
If you dig through the definition of the ITask interface, you won't find methods that do all the various things that I mentioned above. It turns out that ITask is derived from the IScheduledWorkItem interface. I've listed the methods of this interface in Figure 9. For the most part, they're self-explanatory. Here are the additional methods that the ITask interface adds to the IScheduledWorkItem interface.
SetApplicationName / GetApplicationName
You determine when a task should run with a task trigger. A trigger is a set of criteria that, when met, causes a task to be run. There are two types of triggers: time-based and event-based. The ITaskTrigger interface is how you create and modify triggers. The IScheduledWorkItem interface contains the CreateTrigger method, which returns a pointer to an ITaskTrigger interface. There are additional methods to modify and delete an existing ITaskTrigger.
SetParameters / GetParameters
SetWorkingDirectory / GetWorkingDirectory
SetPriority / GetPriority
SetTaskFlags / GetTaskFlags
SetMaxRunTime / GetMaxRunTime
Time-based triggers activate at a specified point in time. Not only can you set the time that they become active, you can also have them activate once, daily, weekly, monthly,
or on a specified day of the month. Event-based triggers activate in response to certain system events, like when the system starts up, when a user logs on, or when the system goes into an idle state.
The last interface of the task scheduling group is IProvide-TaskPage. It allows you to display three property pages that allow the user to alter the behavior of a task. To show some of the TaskScheduler interfaces in action, I wrote the ScheduledTasks sample program shown in Figure 10. It just goes through the motions of creating the various required interface instances, and pops up the system-supplied UI that lets you edit a task's properties.
While I've covered a lot of material in this article, there's still much more that's new in Windows NT 5.0. My intent was to highlight the significant additions and improvements to the core components, as well as some of the extended system DLLs that are commonly used. Without a doubt, Windows NT 5.0 contains goodies for almost every type of programmer, even if you don't count technology that was slipstreamed in via service packs.
Before I finish, I'd like to thank Dan Babcock of NuMega Technologies. Dan went beyond the call of duty and provided me with a special build of SoftIce/Windows NT that worked on my pre-beta copy of Windows NT 5.0. Much of this article came from direct experimentation with Windows NT 5.0 during execution, and wouldn't have been possible without Dan's help.
From the December 1997 issue of Microsoft Systems Journal.
Get it at your local newsstand, or better yet, subscribe.