The previous article said how to design a pseudo-com, now let's see how the real COM library is modular.
First of all, the previous section of our implementation of pseudo-com there is nothing to make you uncomfortable place? Yes, I believe a lot of people are very uncomfortable with the need to load DLLs and export interfaces themselves , Microsoft is also very uncomfortable, so the COM library is to solve the problem.
1.COM Library-load and export interface a). Loading
First look at how the COM library solves the problem of loading the DLL. To load the DLL, first to find it, the previous article we assume that the DLL is in the current calling program directory, but the COM standard requires location Transparency -no matter where the DLL can find and load it.
The COM implementation method is simple and writes the location of the DLL to the registry. The following is a typical COM registry,
where the GUID string {fffd-... B52} is the CLSID that indicates the specific Component object in the previous section,
See InprocServer32 content as follows
InprocServer32 meaning is in-process components, ThreadingModel meaning threading model, these later said, now know. focus on the default value is the full path of the component DLL , and when we pass in the CLSID to specify an interface to export the corresponding object, the COM library will help us query the registry to find and load the DLL.
So who wrote this registry, the answer is DLL itself. COM requires DLLs to export registered functions DllUnregisterServer and unregister functions DllUnregisterServer, and they will complete the corresponding registry write and delete operations. Use the command regsvr32 + DLL path to invoke the corresponding DLL registration interface.
In addition, here the ProgID is the moment-readable Component object name corresponding to the CLSID, as follows
You can use the function CLSIDFromProgID to complete the conversion of the ProgID to the CLSID .
b). Export interface
Find the DLL, the next is to export the corresponding interface, here is not like pseudo-COM directly export the corresponding interface, the whole process of reference to the "COM principle and Application" diagram is as follows:
In simple terms, the invocation relationship is as follows:
Customer ->cocreateinstance
COM library ->cogetclassobject-> find dll->dllgetclassobject-> Get factory Object-- Call the CreateInstance method of the factory object to create the Component object
component DLLs, implementing DllGetClassObject export functions, returning factory objects
CoGetClassObject will help us. To find the corresponding DLL based on the CLSID, exporting the corresponding interface is not an interface to directly export the Component object, but rather a factory object to complete the export. The introduction of factory objects rather than direct export is because the use of factory objects can be used as intermediate transitions, such as judging different scenarios to create different component objects.
DllGetClassObject returns the corresponding factory object based on the incoming CLSID, and the factory object further creates the Component object.
c). Component Offload
When we use LoadLibrary to load DLLs, corresponding DLL uninstallation needs to be done using FreeLibrary. So in the COM library, DLL loading is managed by the COM library, how do you know when to unload the DLL? Very simply, the DLL exports a function DllCanUnloadNow, when the COM library detects that this function returns a value of True when it freelibrary, similar to the Java Memory Recycling mechanism.
2.COM Library Interface Implementation a). Register and unregister DLLs
The corresponding two functions are implemented as follows:
Component Registration function Stdapi DllRegisterServer (void) {TCHAR Szmodule[max_path];D Word dwresult =:: GetModuleFileName (G_hmodule, Szmodule, MAX_PATH); if (0 = = dwresult) {return selfreg_e_class;} Return Ctoolhelper::registerserver (Clsid_easycompeople, TEXT ("Easycom.object"), Szmodule,text ("Easycom Component Description ")) ? S_ok:selfreg_e_class;} The component cancels the registration function Stdapi DllUnregisterServer (void) {return ctoolhelper::unregisterserver (clsid_easycompeople, TEXT (" Easycom.object ")) ? S_ok:selfreg_e_class;}
Register to write CLSID, ProgID, InprocServer32 information, anti-registration when the corresponding CLSID deleted.
b). Export interface
Component information Functions Stdapi DllGetClassObject (__in refclsid rclsid, __in refiid riid, LPVOID far* PPV) {if (Rclsid = = clsid_easycom People) {cpeoplefactory *pfactory = new Cpeoplefactory;if (NULL = = pfactory) {return e_fail;} HRESULT hr = Pfactory->queryinterface (riid, PPV); return HR;} Else{return class_e_classnotavailable;}}
This creates a factory object that returns a component of the specified CLSID.
c). Uninstalling components
The corresponding implementations are as follows:
Component Unload function Stdapi dllcanunloadnow (void) {if (G_locknumber = = 0 && g_easycomnumber==0) {return S_OK;} Else{return S_FALSE;}}
You can see here that G_locknumber and G_easycomnumber are both 0 o'clock to tell the COM library that the current component DLL can be unloaded. G_locknumber is a lock on a factory object that locks the factory object so that the DLL component is not unloaded when the component object needs to be persisted to the factory object, and the G_easycomnumber is the reference count of all the component objects , When all the interfaces of a Component object are no longer referenced as 0, they can be unloaded.
Implementation of the 3.COM object a). Component Interface Definition
{2f8c8811-1d6d-4e1b-abd0-686f2641f1c3}_declspec (selectany) GUID clsid_easycompeople = {0x2f8c8811, 0x1d6d, 0X4E1B, {0xAB, 0xd0, 0x68, 0x6f, 0x26, 0x41, 0xf1, 0xc3}};//{F4d72691-1361-4ece-b550-7c753874b880}_declspec (selectany) GUID II D_iage = {0xf4d72691, 0x1361, 0x4ece, {0xb5, 0x50, 0x7c, 0x75, 0x38, 0x74, 0xb8, 0x80}};//{fdfca635-07f6-4ac0-9978-3b 7bf1a4840c}_declspec (selectany) GUID iid_iname = {0xfdfca635, 0x7f6, 0x4ac0, {0x99, 0x78, 0x3b, 0x7b, 0xf1, 0xa4, 0x84, 0XC}};class iage:public iunknown{public:virtual HRESULT stdmethodcalltype printage (int nAge) =0;//must be a virtual function};class IName: Public iunknown{public:virtual HRESULT stdmethodcalltype printname (pwchar szName) = 0;};
Here we implement a Component object that prints personal information, withtwo interfaces, respectively, responsible for printing age and printing names。 b). Component Object Declaration
Class Cpeople:iname, iage{public:cpeople (void); ~cpeople ();//iunknownhresult Stdmethodcalltype QueryInterface (_In_ REFIID riid, _out_ void **ppvobject); ULONG stdmethodcalltype AddRef (void); ULONG stdmethodcalltype Release (void);//inamehresult stdmethodcalltype printname (Pwchar szName);//iagehresult Stdmethodcalltype printage (int nAge);p rivate:ulong m_nref;};
c). Component Object Implementation
Respectively, the constructors, destructors, interface queries, declaration cycle management, and the implementation of each interface. The other basics are consistent with the previous article, but it is important to note that the total Component Object count is increased and decreased in the construction and destructor .
Cpeople::cpeople (void) {g_easycomnumber++;//component reference count This->m_nref = 0;} Cpeople::~cpeople () {g_easycomnumber--;//component reference count}//iunknownhresult stdmethodcalltype cpeople::queryinterface (_In_ REFIID riid, _out_ void **ppvobject) {if (riid = = IID_IUnknown) {*ppvobject = (iage*) this; ((iage*) this)->addref ();} else if (riid = = iid_iage) {*ppvobject = (iage*) This, ((iage*) this)->addref ();} else if (riid = = iid_iname) {*ppvobject = (iname*) This, ((iname*) this)->addref ();} Else{*ppvobject = Null;return e_nointerface;} return S_OK;} ULONG stdmethodcalltype cpeople::addref (void) {m_nref++; #ifdef _debugcout << __function__ << "\ Tdebuginfo-refcount: "<< m_nref << Endl; #endifreturn (ULONG) m_nref;} ULONG stdmethodcalltype cpeople::release (void) {m_nref--; #ifdef _debugcout << __function__ << "\ Tdebuginfo-refcount: "<< m_nref << Endl; #endifif (m_nref = = 0) {#ifdef _debugcout << __function__ <&L T "\trefcount=0=>delete cpeople" << Endl; #endifdeleTe This;return 0;} Return (ULONG) m_nref; Inamehresult stdmethodcalltype cpeople::P rintname (Pwchar szName) {wcout << L "cpeople->iname:my name is" < < SzName << Endl;return S_OK;} Iagehresult stdmethodcalltype cpeople::P rintage (int nAge) {cout << ' cpeople->iage:my age ' << NAge & lt;< Endl;return S_OK;}
d). Declaration and implementation of factory objects
Statement
Class Cpeoplefactory:public iclassfactory{public:cpeoplefactory (void);//iunknownhresult stdmethodcalltype QueryInterface (_in_ refiid riid, _out_ void **ppvobject); ULONG stdmethodcalltype AddRef (void); ULONG stdmethodcalltype Release (void);//iclassfactoryhresult stdmethodcalltype CreateInstance (_in_ IUnknown * Punkouter, _in_ refiid riid, _out_ void **ppvobject); HRESULT stdmethodcalltype lockserver (_in_ BOOL fLock);p rotected:ulong m_nref;};
Realize
Cpeoplefactory::cpeoplefactory (void) {this->m_nref = 0;} Iunknownhresult stdmethodcalltype cpeoplefactory::queryinterface (_in_ refiid riid, _out_ void **ppvObject) {if (riid = = IID_IUnknown) {*ppvobject = (iunknown*) This, ((iunknown*) this)->addref (); else if (riid = = iid_iclassfactory) {*ppvobject = (iclassfactory*) This, ((iclassfactory*) this)->addref ();} Else{*ppvobject = Null;return e_nointerface;} return S_OK;} ULONG stdmethodcalltype cpeoplefactory::addref (void) {M_nref++;return (ULONG) m_nref;} ULONG stdmethodcalltype cpeoplefactory::release (void) {m_nref--;if (M_nref = = 0) {delete This;return 0;} Return (ULONG) m_nref; Iclassfactoryhresult stdmethodcalltype cpeoplefactory::createinstance (_in_ IUnknown *punkouter, _In_ REFIID riid, _ Out_ void **ppvobject) {cpeople *pobj = NULL; HRESULT hr = S_false;*ppvobject = null;//Create Component Object POBJ = new Cpeople;if (POBJ = NULL) {return hr;} Gets the unmanaged first interface pointer hr = Pobj->queryinterface (riid, ppvobject); if (S_OK! = hr) {delete pObj;} return HR;} HRESULT STdmethodcalltype cpeoplefactory::lockserver (_in_ BOOL fLock) {fLock? g_locknumber++: G_locknumber--;return S_OK;}
Visible, in fact, the factory object is also a COM object, except that he is called to the COM library, the equivalent of a standard object , is the COM library and the actual COM object bridge. You can see the factory object, in addition to the query interface and the declaration cycle management, contains the CreateInstance and Lockserver functions, which are used to create the actual COM object, which locks the component DLL when passed in the parameter true and is not unloaded at this time.
It is also important to note that in a factory object there is no need to manipulate the global Component Object Count G_easycomnumber, because the COM library is loading the DLL export interface is bound to not unload the DLL.
4. Running Results
Called as follows
int _tmain (int argc, _tchar* argv[]) {HRESULT hr = S_FALSE; CLSID easycomclsid;iunknown *punknown= null;iage *page= null;iname *pname= null;cout << "easycom Demo:" << en dl;//Initialize COM library if (CoInitialize (NULL)! = S_OK) {cout << "Fail to Initialize COM" << endl;return-1;} Find the corresponding CLSIDHR by the known ProgID =:: CLSIDFromProgID (L "Easycom.object", &easycomclsid); if (hr! = S_OK) {cout << "Fail to Find CLSID "<< endl;return-2;} Create the corresponding interface instance hr = CoCreateInstance (Easycomclsid, NULL, Clsctx_inproc_server, IID_IUnknown, (void * *) &punknown); HR! = S_OK) {cout << "Fail to Create Object" << endl;return-2;} Query interface hr = Punknown->queryinterface (Iid_iname, (void * *) &pname); if (hr! = S_OK) {cout << "Fail to Create Iname "<< endl;return-2;} Pname->printname (L "Wenzhou (http://www.jimwen.net)"); hr = Punknown->queryinterface (Iid_iage, (void * *) & page); if (hr! = S_OK) {cout << "Fail to Create iage" << endl;return-2;} Page->printage (23);//cleanup Work page->release ();p name->release ();p unknown->release (); CoUninitialize (); return 0;}
To make the COM library work properly, you need to call CoInitialize to initialize the COM library and use CoUninitialize to unload the COM library.
The results are shown below
Note the debug information shown in the red box here.
Ignore the contents of the red box first, CoCreateInstance export interface IID_IUnknown, reference count is 1, export interface Iid_iname, reference count is 2, export interface Iid_iage, reference count is 3, logical.
So how did the contents of the red box come from here?
The answer is that this is used by the CoCreateInstance internal call function , each call to the function before the AddRef increase the reference count, passed to the function, the use of re-release, so that the use of the COM object is unloaded.
This article shows the full demo code download link
Original, reprint please indicate from http://blog.csdn.net/wenzhou1219
2. Implement one of the simplest COM