ID Number: Q83457
3.10
WINDOWS
Summary:
In general, object linking and embedding (OLE) client and server
applications deal with the data structures defined in the OLE.H header
file. However, these data structures contain only a single pointer to
a table of function pointers (a VTBL structure).
To associate additional data with the item represented by the VTBL
data structure, an application must create a superset of the
appropriate structures in OLE.H. The new structure must contain all
fields of the original structure and any additional application-
specific data desired.
This article describes the OLE data structures and provides examples
of application-specific structures. This article also discusses how
the OLE libraries use pointers to structures and details the most
efficient way to allocate memory for OLE data structures.
More Information:
OLE Data Structures and Parameter Types
---------------------------------------
The OLE.H header file defines five main data structures and a VTBL
structure for each. The following table lists these structures and
their contents:
Data Structure Contents
-------------- --------
OLECLIENT One LPOLECLIENTVTBL
OLECLIENTVTBL One far pointer to the client application's
notification procedure: CallBack.
OLESTREAM One LPOLESTREAMVTBL
OLESTREAMVTBL Far pointers to the stream methods, Get and Put.
OLEOBJECT One LPOLEOBJECTVTBL
OLEOBJECTVTBL Far pointers to object methods. For example:
DoVerb, GetData, Release, and Show.
OLESERVER One LPOLESERVERVTBL
OLESERVERVTBL Far pointers to server methods. For example:
Create, Edit, and Release.
OLESERVERDOC One LPOLESERVERDOCVTBL
OLESERVERDOCVTBL Far pointers to document methods. For example:
GetObject, Release, and SetHostNames.
OLE client applications use only the OLECLIENT and OLESTREAM data
structures and their associated VTBL structures. OLE server
applications use all the structures above except for the OLESTREAM
data structure.
Each of the five data structures above contains only a single pointer
to its associated VTBL data type. An application can achieve the
greatest use of these structures by defining its own data types. Make
sure that the VTBL pointer is the first member of each application-
specific data structure.
For example, the following code defines MYOLESERVER as a replacement
for the OLESERVER data structure:
typedef struct
{
LPOLESERVERVTBL pvtbl; // Standard
LHSERVER lh; // Required by OleRegisterServer
BOOL fRelease; // TRUE if waiting required
BOOL fEmbed; // TRUE if only embedding
BOOL fLink; // TRUE if only linking
WORD wCmdShow; // OLE-specific window show command
HWND hWnd; // Main application window
HANDLE hMem; // Memory handle to this structure
LPDOCUMENT pDoc; // Last document allocated
} MYOLESERVER;
An LPOLESERVERVTBL must be the first member of the MYOLESERVER data
structure. The MYOLESERVER structure also contains other information
that is useful in OLESERVTBL methods, such as a pointer to the default
document.
An application can pass a pointer to a variable of the MYOLESERVER
data type to any OLE function that requires a pointer to an OLESERVER.
OLE will typecast the pointer to an LPOLESERVER to access the VTBL
member, leaving the remainder of the structure untouched.
Any time that an OLE library calls any callback function (method) in
the application, it uses the pointer the application passed in, which
continues to point to the application-specific data.
This technique applies to both client and server applications for all
five data structures. Specifically,
- A pointer to an OLECLIENT structure is provided by the client
application's CallBack function.
- Pointers to OLESTREAM structures are provided by the client
application's Get and Put methods (in OLESTREAMVTBL).
- Pointers to OLEOBJECT, OLESERVER and OLESERVERDOC structures are
provided by the methods defined and referenced in the appropriate
VTBL structures.
During the process of creating an application's methods and callback
functions (to fill in VTBL structures), the application can replace
any standard OLE structure pointer with a pointer to an application-
defined data type. For example, all OLESERVERVTBL methods are defined
to accept an LPOLESERVER type. However, the OLESERVTBL method accepts
the same pointer that the application passed to a function such as
OleRegisterServer, the application can replace the LPOLESERVER type in
the function with an application-defined far pointer data type.
Replacing standard OLE data types with application-defined data types
eliminates local variables in which the application stores a typecast
copy of a parameter with a standard data type. This technique applies
to all five OLE data structures; the client application's CallBack can
change the LPOLECLIENT data type, and other methods can perform
similar changes.
How OLE Uses Pointers, Allocating Structures
--------------------------------------------
OLE uses pointers to structures to allow an application to define its
own data structures as outlined above. However, doing so precludes the
application from running in real mode Windows 3.0. An OLE application
will run only in protected mode.
After an application allocates a data structure, it must lock the
allocated memory to obtain a pointer to the memory. It is important to
plan how the application will use memory to ensure that leaving the
block of memory locked is of little consequence.
If the application allocates local memory with the LocalAlloc
function, specify LMEM_FIXED as the value for the wFlags parameter. Do
not use the alternative method to allocate fixed local memory (calling
LocalAlloc with wFlags set to LMEM_MOVEABLE, followed by a call to
LocalLock) because this creates a "sand bar" in the local heap.
LMEM_FIXED local memory is the better choice for local memory that
will remain locked.
If the application allocates global memory with the GlobalAlloc
function, specify GMEM_MOVEABLE as the wFlags parameter. Then call
GlobalLock to lock the memory. If GMEM_FIXED is specified in the
GlobalAlloc call, the data structure is allocated from the scarce
memory with an address below 1 megabyte (MB). Allocating the object as
GMEM_MOVEABLE and keeping the memory locked enables the application to
retain a pointer to the memory block without creating any "sand bars"
that interfere with memory management.