Dan Appleman - Desaware, Inc.
(Portions of the following article are excerpted from the Visual Basic Programmerís Guide to the Win32 API.)
Youíve probably heard that Windows 95 and Windows NT are considerably more stable than Windows 3.x. But what does this really mean? To a large degree it means that it is much more difficult for one application to crash another application or the entire system. Under Windows NT it is nearly impossible. Windows 95 is not quite as stable, but it is still better than 16 bit Windows.
How is this stability accomplished? One of the most important mechanisms has to do with the way Windows organizes memory. This is something that most Visual Basic programmers rarely need to think about, but it is both interesting, and important to know when you start working with 32 bit subclassing and hooks.
Imagine, for a moment, that you have a system with 16 megabytes of memory. The physical addresses of memory will range from address 0 through &H1000000. For now weíll ignore the issue of virtual memory, in which the hard disk is used to simulate additional memory. Purists will also note that this discussion will ignore the implications of the segmentation architecture used under Windows 3.x - an issue which would complicate the subject considerably and distract from the principles that are the focus of this article.
In this example assume that three programs are running, the Program Manager, File Manager and a user application. Under Windows 3.x, they might be organized in memory as shown in Figure 1. A couple of system dynamic link libraries are shown as well. Each of these modules needs to access memory in the normal course of running, and uses pointers internally to reference locations in memory. If the program is working correctly and has no bugs, it will never try to access memory outside of the memory space that is allocated to it (except under certain special conditions, as you will see). However, if something goes wrong or the program has a bug that gives one of these pointers an invalid value, it is possible for the program to write into the memory belonging to another application or even the operating system. This means that one application has the potential to corrupt memory on a systemwide basis.
|This memory organization does have one advantage when it comes to performing subclassing. Since each application can easily access memory belonging to other applications, it is relatively easy for an application to hook into another applicationís internal data and substitute its own function address for the window function of any window belonging to that application. It would not be accurate to say that this type of cross-task subclassing is simple - there are many other issues involved in doing it safely, but it certainly is easy compared to what comes next.|
As you have seen, under Windows 3.x each application shares the same view of system memory and can access all available memory. Windows NT uses a much more advanced memory scheme in which every memory access by an application undergoes a translation step that converts it into a physical memory address. This fact has far reaching implications. For one thing, since each applicationís memory access has to be translated anyway, there is no reason why more than one application should not use the same addresses. This is, in fact, what Windows NT does - each application is given an address space of 4 gigabytes - thatís &0 through &FFFFFFFF&. This does not mean that you need 4 gigabytes of memory or even disk space to use Windows NT. It does mean that each application can allocate blocks of memory anywhere in that address range and have the operating system assign those blocks to real memory. Figure 2 illustrates how two applications might view memory and how it might be translated into physical memory.
The first thing you might notice is that there is no relationship between the location of data or code in the address space of the application and its location in physical memory. In fact, a dynamic link library that is shared by two applications might appear at completely different addresses as viewed by each application. Also note that the memory for one application does not even exist in the address space of the other application. This means that it is completely impossible for one application to accidentally write into physical memory assigned to another - and difficult to do intentionally (and only possible when explicitly requested through the operating system). It also means that an address that is valid in one application, is almost certainly not valid in another.
What about Subclassing?
Despite these obstacles, the SpyWorks 4.0 subclassing control dwsbc32.ocx, has no trouble subclassing windows that belong to other applications. Well, to be fair - it does have trouble, but that trouble is almost completely hidden from the Visual Basic programmer.
The secret to how this is accomplished lies in the dwspy32.dll module - the 32 bit subclassing engine. You see, it really is impossible for one application to directly subclass another. But it is possible for a dynamic link library to be loaded by more than one application at once. As a first step toward any cross task operation, dwspy32.dll maps itself into the other applicationís address space. This means that the other application becomes able to execute code in dwspy32, just as each application is able to execute operating system code. dwspy32.dll is able to keep track of which applications are mapped and has the ability to communicate from one address space to another. When you try to subclass another applicationís window using dwsbc32.ocx, the control tells dwspy32.dll which window it wants to subclass. dwspy32.dll checks to see if the window is already subclassed - if so, it just notes that your application wants to intercept messages for that window as well. If it is not already subclassed, dwspy32.dll maps itself into the other applicationís address space and subclasses it in that address space. Any time a message comes in, dwspy32.dll notifies your application and triggers the appropriate event.
A similar mechanism comes into play for windows hooks and keyboard hooks - any time you try to work with information outside of your own application or on a systemwide basis, dwspy32.dll takes over.
Dealing with Pointers
There is still one catch - once you subclass a window belonging to another process, some of the information you retrieve will consist of memory addresses in the other applicationís address space. For example: the WM_GETTEXT message returns a pointer to a string containing a window caption. Due to the memory translation scheme, this pointer is useless in your application - it only has meaning in the other programís address space. For this reason dwspy32.dll provides a set of cross task memory functions that are designed to make it easy to safely read and write memory belonging to other applications. Safely in this context is relative - if you are careful, you should be fine, but this ability to access another programís memory space has the potential of bringing back the problems that are inherent in Windows 3.x - the ability of one program to crash another or the entire system. The SpyWorks tools always strive to let you keep your level of invasiveness to a minimum.
What about Windows 95?
Why the focus so far on Windows NT? Because NT provides the highest level of security available. Under Windows 95, the separation between applications is not quite as extreme. For example: the upper two gigabytes of memory are actually shared among all applications. This area in memory contains dynamic link libraries and operating system modules that are mapped into the same addresses for all applications. Application modules, which are placed in the lower two gigabyte space, are still protected from each other. This means that while Windows 95 is a dramatic improvement over Windows 3.x, it is nowhere near as secure as Windows NT.
The subject of process spaces can be complex and this is a somewhat simplified version of what is going on behind the scenes. It did not, for example, discuss how 16 bit application memory is managed, or what happens when an application tries to write into a dynamic link library that is mapped into several applications (which is possible under Windows NT, but is, believe it or not, safe due to a built in copy on write mode). Chapter 14 in the VB Programmerís Guide to the Win32 API goes into substantially more detail, as it takes the next step and explains how to use the many new API functions that are designed to work with virtual memory spaces.
The VB Programmer's Guide to the Win32 API is available from GUI Computing for $99.95. Contact email@example.com for more details.