Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1382 lines
40 KiB

  1. //=--------------------------------------------------------------------------=
  2. // AutomationObject.Cpp
  3. //=--------------------------------------------------------------------------=
  4. // Copyright 1995 Microsoft Corporation. All Rights Reserved.
  5. //
  6. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  7. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  8. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  9. // PARTICULAR PURPOSE.
  10. //=--------------------------------------------------------------------------=
  11. //
  12. // all of our objects will inherit from this class to share as much of the same
  13. // code as possible. this super-class contains the unknown, dispatch and
  14. // error info implementations for them.
  15. //
  16. #include "pch.h"
  17. #include "LocalSrv.H"
  18. #include "AutoObj.H"
  19. #include "StdEnum.H"
  20. // for ASSERT and FAIL
  21. //
  22. SZTHISFILE
  23. // private function prototypes
  24. //
  25. void WINAPI CopyAndAddRefObject(void *, const void *, DWORD);
  26. void WINAPI CopyConnectData(void *, const void *, DWORD);
  27. //=--------------------------------------------------------------------------=
  28. // CAutomationObject::CAutomationObject
  29. //=--------------------------------------------------------------------------=
  30. // create the object and initialize the refcount
  31. //
  32. // Parameters:
  33. // IUnknown * - [in] controlling Unknown
  34. // int - [in] the object type that we are
  35. // void * - [in] the VTable of of the object we really are.
  36. //
  37. // Notes:
  38. //
  39. CAutomationObject::CAutomationObject
  40. (
  41. IUnknown *pUnkOuter,
  42. int ObjType,
  43. void *pVTable
  44. )
  45. : CUnknownObject(pUnkOuter, pVTable), m_ObjectType (ObjType)
  46. {
  47. m_fLoadedTypeInfo = FALSE;
  48. #ifdef MDAC_BUILD
  49. m_pTypeLibId = g_pLibid;
  50. #endif
  51. }
  52. //=--------------------------------------------------------------------------=
  53. // CAutomationObject::~CAutomationObject
  54. //=--------------------------------------------------------------------------=
  55. // "I have a rendezvous with Death, At some disputed barricade"
  56. // - Alan Seeger (1888-1916)
  57. //
  58. // Notes:
  59. //
  60. CAutomationObject::~CAutomationObject ()
  61. {
  62. // if we loaded up a type info, release our count on the globally stashed
  63. // type infos, and release if it becomes zero.
  64. //
  65. if (m_fLoadedTypeInfo) {
  66. // we have to crit sect this since it's possible to have more than
  67. // one thread partying with this object.
  68. //
  69. ENTERCRITICALSECTION1(&g_CriticalSection);
  70. ASSERT(CTYPEINFOOFOBJECT(m_ObjectType), "Bogus ref counting on the Type Infos");
  71. CTYPEINFOOFOBJECT(m_ObjectType)--;
  72. // if we're the last one, free that sucker!
  73. //
  74. if (!CTYPEINFOOFOBJECT(m_ObjectType)) {
  75. PTYPEINFOOFOBJECT(m_ObjectType)->Release();
  76. PTYPEINFOOFOBJECT(m_ObjectType) = NULL;
  77. }
  78. LEAVECRITICALSECTION1(&g_CriticalSection);
  79. }
  80. return;
  81. }
  82. //=--------------------------------------------------------------------------=
  83. // CAutomationObject::InternalQueryInterface
  84. //=--------------------------------------------------------------------------=
  85. // the controlling unknown will call this for us in the case where they're
  86. // looking for a specific interface.
  87. //
  88. // Parameters:
  89. // REFIID - [in] interface they want
  90. // void ** - [out] where they want to put the resulting object ptr.
  91. //
  92. // Output:
  93. // HRESULT - S_OK, E_NOINTERFACE
  94. //
  95. // Notes:
  96. //
  97. HRESULT CAutomationObject::InternalQueryInterface
  98. (
  99. REFIID riid,
  100. void **ppvObjOut
  101. )
  102. {
  103. ASSERT(ppvObjOut, "controlling Unknown should be checking this!");
  104. // start looking for the guids we support, namely IDispatch, and the
  105. //
  106. if (DO_GUIDS_MATCH(riid, IID_IDispatch)) {
  107. *ppvObjOut = (void *)(IDispatch *)m_pvInterface;
  108. ((IUnknown *)(*ppvObjOut))->AddRef();
  109. return S_OK;
  110. }
  111. // just get our parent class to process it from here on out.
  112. //
  113. return CUnknownObject::InternalQueryInterface(riid, ppvObjOut);
  114. }
  115. //=--------------------------------------------------------------------------=
  116. // CAutomationObject::GetTypeInfoCount
  117. //=--------------------------------------------------------------------------=
  118. // returns the number of type information interfaces that the object provides
  119. //
  120. // Parameters:
  121. // UINT * - [out] the number of interfaces supported.
  122. //
  123. // Output:
  124. // HRESULT - S_OK, E_NOTIMPL, E_INVALIDARG
  125. //
  126. // Notes:
  127. //
  128. STDMETHODIMP CAutomationObject::GetTypeInfoCount
  129. (
  130. UINT *pctinfo
  131. )
  132. {
  133. // arg checking
  134. //
  135. if (!pctinfo)
  136. return E_INVALIDARG;
  137. // we support GetTypeInfo, so we need to return the count here.
  138. //
  139. *pctinfo = 1;
  140. return S_OK;
  141. }
  142. //=--------------------------------------------------------------------------=
  143. // CAutomationObject::GetTypeInfo
  144. //=--------------------------------------------------------------------------=
  145. // Retrieves a type information object, which can be used to get the type
  146. // information for an interface.
  147. //
  148. // Parameters:
  149. // UINT - [in] the type information they'll want returned
  150. // LCID - [in] the LCID of the type info we want
  151. // ITypeInfo ** - [out] the new type info object.
  152. //
  153. // Output:
  154. // HRESULT - S_OK, E_INVALIDARG, etc.
  155. //
  156. // Notes:
  157. //
  158. STDMETHODIMP CAutomationObject::GetTypeInfo
  159. (
  160. UINT itinfo,
  161. LCID lcid,
  162. ITypeInfo **ppTypeInfoOut
  163. )
  164. {
  165. DWORD dwPathLen;
  166. char szDllPath[MAX_PATH];
  167. HRESULT hr;
  168. ITypeLib *pTypeLib;
  169. ITypeInfo **ppTypeInfo =NULL;
  170. // arg checking
  171. //
  172. if (itinfo != 0)
  173. return DISP_E_BADINDEX;
  174. if (!ppTypeInfoOut)
  175. return E_POINTER;
  176. *ppTypeInfoOut = NULL;
  177. // ppTypeInfo will point to our global holder for this particular
  178. // type info. if it's null, then we have to load it up. if it's not
  179. // NULL, then it's already loaded, and we're happy.
  180. // crit sect this entire nightmare so we're okay with multiple
  181. // threads trying to use this object.
  182. //
  183. ENTERCRITICALSECTION1(&g_CriticalSection);
  184. ppTypeInfo = PPTYPEINFOOFOBJECT(m_ObjectType);
  185. if (*ppTypeInfo == NULL) {
  186. ITypeInfo *pTypeInfoTmp;
  187. HREFTYPE hrefType;
  188. // we don't have the type info around, so go load the sucker.
  189. //
  190. #ifdef MDAC_BUILD
  191. hr = LoadRegTypeLib(*m_pTypeLibId, (USHORT)VERSIONOFOBJECT(m_ObjectType),
  192. (USHORT)VERSIONMINOROFOBJECT(m_ObjectType),
  193. LANG_NEUTRAL, &pTypeLib);
  194. #else
  195. hr = LoadRegTypeLib(*g_pLibid, (USHORT)VERSIONOFOBJECT(m_ObjectType),
  196. (USHORT)VERSIONMINOROFOBJECT(m_ObjectType),
  197. LANG_NEUTRAL, &pTypeLib);
  198. #endif
  199. // if, for some reason, we failed to load the type library this
  200. // way, we're going to try and load the type library directly out of
  201. // our resources. this has the advantage of going and re-setting all
  202. // the registry information again for us.
  203. //
  204. if (FAILED(hr)) {
  205. dwPathLen = GetModuleFileName(g_hInstance, szDllPath, MAX_PATH);
  206. if (!dwPathLen) {
  207. hr = E_FAIL;
  208. goto CleanUp;
  209. }
  210. MAKE_WIDEPTR_FROMANSI(pwsz, szDllPath);
  211. hr = LoadTypeLib(pwsz, &pTypeLib);
  212. CLEANUP_ON_FAILURE(hr);
  213. }
  214. // we've got the Type Library now, so get the type info for the interface
  215. // we're interested in.
  216. //
  217. hr = pTypeLib->GetTypeInfoOfGuid((REFIID)INTERFACEOFOBJECT(m_ObjectType), &pTypeInfoTmp);
  218. pTypeLib->Release();
  219. CLEANUP_ON_FAILURE(hr);
  220. // the following couple of lines of code are to dereference the dual
  221. // interface stuff and take us right to the non dispatch portion of the
  222. // interfaces.
  223. //
  224. hr = pTypeInfoTmp->GetRefTypeOfImplType(0xffffffff, &hrefType);
  225. if (FAILED(hr)) {
  226. pTypeInfoTmp->Release();
  227. goto CleanUp;
  228. }
  229. hr = pTypeInfoTmp->GetRefTypeInfo(hrefType, ppTypeInfo);
  230. pTypeInfoTmp->Release();
  231. CLEANUP_ON_FAILURE(hr);
  232. // add an extra reference to this object. if it ever becomes zero, then
  233. // we need to release it ourselves. crit sect this since more than
  234. // one thread can party on this object.
  235. //
  236. CTYPEINFOOFOBJECT(m_ObjectType)++;
  237. m_fLoadedTypeInfo = TRUE;
  238. }
  239. // we still have to go and addref the Type info object, however, so that
  240. // the people using it can release it.
  241. //
  242. (*ppTypeInfo)->AddRef();
  243. *ppTypeInfoOut = *ppTypeInfo;
  244. hr = S_OK;
  245. CleanUp:
  246. LEAVECRITICALSECTION1(&g_CriticalSection);
  247. return hr;
  248. }
  249. //=--------------------------------------------------------------------------=
  250. // CAutomationObject::GetIDsOfNames
  251. //=--------------------------------------------------------------------------=
  252. // Maps a single member and an optional set of argument names to a
  253. // corresponding set of integer DISPIDs
  254. //
  255. // Parameters:
  256. // REFIID - [in] must be IID_NULL
  257. // OLECHAR ** - [in] array of names to map.
  258. // UINT - [in] count of names in the array.
  259. // LCID - [in] LCID on which to operate
  260. // DISPID * - [in] place to put the corresponding DISPIDs.
  261. //
  262. // Output:
  263. // HRESULT - S_OK, E_OUTOFMEMORY, DISP_E_UNKNOWNNAME,
  264. // DISP_E_UNKNOWNLCID
  265. //
  266. // Notes:
  267. // - we're just going to use DispGetIDsOfNames to save us a lot of hassle,
  268. // and to let this superclass handle it.
  269. //
  270. STDMETHODIMP CAutomationObject::GetIDsOfNames
  271. (
  272. REFIID riid,
  273. OLECHAR **rgszNames,
  274. UINT cNames,
  275. LCID lcid,
  276. DISPID *rgdispid
  277. )
  278. {
  279. HRESULT hr;
  280. ITypeInfo *pTypeInfo;
  281. if (!DO_GUIDS_MATCH(riid, IID_NULL))
  282. return E_INVALIDARG;
  283. // get the type info for this dude!
  284. //
  285. hr = GetTypeInfo(0, lcid, &pTypeInfo);
  286. RETURN_ON_FAILURE(hr);
  287. // use the standard provided routines to do all the work for us.
  288. //
  289. hr = pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgdispid);
  290. pTypeInfo->Release();
  291. return hr;
  292. }
  293. //=--------------------------------------------------------------------------=
  294. // CAutomationObject::Invoke
  295. //=--------------------------------------------------------------------------=
  296. // provides access to the properties and methods on this object.
  297. //
  298. // Parameters:
  299. // DISPID - [in] identifies the member we're working with.
  300. // REFIID - [in] must be IID_NULL.
  301. // LCID - [in] language we're working under
  302. // USHORT - [in] flags, propput, get, method, etc ...
  303. // DISPPARAMS * - [in] array of arguments.
  304. // VARIANT * - [out] where to put result, or NULL if they don't care.
  305. // EXCEPINFO * - [out] filled in in case of exception
  306. // UINT * - [out] where the first argument with an error is.
  307. //
  308. // Output:
  309. // HRESULT - tonnes of them.
  310. //
  311. // Notes:
  312. //
  313. STDMETHODIMP CAutomationObject::Invoke
  314. (
  315. DISPID dispid,
  316. REFIID riid,
  317. LCID lcid,
  318. WORD wFlags,
  319. DISPPARAMS *pdispparams,
  320. VARIANT *pvarResult,
  321. EXCEPINFO *pexcepinfo,
  322. UINT *puArgErr
  323. )
  324. {
  325. HRESULT hr;
  326. ITypeInfo *pTypeInfo;
  327. if (!DO_GUIDS_MATCH(riid, IID_NULL))
  328. return E_INVALIDARG;
  329. // get our typeinfo first!
  330. //
  331. hr = GetTypeInfo(0, lcid, &pTypeInfo);
  332. RETURN_ON_FAILURE(hr);
  333. // Clear exceptions
  334. //
  335. SetErrorInfo(0L, NULL);
  336. // This is exactly what DispInvoke does--so skip the overhead.
  337. //
  338. hr = pTypeInfo->Invoke(m_pvInterface, dispid, wFlags,
  339. pdispparams, pvarResult,
  340. pexcepinfo, puArgErr);
  341. pTypeInfo->Release();
  342. return hr;
  343. }
  344. //=--------------------------------------------------------------------------=
  345. // CAutomationObject::Exception
  346. //=--------------------------------------------------------------------------=
  347. // fills in the rich error info object so that both our vtable bound interfaces
  348. // and calls through ITypeInfo::Invoke get the right error informaiton.
  349. //
  350. // See also the version of Exception() that takes a resource ID instead
  351. // of the actual string for the error message.
  352. //
  353. // Parameters:
  354. // HRESULT - [in] the SCODE that should be associated with this err
  355. // LPWSTR - [in] the text of the error message.
  356. // DWORD - [in] helpcontextid for the error
  357. //
  358. // Output:
  359. // HRESULT - the HRESULT that was passed in.
  360. //
  361. // Notes:
  362. //
  363. HRESULT CAutomationObject::Exception
  364. (
  365. HRESULT hrExcep,
  366. LPWSTR wszException,
  367. DWORD dwHelpContextID
  368. )
  369. {
  370. ICreateErrorInfo *pCreateErrorInfo;
  371. IErrorInfo *pErrorInfo;
  372. WCHAR wszTmp[256];
  373. HRESULT hr;
  374. // first get the createerrorinfo object.
  375. //
  376. hr = CreateErrorInfo(&pCreateErrorInfo);
  377. if (FAILED(hr)) return hrExcep;
  378. MAKE_WIDEPTR_FROMANSI(wszHelpFile, HELPFILEOFOBJECT(m_ObjectType));
  379. // set up some default information on it.
  380. //
  381. hr = pCreateErrorInfo->SetGUID((REFIID)INTERFACEOFOBJECT(m_ObjectType));
  382. ASSERT(SUCCEEDED(hr), "Unable to set GUID of error");
  383. hr = pCreateErrorInfo->SetHelpFile(HELPFILEOFOBJECT(m_ObjectType) ? wszHelpFile : NULL);
  384. ASSERT(SUCCEEDED(hr), "Uable to set help file of error");
  385. hr = pCreateErrorInfo->SetHelpContext(dwHelpContextID);
  386. ASSERT(SUCCEEDED(hr), "Unable to set help context of error");
  387. hr = pCreateErrorInfo->SetDescription(wszException);
  388. ASSERT(SUCCEEDED(hr), "Unable to set description of error");
  389. // load in the source
  390. //
  391. MultiByteToWideChar(CP_ACP, 0, NAMEOFOBJECT(m_ObjectType), -1, wszTmp, 256);
  392. hr = pCreateErrorInfo->SetSource(wszTmp);
  393. ASSERT(SUCCEEDED(hr), "Unable to set source name of error");
  394. // now set the Error info up with the system
  395. //
  396. hr = pCreateErrorInfo->QueryInterface(IID_IErrorInfo, (void **)&pErrorInfo);
  397. CLEANUP_ON_FAILURE(hr);
  398. SetErrorInfo(0, pErrorInfo);
  399. pErrorInfo->Release();
  400. CleanUp:
  401. pCreateErrorInfo->Release();
  402. return hrExcep;
  403. }
  404. //=--------------------------------------------------------------------------=
  405. // CAutomationObject::Exception
  406. //=--------------------------------------------------------------------------=
  407. // fills in the rich error info object so that both our vtable bound interfaces
  408. // and calls through ITypeInfo::Invoke get the right error informaiton.
  409. //
  410. // See also the version of Exception() that takes the actual string of the
  411. // error message instead of a resource ID.
  412. //
  413. // Parameters:
  414. // HRESULT - [in] the SCODE that should be associated with this err
  415. // WORD - [in] the RESOURCE ID of the error message.
  416. // DWORD - [in] helpcontextid for the error
  417. //
  418. // Output:
  419. // HRESULT - the HRESULT that was passed in.
  420. //
  421. // Notes:
  422. //
  423. HRESULT CAutomationObject::Exception
  424. (
  425. HRESULT hrExcep,
  426. WORD idException,
  427. DWORD dwHelpContextID
  428. )
  429. {
  430. char szTmp[256];
  431. WCHAR wszTmp[256];
  432. int cch;
  433. // load in the actual error string value. max of 256.
  434. //
  435. cch = LoadString(GetResourceHandle(), idException, szTmp, 256);
  436. ASSERT(cch != 0, "Resource string for exception not found");
  437. MultiByteToWideChar(CP_ACP, 0, szTmp, -1, wszTmp, 256);
  438. return Exception(hrExcep, wszTmp, dwHelpContextID);
  439. }
  440. //=--------------------------------------------------------------------------=
  441. // CAutomationObject::InterfaceSupportsErrorInfo
  442. //=--------------------------------------------------------------------------=
  443. // indicates whether or not the given interface supports rich error information
  444. //
  445. // Parameters:
  446. // REFIID - [in] the interface we want the answer for.
  447. //
  448. // Output:
  449. // HRESULT - S_OK = Yes, S_FALSE = No.
  450. //
  451. // Notes:
  452. //
  453. HRESULT CAutomationObject::InterfaceSupportsErrorInfo
  454. (
  455. REFIID riid
  456. )
  457. {
  458. // see if it's the interface for the type of object that we are.
  459. //
  460. if (riid == (REFIID)INTERFACEOFOBJECT(m_ObjectType))
  461. return S_OK;
  462. return S_FALSE;
  463. }
  464. //=--------------------------------------------------------------------------=
  465. // CAutomationObject::GetResourceHandle [helper]
  466. //=--------------------------------------------------------------------------=
  467. // virtual routine to get the resource handle. virtual, so that inheriting
  468. // objects, such as COleControl can use theirs instead, which goes and gets
  469. // the Host's version ...
  470. //
  471. // Output:
  472. // HINSTANCE
  473. //
  474. // Notes:
  475. //
  476. HINSTANCE CAutomationObject::GetResourceHandle
  477. (
  478. void
  479. )
  480. {
  481. return ::GetResourceHandle();
  482. }
  483. //////////////////////////////////////////////////////////////////////////////
  484. //////////////////////////////////////////////////////////////////////////////
  485. // //
  486. // CAutomationObjectWEvents //
  487. // //
  488. //////////////////////////////////////////////////////////////////////////////
  489. //////////////////////////////////////////////////////////////////////////////
  490. //////////////////////////////////////////////////////////////////////////////
  491. //=--------------------------------------------------------------------------=
  492. // CAutomationObjectWEvents::CAutomationObjectWEvents
  493. //=--------------------------------------------------------------------------=
  494. // constructor
  495. //
  496. // Parameters:
  497. //
  498. // IUnknown * - [in] controlling Unknown
  499. // int - [in] the object type that we are
  500. // void * - [in] the VTable of of the object we really are.
  501. //
  502. // Notes:
  503. //
  504. CAutomationObjectWEvents::CAutomationObjectWEvents
  505. (
  506. IUnknown *pUnkOuter,
  507. int ObjType,
  508. void *pVTable
  509. )
  510. : CAutomationObject(pUnkOuter, ObjType, pVTable),
  511. m_cpEvents(SINK_TYPE_EVENT),
  512. m_cpPropNotify(SINK_TYPE_PROPNOTIFY)
  513. {
  514. // not much to do yet.
  515. }
  516. //=--------------------------------------------------------------------------=
  517. // CAutomationObjectWEvents::~CAutomationObjectWEvents
  518. //=--------------------------------------------------------------------------=
  519. // virtual destructor
  520. //
  521. // Notes:
  522. //
  523. CAutomationObjectWEvents::~CAutomationObjectWEvents()
  524. {
  525. // homey don't play that
  526. }
  527. //=--------------------------------------------------------------------------=
  528. // CAutomationObjectWEvents::InternalQueryInterface
  529. //=--------------------------------------------------------------------------=
  530. // our internal query interface routine. we only add IConnectionPtContainer
  531. // on top of CAutomationObject
  532. //
  533. // Parameters:
  534. // REFIID - [in] interface they want
  535. // void ** - [out] where they want to put the resulting object ptr.
  536. //
  537. // Output:
  538. // HRESULT - S_OK, E_NOINTERFACE
  539. //
  540. // Notes:
  541. //
  542. HRESULT CAutomationObjectWEvents::InternalQueryInterface
  543. (
  544. REFIID riid,
  545. void **ppvObjOut
  546. )
  547. {
  548. // we only add one interface
  549. //
  550. if (DO_GUIDS_MATCH(riid, IID_IConnectionPointContainer)) {
  551. *ppvObjOut = (IConnectionPointContainer *)this;
  552. ((IUnknown *)(*ppvObjOut))->AddRef();
  553. return S_OK;
  554. }
  555. // just get our parent class to process it from here on out.
  556. //
  557. return CAutomationObject::InternalQueryInterface(riid, ppvObjOut);
  558. }
  559. //=--------------------------------------------------------------------------=
  560. // CAutomationObjectWEvents::FindConnectionPoint [IConnectionPointContainer]
  561. //=--------------------------------------------------------------------------=
  562. // given an IID, find a connection point sink for it.
  563. //
  564. // Parameters:
  565. // REFIID - [in] interfaces they want
  566. // IConnectionPoint ** - [out] where the cp should go
  567. //
  568. // Output:
  569. // HRESULT
  570. //
  571. // Notes:
  572. //
  573. STDMETHODIMP CAutomationObjectWEvents::FindConnectionPoint
  574. (
  575. REFIID riid,
  576. IConnectionPoint **ppConnectionPoint
  577. )
  578. {
  579. CHECK_POINTER(ppConnectionPoint);
  580. // we support the event interface, and IDispatch for it, and we also
  581. // support IPropertyNotifySink.
  582. //
  583. if ((ISVALIDEVENTIID(m_ObjectType) && DO_GUIDS_MATCH(riid, EVENTIIDOFOBJECT(m_ObjectType))) ||
  584. DO_GUIDS_MATCH(riid, IID_IDispatch))
  585. *ppConnectionPoint = &m_cpEvents;
  586. else if (DO_GUIDS_MATCH(riid, IID_IPropertyNotifySink))
  587. *ppConnectionPoint = &m_cpPropNotify;
  588. else
  589. return E_NOINTERFACE;
  590. // generic post-processing.
  591. //
  592. (*ppConnectionPoint)->AddRef();
  593. return S_OK;
  594. }
  595. //=--------------------------------------------------------------------------=
  596. // CAutomationObjectWEvents::EnumConnectionPoints [IConnectionPointContainer]
  597. //=--------------------------------------------------------------------------=
  598. // creates an enumerator for connection points.
  599. //
  600. // Parameters:
  601. // IEnumConnectionPoints ** - [out]
  602. //
  603. // Output:
  604. // HRESULT
  605. //
  606. // Notes:
  607. //
  608. STDMETHODIMP CAutomationObjectWEvents::EnumConnectionPoints
  609. (
  610. IEnumConnectionPoints **ppEnumConnectionPoints
  611. )
  612. {
  613. IConnectionPoint **rgConnectionPoints;
  614. CHECK_POINTER(ppEnumConnectionPoints);
  615. // HeapAlloc an array of connection points [since our standard enum
  616. // assumes this and HeapFree's it later ]
  617. //
  618. rgConnectionPoints = (IConnectionPoint **)CtlHeapAlloc(g_hHeap, 0, sizeof(IConnectionPoint *) * 2);
  619. RETURN_ON_NULLALLOC(rgConnectionPoints);
  620. // we support the event interface for this dude as well as IPropertyNotifySink
  621. //
  622. rgConnectionPoints[0] = &m_cpEvents;
  623. rgConnectionPoints[1] = &m_cpPropNotify;
  624. *ppEnumConnectionPoints = (IEnumConnectionPoints *)(IEnumGeneric *) New CStandardEnum(IID_IEnumConnectionPoints,
  625. 2, sizeof(IConnectionPoint *), (void *)rgConnectionPoints,
  626. CopyAndAddRefObject);
  627. if (!*ppEnumConnectionPoints) {
  628. CtlHeapFree(g_hHeap, 0, rgConnectionPoints);
  629. return E_OUTOFMEMORY;
  630. }
  631. return S_OK;
  632. }
  633. //=--------------------------------------------------------------------------=
  634. // CAutomationObjectWEvents::CConnectionPoint::m_pObject
  635. //=--------------------------------------------------------------------------=
  636. // returns a pointer to the control in which we are nested.
  637. //
  638. // Output:
  639. // CAutomationObjectWEvents *
  640. //
  641. // Notes:
  642. //
  643. inline CAutomationObjectWEvents *CAutomationObjectWEvents::CConnectionPoint::m_pObject
  644. (
  645. void
  646. )
  647. {
  648. return (CAutomationObjectWEvents *)((BYTE *)this - ((m_bType == SINK_TYPE_EVENT)
  649. ? offsetof(CAutomationObjectWEvents, m_cpEvents)
  650. : offsetof(CAutomationObjectWEvents, m_cpPropNotify)));
  651. }
  652. //=--------------------------------------------------------------------------=
  653. // CAutomationObjectWEvents::CConnectionPoint::QueryInterface
  654. //=--------------------------------------------------------------------------=
  655. // standard qi
  656. //
  657. // Parameters:
  658. // REFIID - [in] interface they want
  659. // void ** - [out] where they want to put the resulting object ptr.
  660. //
  661. // Output:
  662. // HRESULT - S_OK, E_NOINTERFACE
  663. //
  664. // Notes:
  665. //
  666. STDMETHODIMP CAutomationObjectWEvents::CConnectionPoint::QueryInterface
  667. (
  668. REFIID riid,
  669. void **ppvObjOut
  670. )
  671. {
  672. if (DO_GUIDS_MATCH(riid, IID_IConnectionPoint) || DO_GUIDS_MATCH(riid, IID_IUnknown)) {
  673. *ppvObjOut = (IConnectionPoint *)this;
  674. AddRef();
  675. return S_OK;
  676. }
  677. return E_NOINTERFACE;
  678. }
  679. //=--------------------------------------------------------------------------=
  680. // CAutomationObjectWEvents::CConnectionPoint::AddRef
  681. //=--------------------------------------------------------------------------=
  682. //
  683. // Output:
  684. // ULONG - the new reference count
  685. //
  686. // Notes:
  687. //
  688. ULONG CAutomationObjectWEvents::CConnectionPoint::AddRef
  689. (
  690. void
  691. )
  692. {
  693. return m_pObject()->ExternalAddRef();
  694. }
  695. //=--------------------------------------------------------------------------=
  696. // CAutomationObjectWEvents::CConnectionPoint::Release
  697. //=--------------------------------------------------------------------------=
  698. //
  699. // Output:
  700. // ULONG - remaining refs
  701. //
  702. // Notes:
  703. //
  704. ULONG CAutomationObjectWEvents::CConnectionPoint::Release
  705. (
  706. void
  707. )
  708. {
  709. return m_pObject()->ExternalRelease();
  710. }
  711. //=--------------------------------------------------------------------------=
  712. // CAutomationObjectWEvents::CConnectionPoint::GetConnectionInterface
  713. //=--------------------------------------------------------------------------=
  714. // returns the interface we support connections on.
  715. //
  716. // Parameters:
  717. // IID * - [out] interface we support.
  718. //
  719. // Output:
  720. // HRESULT
  721. //
  722. // Notes:
  723. //
  724. STDMETHODIMP CAutomationObjectWEvents::CConnectionPoint::GetConnectionInterface
  725. (
  726. IID *piid
  727. )
  728. {
  729. if (m_bType == SINK_TYPE_EVENT && ISVALIDEVENTIID(m_pObject()->m_ObjectType))
  730. *piid = EVENTIIDOFOBJECT(m_pObject()->m_ObjectType);
  731. else
  732. *piid = IID_IPropertyNotifySink;
  733. return S_OK;
  734. }
  735. //=--------------------------------------------------------------------------=
  736. // CAutomationObjectWEvents::CConnectionPoint::GetConnectionPointContainer
  737. //=--------------------------------------------------------------------------=
  738. // returns the connection point container
  739. //
  740. // Parameters:
  741. // IConnectionPointContainer **ppCPC
  742. //
  743. // Output:
  744. // HRESULT
  745. //
  746. // Notes:
  747. //
  748. STDMETHODIMP CAutomationObjectWEvents::CConnectionPoint::GetConnectionPointContainer
  749. (
  750. IConnectionPointContainer **ppCPC
  751. )
  752. {
  753. return m_pObject()->ExternalQueryInterface(IID_IConnectionPointContainer, (void **)ppCPC);
  754. }
  755. //=--------------------------------------------------------------------------=
  756. // CAutomationObjectWEvents::CConnectiontPoint::Advise
  757. //=--------------------------------------------------------------------------=
  758. // someboyd wants to be advised when something happens.
  759. //
  760. // Parameters:
  761. // IUnknown * - [in] guy who wants to be advised.
  762. // DWORD * - [out] cookie
  763. //
  764. // Output:
  765. // HRESULT
  766. //
  767. // Notes:
  768. //
  769. STDMETHODIMP CAutomationObjectWEvents::CConnectionPoint::Advise
  770. (
  771. IUnknown *pUnk,
  772. DWORD *pdwCookie
  773. )
  774. {
  775. HRESULT hr = E_FAIL;
  776. void *pv;
  777. CHECK_POINTER(pdwCookie);
  778. // first, make sure everybody's got what they thinks they got
  779. //
  780. if (m_bType == SINK_TYPE_EVENT)
  781. {
  782. // CONSIDER: 12.95 -- this theoretically is broken -- if they do a find
  783. // connection point on IDispatch, and they just happened to also support
  784. // the Event IID, we'd advise on that. this is not awesome, but will
  785. // prove entirely acceptable short term.
  786. //
  787. ASSERT(hr == E_FAIL, "Somebody has changed our assumption that hr is initialized to E_FAIL");
  788. if (ISVALIDEVENTIID(m_pObject()->m_ObjectType))
  789. hr = pUnk->QueryInterface(EVENTIIDOFOBJECT(m_pObject()->m_ObjectType), &pv);
  790. if (FAILED(hr))
  791. hr = pUnk->QueryInterface(IID_IDispatch, &pv);
  792. }
  793. else
  794. {
  795. hr = pUnk->QueryInterface(IID_IPropertyNotifySink, &pv);
  796. }
  797. RETURN_ON_FAILURE(hr);
  798. // finally, add the sink. it's now been cast to the correct type and has
  799. // been AddRef'd.
  800. //
  801. return AddSink(pv, pdwCookie);
  802. }
  803. //=--------------------------------------------------------------------------=
  804. // CAutomationObjectWEvents::CConnectionPoint::AddSink
  805. //=--------------------------------------------------------------------------=
  806. // in some cases, we'll already have done the QI, and won't need to do the
  807. // work that is done in the Advise routine above. thus, these people can
  808. // just call this instead. [this stems really from IQuickActivate]
  809. //
  810. // Parameters:
  811. // void * - [in] the sink to add. it's already been addref'd
  812. // DWORD * - [out] cookie
  813. //
  814. // Output:
  815. // HRESULT
  816. //
  817. // Notes:
  818. //
  819. HRESULT CAutomationObjectWEvents::CConnectionPoint::AddSink
  820. (
  821. void *pv,
  822. DWORD *pdwCookie
  823. )
  824. {
  825. IUnknown **rgUnkNew;
  826. int i = 0;
  827. // we optimize the case where there is only one sink to not allocate
  828. // any storage. turns out very rarely is there more than one.
  829. //
  830. switch (m_cSinks) {
  831. case 0:
  832. ASSERT(!m_rgSinks, "this should be null when there are no sinks");
  833. m_rgSinks = (IUnknown **)pv;
  834. break;
  835. case 1:
  836. // go ahead and do the initial allocation. we'll get 8 at a time
  837. //
  838. rgUnkNew = (IUnknown **)CtlHeapAlloc(g_hHeap, 0, 8 * sizeof(IUnknown *));
  839. RETURN_ON_NULLALLOC(rgUnkNew);
  840. rgUnkNew[0] = (IUnknown *)m_rgSinks;
  841. rgUnkNew[1] = (IUnknown *)pv;
  842. m_rgSinks = rgUnkNew;
  843. break;
  844. default:
  845. // if we're out of sinks, then we have to increase the size
  846. // of the array
  847. //
  848. if (!(m_cSinks & 0x7)) {
  849. rgUnkNew = (IUnknown **)CtlHeapReAlloc(g_hHeap, 0, m_rgSinks, (m_cSinks + 8) * sizeof(IUnknown *));
  850. RETURN_ON_NULLALLOC(rgUnkNew);
  851. m_rgSinks = rgUnkNew;
  852. } else
  853. rgUnkNew = m_rgSinks;
  854. rgUnkNew[m_cSinks] = (IUnknown *)pv;
  855. break;
  856. }
  857. *pdwCookie = (DWORD)pv;
  858. m_cSinks++;
  859. return S_OK;
  860. }
  861. //=--------------------------------------------------------------------------=
  862. // CAutomationObjectWEvents::CConnectionPoint::Unadvise
  863. //=--------------------------------------------------------------------------=
  864. // they don't want to be told any more.
  865. //
  866. // Parameters:
  867. // DWORD - [in] the cookie we gave 'em.
  868. //
  869. // Output:
  870. // HRESULT
  871. //
  872. // Notes:
  873. //
  874. STDMETHODIMP CAutomationObjectWEvents::CConnectionPoint::Unadvise
  875. (
  876. DWORD dwCookie
  877. )
  878. {
  879. IUnknown *pUnk;
  880. int x;
  881. if (!dwCookie)
  882. return S_OK;
  883. // see how many sinks we've currently got, and deal with things based
  884. // on that.
  885. //
  886. switch (m_cSinks) {
  887. case 1:
  888. // it's the only sink. make sure the ptrs are the same, and
  889. // then free things up
  890. //
  891. if ((DWORD)m_rgSinks != dwCookie)
  892. return CONNECT_E_NOCONNECTION;
  893. m_rgSinks = NULL;
  894. break;
  895. case 2:
  896. // there are two sinks. go back down to one sink scenario
  897. //
  898. if ((DWORD)m_rgSinks[0] != dwCookie && (DWORD)m_rgSinks[1] != dwCookie)
  899. return CONNECT_E_NOCONNECTION;
  900. pUnk = ((DWORD)m_rgSinks[0] == dwCookie)
  901. ? m_rgSinks[1]
  902. : ((DWORD)m_rgSinks[1] == dwCookie) ? m_rgSinks[0] : NULL;
  903. if (!pUnk) return CONNECT_E_NOCONNECTION;
  904. CtlHeapFree(g_hHeap, 0, m_rgSinks);
  905. m_rgSinks = (IUnknown **)pUnk;
  906. break;
  907. default:
  908. // there are more than two sinks. just clean up the hole we've
  909. // got in our array now.
  910. //
  911. for (x = 0; x < m_cSinks; x++) {
  912. if ((DWORD)m_rgSinks[x] == dwCookie)
  913. break;
  914. }
  915. if (x == m_cSinks) return CONNECT_E_NOCONNECTION;
  916. if (x < m_cSinks - 1)
  917. memcpy(&(m_rgSinks[x]), &(m_rgSinks[x + 1]), (m_cSinks -1 - x) * sizeof(IUnknown *));
  918. else
  919. m_rgSinks[x] = NULL;
  920. break;
  921. }
  922. // we're happy
  923. //
  924. m_cSinks--;
  925. ((IUnknown *)dwCookie)->Release();
  926. return S_OK;
  927. }
  928. //=--------------------------------------------------------------------------=
  929. // CAutomationObjectWEvents::CConnectionPoint::EnumConnections
  930. //=--------------------------------------------------------------------------=
  931. // enumerates all current connections
  932. //
  933. // Paramters:
  934. // IEnumConnections ** - [out] new enumerator object
  935. //
  936. // Output:
  937. // HRESULT
  938. //
  939. // NOtes:
  940. //
  941. STDMETHODIMP CAutomationObjectWEvents::CConnectionPoint::EnumConnections
  942. (
  943. IEnumConnections **ppEnumOut
  944. )
  945. {
  946. CONNECTDATA *rgConnectData = NULL;
  947. int i;
  948. if (m_cSinks) {
  949. // allocate some memory big enough to hold all of the sinks.
  950. //
  951. rgConnectData = (CONNECTDATA *)CtlHeapAlloc(g_hHeap, 0, m_cSinks * sizeof(CONNECTDATA));
  952. RETURN_ON_NULLALLOC(rgConnectData);
  953. // fill in the array
  954. //
  955. if (m_cSinks == 1) {
  956. rgConnectData[0].pUnk = (IUnknown *)m_rgSinks;
  957. rgConnectData[0].dwCookie = (DWORD)m_rgSinks;
  958. } else {
  959. // loop through all available sinks.
  960. //
  961. for (i = 0; i < m_cSinks; i++) {
  962. rgConnectData[i].pUnk = m_rgSinks[i];
  963. rgConnectData[i].dwCookie = (DWORD)m_rgSinks[i];
  964. }
  965. }
  966. }
  967. // create yon enumerator object.
  968. //
  969. *ppEnumOut = (IEnumConnections *)(IEnumGeneric *)New CStandardEnum(IID_IEnumConnections,
  970. m_cSinks, sizeof(CONNECTDATA), rgConnectData, CopyConnectData);
  971. if (!*ppEnumOut) {
  972. CtlHeapFree(g_hHeap, 0, rgConnectData);
  973. return E_OUTOFMEMORY;
  974. }
  975. return S_OK;
  976. }
  977. //=--------------------------------------------------------------------------=
  978. // CAutomationObjectWEvents::CConnectionPoint::~CConnectionPoint
  979. //=--------------------------------------------------------------------------=
  980. // cleans up
  981. //
  982. // Notes:
  983. //
  984. CAutomationObjectWEvents::CConnectionPoint::~CConnectionPoint ()
  985. {
  986. int x;
  987. // clean up some memory stuff
  988. //
  989. if (!m_cSinks)
  990. return;
  991. else if (m_cSinks == 1)
  992. ((IUnknown *)m_rgSinks)->Release();
  993. else {
  994. for (x = 0; x < m_cSinks; x++)
  995. QUICK_RELEASE(m_rgSinks[x]);
  996. CtlHeapFree(g_hHeap, 0, m_rgSinks);
  997. }
  998. }
  999. //=--------------------------------------------------------------------------=
  1000. // CAutomationObjectWEvents::CConnectionPiont::DoInvoke
  1001. //=--------------------------------------------------------------------------=
  1002. // fires an event to all listening on our event interface.
  1003. //
  1004. // Parameters:
  1005. // DISPID - [in] event to fire.
  1006. // DISPPARAMS - [in]
  1007. //
  1008. // Notes:
  1009. //
  1010. void CAutomationObjectWEvents::CConnectionPoint::DoInvoke
  1011. (
  1012. DISPID dispid,
  1013. DISPPARAMS *pdispparams
  1014. )
  1015. {
  1016. int iConnection;
  1017. // if we don't have any sinks, then there's nothing to do. we intentionally
  1018. // ignore errors here.
  1019. //
  1020. if (m_cSinks == 0)
  1021. return;
  1022. else if (m_cSinks == 1)
  1023. ((IDispatch *)m_rgSinks)->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, pdispparams, NULL, NULL, NULL);
  1024. else
  1025. for (iConnection = 0; iConnection < m_cSinks; iConnection++)
  1026. ((IDispatch *)m_rgSinks[iConnection])->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, pdispparams, NULL, NULL, NULL);
  1027. }
  1028. //=--------------------------------------------------------------------------=
  1029. // CAutomationObjectWEvents::CConnectionPoint::DoOnChanged
  1030. //=--------------------------------------------------------------------------=
  1031. // fires the OnChanged event for IPropertyNotifySink listeners.
  1032. //
  1033. // Parameters:
  1034. // DISPID - [in] dude that changed.
  1035. //
  1036. // Output:
  1037. // none
  1038. //
  1039. // Notes:
  1040. //
  1041. void CAutomationObjectWEvents::CConnectionPoint::DoOnChanged
  1042. (
  1043. DISPID dispid
  1044. )
  1045. {
  1046. int iConnection;
  1047. // if we don't have any sinks, then there's nothing to do.
  1048. //
  1049. if (m_cSinks == 0)
  1050. return;
  1051. else if (m_cSinks == 1)
  1052. ((IPropertyNotifySink *)m_rgSinks)->OnChanged(dispid);
  1053. else
  1054. for (iConnection = 0; iConnection < m_cSinks; iConnection++)
  1055. ((IPropertyNotifySink *)m_rgSinks[iConnection])->OnChanged(dispid);
  1056. }
  1057. //=--------------------------------------------------------------------------=
  1058. // CAutomationObjectWEvents::CConnectionPoint::DoOnRequestEdit
  1059. //=--------------------------------------------------------------------------=
  1060. // fires the OnRequestEdit for IPropertyNotifySinkListeners
  1061. //
  1062. // Parameters:
  1063. // DISPID - [in] dispid user wants to change.
  1064. //
  1065. // Output:
  1066. // BOOL - false means you cant
  1067. //
  1068. // Notes:
  1069. //
  1070. BOOL CAutomationObjectWEvents::CConnectionPoint::DoOnRequestEdit
  1071. (
  1072. DISPID dispid
  1073. )
  1074. {
  1075. HRESULT hr;
  1076. int iConnection;
  1077. // if we don't have any sinks, then there's nothing to do.
  1078. //
  1079. if (m_cSinks == 0)
  1080. hr = S_OK;
  1081. else if (m_cSinks == 1)
  1082. hr =((IPropertyNotifySink *)m_rgSinks)->OnRequestEdit(dispid);
  1083. else {
  1084. for (iConnection = 0; iConnection < m_cSinks; iConnection++) {
  1085. hr = ((IPropertyNotifySink *)m_rgSinks[iConnection])->OnRequestEdit(dispid);
  1086. if (hr != S_OK) break;
  1087. }
  1088. }
  1089. return (hr == S_OK) ? TRUE : FALSE;
  1090. }
  1091. //=--------------------------------------------------------------------------=
  1092. // CAutomationObjectWEvents::FireEvent
  1093. //=--------------------------------------------------------------------------=
  1094. // fires an event. handles arbitrary number of arguments.
  1095. //
  1096. // Parameters:
  1097. // EVENTINFO * - [in] struct that describes the event.
  1098. // ... - arguments to the event
  1099. //
  1100. // Output:
  1101. // none
  1102. //
  1103. // Notes:
  1104. // - use stdarg's va_* macros.
  1105. //
  1106. void __cdecl CAutomationObjectWEvents::FireEvent
  1107. (
  1108. EVENTINFO *pEventInfo,
  1109. ...
  1110. )
  1111. {
  1112. va_list valist;
  1113. DISPPARAMS dispparams;
  1114. VARIANT rgvParameters[MAX_ARGS];
  1115. VARIANT *pv;
  1116. VARTYPE vt;
  1117. int iParameter;
  1118. int cbSize;
  1119. ASSERT(pEventInfo->cParameters <= MAX_ARGS, "Don't support more than MAX_ARGS params. sorry.");
  1120. va_start(valist, pEventInfo);
  1121. // copy the Parameters into the rgvParameters array. make sure we reverse
  1122. // them for automation
  1123. //
  1124. pv = &(rgvParameters[pEventInfo->cParameters - 1]);
  1125. for (iParameter = 0; iParameter < pEventInfo->cParameters; iParameter++) {
  1126. // CONSIDER: are we properly handling all vartypes, e.g., VT_DECIMAL
  1127. vt = pEventInfo->rgTypes[iParameter];
  1128. // if it's a by value variant, then just copy the whole
  1129. // dang thing
  1130. //
  1131. if (vt == VT_VARIANT)
  1132. *pv = va_arg(valist, VARIANT);
  1133. else {
  1134. // copy the vt and the data value.
  1135. //
  1136. pv->vt = vt;
  1137. if (vt & VT_BYREF)
  1138. cbSize = sizeof(void *);
  1139. else
  1140. cbSize = g_rgcbDataTypeSize[vt];
  1141. // small optimization -- we can copy 2/4 bytes over quite
  1142. // quickly.
  1143. //
  1144. if (cbSize == sizeof(short))
  1145. V_I2(pv) = va_arg(valist, short);
  1146. else if (cbSize == 4) {
  1147. if (vt == VT_R4)
  1148. V_R4(pv) = va_arg(valist, float);
  1149. else
  1150. V_I4(pv) = va_arg(valist, long);
  1151. }
  1152. else {
  1153. // copy over 8 bytes
  1154. //
  1155. ASSERT(cbSize == 8, "don't recognize the type!!");
  1156. if ((vt == VT_R8) || (vt == VT_DATE))
  1157. V_R8(pv) = va_arg(valist, double);
  1158. else
  1159. V_CY(pv) = va_arg(valist, CURRENCY);
  1160. }
  1161. }
  1162. pv--;
  1163. }
  1164. // fire the event
  1165. //
  1166. dispparams.rgvarg = rgvParameters;
  1167. dispparams.cArgs = pEventInfo->cParameters;
  1168. dispparams.rgdispidNamedArgs = NULL;
  1169. dispparams.cNamedArgs = 0;
  1170. m_cpEvents.DoInvoke(pEventInfo->dispid, &dispparams);
  1171. va_end(valist);
  1172. }
  1173. //=--------------------------------------------------------------------------=
  1174. // CopyAndAddRefObject
  1175. //=--------------------------------------------------------------------------=
  1176. // copies an object pointer, and then addref's the object.
  1177. //
  1178. // Parameters:
  1179. // void * - [in] dest.
  1180. // const void * - [in] src
  1181. // DWORD - [in] size, ignored, since it's always 4
  1182. //
  1183. // Notes:
  1184. //
  1185. void WINAPI CopyAndAddRefObject
  1186. (
  1187. void *pDest,
  1188. const void *pSource,
  1189. DWORD dwSize
  1190. )
  1191. {
  1192. ASSERT(pDest && pSource, "Bogus Pointer(s) passed into CopyAndAddRefObject!!!!");
  1193. *((IUnknown **)pDest) = *((IUnknown **)pSource);
  1194. ADDREF_OBJECT(*((IUnknown **)pDest));
  1195. }
  1196. //=--------------------------------------------------------------------------=
  1197. // CopyConnectData
  1198. //=--------------------------------------------------------------------------=
  1199. // copies over a connectdata structure and addrefs the pointer
  1200. //
  1201. // Parameters:
  1202. // void * - [in] dest.
  1203. // const void * - [in] src
  1204. // DWORD - [in] size
  1205. //
  1206. // Notes:
  1207. //
  1208. void WINAPI CopyConnectData
  1209. (
  1210. void *pDest,
  1211. const void *pSource,
  1212. DWORD dwSize
  1213. )
  1214. {
  1215. ASSERT(pDest && pSource, "Bogus Pointer(s) passed into CopyAndAddRefObject!!!!");
  1216. *((CONNECTDATA *)pDest) = *((const CONNECTDATA *)pSource);
  1217. ADDREF_OBJECT(((CONNECTDATA *)pDest)->pUnk);
  1218. }
  1219. #ifdef DEBUG
  1220. //=--------------------------------------------------------------------------=
  1221. // DebugVerifyData1Guids [helper]
  1222. //=--------------------------------------------------------------------------=
  1223. // Given an array of match Data1_ #define and interface guid values, this
  1224. // function validates that all entries match.
  1225. //
  1226. void DebugVerifyData1Guids(GUIDDATA1_COMPARE *pGuidData1_Compare)
  1227. {
  1228. while(pGuidData1_Compare->dwData1a)
  1229. {
  1230. ASSERT(pGuidData1_Compare->pdwData1b, "Data1 pointer is NULL");
  1231. ASSERT(pGuidData1_Compare->dwData1a == *pGuidData1_Compare->pdwData1b,
  1232. "Data1_ #define value doesn't match interface guid value");
  1233. pGuidData1_Compare++;
  1234. }
  1235. }
  1236. #endif