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, ®_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