Writing a 16-bit Custom Control for Windows 95/98/Me
Introduction
Operating systems are becoming faster, powerful, and reliable every day and Microsoft has been a forerunner in this. From the humble beginnings of MS-DOS and Windows 3.l Microsoft have raised the PC platform to a new level with its Windows 95. Increasing the ease of use and power over it predecessors Windows 95 is the beginning of a new breed of operating system like Window 98 and Windows Me, Also sometime collectively referred to as Windows 9x. Windows 95 is also one of the first Microsoft operating system that was developed for 32-bit applications.
While it is certainly worth exploring the new and exciting features of these operating systems it is also equally important to see what they offer in terms of new features and improved performance for legacy applications. We will now try to understand Windows 9x operating systems in detail especially from the point of view of a legacy 16-bit application written for Windows 3.1. We will also discuss in detail a real-time 16-bit application, which needs to take advantage of one of the features available for 32-bit based applications. As a reference we will use Window 98 as the reference architecture to understand Windows 9x operating system family.
Windows 98 Model
Windows 98 is a 32-bit operating system. It has a complete 32-bit kernel, which includes memory management, preemptive multitasking and multithreading support. The diagram below shows the various components of the system. The ones in color are the ones that we will be discussing in detail as they have the responsibility of running 16-bit applications in the operating system.

Hardware
Registry
This is the central information store of windows 98. This mainly used for storing hardware-specific information for use by the system components. It also stores user-specific and configuration-specific information making the operating system more adaptable.
Virtual Machine Manager
The Virtual Machine Manager manages resources each application and system process running on the computer. It provides and maintains a virtual machine environment for applications and system processes to run. All the system process runs in a single VM called the system VM. Each and every Win32-based application runs in its own VM, whereas all the 16-bit applications share a single VM. It is the responsibility of the VM to do the following services:
- Process Scheduling
- Memory Paging
- MS-DOS Mode Support
Process Scheduling:
Process scheduling is responsible for providing system resources to application and to other processes, and to schedule processes to facilitate multiple application to run concurrently. Widows 98 supports two types of multitasking cooperative multitasking and pre-emptive multitasking.

Win32-based applications are run using pre-emptive multitasking. In this method the applications need not yield to other running process to be able to multitask properly. The operating system treats each Win32-based application running in the system as a process. Each process consists of at least a single thread, which is a unit of code that can get a time slice from the operating system to be run concurrently with other units of code. A thread is always associated with a running process. A single process can have multiple threads associated with it.
While Win32-based applications are run using pre-emptive multitasking 16-bit applications are run using cooperative multitasking. In this style of multitasking the operating system requires that the applications check the message queue and relinquish control of the system to other applications. Any applications that fail to do so will effectively hog the CPU time. This type of multitasking is supported purely for backward compatibility with Windows 3.1.
Memory Paging:
Windows 98 uses a memory system called the demand-paged virtual memory system. This system is based on flat, linear address space, accessed using 32-bit addresses. Each process is allocated a 4 GB virtual address space. Of this 4 GB only the lower 2 GB is private to the application. The upper 2 GB is shared and used by the system. In demand paging the code and data are moved in pages from physical memory to a temporary paging file on the disk. If the information in the disk is needed by the process its paged back to the physical memory on demand.
To support a 16-bit process segments are used. A segment references memory using a 16-bit segment address and a 16-bit offset address within the segment. A segment is 64KB and to access information across segments both the application and the operating system suffer performance degradation. Windows 98 supports segmented address spaces purely for backward compatibility. All the 16-bit applications running in the system share a single virtual memory and so can access information across process boundaries unlike Win32-based applications.
Windows 98 Core
Widows 98 core is composed of three components User, Kernel and Graphics Device Interface (GDI). Each of the components is made of 2 dynamic-link libraries (DLLs) one 32-bit and one 16-bit to provide service for the applications that run. Though Windows 98 uses 32-bit code whenever possible it uses 16-bit code when a 16-bit process requires the operating system services or when the 16-bit code delivers better performance over the equivalent 32-bit code.
The kernel provides the base operating system services like file I/O services, virtual memory management and task scheduling. It is the kernels job to load the EXE and DLL files for the applications. The kernel provides services to both 32-bit and 16-bit applications by using a translation process called thunking. Thunking maps between the 16-bit and 32-bit formats it also converts a 16-bit value to its 32-bit equivalent.
Overall Windows 98 provides complete backward compatibility with the Windows 3.1 in running 16-bit applications. Also the 16-bit applications can take advantage of the 32-bit features of the operating system using thunking. But a 16-bit applications running on Windows 98 has a few restrictions that is not true for a 32-bit application. All the 16-bit applications running in the system are run together within a unified address space and are run in a cooperative multitasking manner.
Using the core service like Thunking 16-bit application in Windows 98 can now use 32-bit application features. Though there are a few restrictions on the 32-bit code that can be called from a 16-bit application these can be overcome by a lot of other features of the operating system. On of the best example of how a 16-bit application can use this is illustrated in detail below.
A Case Study
The support for 16-bit application in Windows 9x means that it is possible to use the same applications and services that were meant for Windows 3.1. This will save a lot of time and energy in some cases. More than backward compatibility these legacy applications can even be enhanced to use 32-bit code if needed. This is possible through thunking. Though there are some restrictions on the 32-bit code that can be called from a 16-bit process its still a very useful feature
Consider the possibility of a 16-bit application being able to use the services of new 32-bit components. Take for instance a 16-bit application that uses a web browser style interface. Writing a custom control that can render HTML pages is a time consuming process. Microsoft provides a WebBrowser control for 32-bit application that requires the same service. Thanks to Thunking even 16-bit applications can now use the WebBrowser control. The WebBrowser control makes a perfect case study because of its uses a lot of core services offered by windows 9x to 32-bit applications like multi-threading and so on. Thunking is the key to this process and will dictate the whole approach of how exactly controls like this can be used by 16-bit applications.
Thunking
Windows 9x as we saw before has a core composed of both 16-bit and 32-bit components. It also provides a mechanism by which both these components can be combined to provide better performance and legacy compatibility. This mechanism is called Thunking. Thunking is the process of translation during a call between 16-bit and 32-bit code. There are two types of thunking available in Windows 9x, Generic Thunking and Flat Thunking.
Windows 95/98/Me implements a thunking model called the Flat Thunks. This method of thunking allows 32-bit functions to call 16-bit code and vice versa. Flat thunking involves a thunk compiler. This compiler is used to generate stubs for both the 32-bit and 16-bit sides of the code. To use the thunk DLL, simply call the function in the stub DLL, which will in turn call the corresponding function on the other side.
Window NT/2000 implements a different model of thunking called generic thunking. Generic thunking allows a 16-bit function to call a 32-bit code. Windows 9x also implements this method of thunking. Because of the difference in the underlying process model it is not guaranteed that a generic thunking code will work identically under Windows NT/2000 as it does on Windows 9x.
Since we are interested in calling a 32-bit function from a 16-bit DLL and not otherwise it is simpler and more effective using generic thunking. Though generic thunking allows a 16-bit application to call a 32-bit DLL there are a few restrictions on the functionality that can be used in the 32-bit code.
- Any 32-bit DLL called from the 16-bit process will have to use the stack reserved for the 16-bit application (typically between 5-45 KB), which is much smaller compared to the default 1 MB stack used by the 32-bit applications.
- Any 32-bit DLL called from a 16-bit process cannot create threads. They also cannot call any Win32 functions, which creates thread on behalf of the calling application.

One important thing to keep in mind when calling a 32-bit code in Windows 9x is that, when translating pointer from 16:16 pointer to 32-bit pointer of a movable global memory block is to fix the memory. Fixing the memory ensures that the memory block will not be moved until the block is unlocked. If the memory used by the 32-bit side of the code is not fixed its possible that the system might move the memory and the translated 32-bit linear address will no longer be valid. This might cause the application to crash. If the same code is executed in Windows NT/2000 the 16-bit side of the thunk is completely blocked when the 32-bit code is executed this eliminates the need to fix the memory.
Coming back to our problem we can now examine in detail how thunking can be used to use the WebBrowser control or any other ActiveX Control from a 16-bit application. Any 16-bit application using the services 32-bit code needs to take into account the restrictions imposed on the 32-bit side by thunking. Since we are using an existing control it is very important to find out if the WebBrowser control violates any of them, and it does. WebBrowser control uses threads when loading pages. Any 32-bit code called from a 16-bit process cannot create threads. This implies that the WebBrowser control cannot be used as is from a 16-bit process by thunking.
The problem with creating threads on a 32-bit code called from a 16-bit DLL is that Windows 98 runs all the 16-bit applications in the system in a single VM using cooperative multitasking. This will mean that the normal multithreaded applications coded in 32-bit will not be able to run on this environment. To overcome this drawback and to be able to host WebBrowser control in a 16-bit application we must try and introduce a new 32-bit process in to the picture. If we are able to do this then this 32-bit process can then become the parent of the threads that WebBrowser control creates. Though there might be more than one way to introduce a 32-bit process into the system the best method might be to use COM itself to do the job for us. COM provides us a way to create a new component by reusing the code of an existing component called Containment. In other words the outer object uses the inner object to implement itself. We can use this feature of COM to implement an ActiveX control, which will contain th e WebBrowser. If the ActiveX control we implement is made as an ActiveX server EXE then this EXE could act as the parent to the threads that the WebBrowser control needs to create. We can further implement Toolbar and Status Bar in the control to create a well-defined ActiveX control that implements all the functionality needed by the 16-bit application. Now the trick is to implement an ActiveX control container in a 32-bit DLL. This DLL can then be called via thunks whenever the 16-bit process needs to create the WebBrowser control.
The three main components used to achieve the goals are:
- The 16-bit custom control
- The 32-bit ActiveX control
- The 32-bit ActiveX control container

The role of each of these components are explained in details in the coming section
The ActiveX Control
The 32-bit ActiveX control is implemented as an out-of-process server. An out-of-process server is an executable file and the components run in a separate address space than that of the automation controller. This enables the WebBrowser control to work properly even in 16-bit environment as this 32-bit executable can now be the parent of the threads that the WebBrowser control needs to work properly. The control uses the services of the WebBrowser control to display the HTML pages. In other word the control contains the WebBrowser control. This process is called Containment; this is a method by which a control can use the services of another control to implement itself. An outer component now acts as a client to the inner component and whenever it is required to perform a service implemented by the inner component the outer component just calls the inner component to do the same. This method of hosting the WebBrowser control is exactly what we need here as how the control displays the HTML control is of no consequence to the application. Also this method allows the control to extend the WebBrowser's functionality to implement toolbar, status-bar and a property database all of which is needed by the application.
The Control Container
The control container is also a 32-bit DLL, which hosts the ActiveX control. The advantage of writing the container in 32-bit side of the code is that it makes initialising COM and hosting the control straightforward. The same code if written in the 16-bit side will have to use thunking to initialise COM subsystem and also a lot more thunked calls to be able to implement container. A 32-bit DLL that can do all these job and also can export the functions that can be called to initialise and to load the control will be much more effective. Also since we have implemented all the features need in the control, all that the container has to do when the application needs those services is to simply pass them on to the control.
The Custom Control
The custom control is just a small 16-bit custom control that will be loaded on the application when it needs to display HTML pages in its user interface. The custom control is just a stub to the services implemented in the 32-bit side of the system. To the end application it is transparent as to where the actual functionality is implemented. All it knows is that this custom control implements all the services it needs to display HTML pages. The application communicates with the custom control through messages. The custom will receive messages on what service it needs to perform the control just calls the control container passing any parameters supplied to it by the application. All the calls to the control container are made via thunks.
Windows 9x though in its core is a 32-bit operating system still has a lot of 16-bit components like some drivers that haven't still been ported to 32-bit to maintain backward compatibility to 16-bit applications. Device drivers are significant in the sense that there is still a lot of 16-bit code being written to use them. Thunking can be used in these cases to make it code simpler and more efficient.
References
Web site reference:
Newsgroups:
- Experts-Exchange
- microsoft.public.platformsdk.win16
- microsoft.public.platformsdk.com_ole
Books:
- Systems programming for Windows 95 by Walter Oney.


