How Windows 3.11 Processed OLE Automation Commands
In Microsoft Windows 3.11, Object Linking and Embedding (OLE) 2.0
introduced OLE Automation, enabling applications to expose programmatic
functionality to external scripts and other desktop programs. Because
Windows 3.11 operated as a 16-bit cooperative multitasking environment,
cross-application communication relied on a combination of the system
registration database, Lightweight Remote Procedure Calls (LRPC), shared
global memory, and standard COM interfaces like IDispatch.
This architecture allowed a client application, such as Visual Basic or
Microsoft Excel, to locate, instantiate, and invoke methods on an
external automation server seamlessly.
1. Server Registration and Resolution
The automation lifecycle began with the Windows Registration Database
(REG.DAT). Developers registered their automation servers
by defining a Programmatic Identifier (ProgID), such as
Word.Basic, mapped to a unique 128-bit Class Identifier
(CLSID). The registry mapped the CLSID to the exact file path of the
server's executable (.EXE) or dynamic-link library
(.DLL), along with its exposed interfaces and type
libraries (.TLB).
When a client application requested a connection via an automation
call like CreateObject("Word.Basic"), the OLE subsystem
parsed the registry to resolve the ProgID to its CLSID.
2. Process Instantiation and Interface Acquisition
Once the target application was identified, the OLE runtime evaluated whether an instance of the server was already active:
- Out-of-Process Servers (.EXE): If the server was
not currently executing, the OLE runtime used the standard Windows 3.11
kernel routine
WinExecorLoadModuleto launch the server executable with specific automation switches (such as/Automation). Upon initialization, the server registered its class factory with the system usingCoRegisterClassObject. - In-Process Servers (.DLL): The OLE libraries loaded
the DLL directly into the calling application's task context using
LoadLibrary.
The client then invoked CoCreateInstance to request the
base interface for automation: IDispatch. The server's
class factory instantiated the requested object and returned a pointer
to this interface.
3. Inter-Process Communication via 16-Bit LRPC
Because 16-bit Windows ran applications within segmented memory,
separate .EXE processes could not simply pass raw, native
pointers to one another. To overcome this limitation, Windows 3.11
utilized Lightweight Remote Procedure Calls (LRPC) built atop the
Windows message queue.
When crossing process boundaries:
- The OLE runtime generated a 16-bit proxy object on the client side and a stub object on the server side.
- The proxy serialized (marshalled) the method arguments into a
standard data structure called a
VARIANT, which packaged data types such as integers, strings, and dates into a uniform binary format. - For large data payloads or string transfers, the system utilized
Windows global memory blocks allocated with the
GMEM_DDESHAREflag, making the memory address accessible across separate task selectors. - The proxy posted specialized Windows messages containing pointers to these shared memory buffers directly to the server window’s message loop.
4. Dynamic Execution via IDispatch
OLE Automation in Windows 3.11 primarily used late binding, requiring
a two-step dispatch mechanism handled by the IDispatch
interface:
- Name Resolution (
GetIDsOfNames): The client sent the textual name of the property or method (for example,"Insert") to the server viaIDispatch::GetIDsOfNames. The server matched the string against its internal dispatch tables and returned a 32-bit Dispatch Identifier (DISPID). - Method Invocation (
Invoke): The client calledIDispatch::Invoke, passing theDISPID, execution flags indicating whether the operation was a method call, property read, or property write, and an array ofVARIANTparameters.
5. Cooperative Return and Memory Cleanup
The server task processed the incoming message, unpacked the
parameters from the shared global memory segment, executed the requested
method internally, and populated an output VARIANT with the
return value.
The server then posted an acknowledgment message back to the client’s message queue containing the result. Because Windows 3.11 utilized cooperative multitasking, the client application entered a message dispatch loop while waiting for the server to process the request, yielding control back to the operating system until the server finished the operation. Once received, the proxy unpacked the return value, freed any allocated global memory segments, and returned execution flow to the client's local script or code.