1 /* Much of this code is from "COM in Plain C" by Jeff Gatt on Code
2    Project. That code is licensed under the Code Project Open License
3    (CPOL). */
4 
5 #include <windows.h>
6 #include <objbase.h>
7 #include <activscp.h>
8 #include <olectl.h>
9 #include <stddef.h>
10 #define FOR_GLUE
11 #include "com_glue.h"
12 
13 // A count of how many objects our DLL has created (by some
14 // app calling our IClassFactory object's CreateInstance())
15 // which have not yet been Release()'d by the app
16 static DWORD		OutstandingObjects;
17 
18 // A count of how many apps have locked our DLL via calling our
19 // IClassFactory object's LockServer()
20 static DWORD		LockCount;
21 
22 // Where I store a pointer to my type library's TYPEINFO
23 static ITypeInfo	*MyTypeInfo;
24 
25 // The MzObj object ////////////////////////////////////////////////////////////
26 
27 // In our .H file, we use a macro which defines our MzObj struct
28 // as so:
29 //
30 // typedef struct {
31 //    IMzObjVtbl  *lpVtbl;
32 // } MzObj;
33 //
34 // In other words, the .H file defines our MzObj to have nothing
35 // but a pointer to its VTable. And of course, every COM object must
36 // start with a pointer to its VTable.
37 //
38 // But we actually want to add some more members to our MzObj.
39 // We just don't want any app to be able to know about, and directly
40 // access, those members. So here we'll define a MyRealMzObj that
41 // contains those extra members. The app doesn't know that we're
42 // really allocating and giving it a MyRealMzObj object. We'll
43 // lie and tell it we're giving a plain old MzObj. That's ok
44 // because a MyRealMzObj starts with the same VTable pointer.
45 //
46 // We add a DWORD reference count so that this MzObj
47 // can be allocated (which we do in our IClassFactory object's
48 // CreateInstance()) and later freed. And, we have an extra
49 // BSTR (pointer) string, which is used by some of the functions we'll
50 // add to MzObj
51 typedef struct {
52   IMzObjVtbl *lpVtbl;
53   DWORD      count;
54   void      *obj;
55   IConnectionPointContainer container;
56   IConnectionPoint          point;
57   IMzObjEvents              *evts;
58 } MyRealMzObj;
59 
60 // Here are MzObj's functions.
61 //
62 // Every COM object's interface must have the 3 functions QueryInterface(),
63 // AddRef(), and Release().
64 //
65 // I also chose to add 2, extra functions to MzObj, which a program
66 // will call with the names GetString and SetString.
67 
68 // MzObj's QueryInterface()
QueryInterface(IMzObj * com_obj,REFIID vTableGuid,void ** ppv)69 static HRESULT STDMETHODCALLTYPE QueryInterface(IMzObj *com_obj, REFIID vTableGuid, void **ppv)
70 {
71   // Because our IMzObj sources events, we must return an
72   // IConnectionPointContainer sub-object if the app asks for one. Because we've
73   // embedded our IConnectionPointContainer object inside of our MyRealIMzObj,
74   // we can get that sub-object very easily using pointer arithmetic
75   if (IsEqualIID(vTableGuid, &IID_IConnectionPointContainer))
76     *ppv = ((unsigned char *)com_obj + offsetof(MyRealMzObj, container));
77   else if (IsEqualIID(vTableGuid, &IID_IConnectionPoint))
78     *ppv = ((unsigned char *)com_obj + offsetof(MyRealMzObj, point));
79 
80   // Check if the GUID matches MzObj VTable's GUID. We gave the C variable name
81   // IID_MzObj to our VTable GUID. We can use an OLE function called
82   // IsEqualIID to do the comparison for us. Also, if the caller passed a
83   // IUnknown GUID, then we'll likewise return the MzObj, since it can
84   // masquerade as an IUnknown object too. Finally, if the called passed a
85   // IDispatch GUID, then we'll return the MzObj, since it can masquerade
86   // as an IDispatch too
87   else if (!IsEqualIID(vTableGuid, &IID_IUnknown) && !IsEqualIID(vTableGuid, &IID_IMzObj) && !IsEqualIID(vTableGuid, &IID_IDispatch))
88     {
89       // We don't recognize the GUID passed to us. Let the caller know this,
90       // by clearing his handle, and returning E_NOINTERFACE.
91       *ppv = 0;
92       return(E_NOINTERFACE);
93     }
94   else
95     // Fill in the caller's handle
96     *ppv = com_obj;
97 
98   // Increment the count of callers who have an outstanding pointer to this object
99   com_obj->lpVtbl->AddRef(com_obj);
100 
101   return(NOERROR);
102 }
103 
104 // MzObj's AddRef()
AddRef(IMzObj * com_obj)105 static ULONG STDMETHODCALLTYPE AddRef(IMzObj *com_obj)
106 {
107   // Increment MzObj's reference count, and return the updated value.
108   // NOTE: We have to typecast to gain access to any data members. These
109   // members are not defined in our .H file (so that an app can't directly
110   // access them). Rather they are defined only above in our MyRealMzObj
111   // struct. So typecast to that in order to access those data members
112   return(++((MyRealMzObj *)com_obj)->count);
113 }
114 
115 // MzObj's Release()
Release(IMzObj * com_obj)116 static ULONG STDMETHODCALLTYPE Release(IMzObj *com_obj)
117 {
118   // Decrement MzObj's reference count. If 0, then we can safely free
119   // this MzObj now
120   if (--((MyRealMzObj *)com_obj)->count == 0)
121     {
122       delete_mzobj(((MyRealMzObj *)com_obj)->obj);
123       GlobalFree(com_obj);
124       InterlockedDecrement(&OutstandingObjects);
125 
126       if (com_can_unregister()) {
127         /* Only allowed object is released... */
128         PostMessage(NULL, WM_QUIT, 0, 0);
129       }
130 
131       return(0);
132     }
133   return(((MyRealMzObj *)com_obj)->count);
134 }
135 
136 // ================== The standard IDispatch functions
137 
138 // This is just a helper function for the IDispatch functions below
loadMyTypeInfo(void)139 static HRESULT loadMyTypeInfo(void)
140 {
141   register HRESULT	hr;
142   LPTYPELIB			pTypeLib;
143 
144   // Load our type library and get a ptr to its TYPELIB. Note: This does an
145   // implicit pTypeLib->lpVtbl->AddRef(pTypeLib)
146   if (!(hr = LoadRegTypeLib(&CLSID_TypeLib, 1, 0, 0, &pTypeLib)))
147     {
148       // Get Microsoft's generic ITypeInfo, giving it our loaded type library. We only
149       // need one of these, and we'll store it in a global Tell Microsoft this is for
150       // our MzObj's VTable, by passing that VTable's GUID
151       if (!(hr = pTypeLib->lpVtbl->GetTypeInfoOfGuid(pTypeLib, &IID_IMzObj, &MyTypeInfo)))
152         {
153           // We no longer need the ptr to the TYPELIB now that we've given it
154           // to Microsoft's generic ITypeInfo. Note: The generic ITypeInfo has done
155           // a pTypeLib->lpVtbl->AddRef(pTypeLib), so this TYPELIB ain't going away
156           // until the generic ITypeInfo does a pTypeLib->lpVtbl->Release too
157           pTypeLib->lpVtbl->Release(pTypeLib);
158 
159           // Since caller wants us to return our ITypeInfo pointer,
160           // we need to increment its reference count. Caller is
161           // expected to Release() it when done
162           MyTypeInfo->lpVtbl->AddRef(MyTypeInfo);
163         }
164     }
165 
166   return(hr);
167 }
168 
169 // MzObj's GetTypeInfoCount()
GetTypeInfoCount(IMzObj * com_obj,UINT * pCount)170 static ULONG STDMETHODCALLTYPE GetTypeInfoCount(IMzObj *com_obj, UINT *pCount)
171 {
172   *pCount = 1;
173   return(S_OK);
174 }
175 
176 // MzObj's GetTypeInfo()
GetTypeInfo(IMzObj * com_obj,UINT itinfo,LCID lcid,ITypeInfo ** pTypeInfo)177 static ULONG STDMETHODCALLTYPE GetTypeInfo(IMzObj *com_obj, UINT itinfo, LCID lcid, ITypeInfo **pTypeInfo)
178 {
179   register HRESULT	hr;
180 
181   // Assume an error
182   *pTypeInfo = 0;
183 
184   if (itinfo)
185     hr = ResultFromScode(DISP_E_BADINDEX);
186 
187   // If our ITypeInfo is already created, just increment its ref count. NOTE: We really should
188   // store the LCID of the currently created TYPEINFO and compare it to what the caller wants.
189   // If no match, unloaded the currently created TYPEINFO, and create the correct one. But since
190   // we support only one language in our IDL file anyway, we'll ignore this
191   else if (MyTypeInfo)
192     {
193       MyTypeInfo->lpVtbl->AddRef(MyTypeInfo);
194       hr = 0;
195     }
196   else
197     {
198       // Load our type library and get Microsoft's generic ITypeInfo object. NOTE: We really
199       // should pass the LCID to match, but since we support only one language in our IDL
200       // file anyway, we'll ignore this
201       hr = loadMyTypeInfo();
202     }
203 
204   if (!hr) *pTypeInfo = MyTypeInfo;
205 
206   return(hr);
207 }
208 
209 // MzObj's GetIDsOfNames()
GetIDsOfNames(IMzObj * com_obj,REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgdispid)210 static ULONG STDMETHODCALLTYPE GetIDsOfNames(IMzObj *com_obj, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
211 {
212   if (!MyTypeInfo)
213     {
214       register HRESULT	hr;
215 
216       if ((hr = loadMyTypeInfo())) return(hr);
217     }
218 
219   // Let OLE32.DLL's DispGetIDsOfNames() do all the real work of using our type
220   // library to look up the DISPID of the requested function in our object
221   return(DispGetIDsOfNames(MyTypeInfo, rgszNames, cNames, rgdispid));
222 }
223 
224 // MzObj's Invoke()
Invoke(IMzObj * com_obj,DISPID dispid,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * params,VARIANT * result,EXCEPINFO * pexcepinfo,UINT * puArgErr)225 static ULONG STDMETHODCALLTYPE Invoke(IMzObj *com_obj, DISPID dispid, REFIID riid, LCID lcid, WORD wFlags,
226                                       DISPPARAMS *params, VARIANT *result, EXCEPINFO *pexcepinfo,
227                                       UINT *puArgErr)
228 {
229   // We implement only a "default" interface
230   if (!IsEqualIID(riid, &IID_NULL))
231     return(DISP_E_UNKNOWNINTERFACE);
232 
233   // We need our type lib's TYPEINFO (to pass to DispInvoke)
234   if (!MyTypeInfo)
235     {
236       register HRESULT	hr;
237 
238       if ((hr = loadMyTypeInfo())) return(hr);
239     }
240 
241   // Let OLE32.DLL's DispInvoke() do all the real work of calling the appropriate
242   // function in our object, and massaging the passed args into the correct format
243   return(DispInvoke(com_obj, MyTypeInfo, dispid, wFlags, params, result, pexcepinfo, puArgErr));
244 }
245 
246 // ================== The following are my own extra functions added to MzObj
247 
Eval(IMzObj * com_obj,BSTR str,BSTR * res)248 static HRESULT STDMETHODCALLTYPE Eval(IMzObj *com_obj, BSTR str, BSTR *res)
249 {
250   if (!str) return(E_POINTER);
251 
252   return mzobj_eval(((MyRealMzObj*)com_obj)->obj, str, res);
253 }
254 
About(IMzObj * com_obj)255 static HRESULT STDMETHODCALLTYPE About(IMzObj *com_obj)
256 {
257   return mzobj_about(((MyRealMzObj*)com_obj)->obj);
258 }
259 
Reset(IMzObj * com_obj)260 static HRESULT STDMETHODCALLTYPE Reset(IMzObj *com_obj)
261 {
262   return mzobj_reset(((MyRealMzObj*)com_obj)->obj);
263 }
264 
265 // Here's MzObj's VTable. It never changes so we can declare it
266 // static
267 static const IMzObjVtbl IMzObj_Vtbl = {QueryInterface,
268                                        AddRef,
269                                        Release,
270                                        GetTypeInfoCount,
271                                        GetTypeInfo,
272                                        GetIDsOfNames,
273                                        Invoke,
274                                        Eval,
275                                        About,
276                                        Reset};
277 
278 
Fire_SchemeError(IMzObj * com_obj,BSTR description)279 VOID Fire_SchemeError(IMzObj *com_obj, BSTR description)
280 {
281   if (((MyRealMzObj*)com_obj)->evts) {
282     VARIANTARG pvars[1];
283     DISPPARAMS disp = { pvars, NULL, 1, 0 };
284     memset(pvars, 0, sizeof(pvars));
285     pvars[0].vt = VT_BSTR;
286     pvars[0].bstrVal = description;
287     ((MyRealMzObj*)com_obj)->evts->lpVtbl->Invoke(((MyRealMzObj*)com_obj)->evts, 0x1, &IID_NULL,
288                                                   LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp,
289                                                   NULL, NULL, NULL);
290   }
291 }
292 
293 // Our IConnectionPointContainer sub-object (for IMzObj) ////////////////////////
294 
QueryInterface_Connect(IConnectionPointContainer * com_obj,REFIID vTableGuid,void ** ppv)295 static STDMETHODIMP QueryInterface_Connect(IConnectionPointContainer *com_obj, REFIID vTableGuid, void **ppv)
296 {
297   // Because this is a sub-object of our IMzObj (ie, MyRealMzObj) object,
298   // we delegate to IMzObj's QueryInterface. And because we embedded the
299   // IConnectionPointContainer directly inside of MyRealMzObj, all we need
300   // is a little pointer arithmetic to get our IMzObj
301   return(QueryInterface((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, container)), vTableGuid, ppv));
302 }
303 
AddRef_Connect(IConnectionPointContainer * com_obj)304 static STDMETHODIMP_(ULONG) AddRef_Connect(IConnectionPointContainer *com_obj)
305 {
306   // Because we're a sub-object of IMzObj, delegate to its AddRef()
307   // in order to increment IMzObj's reference count
308   return(AddRef((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, container))));
309 }
310 
Release_Connect(IConnectionPointContainer * com_obj)311 static STDMETHODIMP_(ULONG) Release_Connect(IConnectionPointContainer *com_obj)
312 {
313   // Because we're a sub-object of IMzObj, delegate to its Release()
314   // in order to decrement IMzObj's reference count
315   return(Release((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, container))));
316 }
317 
EnumConnectionPoints(IConnectionPointContainer * com_obj,IEnumConnectionPoints ** enumPoints)318 static STDMETHODIMP EnumConnectionPoints(IConnectionPointContainer *com_obj, IEnumConnectionPoints **enumPoints)
319 {
320   // The app had better know the GUIDs of whatever objects our
321   // IMzObj supports for callbacks (ie, an IMzObjEvents), because
322   // we're not going to bother providing him with an object to
323   // enumerate the VTable GUIDs of all those supported objects
324   *enumPoints = 0;
325   return(E_NOTIMPL);
326 }
327 
FindConnectionPoint(IConnectionPointContainer * com_obj,REFIID vTableGuid,IConnectionPoint ** ppv)328 static STDMETHODIMP FindConnectionPoint(IConnectionPointContainer *com_obj, REFIID vTableGuid, IConnectionPoint **ppv)
329 {
330   // Is the app asking us to return an IConnectionPoint object it can use
331   // to give us its IMzObjEvents object? The app asks this by passing us
332   // IMzObjEvents VTable's GUID (which we defined in IMzObj.h)
333   if (IsEqualIID(vTableGuid, &DIID_IMzObjEvents))
334     {
335       MyRealMzObj *iExample;
336 
337       // The app obviously wants to connect its IMzObjEvents object
338       // to IMzObj. In order to do that, we need to give the app a
339       // standard IConnectionPoint, so the app can call its Advise function
340       // to give us its IMzObjEvents. This is easy to do since we embedded both
341       // our IConnectionPointContainer and IConnectionPoint inside of our
342       // IMzObj. All we need is a little pointer arithmetic
343       iExample = (MyRealMzObj *)((char *)com_obj - offsetof(MyRealMzObj, container));
344       *ppv = &iExample->point;
345 
346       // Because we're giving the app a pointer to our IConnectionPoint, and
347       // our IConnectionPoint is a sub-object of IMzObj, we need to
348       // increment IMzObj's reference count. The easiest way to do this is to call
349       // our IConnectionPointContainer's AddRef, because all we do there is delegate
350       // to our IMzObj's AddRef
351       AddRef_Connect(com_obj);
352 
353       return(S_OK);
354     }
355 
356   // We don't support any other app objects connecting to IMzObj
357   // events. All we've defined, and support, is an IMzObjEvents object. Tell
358   // the app we don't know anything about the GUID he passed to us, and
359   // do not give him any IConnectPoint object
360   *ppv = 0;
361   return(E_NOINTERFACE);
362 }
363 
364 
365 static const IConnectionPointContainerVtbl IConnectionPointContainer_Vtbl = {QueryInterface_Connect,
366                                                                              AddRef_Connect,
367                                                                              Release_Connect,
368                                                                              EnumConnectionPoints,
369                                                                              FindConnectionPoint};
370 
371 // Our IConnectionPoint sub-object (for IMzObj) ////////////////////////////
372 
QueryInterface_Point(IConnectionPoint * com_obj,REFIID vTableGuid,void ** ppv)373 static STDMETHODIMP QueryInterface_Point(IConnectionPoint *com_obj, REFIID vTableGuid, void **ppv)
374 {
375   // Because this is a sub-object of our IMzObj (ie, MyRealMzObj) object,
376   // we delegate to IMzObj's QueryInterface. And because we embedded the
377   // IConnectionPoint directly inside of MyRealMzObj, all we need
378   // is a little pointer arithmetic to get our IMzObj
379   return(QueryInterface((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point)), vTableGuid, ppv));
380 }
381 
AddRef_Point(IConnectionPoint * com_obj)382 static STDMETHODIMP_(ULONG) AddRef_Point(IConnectionPoint *com_obj)
383 {
384   // Because we're a sub-object of IMzObj, delegate to its AddRef()
385   // in order to increment IMzObj's reference count
386   return(AddRef((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point))));
387 }
388 
Release_Point(IConnectionPoint * com_obj)389 static STDMETHODIMP_(ULONG) Release_Point(IConnectionPoint *com_obj)
390 {
391   // Because we're a sub-object of IMzObj, delegate to its Release()
392   // in order to decrement IMzObj's reference count
393   return(Release((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point))));
394 }
395 
396 // Called by the app to get our IMzObjEvents VTable's GUID (which we defined in IMzObj.h).
397 // The app would call GetConnectionInterface() if it didn't link with IMzObj.h, and
398 // therefore doesn't know our IMzObjEvents VTable's GUID. The app needs to know this GUID
399 // because our Advise function below is going to pass this same GUID to some app object's
400 // QueryInterface. The app's QueryInterface had better recognize this GUID if it intends
401 // to honor our request to give us its IMzObjEvents object
GetConnectionInterface(IConnectionPoint * com_obj,IID * vTableGuid)402 static STDMETHODIMP GetConnectionInterface(IConnectionPoint *com_obj, IID *vTableGuid)
403 {
404   // Tell the app to recognize our IMzObjEvents VTable GUID (defined as
405   // DIID_IFeedback in IMzObj.h) when our Advise function calls
406   // some app QueryInterface function
407   CopyMemory(vTableGuid, &DIID_IMzObjEvents, sizeof(GUID));
408   return(S_OK);
409 }
410 
411 // Called by the app to get the IConnectionPointContainer sub-object for our
412 // IMzObj object.
GetConnectionPointContainer(IConnectionPoint * com_obj,IConnectionPointContainer ** ppv)413 static STDMETHODIMP GetConnectionPointContainer(IConnectionPoint *com_obj, IConnectionPointContainer **ppv)
414 {
415   MyRealMzObj	*iExample;
416 
417   // Get the MyRealMzObj that this IConnectionPoint sub-object belongs
418   // to. Because this IConnectPoint sub-object is embedded directly inside its
419   // MyRealMzObj, all we need is a little pointer arithmetic
420   iExample = (MyRealMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point));
421 
422   // Because the IConnectionPointContainer sub-object is also embedded right inside
423   // the same MyRealMzObj, we can get a pointer to it easily as so
424   *ppv = &iExample->container;
425 
426   // Because we're giving the app a pointer to our IConnectionPointContainer, and
427   // our IConnectionPointContainer is a sub-object of IMzObj, we need to
428   // increment IMzObj's reference count. The easiest way to do this is to call
429   // our IConnectionPoint's AddRef, because all we do there is delegate
430   // to our IMzObj's AddRef
431   AddRef_Point(com_obj);
432 
433   return(S_OK);
434 }
435 
436 // Called by the app to give us its IMzObjEvents object. Actually, the app doesn't
437 // just give us its IMzObjEvents. Rather, the app calls our Advise, passing us some
438 // app object from which we can request the app to give us its IMzObjEvents. All of
439 // this convoluted stuff is a combination of poor pre-planning by Microsoft
440 // programmers when they designed this stuff, as well as the colossal blunder of
441 // designing COM to accommodate the limitations of early, primitive editions of
442 // Visual Basic.
443 //
444 // The second arg passed here is some app object whose QueryInterface function
445 // we call to request the app's IMzObjEvents. We pass the GUID DIID_IMzObjEvents to
446 // this QueryInterface in order to tell the app to give us its IMzObjEvents
Advise(IConnectionPoint * com_obj,IUnknown * obj,DWORD * cookie)447 static STDMETHODIMP Advise(IConnectionPoint *com_obj, IUnknown *obj, DWORD *cookie)
448 {
449   HRESULT			hr;
450   MyRealMzObj	*iExample;
451 
452   // Get the MyRealMzObj that this IConnectionPoint sub-object belongs
453   // to. Because this IConnectPoint sub-object is embedded directly inside its
454   // MyRealMzObj, all we need is a little pointer arithmetic
455   iExample = (MyRealMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point));
456 
457   // We allow only one IMzObjEvents for our IMzObj, so see if the app already
458   // called our Advise(), and we got one. If so, let the app know that it is trying
459   // to give us more IFeedbacks2 than we allow
460   if (iExample->evts) return(CONNECT_E_ADVISELIMIT);
461 
462   // Ok, we haven't yet gotten the one IMzObjEvents we allow from the app. Get the app's
463   // IMzObjEvents object. We do this by calling the QueryInterface function of the
464   // app object passed to us. We pass IMzObjEvents VTable's GUID (which we defined
465   // in IMzObj.h).
466   //
467   // Save the app's IMzObjEvents pointer in our IMzObj feedback member, so we
468   // can get it when we need it
469   hr = obj->lpVtbl->QueryInterface(obj, &DIID_IMzObjEvents, (void **)&iExample->evts);
470 
471   // We need to return (to the app) some value that will clue our Unadvise() function
472   // below how to locate this app IMzObjEvents. The simpliest thing is to just use the
473   // app's IMzObjEvents pointer as that returned value. We may lose some bits here,
474   // but that's ok:
475   *cookie = (DWORD)(intptr_t)iExample->evts;
476 
477   return(hr);
478 }
479 
480 // Called by the app to tell us to stop using, and Release(), its IMzObjEvents object.
481 // The second arg passed here is the value our Advise() function above returned when
482 // we got the IMzObjEvents from the app. This value should help us locate wherever we
483 // stored that IMzObjEvents pointer we got in Advise()
Unadvise(IConnectionPoint * com_obj,DWORD cookie)484 static STDMETHODIMP Unadvise(IConnectionPoint *com_obj, DWORD cookie)
485 {
486   MyRealMzObj	*iExample;
487 
488   // Get the MyRealMzObj that this IConnectionPoint sub-object belongs
489   // to. Because this IConnectPoint sub-object is embedded directly inside its
490   // MyRealMzObj, all we need is a little pointer arithmetic
491   iExample = (MyRealMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point));
492 
493   // Use the passed value to find wherever we stored his IMzObjEvents pointer.
494   // Well, since we allow only one IMzObjEvents for our IMzObj, we already
495   // know we stored it in our IMzObj->feedback member. And Advise()
496   // returned that pointer as the "cookie" value. So we already got the
497   // IMzObjEvents right now.
498   //
499   // Let's just make sure the cookie he passed is really the pointer we expect
500   if (cookie && cookie == (DWORD)(intptr_t)iExample->evts)
501     {
502       // Release the app's IMzObjEvents
503       iExample->evts->lpVtbl->Release(iExample->evts);
504 
505       // We no longer have the app's IMzObjEvents, so clear the IMzObj
506       // feedback member
507       iExample->evts = 0;
508 
509       return(S_OK);
510     }
511   return(CONNECT_E_NOCONNECTION);
512 }
513 
EnumConnections(IConnectionPoint * com_obj,IEnumConnections ** enumConnects)514 static STDMETHODIMP EnumConnections(IConnectionPoint *com_obj, IEnumConnections **enumConnects)
515 {
516   *enumConnects = 0;
517   return(E_NOTIMPL);
518 }
519 
520 
521 static const IConnectionPointVtbl IConnectionPoint_Vtbl = {
522   QueryInterface_Point,
523   AddRef_Point,
524   Release_Point,
525   GetConnectionInterface,
526   GetConnectionPointContainer,
527   Advise,
528   Unadvise,
529   EnumConnections};
530 
531 // The IClassFactory object ///////////////////////////////////////////////////////
532 
533 // Since we only ever need one IClassFactory object, we declare
534 // it static. The only requirement is that we ensure any
535 // access to its members is thread-safe
536 static IClassFactory	MyIClassFactoryObj;
537 
538 // IClassFactory's AddRef()
classAddRef(IClassFactory * com_obj)539 static ULONG STDMETHODCALLTYPE classAddRef(IClassFactory *com_obj)
540 {
541   // Someone is obtaining my IClassFactory, so inc the count of
542   // pointers that I've returned which some app needs to Release()
543   InterlockedIncrement(&OutstandingObjects);
544 
545   // Since we never actually allocate/free an IClassFactory (ie, we
546   // use just 1 static one), we don't need to maintain a separate
547   // reference count for our IClassFactory. We'll just tell the caller
548   // that there's at least one of our IClassFactory objects in existence
549   return(1);
550 }
551 
552 // IClassFactory's QueryInterface()
classQueryInterface(IClassFactory * com_obj,REFIID factoryGuid,void ** ppv)553 static HRESULT STDMETHODCALLTYPE classQueryInterface(IClassFactory *com_obj, REFIID factoryGuid, void **ppv)
554 {
555   // Make sure the caller wants either an IUnknown or an IClassFactory.
556   // In either case, we return the same IClassFactory pointer passed to
557   // us since it can also masquerade as an IUnknown
558   if (IsEqualIID(factoryGuid, &IID_IUnknown) || IsEqualIID(factoryGuid, &IID_IClassFactory))
559     {
560       // Call my IClassFactory's AddRef
561       com_obj->lpVtbl->AddRef(com_obj);
562 
563       // Return (to the caller) a ptr to my IClassFactory
564       *ppv = com_obj;
565 
566       return(NOERROR);
567     }
568 
569   // We don't know about any other GUIDs
570   *ppv = 0;
571   return(E_NOINTERFACE);
572 }
573 
574 // IClassFactory's Release()
classRelease(IClassFactory * com_obj)575 static ULONG STDMETHODCALLTYPE classRelease(IClassFactory *com_obj)
576 {
577   // One less object that an app has not yet Release()'ed
578   return(InterlockedDecrement(&OutstandingObjects));
579 }
580 
581 // IClassFactory's CreateInstance() function. It is called by
582 // someone who has a pointer to our IClassFactory object and now
583 // wants to create and retrieve a pointer to our MzObj
classCreateInstance(IClassFactory * com_obj,IUnknown * punkOuter,REFIID vTableGuid,void ** objHandle)584 static HRESULT STDMETHODCALLTYPE classCreateInstance(IClassFactory *com_obj, IUnknown *punkOuter, REFIID vTableGuid, void **objHandle)
585 {
586   HRESULT hr;
587   IMzObj *thisobj;
588 
589   // Assume an error by clearing caller's handle
590   *objHandle = 0;
591 
592   // We don't support aggregation in this example
593   if (punkOuter)
594     hr = CLASS_E_NOAGGREGATION;
595   else
596     {
597       // Allocate our MzObj object (actually a MyRealMzObj)
598       if (!(thisobj = (IMzObj *)GlobalAlloc(GMEM_FIXED, sizeof(MyRealMzObj))))
599         hr = E_OUTOFMEMORY;
600       else
601         {
602           // Store MzObj's VTable in the object
603           thisobj->lpVtbl = (IMzObjVtbl *)&IMzObj_Vtbl;
604 
605           // Our MyRealIMzObj is a multiple interface object. It has an
606           // IConnectionPointContainer sub-object embedded directly inside of
607           // it. And we just allocated it when we allocated the MyRealIMzObj
608           // above. Now we need to set its VTable into its lpVtbl member and
609           // we're done initializing this sub-object
610           ((MyRealMzObj *)thisobj)->container.lpVtbl = (IConnectionPointContainerVtbl *)&IConnectionPointContainer_Vtbl;
611 
612           // Our MyRealIMzObj also has an IConnectionPoint sub-object
613           // embedded directly inside of it. And we just allocated it when we
614           // allocated the MyRealIMzObj above. Now we need to set its
615           // VTable into its lpVtbl member and we're done initializing this sub-object
616           ((MyRealMzObj *)thisobj)->point.lpVtbl = (IConnectionPointVtbl *)&IConnectionPoint_Vtbl;
617 
618           // Increment the reference count so we can call Release() below and
619           // it will deallocate only if there is an error with QueryInterface()
620           ((MyRealMzObj *)thisobj)->count = 1;
621 
622           // Initialize any other members we added to the MzObj. We added
623           // a string member
624           ((MyRealMzObj *)thisobj)->obj = new_mzobj(thisobj);
625 
626           ((MyRealMzObj *)thisobj)->evts = NULL;
627 
628           // Fill in the caller's handle with a pointer to the MzObj we just
629           // allocated above. We'll let MzObj's QueryInterface do that, because
630           // it also checks the GUID the caller passed, and also increments the
631           // reference count (to 2) if all goes well
632           hr = IMzObj_Vtbl.QueryInterface(thisobj, vTableGuid, objHandle);
633 
634           // Decrement reference count. NOTE: If there was an error in QueryInterface()
635           // then Release() will be decrementing the count back to 0 and will free the
636           // MzObj for us. One error that may occur is that the caller is asking for
637           // some sort of object that we don't support (ie, it's a GUID we don't recognize)
638           IMzObj_Vtbl.Release(thisobj);
639 
640           // If success, inc static object count to keep this DLL loaded
641           if (!hr) InterlockedIncrement(&OutstandingObjects);
642         }
643     }
644 
645   return(hr);
646 }
647 
648 // IClassFactory's LockServer(). It is called by someone
649 // who wants to lock this DLL in memory
classLockServer(IClassFactory * com_obj,BOOL flock)650 static HRESULT STDMETHODCALLTYPE classLockServer(IClassFactory *com_obj, BOOL flock)
651 {
652   if (flock) InterlockedIncrement(&LockCount);
653   else InterlockedDecrement(&LockCount);
654 
655   return(NOERROR);
656 }
657 
658 // IClassFactory's VTable
659 static const IClassFactoryVtbl IClassFactory_Vtbl = {classQueryInterface,
660                                                      classAddRef,
661                                                      classRelease,
662                                                      classCreateInstance,
663                                                      classLockServer};
664 
665 
666 
667 // Miscellaneous functions ///////////////////////////////////////////////////////
668 
669 static DWORD reg_cookie;
670 
com_register()671 HRESULT com_register()
672 {
673   // Initialize my IClassFactory with the pointer to its vtable
674   MyIClassFactoryObj.lpVtbl = (IClassFactoryVtbl *)&IClassFactory_Vtbl;
675 
676   return CoRegisterClassObject(&CLSID_IMzObj, (IUnknown*)&MyIClassFactoryObj,
677                                CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &reg_cookie);
678 }
679 
com_can_unregister()680 int com_can_unregister()
681 /* called from multiple threads */
682 {
683   /* Note that OutstandingObjects will stay at least 1 after the class is registered. */
684   return !((OutstandingObjects > 1) || (LockCount > 0));
685 }
686 
com_unregister()687 int com_unregister()
688 {
689   // If someone has retrieved pointers to any of our objects, and
690   // not yet Release()'ed them, then we return S_FALSE to indicate
691   // not to unload this DLL. Also, if someone has us locked, return
692   // S_FALSE
693   if (!com_can_unregister())
694     return 0;
695   else {
696     if (MyTypeInfo) MyTypeInfo->lpVtbl->Release(MyTypeInfo);
697     CoRevokeClassObject(reg_cookie);
698     return 1;
699   }
700 }
701 
com_get_class_iid()702 const GUID com_get_class_iid()
703 {
704   return IID_IMzObj;
705 }
706