Component object model rules
Charlie Kindel program administrator, Windows NT, October 20, 1995
Summary
The purpose of this paper is to provide a fast reference for using and implementing Microsoft Component Object Models (COM). Readers If you want to better understand what is COM, and hidden in its design and motivation, you should read the starting chapters. They are technical specification (MSDN libraries, technical manual) for component object models. The first chapter is a brief introduction, and the second chapter provides a thorough review. The information here is from the technical manual of COM.
Rules 1: IUNKNOWN must be implemented
If an object does not implement at least one minimum level of interface, it is not a Microsoft's Component Object Model (COM).
Interface design rules
The interface must be inherited directly or indirectly from iUnknown. The interface must have a unique recognition (IID). The interface is constant. Any factor in interface definitions cannot be changed once allocated and published IIDs. The member function of the interface should have a return value of the HRESULT type, so that the remote structure can report the remote process call (RPC) error. The string parameter of the interface member function should be Unicode.
Implement IUNKNOWN
The identity of the object. This requires the same physical pointer variable to the QueryInterface call to the given object instance of any particular IUNKNOWN interface. This results in a comparison of the QueryInterface (IID_IUNKNOWN, ...) and results of the two interfaces to determine if they are the same object (the COM object identity). Set of static interfaces. Any interface for accessing an object via queryinterface must be static instead of dynamic. That is, if QueryInterface is a given IID, it always calls the same object (unless you do not think about the situation), if QueryInterface cannot get a given IID, then subsequent object calls to the same IID Will fail. Object integrity. For the disposable interface settings, there must be reflow, symmetry, and transition. That is, the given code is as follows: IA * PA = (Some function retraining an ia *); IB * Pb = null; HRESULT HR; HR = PA-> queryinterface (IID_IB, & PB); // line 4symmetric: PA-> queryinterface IID_IA, ...) Reflexive: if, in line 4, PB WAS successful ibtained, thenpb-> queryinterface (iid_ia, ...) Must succeed (a >> b, then b >> a) .Transitive: if, in line 4, PB WAS SUCCESSFULLY OBTAINED, AND WE DOIC * PC = NULL; HR = Pb-> Queryinterface (IID_IC, & PC); // Line 7and PC Is SuccessFully Obtained in line 7, Thenpa- > Queryinterface (IID_IC, ...) Must succeed (A >> B, And B >> C, THEN A >> C). Minimum reference service size. We need to implement AddRef to maintain a desk, which is large enough to support 2 31 -1 with excellent overall instructions for all interfaces of the given object. A 32-bit unsigned integer is satisfied. Release does not mean failure. If the customer wants to know about the resource has been released, you must use a higher semantic in some object interfaces before calling Release.
Memory management rules
The life management of the interface pointer is always implemented by establishing the AddRef and Release methods on each COM interface. (See "Reference Count Rules" below) The following rules apply to the parameters of the interface member function, including the return value that is not "value" passed. For parameters, the calling program should assign and release memory. The export parameters must be allocated by the called program, released by the calling program with standard COM memory allocation programs. The access parameter is first assigned by the calling program, and it is released and redistributed by the calling program when necessary. As for the export parameters, the calling program is responsible for the release of the final return variable. Standard COM memory allocation programs must be used. If the function returns the code that the call failed, the usually the caller has no way to clear the exit and the export parameters. This leads to some additional rules: When the error returns, the export parameters must be reliably set to clear variables, which cannot have an impact on the calling program. In addition, all export pointer parameters (including call allocation, the appointment structure) must be significantly set to NULL. The most direct method is to set into null in the function description. When an error is returned, all the outlet parameters must be put on hold by the caller (thus maintaining the value initialized by the calling program; if the calling program does not initialize it, it is an export parameter, not an export parameter), or obviously Set to the export error return. Reference count rules:
Rule 1: For each new copy of the interface pointer, the addRef must be called; Release is called when the interface pointer is destroyed, except that the child rules are significantly allowed. The following rules correspond to a non-exceptional situation of rule 1. Rule 1A: The entry exit parameters of the function. The calling program must be ADDREF actual parameters, because when the exit variable is placed on it, it will be released by the schedule. Rule 1B: Get global variables. The local copy of the interface pointer obtained from the existing pointer copy of the global variable must be independently referenced. Since there is a local copy, the modulated function will damage global copies. Rule 1C: There are not many resources required for new pointers. The function uses an intrinsic knowledge synthesis interface pointer instead of from other resources, the new pointer must be initially addRef. Such an important example has an example generation rule, an implementation of IUNKNOWN :: QueryInterface, and so on. Rules 1D: The internal storage pointer copy returns. After the pointer returns, the tuning procedure does not know how its life period and the internal storage copy of the pointer. Therefore, the tuning program must call the addref to the pointer copy before returning. Rules 2: Two or more copies of the interface pointer, their life period and specific knowledge of the end of the relationship code, so that addRef / release can be omitted. From the perspective of COM customers, the reference count is the concept corresponding to the interface. Customers should not think that all interfaces of the object have the same reference count. It should not depend on the return value of AddRef & Release, and it is applied to debug purposes. Pointer stability; see the child part in the OLE help file under "Reference-Counting Rules": "Stabilizing The this Pointer and Keeping It Valid". See the Excellent Technical Articles written by Douglas Hodge "" Managing Object Lifetimes in Ole ", and Kraig Brockschmidt (MSDN Library, Books) Write the INSIDE OLE's third chapter to get more information.
COM application responsibility:
Using COM's processes in one of customers, servers, and object executives, you must be responsible for three things: Determine the COM library is the same as the COM function CobuildVersion. Initialize the COM library by calling Coinitialize before using other functions. The initialization of the COM library is canceled without Couninitialize. These steps have been performed in the process within the process. Server rule
In-process server must output DllgetClassObject and DllcanunloadNow. The server must support COM self-registration. The internal and partial servers should provide OleselfReg strings in their file versions. The server must output DllRegisterServer and DllunregisterServer. The partial server should support the / regserver and / unregserver command line switch.
Generate set objects
Generating a total object is optional and is simple, and there are many benefits. The following rules are used to create a total object (commonly referred to as internal object). By QueryInterface, AddRef, and Release, the internal object of the IUNKNOWN interface performs a reference count of the internal interface, and cannot be authorized to the external unknown pointer. This IUNKNOWN is called implicit IUNKNOWN. The internal object executes the Queryinterface, AddRef, and Release members of the interface, except if IUNKNOWN yourself, must authorize the external unknown pointer. These implementation cannot directly affect the reference count of internal objects. Implicit IUNKNOWN implements QueryInterface operations on internal objects. The set object cannot call AddRef when an externally unknown pointer is taken. If you are created, you need to create a failure with E_UBKNOWN in addition to any interface outside IUNKNOWN.
The following example illustrates the use of the code segments to implement a nested set of class interface object: // CSomeObject is an aggregatable object that implements // IUnknown and ISomeInterfaceclass CSomeObject: public IUnknown {private: DWORD m_cRef; // Object reference countIUnknown * m_pUnkOuter; // Outer unknown, no AddRef // Nested class to implement the ISomeInterface interfaceclass CImpSomeInterface: public ISomeInterface {friend class CSomeObject; private: private: DWORD m_cRef; // Interface ref-count, for debuggingprivate: IUnknown * m_pUnkOuter; // Outerunknown, for delegationprivate: public: private: CImpSomeInterface () {m_cRef = 0;}; private: ~ CImpSomeInterface (void) {}; private: // IUnknown members delegate to the outer unknownprivate: // IUnknown members do not control lifetime of objectprivate: STDMETHODIMP QueryInterface (REFIID riid, void ** ppv) private: {return m_pUnkOuter-> QueryInterface (riid, ppv);}; private: STDMETHODIMP_ (DWORD) AddRef (void) private: {return m_pUnkOuter-> AddRef ();}; Private: stdmethodimp_ (dword) Release (void) private: {return m_Punkouter-> release ();}; rivate: // ISomeInterface membersprivate: STDMETHODIMP SomeMethod (void) private: {return S_OK;}; private:}; private: CImpSomeInterface m_ImpSomeInterface; private: public: private: CSomeObject (IUnknown * pUnkOuter) {m_cRef = 0; // No AddRef necessary if non-NULL as we're aggregated.m_pUnkOuter = pUnkOuter; m_ImpSomeInterface.m_pUnkOuter = pUnkOuter;}; // Static member function for creating new instances (do not use // new directly) .Protects against outer objects asking for interfaces // other than IUnknownstatic HRESULT Create (IUnknown * pUnkOuter, REFIID riid, void ** ppv) {CSomeObject * pObj; if (!! pUnkOuter = NULL && riid = IID_IUnknown) return CLASS_E_NOAGGREGATION; pObj = new CSomeObject (pUnkOuter);
IF (POBJ == NULL) RETURN E_OUTOFMEMORY; / / SET UPUNKUT UNKNOWN for DELEGATION (THE NON-AGGREGATION CASE) IF (PUNKOUTER == NULL) POBJ-> M_PUNKOUTER = (iUnknown *) Pobj; HRESULT HR; IF (failed (hr = pObj-> QueryInterface (riid, (void **) ppv))) delete pObj; return hr;} // Implicit IUnknown members, non-delegating // Implicit QueryInterface only controls inner objectSTDMETHODIMP QueryInterface (REFIID riid, void * * ppv) {* ppv = NULL; if (riid == IID_IUnknown) * ppv = this; if (riid == IID_ISomeInterface) * ppv = & m_ImpSomeInterface; if (NULL == * ppv) return ResultFromScode (E_NOINTERFACE); ((IUnknown *) * ppv) -> addref (); return noerror;}; stdmethodimp_ (dword) addref (void) {return m_cref;}; stdmethodimp_ (dword) release (void) {if (--m_cref! = 0) Return m_cref; delete this; return 0;};}; collection object
When another collection object is generated on an object, the following rules must be followed:
When an internal object is created, the external object must be clearly requested to IUNKNOWN. The external object must protect the RELEASE of the manual reference to the destroyed code when rewarding. If an external object queries either internal object interface, it must call your unknown Release. When this pointer is released, the external object is tight with the internal object pointer to call its own externally unknown addRef.
// Obtaining inner object interface pointerpUnkInner-> QueryInterface (IID_IFoo, & pIFoo); pUnkOuter-> Release (); // Releasing inner object interface pointerpUnkOuter-> AddRef (); pIFoo-> Release (); external object not blindly internal The unrecognized interface of the object is queried unless the operation is a specific purpose for external objects.
Room threading model