270 likes | 290 Views
MidTerm Review. Jim Fawcett Spring 2002. COM Interfaces. Interfaces play a critical role in the COM architecture. Interfaces declare the services provided by their components. They support C++ strong type checking and provide a logical view of the component’s activities.
E N D
MidTerm Review Jim Fawcett Spring 2002
COM Interfaces • Interfaces play a critical role in the COM architecture. • Interfaces declare the services provided by their components. • They support C++ strong type checking and provide a logical view of the component’s activities. • The interfaces a component exposes are the only means of access they provide to clients. • COM interfaces are like C++ class public interfaces, but they do not provide implementations for the functions they declare. • They are often implemented with C++ abstract base classes. • This means that reuse of software implementation through inheritance is not supported by COM. MidTerm Review - Spring 02
COM Interface Policy • COM defines a fundamental interface named IUnknown with three methods: • QueryInterface, used by clients to inquire if a component supports other specific interfaces. • AddRef, used by COM to increment a reference count maintained by all COM objects • Release, used by clients to decrement the reference count when finished with interface. When the reference count decrements to zero the object’s server is unloaded from memory. • All COM interfaces must declare the IUnknown methods, usually done by inheriting from IUnknown. • All COM objects are required to implement the IUnknown inter-face along with their own operations. MidTerm Review - Spring 02
COM Interface Policy • COM requires that: • all calls to QueryInterface for a given interface must return the same pointer value • the set of interfaces accessible from QueryInterface must be fixed • if a client queries for an interface through a pointer to that interface the call must succeed • if a client using a pointer for one interface successfully queries for a second interface the client must be able to successfully query through the second interface pointer for the first interface • if a client successfully queries for a second interface and, using that interface pointer successfully queries for a third interface, then a query using the first interface pointer for the third interface must also succeed. MidTerm Review - Spring 02
COM Interface Policy MidTerm Review - Spring 02
Error Codes • Error codes are returned as HRESULTS by all COM interface functions, with the exception of AddRef() and Release(). • Note that the ISUM interface of the Inside COM example we went over in class had a function that did not return an HRESULT. That violates COM tradition. • Test HRESULTS using the macros: • #define SUCCEEDED(hr) (long(hr) >= 0) • #define FAILED(hr) (long(hr) < 0) • Specific Error Codes: • S_OK : successful normal operation • S_FALSE : return logical false as a success code • E_FAIL : generic failure • E_OUTOFMEMORY : memory allocation failed • E_NOTIMPL : method not implemented • E_UNEXPECTED : method call at incorrect time MidTerm Review - Spring 02
Data Types • Marshaling depends on exact knowledge of the sizes of data types. • C and C++ do not define the sizes of their types. Each compiler and platform may define the sizes as they wish. • COM bases its types on the NDR (Network Data Representation) types which have specified sizes. • COM types suitable for marshaling are defined in wtypes.idl. These declarations include a lot of Windows specific data types as well as types useful for general COM programming. • We can use all these types if we import wtypes.idl in our IDL file. MidTerm Review - Spring 02
IDL Decorations • In order to marshal efficiently COM needs to know the direction of data flow, e.g.: [in], [out], [in, out], [out, retval]and will marshal data only in the direction(s) specified. • For languages that have run-time support like Java and Visual Basic, an out parameter may be decorated with retval, indicating that those environments make the call look like a function return value: IDL : HRESULT Method([in] short arg, [out, retval] short *ret) Visual Basic: Function Method(arg as Integer) As IntegerSince C++, which has no such support, its interface looks like this: C++ : virtual HRESULT __stdcall Method(short arg, short *ret) MidTerm Review - Spring 02
Memory Allocation • Memory for [in] parameters is always allocated and freed by caller. Can use any kind of allocation, e.g., stack, heap, static. • Memory for [out] parameters is always allocated by the method and always freed by the caller. • Method : CoTaskMemAlloc(ULONG size); • Client : CoTaskMemFree(LPVOID pv); • Memory for [in,out] is allocated by caller, may be reallocated by method using: • CoTaskMemRealloc(LPVOID pv, ULONG size) • Must be freed by caller. MidTerm Review - Spring 02
Pointer Decorations • In order to marshal efficiently COM needs to know how pointers will be used: • [ref] : pointers are initialized with valid (non-null) addresses at method invocation. This value can not change during method execution. All [out] pointers must be [ref] • [unique] : pointers may be null, can not be aliased. • [ptr] : same as unique except it can be aliased – requires much more work of marshaler, as it requires duplicate detection. • Example: HRESULT method([in,out,ref] int *pInt); MidTerm Review - Spring 02
Strings • All characters in COM are represented using the OLECHAR data type: • typedef wchar_t OLECHAR • IDL uses the string decoration to tell the marshaler that a null terminated wide char string is being sent: • HRESULT method([in,string] const OLECHAR *pOC); • You can initialize an OLECHAR string this way: • Const OLECHAR *pOC = OLESTR(“this is a string”); • The C Run-Time Library provides two conversion functions: • size_t mbstowcs(wchar_t *pOC, const char *pC, size_t count); • Size_t wcstombs(char *pC, const wchar_t *pOC, size_t count); MidTerm Review - Spring 02
Support for OLECHAR Strings • The C Run-Time Library provides wide char string support that parallels its ANSI char string support, e.g.: • wcslen : return number of characters in string (not equal to number of bytes) • wcscpy : copy a wide source string to a wide destination string. You have to allocate enough memory for the destination. • wcscspn : find a substring in a wide char string • wcschr: : find first occurrence of a char in a wide char string. • wcsrchr : find the last occurrence of a char in wide char string. MidTerm Review - Spring 02
BSTRs • The BSTR type is a derived type used in Visual Basic and Microsoft Java (and presumably C#). BSTRs are recognized by the standard marshalers and used frequently by COM developers. • BSTRs are length-prefixed, null terminated strings of OLECHARs. MidTerm Review - Spring 02
BSTR Memory Allocation • COM expects BSTRs to use a COM memory allocator, and provides several API functions for handling BSTRs, declared in oleauto.h: // allocate and initialize • BSTR SysAllocString(const OLECHAR *pOC); • BSTR SysAllocStringLen(BSTR *pBSTR, const OLECHAR *pOC, UINT count); // reallocate and initialize • INT SysReAllocString(BSTR *pBSTR, const OLECHAR *pOC); • INT SysReAllocStringLen(BSTR *pBSTR, const OLECHAR *pOC, UINT count); // free a BSTR • void SysFreeString(BSTR bstr); // peek at length count as OLECHAR count or byte count • UINT SysStringLen(BSTR bstr); • UINT SysStringByteLen(BSTR bstr) MidTerm Review - Spring 02
BSTR Memory Management • When passing BSTRs as [in] parameters, the caller invokes SysAllocString prior to calling the method and SysFreeString after the method has completed. • When passing strings from a method as an [out] parameter, it is the responsibility of the method to call SysAllocString before passing back the string. The caller releases the memory by calling SysFreeString. • When passing BSTRs as [in, out] parameters, you treat them like [in] parameters. • Reference: If you are going to use BSTRs in your project code, make sure you look carefully at “Strings the OLE Way”, Bruce McKinney, in MSDN online or in help. MidTerm Review - Spring 02
Arrays • Fixed arrays have sized determined at compile-time: HRESULT method([in] double arr[8]); • Conformal arrays have size determined at run-time: HRESULT method([in] long dim, [in,size_is(dim)] double *da); • Varying array sends only part of array: HRESULT method([in,out] long *first, [in,out] long *last, [in,out,first_is(first),length_is(last-first+1),size_is(100)] long *la); • Open array sends part of array – size is determined at run-time. Same as above, except argument of size_is( ) is a variable. MidTerm Review - Spring 02
Other Data Types • We will encounter the Variant and Safe Array data types when we discuss Automation and the IDispatch interface. • A variant is a discriminated (tagged) union that will hold any of a large subset of the IDL data types. There are a set of system functions designed to help manipulate variants. These are declared in oleauto.h • Reference: “The Ultimate Data Type”, Bruce McKinney, MSDN • A Safe Array is a structure that holds, possibly multi-dimensioned, arrays with descriptors of their sizes. There are a set of system functions designed to help manipulate safe arrays. These are declared in oleauto.h • Reference: “The Safe OLE Way of Handling Arrays”, Bruce McKinney, MSDN MidTerm Review - Spring 02
References for IDL • MSDN/Platform SDK/Component Services/Microsoft Interface Definition Language • Essential IDL, Martin Gudgin, Addison Wesley, 2001 • Essential COM, Don Box, Addison Wesley, 1998 • COM IDL & Interface Design, Al Major, WROX, 1999 MidTerm Review - Spring 02
Apartments • Apartments currently come in two flavors: • Single Threaded Apartments (STAs) • COM serializes all out of apartment calls on an STA through a windows message loop. The apartment’s single thread services all method invocations by taking requests off the message queue. • This means that a component that is not thread safe can safely operate in a Win32 multithreaded environment as long as it was created by the thread in an STA. • Multithreaded Apartment (MTA) • COM provides no serialization in an MTA. Any component created in an MTA is expected to provide its own synchronization and thus be thread safe. MidTerm Review - Spring 02
Apartment Rules • A process may have many STAs but only one MTA. • Every COM object belongs to exactly one apartment. • A thread executes in exactly one apartment at a time. When a thread enters an apartment COM marks it with the apartment ID. • Objects may be accessed directly only by threads executing in the apartment of the object. • Objects in an STA will always be accessed by the same thread - the one that created the STA. Thus objects can never run concurrently in an STA MidTerm Review - Spring 02
Creating Apartments • An STA is created when client or EXE-based component calls: CoInitialize(NULL) or CoInitializeEx(NULL,COINIT_APARTMENTTHREADED) • An MTA is created when client or EXE-based server calls: CoInitializeEx(NULL, COINIT_MULTITHREADED)for the first time. Subsequent calls by new threads in the same process result in those threads joining the MTA. MidTerm Review - Spring 02
Joining Apartments • An in-proc component with no threading model announced in registry is loaded into a client’s main (first) STA if one exists. Otherwise COM creates a host STA for the component. • An in-proc component with ThreadingModel=Apartment registry entry is loaded into any client STA that instantiates component. • An in-proc component with ThreadingModel=Free registry entry is loaded into a client’s MTA if one exists. Otherwise COM creates a host MTA for the component. • An in-proc component with ThreadingModel=Both registry entry will be loaded into any client apartment that creates it, either STA or MTA. MidTerm Review - Spring 02
Access Within and Between Apartments • All calls within an apartment are direct - no marshaling involved. • instances in an STA can only be directly accessed by the single thread of that STA. • for an in-proc component, that is the client’s STA thread that created the component • instances in an MTA can be directly and concurrently accessed by any thread in the apartment • All calls into components in another apartment are marshaled. • between processes on remote machines • between two process on the same machine • between two apartments in the same process MidTerm Review - Spring 02
Marshaling Interface Pointers • Interface pointers must always be marshaled between apartments. • Interface pointers are apartment relative. They can only be used by threads in that apartment. • On calls to QueryInterface COM marshals all interface pointers from server to client if they reside in different apartments. • If a server is in-proc, residing in an STA of the client then no marshaling is required to send the interface pointer to the client on the thread of the STA. MidTerm Review - Spring 02
Manual Marshaling of Interface Pointers • Ifyou need to provide access to a component in one apartment to a thread in another apartment then an interface pointer must be marshaled to the other apartment. • If client code needs to transfer the pointer then this is done by marshaling the pointer into a stream using: CoMarshalInterThreadInterfaceInStream. The resulting IStream pointer can then be passed to another apartment in the same process through a global variable. Finally the receiving thread unmarshals the pointer using: CoGetInterfaceAndReleaseStream in a form usable by that apartment. MidTerm Review - Spring 02
Single Threaded Apartments (STAs) • An STA is created when a thread calls CoInitialize(NULL) or CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) • Only one thread may reside in an STA, the thread that made the call to create the STA. That thread must ultimately call CoUninitalize(). The thread is marked with an identifier of the apartment, e.g., OXID. • a process can have zero, one, or many STAs. • An STA owns any COM object instantiated by its thread. Only that thread can make method calls on the component. • During CoInitialize(EX) creating an STA in an apartment separate from the client, COM calls CreateWindow to create a hidden window. All method calls to an STA component are dispatched with that window’s message loop. This means that an STA component must include a windows message loop in the code you write. MidTerm Review - Spring 02
Multithreaded Apartments (MTAs) • An MTA is created when a thread calls CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) • More than one thread may reside in an MTA • the first thread that calls CoInitializeEx(NULL,COINIT_MULTITHREADED) creates the multithreaded apartment and joins it. • subsequent threads in the same process that call CoInitializeEx(NULL, COINIT_MULTITHREADED)join the MTA. • Each thread is marked with an identifier of the apartment, e.g., OXID. • a process can have only one MTA. • An MTA owns any COM object instantiated by any of its threads. Any thread from the MTA can make direct calls to the object. MidTerm Review - Spring 02