# Overview

Brief overview of Wine's architecture...

## Basic Overview

With the fundamental architecture of Wine stabilizing, and people starting to think that we might soon be ready to actually release this thing, it may be time to take a look at how Wine actually works and operates.

### Wine Overview

Wine is often used as a recursive acronym, standing for "Wine Is Not an Emulator". Sometimes it is also known to be used for "Windows Emulator". In a way, both meanings are correct, only seen from different perspectives. The first meaning says that Wine is not a virtual machine, it does not emulate a CPU, and you are not supposed to install Windows nor any Windows device drivers on top of it; rather, Wine is an implementation of the Windows API, and can be used as a library to port Windows applications to Unix. The second meaning, obviously, is that to Windows binaries (`.exe` files), Wine does look like Windows, and emulates its behaviour and quirks rather closely.

#### Note

The "Emulator" perspective should not be thought of as if Wine is a typical inefficient emulation layer that means Wine can't be anything but slow - the faithfulness to the badly designed Windows API may of course impose a minor overhead in some cases, but this is both balanced out by the higher efficiency of the Unix platforms Wine runs on, and that other possible abstraction libraries (like Motif, GTK+, CORBA, etc) has a runtime overhead typically comparable to Wine's.

### Win16 and Win32

Win16 and Win32 applications have different requirements; for example, Win16 apps expect cooperative multitasking among themselves, and to exist in the same address space, while Win32 apps expect the complete opposite, i.e. preemptive multitasking, and separate address spaces.

Wine now deals with this issue by launching a separate Wine process for each Win32 process, but not for Win16 tasks. Win16 tasks are now run as different intersynchronized threads in the same Wine process; this Wine process is commonly known as a WOW process, referring to a similar mechanism used by Windows NT. Synchronization between the Win16 tasks running in the WOW process is normally done through the Win16 mutex - whenever one of them is running, it holds the Win16 mutex, keeping the others from running. When the task wishes to let the other tasks run, the thread releases the Win16 mutex, and one of the waiting threads will then acquire it and let its task run.

### The Wine server

The Wine server is among the most confusing concepts in Wine. What is its function in Wine? Well, to be brief, it provides Inter-Process Communication (IPC), synchronization, and process/thread management. When the wineserver launches, it creates a Unix socket for the current host based on (see below) your home directory's `.wine` subdirectory (or wherever the `WINEPREFIX` environment variable points) - all Wine processes launched later connects to the wineserver using this socket. (If a wineserver was not already running, the first Wine process will start up the wineserver in auto-terminate mode (i.e. the wineserver will then terminate itself once the last Wine process has terminated).)

In earlier versions of Wine the master socket mentioned above was actually created in the configuration directory; either your home directory's `/wine` subdirectory or wherever the `WINEPREFIX` environment variable points>. Since that might not be possible the socket is actually created within the `/tmp` directory with a name that reflects the configuration directory. This means that there can actually be several separate copies of the wineserver running; one per combination of user and configuration directory. Note that you should not have several users using the same configuration directory at the same time; they will have different copies of the wineserver running and this could well lead to problems with the registry information that they are sharing.

Every thread in each Wine process has its own request buffer, which is shared with the wineserver. When a thread needs to synchronize or communicate with any other thread or process, it fills out its request buffer, then writes a command code through the socket. The wineserver handles the command as appropriate, while the client thread waits for a reply. In some cases, like with the various `WaitFor` synchronization primitives, the server handles it by marking the client thread as waiting and does not send it a reply before the wait condition has been satisfied.

The wineserver itself is a single and separate process and does not have its own threading - instead, it is built on top of a large `poll()` loop that alerts the wineserver whenever anything happens, such as a client having sent a command, or a wait condition having been satisfied. There is thus no danger of race conditions inside the wineserver itself - it is often called upon to do operations that look completely atomic to its clients.

Because the wineserver needs to manage processes, threads, shared handles, synchronization, and any related issues, all the clients' Win32 objects are also managed by the wineserver, and the clients must send requests to the wineserver whenever they need to know any Win32 object handle's associated Unix file descriptor (in which case the wineserver duplicates the file descriptor, transmits it to the client, and leaves it to the client to close the duplicate when the client has finished with it).

### Relays, Thunks, and DLL descriptors

Loading a Windows binary into memory isn't that hard by itself, the hard part is all those various DLLs and entry points it imports and expects to be there and function as expected; this is, obviously, what the entire Wine implementation is all about. Wine contains a range of DLL implementations. Each of the implemented (or half-implemented) DLLs (which can be found in the `dlls/` directory) need to make themselves known to the Wine core through a DLL descriptor. These descriptors point to such things as the DLL's resources and the entry point table.

The DLL descriptor and entry point table is generated by the `winebuild` tool (previously just named `build`), taking DLL specification files with the extension `.spec` as input. The output file contains a global constructor that automatically registers the DLL's descriptor with the Wine core at runtime.

Once an application module wants to import a DLL, Wine will look through its list of registered DLLs (if it's not registered, it will look for it on disk). (Failing that, it will look for a real Windows `.DLL` file to use, and look through its imports, etc.) To resolve the module's imports, Wine looks through the entry point table and finds if it's defined there. (If not, it'll emit the error "No handler for ...", which, if the application called the entry point, is a fatal error.)

Since Wine is 32-bit code itself, and if the compiler supports Windows' calling convention, `stdcall` (`gcc` does), Wine can resolve imports into Win32 code by substituting the addresses of the Wine handlers directly without any thunking layer in between. This eliminates the overhead most people associate with "emulation", and is what the applications expect anyway.

However, if the user specified `WINEDEBUG=+relay` , a thunk layer is inserted between the application imports and the Wine handlers; this layer is known as "relay" because all it does is print out the arguments/return values (by using the argument lists in the DLL descriptor's entry point table), then pass the call on, but it's invaluable for debugging misbehaving calls into Wine code. A similar mechanism also exists between Windows DLLs - Wine can optionally insert thunk layers between them, by using `WINEDEBUG=+snoop`, but since no DLL descriptor information exists for non-Wine DLLs, this is less reliable and may lead to crashes.

For Win16 code, there is no way around thunking - Wine needs to relay between 16-bit and 32-bit code. These thunks switch between the app's 16-bit stack and Wine's 32-bit stack, copies and converts arguments as appropriate, and handles the Win16 mutex. Suffice to say that the kind of intricate stack content juggling this results in, is not exactly suitable study material for beginners.

### Core and non-core DLLs

Wine must at least completely replace the "Big Three" DLLs (KERNEL/KERNEL32, GDI/GDI32, and USER/USER32), which all other DLLs are layered on top of. But since Wine is (for various reasons) leaning towards the NT way of implementing things, the NTDLL is another core DLL to be implemented in Wine, and many KERNEL32 and ADVAPI32 features will be implemented through the NTDLL. The wineserver and the service thread provide the backbone for the implementation of these core DLLs, and integration with the X11 driver (which provides GDI/GDI32 and USER/USER32 functionality along with the Windows standard controls). All non-core DLLs, on the other hand, are expected to only use routines exported by other DLLs (and none of these backbone services directly), to keep the code base as tidy as possible. An example of this is COMCTL32 (Common Controls), which should only use standard GDI32- and USER32-exported routines.

## Module Overview

### KERNEL Module

Needs some content...

### GDI Module

#### X Windows System interface

The X libraries used to implement X clients (such as Wine) do not work properly if multiple threads access the same display concurrently. It is possible to compile the X libraries to perform their own synchronization (initiated by calling `XInitThreads()`). However, Wine does not use this approach. Instead Wine performs its own synchronization using the `wine_tsx11_lock()` / `wine_tsx11_unlock()` functions. This locking protects library access with a critical section, and also arranges things so that X libraries compiled without `-D_REENTRANT` (eg. with global `errno` variable) will work with Wine.

In the past, all calls to X used to go through a wrapper called `TSX...()` (for "Thread Safe X ..."). While it is still being used in the code, it's inefficient as the lock is potentially aquired and released unnecessarily. New code should explicitly aquire the lock.

### USER Module

USER implements windowing and messaging subsystems. It also contains code for common controls and for other miscellaneous stuff (rectangles, clipboard, WNet, etc). Wine USER code is located in `windows/`, `controls/`, and `misc/` directories.

#### Windowing subsystem

`windows/win.c`

`windows/winpos.c`

Windows are arranged into parent/child hierarchy with one common ancestor for all windows (desktop window). Each window structure contains a pointer to the immediate ancestor (parent window if `WS_CHILD` style bit is set), a pointer to the sibling (returned by `GetWindow(..., GW_NEXT)`), a pointer to the owner window (set only for popup window if it was created with valid `hwndParent` parameter), and a pointer to the first child window (`GetWindow(.., GW_CHILD)`). All popup and non-child windows are therefore placed in the first level of this hierarchy and their ancestor link (`wnd->parent`) points to the desktop window.

       Desktop window			- root window
        |     \      `-.
        |      \        `-.
       popup -> wnd1  ->  wnd2		- top level windows
        |       \   `-.      `-.
        |        \     `-.      `-.
       child1  child2 -> child3  child4     - child windows

Horizontal arrows denote sibling relationship, vertical lines - ancestor/child. To summarize, all windows with the same immediate ancestor are sibling windows, all windows which do not have desktop as their immediate ancestor are child windows. Popup windows behave as topmost top-level windows unless they are owned. In this case the only requirement is that they must precede their owners in the top-level sibling list (they are not topmost). Child windows are confined to the client area of their parent windows (client area is where window gets to do its own drawing, non-client area consists of caption, menu, borders, intrinsic scrollbars, and minimize/maximize/close/help buttons).

Another fairly important concept is z-order. It is derived from the ancestor/child hierarchy and is used to determine "above/below" relationship. For instance, in the example above, z-order is

    child1->popup->child2->child3->wnd1->child4->wnd2->desktop.

Current active window ("foreground window" in Win32) is moved to the front of z-order unless its top-level ancestor owns popup windows.

All these issues are dealt with (or supposed to be) in `windows/winpos.c` with `SetWindowPos()` being the primary interface to the window manager.

Wine specifics: in default and managed mode each top-level window gets its own X counterpart with desktop window being basically a fake stub. In desktop mode, however, only desktop window has an X window associated with it. Also, `SetWindowPos()` should eventually be implemented via `Begin/End/DeferWindowPos()` calls and not the other way around.

##### Visible region, clipping region and update region

`windows/dce.c`

`windows/winpos.c`

`windows/painting.c`

        ________________________
       |_________               |  A and B are child windows of C
       |    A    |______        |
       |         |      |       |
       |---------'      |       |
       |   |      B     |       |
       |   |            |       |
       |   `------------'       |
       |                   C    |
       `------------------------'

Visible region determines which part of the window is not obscured by other windows. If a window has the `WS_CLIPCHILDREN` style then all areas below its children are considered invisible. Similarly, if the `WS_CLIPSIBLINGS` bit is in effect then all areas obscured by its siblings are invisible. Child windows are always clipped by the boundaries of their parent windows.

B has a `WS_CLIPSIBLINGS` style:

       .          ______
       :         |      |
       |   ,-----'      |
       |   |      B     | - visible region of B
       |   |            |
       :   `------------'

When the program requests a display context (DC) for a window it can specify an optional clipping region that further restricts the area where the graphics output can appear. This area is calculated as an intersection of the visible region and a clipping region.

Program asked for a DC with a clipping region:

              ______
          ,--|--.   |     .    ,--.
       ,--+--'  |   |     :   _:  |
       |  |   B |   |  => |  |    | - DC region where the painting will
       |  |     |   |     |  |    |   be visible
       `--|-----|---'     :  `----'
          `-----'

When the window manager detects that some part of the window became visible it adds this area to the update region of this window and then generates `WM_ERASEBKGND` and `WM_PAINT` messages. In addition, `WM_NCPAINT` message is sent when the uncovered area intersects a nonclient part of the window. Application must reply to the `WM_PAINT` message by calling the `BeginPaint()`/`EndPaint()` pair of functions. `BeginPaint()` returns a DC that uses accumulated update region as a clipping region. This operation cleans up invalidated area and the window will not receive another `WM_PAINT` until the window manager creates a new update region.

A was moved to the left:

        ________________________       ...          / C update region
       |______                  |     :      .___ /
       | A    |_________        |  => |   ...|___|..
       |      |         |       |     |   :  |   |
       |------'         |       |     |   :  '---'
       |   |      B     |       |     |   :      \
       |   |            |       |     :            \
       |   `------------'       |                    B update region
       |                   C    |
       `------------------------'

Windows maintains a display context cache consisting of entries that include the DC itself, the window to which it belongs, and an optional clipping region (visible region is stored in the DC itself). When an API call changes the state of the window tree, window manager has to go through the DC cache to recalculate visible regions for entries whose windows were involved in the operation. DC entries (DCE) can be either private to the window, or private to the window class, or shared between all windows (Windows 3.1 limits the number of shared DCEs to 5).

#### Messaging subsystem

`windows/queue.c`

`windows/message.c`

Each Windows task/thread has its own message queue - this is where it gets messages from. Messages can be:

1. generated on the fly (`WM_PAINT`, `WM_NCPAINT`, `WM_TIMER`)

2. created by the system (hardware messages)

3. posted by other tasks/threads (`PostMessage`)

4. sent by other tasks/threads (`SendMessage`)

Message priority:

First the system looks for sent messages, then for posted messages, then for hardware messages, then it checks if the queue has the "dirty window" bit set, and, finally, it checks for expired timers. See `windows/message.c`.

From all these different types of messages, only posted messages go directly into the private message queue. System messages (even in Win95) are first collected in the system message queue and then they either sit there until `Get/PeekMessage` gets to process them or, as in Win95, if system queue is getting clobbered, a special thread ("raw input thread") assigns them to the private queues. Sent messages are queued separately and the sender sleeps until it gets a reply. Special messages are generated on the fly depending on the window/queue state. If the window update region is not empty, the system sets the `QS_PAINT` bit in the owning queue and eventually this window receives a `WM_PAINT` message (`WM_NCPAINT` too if the update region intersects with the non-client area). A timer event is raised when one of the queue timers expire. Depending on the timer parameters `DispatchMessage` either calls the callback function or the window procedure. If there are no messages pending the task/thread sleeps until messages appear.

There are several tricky moments (open for discussion) -

- System message order has to be honored and messages should be processed within correct task/thread context. Therefore when `Get/PeekMessage` encounters unassigned system message and this message appears not to be for the current task/thread it should either skip it (or get rid of it by moving it into the private message queue of the target task/thread - Win95, AFAIK) and look further or roll back and then yield until this message gets processed when system switches to the correct context (Win16). In the first case we lose correct message ordering, in the second case we have the infamous synchronous system message queue. Here is a post to one of the OS/2 newsgroup I found to be relevant:

  > by David Charlap

  > " Here's the problem in a nutshell, and there is no good solution. Every possible solution creates a different problem.

  > With a windowing system, events can go to many different windows. Most are sent by applications or by the OS when things relating to that window happen (like repainting, timers, etc.)

  > Mouse input events go to the window you click on (unless some window captures the mouse).

  > So far, no problem. Whenever an event happens, you put a message on the target window's message queue. Every process has a message queue. If the process queue fills up, the messages back up onto the system queue.

  > This is the first cause of apps hanging the GUI. If an app doesn't handle messages and they back up into the system queue, other apps can't get any more messages. The reason is that the next message in line can't go anywhere, and the system won't skip over it.

  > This can be fixed by making apps have bigger private message queues. The SIQ fix does this. PMQSIZE does this for systems without the SIQ fix. Applications can also request large queues on their own.

  > Another source of the problem, however, happens when you include keyboard events. When you press a key, there's no easy way to know what window the keystroke message should be delivered to.

  > Most windowing systems use a concept known as "focus". The window with focus gets all incoming keyboard messages. Focus can be changed from window to window by apps or by users clicking on windows.

  > This is the second source of the problem. Suppose window A has focus. You click on window B and start typing before the window gets focus. Where should the keystrokes go? On the one hand, they should go to A until the focus actually changes to B. On the other hand, you probably want the keystrokes to go to B, since you clicked there first.

  > OS/2's solution is that when a focus-changing event happens (like clicking on a window), OS/2 holds all messages in the system queue until the focus change actually happens. This way, subsequent keystrokes go to the window you clicked on, even if it takes a while for that window to get focus.

  > The downside is that if the window takes a real long time to get focus (maybe it's not handling events, or maybe the window losing focus isn't handling events), everything backs up in the system queue and the system appears hung.

  > There are a few solutions to this problem.

  > One is to make focus policy asynchronous. That is, focus changing has absolutely nothing to do with the keyboard. If you click on a window and start typing before the focus actually changes, the keystrokes go to the first window until focus changes, then they go to the second. This is what X-windows does.

  > Another is what NT does. When focus changes, keyboard events are held in the system message queue, but other events are allowed through. This is "asynchronous" because the messages in the system queue are delivered to the application queues in a different order from that with which they were posted. If a bad app won't handle the "lose focus" message, it's of no consequence - the app receiving focus will get its "gain focus" message, and the keystrokes will go to it.

  > The NT solution also takes care of the application queue filling up problem. Since the system delivers messages asynchronously, messages waiting in the system queue will just sit there and the rest of the messages will be delivered to their apps.

  > The OS/2 SIQ solution is this: When a focus-changing event happens, in addition to blocking further messages from the application queues, a timer is started. When the timer goes off, if the focus change has not yet happened, the bad app has its focus taken away and all messages targeted at that window are skipped. When the bad app finally handles the focus change message, OS/2 will detect this and stop skipping its messages.

  > As for the pros and cons:

  > The X-windows solution is probably the easiest. The problem is that users generally don't like having to wait for the focus to change before they start typing. On many occasions, you can type and the characters end up in the wrong window because something (usually heavy system load) is preventing the focus change from happening in a timely manner.

  > The NT solution seems pretty nice, but making the system message queue asynchronous can cause similar problems to the X-windows problem. Since messages can be delivered out of order, programs must not assume that two messages posted in a particular order will be delivered in that same order. This can break legacy apps, but since Win32 always had an asynchronous queue, it is fair to simply tell app designers "don't do that". It's harder to tell app designers something like that on OS/2 - they'll complain "you changed the rules and our apps are breaking."

  > The OS/2 solution's problem is that nothing happens until you try to change window focus, and then wait for the timeout. Until then, the bad app is not detected and nothing is done."

- Intertask/interthread `SendMessage`. The system has to inform the target queue about the forthcoming message, then it has to carry out the context switch and wait until the result is available. Win16 stores necessary parameters in the queue structure and then calls `DirectedYield()` function. However, in Win32 there could be several messages pending sent by preemptively executing threads, and in this case `SendMessage` has to build some sort of message queue for sent messages. Another issue is what to do with messages sent to the sender when it is blocked inside its own `SendMessage`.

## Wine/Windows DLLs

This document mainly deals with the status of current DLL support by Wine. The Wine ini file currently supports settings to change the load order of DLLs. The load order depends on several issues, which results in different settings for various DLLs.

### Pros of Native DLLs

Native DLLs of course guarantee 100% compatibility for routines they implement. For example, using the native USER DLL would maintain a virtually perfect and Windows 95-like look for window borders, dialog controls, and so on. Using the built-in Wine version of this library, on the other hand, would produce a display that does not precisely mimic that of Windows 95. Such subtle differences can be engendered in other important DLLs, such as the common controls library COMMCTRL or the common dialogs library COMMDLG, when built-in Wine DLLs outrank other types in load order.

More significant, less aesthetically-oriented problems can result if the built-in Wine version of the SHELL DLL is loaded before the native version of this library. SHELL contains routines such as those used by installer utilities to create desktop shortcuts. Some installers might fail when using Wine's built-in SHELL.

### Cons of Native DLLs

Not every application performs better under native DLLs. If a library tries to access features of the rest of the system that are not fully implemented in Wine, the native DLL might work much worse than the corresponding built-in one, if at all. For example, the native Windows GDI library must be paired with a Windows display driver, which of course is not present under Intel Unix and Wine.

Finally, occasionally built-in Wine DLLs implement more features than the corresponding native Windows DLLs. Probably the most important example of such behavior is the integration of Wine with X provided by Wine's built-in USER DLL. Should the native Windows USER library take load-order precedence, such features as the ability to use the clipboard or drag-and-drop between Wine windows and X windows will be lost.

### Deciding Between Native and Built-In DLLs

Clearly, there is no one rule-of-thumb regarding which load-order to use. So, you must become familiar with what specific DLLs do and which other DLLs or features a given library interacts with, and use this information to make a case-by-case decision.

### Load Order for DLLs

Using the DLL sections from the wine configuration file, the load order can be tweaked to a high degree. In general it is advised not to change the settings of the configuration file. The default configuration specifies the right load order for the most important DLLs.

The default load order follows this algorithm: for all DLLs which have a fully-functional Wine implementation, or where the native DLL is known not to work, the built-in library will be loaded first. In all other cases, the native DLL takes load-order precedence.

The `DefaultLoadOrder` from the [DllDefaults] section specifies for all DLLs which version to try first. See manpage for explanation of the arguments.

The [DllOverrides] section deals with DLLs, which need a different-from-default treatment.

The [DllPairs] section is for DLLs, which must be loaded in pairs. In general, these are DLLs for either 16-bit or 32-bit applications. In most cases in Windows, the 32-bit version cannot be used without its 16-bit counterpart. For Wine, it is customary that the 16-bit implementations rely on the 32-bit implementations and cast the results back to 16-bit arguments. Changing anything in this section is bound to result in errors.

For the future, the Wine implementation of Windows DLL seems to head towards unifying the 16 and 32 bit DLLs wherever possible, resulting in larger DLLs. They are stored in the `dlls/` subdirectory using the 32-bit name.

### Understanding What DLLs Do

The following list briefly describes each of the DLLs commonly found in Windows whose load order may be modified during the configuration and compilation of Wine.

(See also `./DEVELOPER-HINTS` or the `dlls/` subdirectory to see which DLLs are currently being rewritten for Wine)

    ADVAPI32.DLL:	   32-bit application advanced programming interfaces
                       like crypto, systeminfo, security and event logging
    AVIFILE.DLL:	   32-bit application programming interfaces for the
                       Audio Video Interleave (AVI) Windows-specific
                       Microsoft audio-video standard
    COMMCTRL.DLL:	   16-bit common controls
    COMCTL32.DLL:	   32-bit common controls
    COMDLG32.DLL:	   32-bit common dialogs
    COMMDLG.DLL:	   16-bit common dialogs
    COMPOBJ.DLL:	   OLE 16- and 32-bit compatibility libraries
    CRTDLL.DLL:	   Microsoft C runtime
    DCIMAN.DLL:	   16-bit
    DCIMAN32.DLL:	   32-bit display controls
    DDEML.DLL:	   DDE messaging
    D3D*.DLL	   DirectX/Direct3D drawing libraries
    DDRAW.DLL:	   DirectX drawing libraries
    DINPUT.DLL:	   DirectX input libraries
    DISPLAY.DLL:	   Display libraries
    DPLAY.DLL, DPLAYX.DLL:  DirectX playback libraries
    DSOUND.DLL:	   DirectX audio libraries
    GDI.DLL:	   16-bit graphics driver interface
    GDI32.DLL:	   32-bit graphics driver interface
    IMAGEHLP.DLL:	   32-bit IMM API helper libraries (for PE-executables)
    IMM32.DLL:	   32-bit IMM API
    IMGUTIL.DLL:
    KERNEL32.DLL	   32-bit kernel DLL
    KEYBOARD.DLL:	   Keyboard drivers
    LZ32.DLL:	   32-bit Lempel-Ziv or LZ file compression
                       used by the installshield installers (???).
    LZEXPAND.DLL:	   LZ file expansion; needed for Windows Setup
    MMSYSTEM.DLL:	   Core of the Windows multimedia system
    MOUSE.DLL:	   Mouse drivers
    MPR.DLL:	   32-bit Windows network interface
    MSACM.DLL:	   Core of the Addressed Call Mode or ACM system
    MSACM32.DLL:	   Core of the 32-bit ACM system
                       Audio Compression Manager ???
    MSNET32.DLL	   32-bit network APIs
    MSVFW32.DLL:	   32-bit Windows video system
    MSVIDEO.DLL:	   16-bit Windows video system
    OLE2.DLL:	   OLE 2.0 libraries
    OLE32.DLL:	   32-bit OLE 2.0 components
    OLE2CONV.DLL:	   Import filter for graphics files
    OLE2DISP.DLL, OLE2NLS.DLL: OLE 2.1 16- and 32-bit interoperability
    OLE2PROX.DLL:	   Proxy server for OLE 2.0
    OLE2THK.DLL:	   Thunking for OLE 2.0
    OLEAUT32.DLL	   32-bit OLE 2.0 automation
    OLECLI.DLL:	   16-bit OLE client
    OLECLI32.DLL:	   32-bit OLE client
    OLEDLG.DLL:	   OLE 2.0 user interface support
    OLESVR.DLL:	   16-bit OLE server libraries
    OLESVR32.DLL:	   32-bit OLE server libraries
    PSAPI.DLL:	   Proces Status API libraries
    RASAPI16.DLL:	   16-bit Remote Access Services libraries
    RASAPI32.DLL:	   32-bit Remote Access Services libraries
    SHELL.DLL:	   16-bit Windows shell used by Setup
    SHELL32.DLL:	   32-bit Windows shell (COM object?)
    TAPI/TAPI32/TAPIADDR:  Telephone API (for Modems)
    W32SKRNL:	   Win32s Kernel ? (not in use for Win95 and up!)
    WIN32S16.DLL:	   Application compatibility for Win32s
    WIN87EM.DLL:	   80387 math-emulation libraries
    WINASPI.DLL:	   Advanced SCSI Peripheral Interface or ASPI libraries
    WINDEBUG.DLL	   Windows debugger
    WINMM.DLL:	   Libraries for multimedia thunking
    WING.DLL:	   Libraries required to "draw" graphics
    WINSOCK.DLL:	   Sockets APIs
    WINSPOOL.DLL:	   Print spooler libraries
    WNASPI32.DLL:	   32-bit ASPI libraries
    WSOCK32.DLL:	   32-bit sockets APIs