connect.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 25k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //
  2. // IConnectionPoint/IDispatch helper functions
  3. //
  4. #include "priv.h"
  5. #include <shlobj.h>
  6. //=============================================================================
  7. //
  8. //  IDispatch helper functions
  9. //-----------------------------------------------------------------------------
  10. //
  11. //  SHPackDispParamsV
  12. //
  13. //  Takes a variable number of parameters for IDispatch, packages
  14. //  them up.
  15. //
  16. //  pdispparams  - The DISPPARAMS structure that receives the result
  17. //                 of the packaging.
  18. //
  19. //  rgvarg       - Array of length cArgs.
  20. //                 It will be used to hold the parameters.
  21. //
  22. //  cArgs        - Number of pairs of generic arguments.
  23. //
  24. //  ap           - va_list of parameters to package.  We package up the
  25. //                 first (2 * cArgs) of them.  See SHPackDispParams
  26. //                 for details.
  27. typedef struct FAKEBSTR {
  28.     ULONG cb;
  29.     WCHAR wsz[1];
  30. } FAKEBSTR;
  31. const FAKEBSTR c_bstrNULL = { 0, L"" };
  32. LWSTDAPI SHPackDispParamsV(DISPPARAMS *pdispparams, VARIANTARG *rgvarg, UINT cArgs, va_list ap)
  33. {
  34.     HRESULT hr = S_OK;
  35.     ZeroMemory(rgvarg, cArgs * SIZEOF(VARIANTARG));
  36.     // fill out DISPPARAMS structure
  37.     pdispparams->rgvarg = rgvarg;
  38.     pdispparams->rgdispidNamedArgs = NULL;
  39.     pdispparams->cArgs = cArgs;
  40.     pdispparams->cNamedArgs = 0;
  41.     // parameters are ordered in ap with the right-most parameter
  42.     // at index zero and the left-most parameter at the highest index; essentially,
  43.     // the parameters are pushed from right to left.  Put the first argument we
  44.     // encounter at the highest index.
  45.     // pVarArg points to the argument structure in the array we are currently
  46.     // filling in.  Initialize this to point at highest argument (zero-based,
  47.     // hence the -1).  For each passed-in argument we process, *decrement*
  48.     // the pVarArg pointer to achieve the "push from right-to-left" effect.
  49.     VARIANTARG * pVarArg = &rgvarg[cArgs - 1];
  50.     int nCount = cArgs;
  51.     while (nCount) 
  52.     {
  53.         VARENUM vaType = va_arg(ap,VARENUM);
  54.         // We don't have to call VariantInit because we zerod out
  55.         // the entire array before entering this loop
  56.         V_VT(pVarArg) = vaType;
  57.         // the next field is a union, so we can be smart about filling it in
  58.         //
  59.         if (vaType & VT_BYREF)
  60.         {
  61.             // All byrefs can be packed the same way
  62.             V_BYREF(pVarArg) = va_arg(ap, LPVOID);
  63.         }
  64.         else
  65.         {
  66.             switch (vaType)
  67.             {
  68.             case VT_BSTR:
  69.             {
  70.                 // parameter is a BSTR
  71.                 // MFC doesn't like it when you pass NULL for a VT_BSTR type
  72.                 V_BSTR(pVarArg) = va_arg(ap, BSTR);
  73.                 if (V_BSTR(pVarArg) == NULL)
  74.                     V_BSTR(pVarArg) =(BSTR)c_bstrNULL.wsz;
  75. #ifdef DEBUG
  76.                 // Check if this BSTR is a valid BSTR
  77.                 FAKEBSTR *bstr = CONTAINING_RECORD(V_BSTR(pVarArg), FAKEBSTR, wsz);
  78.                 ASSERT(bstr->cb == lstrlenW(bstr->wsz) * SIZEOF(WCHAR));
  79. #endif
  80.                 break;
  81.             }
  82.     
  83.             case VT_BOOL:
  84.                 V_BOOL(pVarArg) = va_arg(ap, VARIANT_BOOL);
  85.                 break;
  86.             case VT_DISPATCH:
  87.                 V_DISPATCH(pVarArg) = va_arg(ap, LPDISPATCH);
  88.                 break;
  89.             case VT_UNKNOWN:
  90.                 V_UNKNOWN(pVarArg) = va_arg(ap, LPUNKNOWN);
  91.                 break;
  92.             default:
  93.                 AssertMsg(0, TEXT("Packing unknown variant type 0x%x as VT_I4"), vaType);
  94.                 // if we don't know what it is treat it as VT_I4.
  95.                 // Hopefully it's not a pointer or a VT_R8 or that sort of
  96.                 // thing, or we're screwed.
  97.                 V_VT(pVarArg) = VT_I4;
  98.             case VT_I4:
  99.                 V_I4(pVarArg) = va_arg(ap, LONG);
  100.                 break;
  101.             } 
  102.         }
  103.         nCount--;
  104.         pVarArg--;
  105.     }
  106.     return hr;
  107. }
  108. //-----------------------------------------------------------------------------
  109. //
  110. //  SHPackDispParams
  111. //
  112. //  Takes a variable number of generic parameters, packages
  113. //  them up.
  114. //
  115. //  pdispparams  - The DISPPARAMS structure that receives the result
  116. //                 of the packaging.
  117. //
  118. //  rgvarg       - Array of length cArgs.
  119. //                 It will be used to hold the parameters.
  120. //
  121. //  cArgs        - Number of pairs of generic arguments (below).
  122. //
  123. //  ...          - A collection of (VARNUM, LPVOID) pairs of arguments.
  124. //                 The first is the type of the argument, and the
  125. //                 second is the corresponding value.
  126. //
  127. //                 As a special case, a null VT_BSTR can be passed
  128. //                 as a NULL pointer and we will turn it into a
  129. //                 genuine null BSTR.
  130. //
  131. //  The following VARENUMs are supported:
  132. //
  133. //      VT_BYREF        - Anything that is VT_BYREF is okay
  134. //      VT_BSTR
  135. //      VT_BOOL
  136. //      VT_DISPATCH
  137. //      VT_UNKNOWN
  138. //      VT_I4
  139. //
  140. //  Any other type will be packaged randomly, so don't do that.
  141. //
  142. //  Example:
  143. //
  144. //      DISPPARAMS dispparams;
  145. //      VARIANTARG args[4];                     // room for 4 parameters
  146. //      SHPackDispParams(&dispparams, args, 4,  // and here they are
  147. //                       VT_BSTR,   bstrURL,
  148. //                       VT_I4,     dwFlags,
  149. //                       VT_BSTR,   NULL,       // no post data
  150. //                       VT_BSTR,   bstrHeaders);
  151. //
  152. LWSTDAPI SHPackDispParams(DISPPARAMS *pdispparams, VARIANTARG *rgvarg, UINT cArgs, ...)
  153. {
  154.     va_list ap;
  155.     va_start(ap, cArgs);
  156.     HRESULT hr = SHPackDispParamsV(pdispparams, rgvarg, cArgs, ap);
  157.     va_end(ap);
  158.     return hr;
  159. }
  160. //=============================================================================
  161. //
  162. //  IConnectionPoint helper functions
  163. //-----------------------------------------------------------------------------
  164. //
  165. //  INVOKECALLBACK
  166. //
  167. //  Allows clients to customize the the invoke process.  The callback
  168. //  receives the following parameters:
  169. //
  170. //  pdisp       - The IDispatch that is about to receive an invoke.
  171. //
  172. //  pinv        - SHINVOKEPARAMS structure that describes the invoke
  173. //                that is about to occur.
  174. //
  175. //  The callback function is called before each sink is dispatched.
  176. //  The callback can return any of the following values:
  177. //
  178. //  S_OK          Proceed with the invoke
  179. //  S_FALSE       Skip this invoke but keep invoking others
  180. //  E_FAIL        Stop invoking
  181. //
  182. //  A client can do lazy-evaluation of dispatch arguments by installing
  183. //  a callback that sets up the dispatch arguments on the first callback.
  184. //
  185. //  A client can support a "Cancel" flag by returning E_FAIL once the
  186. //  cancel has occurred.
  187. //
  188. //  A client can pre-validate an IDispatch for compatibility reasons
  189. //  and either touch up the arguments and return S_OK, or decide that
  190. //  the IDispatch should be skipped and return S_FALSE.
  191. //
  192. //  A client can append custom information to the end of the SHINVOKEPARAMS
  193. //  structure to allow it to determine additional context.
  194. //
  195. //  A client can do post-invoke goo by doing work on the pre-invoke
  196. //  of the subsequent callback (plus one final bout of work when the
  197. //  entire enumeration completes).
  198. //
  199. //-----------------------------------------------------------------------------
  200. //
  201. //  GetConnectionPointSink
  202. //
  203. //  Obtaining a connection point sink is supposed to be easy.  You just
  204. //  QI for the interface.  Unfortunately, too many components are buggy.
  205. //
  206. //  mmc.exe is stupid and faults if you QI for IDispatch
  207. //  and punkCB is non-NULL.  And if you do pass in NULL,
  208. //  it returns S_OK but fills punkCB with NULL anyway.
  209. //  Somebody must've had a rough day.
  210. //
  211. //  Java responds only to its dispatch ID and not IID_IDispatch, even
  212. //  though the dispatch ID is derived from IID_IDispatch.
  213. //
  214. //  The Explorer Band responds only to IID_IDispatch and not to
  215. //  the dispatch ID.
  216. //
  217. HRESULT GetConnectionPointSink(IUnknown *pUnk, const IID *piidCB, IUnknown **ppunkCB)
  218. {
  219.     HRESULT hr = E_NOINTERFACE;
  220.     *ppunkCB = NULL;                // Pre-zero it to work around MMC
  221.     if (piidCB)                     // Optional interface (Java/ExplBand)
  222.     {                   
  223.         hr = pUnk->QueryInterface(*piidCB, (void **) ppunkCB);
  224.         if (*ppunkCB == NULL)       // Clean up behind MMC
  225.             hr = E_NOINTERFACE;
  226.     }
  227.     return hr;
  228. }
  229. //-----------------------------------------------------------------------------
  230. //
  231. //  EnumConnectionPointSinks
  232. //
  233. //  Enumerate the connection point sinks, calling the callback for each one
  234. //  found.
  235. //
  236. //  The callback function is called once for each sink.  The IUnknown is
  237. //  whatever interface we could get from the sink (either piidCB or piidCB2).
  238. //
  239. typedef HRESULT (CALLBACK *ENUMCONNECTIONPOINTSPROC)(
  240.     /* [in, iid_is(*piidCB)] */ IUnknown *psink, LPARAM lParam);
  241. HRESULT EnumConnectionPointSinks(
  242.     IConnectionPoint *pcp,              // IConnectionPoint victim
  243.     const IID *piidCB,                  // Interface for callback
  244.     const IID *piidCB2,                 // Alternate interface for callback
  245.     ENUMCONNECTIONPOINTSPROC EnumProc,  // Callback procedure
  246.     LPARAM lParam)                      // Refdata for callback
  247. {
  248.     HRESULT hr;
  249.     IEnumConnections * pec;
  250.     if (pcp)
  251.         EVAL(SUCCEEDED(hr = pcp->EnumConnections(&pec)));
  252.     else
  253.         hr = E_NOINTERFACE;
  254.     if (SUCCEEDED(hr))
  255.     {
  256.         CONNECTDATA cd;
  257.         ULONG cFetched;
  258.         while (S_OK == (hr = pec->Next(1, &cd, &cFetched)))
  259.         {
  260.             IUnknown *punkCB;
  261.             ASSERT(1 == cFetched);
  262.             hr = GetConnectionPointSink(cd.pUnk, piidCB, &punkCB);
  263.             if (FAILED(hr))
  264.                 hr = GetConnectionPointSink(cd.pUnk, piidCB2, &punkCB);
  265.             if (EVAL(SUCCEEDED(hr)))
  266.             {
  267.                 hr = EnumProc(punkCB, lParam);
  268.                 punkCB->Release();
  269.             }
  270.             else
  271.             {
  272.                 hr = S_OK;      // Pretend callback succeeded
  273.             }
  274.             cd.pUnk->Release();
  275.             if (FAILED(hr)) break; // Callback asked to stop
  276.         }
  277.         pec->Release();
  278.         hr = S_OK;
  279.     }
  280.     return hr;
  281. }
  282. //-----------------------------------------------------------------------------
  283. //
  284. //  InvokeCallback
  285. //
  286. //  Send out the callback (if applicable) and then do the invoke if the
  287. //  callback said that was a good idea.
  288. //
  289. //  Parameters:
  290. //
  291. //      pcp          -  IConnectionPoint whose sinks are to be Invoke()d.
  292. //                      If this parameter is NULL, the function does nothing.
  293. //      pinv         -  Structure containing parameters to INVOKE.
  294. HRESULT CALLBACK EnumInvokeCallback(IUnknown *psink, LPARAM lParam)
  295. {
  296.     IDispatch *pdisp = (IDispatch *)psink;
  297.     LPSHINVOKEPARAMS pinv = (LPSHINVOKEPARAMS)lParam;
  298.     HRESULT hr;
  299.     if (pinv->Callback)
  300.     {
  301.         // Now see if the callback wants to do pre-vet the pdisp.
  302.         // It can return S_FALSE to skip this callback or E_FAIL to
  303.         // stop the invoke altogether
  304.         hr = pinv->Callback(pdisp, pinv);
  305.         if (hr != S_OK) return hr;
  306.     }
  307.     pdisp->Invoke(pinv->dispidMember, *pinv->piid, pinv->lcid,
  308.                   pinv->wFlags, pinv->pdispparams, pinv->pvarResult,
  309.                   pinv->pexcepinfo, pinv->puArgErr);
  310.     return S_OK;
  311. }
  312. //-----------------------------------------------------------------------------
  313. //
  314. //  IConnectionPoint_InvokeIndirect
  315. //
  316. //  Given a connection point, call the IDispatch::Invoke for each
  317. //  connected sink.
  318. //
  319. //  The return value merely indicates whether the command was dispatched.
  320. //  If any particular sink fails the IDispatch::Invoke, we will still
  321. //  return S_OK, since the command was indeed dispatched.
  322. //
  323. //  Parameters:
  324. //
  325. //      pcp          -  IConnectionPoint whose sinks are to be Invoke()d.
  326. //                      If this parameter is NULL, the function does nothing.
  327. //      pinv         -  Structure containing parameters to INVOKE.
  328. //                      The pdispparams field can be NULL; we will turn it
  329. //                      into a real DISPPARAMS for you.
  330. //
  331. //  The SHINVOKEPARAMS.flags field can contain the following flags.
  332. //
  333. //      IPFL_USECALLBACK    - The callback field contains a callback function
  334. //                            Otherwise, it will be set to NULL.
  335. //      IPFL_USEDEFAULT     - Many fields in the SHINVOKEPARAMS will be set to
  336. //                            default values to save the caller effort:
  337. //
  338. //                  riid            =   IID_NULL
  339. //                  lcid            =   0
  340. //                  wFlags          =   DISPATCH_METHOD
  341. //                  pvarResult      =   NULL
  342. //                  pexcepinfo      =   NULL
  343. //                  puArgErr        =   NULL
  344. //
  345. LWSTDAPI IConnectionPoint_InvokeIndirect(
  346.     IConnectionPoint *pcp,
  347.     SHINVOKEPARAMS *pinv)
  348. {
  349.     HRESULT hr;
  350.     DISPPARAMS dp = { 0 };
  351.     IID iidCP;
  352.     if (pinv->pdispparams == NULL)
  353.         pinv->pdispparams = &dp;
  354.     if (!(pinv->flags & IPFL_USECALLBACK))
  355.     {
  356.         pinv->Callback = NULL;
  357.     }
  358.     if (pinv->flags & IPFL_USEDEFAULTS)
  359.     {
  360.         pinv->piid            =  &IID_NULL;
  361.         pinv->lcid            =   0;
  362.         pinv->wFlags          =   DISPATCH_METHOD;
  363.         pinv->pvarResult      =   NULL;
  364.         pinv->pexcepinfo      =   NULL;
  365.         pinv->puArgErr        =   NULL;
  366.     }
  367.     // Try both the interface they actually connected on,
  368.     // as well as IDispatch.  Apparently Java responds only to
  369.     // the connecting interface, and ExplBand responds only to
  370.     // IDispatch, so we have to try both.  (Sigh.  Too many buggy
  371.     // components in the system.)
  372.     hr = EnumConnectionPointSinks(pcp,
  373.                                   (pcp->GetConnectionInterface(&iidCP) == S_OK) ? &iidCP : NULL,
  374.                                   &IID_IDispatch,
  375.                                   EnumInvokeCallback,
  376.                                   (LPARAM)pinv);
  377.     // Put the original NULL back so the caller can re-use the SHINVOKEPARAMS.
  378.     if (pinv->pdispparams == &dp)
  379.         pinv->pdispparams = NULL;
  380.     return hr;
  381. }
  382. //-----------------------------------------------------------------------------
  383. //
  384. //  IConnectionPoint_InvokeWithCancel
  385. //
  386. //  Wrapper around IConnectionPoint_InvokeIndirect with special Cancel
  387. //  semantics.
  388. //
  389. //  Parameters:
  390. //
  391. //      pcp          -  IConnectionPoint whose sinks are to be Invoke()d.
  392. //                      If this parameter is NULL, the function does nothing.
  393. //      dispid       -  The DISPID to invoke
  394. //      pdispparams  -  The DISPPARAMS for the invoke
  395. //      pfCancel     -  Optional BOOL to cancel the invoke
  396. //      ppvCancel    -  Optional LPVOID to cancel the invoke
  397. //
  398. //  If either *pfCancel or *ppvCancel is nonzero/non-NULL, we stop the invoke
  399. //  process.  This allows a sink to "handle" the event and prevent other
  400. //  sinks from receiving it.  The ppvCancel parameter is for dispid's which
  401. //  are queries that are asking for somebody to create an object and return it.
  402. //
  403. //  It is the caller's responsibility to check the values of *pfCancel
  404. //  and/or *ppvCancel to determine if the operation was cancelled.
  405. //
  406. typedef struct INVOKEWITHCANCEL {
  407.     SHINVOKEPARAMS inv;
  408.     LPBOOL pfCancel;
  409.     void **ppvCancel;
  410. } INVOKEWITHCANCEL;
  411. HRESULT CALLBACK InvokeWithCancelProc(IDispatch *psink, SHINVOKEPARAMS *pinv)
  412. {
  413.     INVOKEWITHCANCEL *piwc = CONTAINING_RECORD(pinv, INVOKEWITHCANCEL, inv);
  414.     if ((piwc->pfCancel && *piwc->pfCancel) ||
  415.         (piwc->ppvCancel && *piwc->ppvCancel))
  416.         return E_FAIL;
  417.     return S_OK;
  418. }
  419. LWSTDAPI IConnectionPoint_InvokeWithCancel(
  420.     IConnectionPoint *pcp,
  421.     DISPID dispidMember,
  422.     DISPPARAMS * pdispparams,
  423.     LPBOOL pfCancel,
  424.     void **ppvCancel)
  425. {
  426.     INVOKEWITHCANCEL iwc;
  427.     iwc.inv.flags = IPFL_USECALLBACK | IPFL_USEDEFAULTS;
  428.     iwc.inv.dispidMember = dispidMember;
  429.     iwc.inv.pdispparams = pdispparams;
  430.     iwc.inv.Callback = InvokeWithCancelProc;
  431.     iwc.pfCancel = pfCancel;
  432.     iwc.ppvCancel = ppvCancel;
  433.     return IConnectionPoint_InvokeIndirect(pcp, &iwc.inv);
  434. }
  435. //-----------------------------------------------------------------------------
  436. //
  437. //  IConnectionPoint_SimpleInvoke
  438. //
  439. //  Wrapper around IConnectionPoint_InvokeIndirect with IPFL_USEDEFAULTS.
  440. //
  441. LWSTDAPI IConnectionPoint_SimpleInvoke(IConnectionPoint *pcp, DISPID dispidMember, DISPPARAMS *pdispparams)
  442. {
  443.     SHINVOKEPARAMS inv;
  444.     inv.flags = IPFL_USEDEFAULTS;
  445.     inv.dispidMember = dispidMember;
  446.     inv.pdispparams = pdispparams;
  447.     return IConnectionPoint_InvokeIndirect(pcp, &inv);
  448. }
  449. //
  450. //  Takes a variable number of parameters for IDispatch, packages
  451. //  them up, and invokes them.
  452. //
  453. //  The parameters to the IDispatch::Invoke will be
  454. //
  455. //      dispidMember    -   dispidMember
  456. //      riid            -   IID_NULL
  457. //      lcid            -   0
  458. //      wFlags          -   DISPATCH_METHOD
  459. //      pdispparams     -   <parameters to this function>
  460. //      pvarResult      -   NULL
  461. //      pexcepinfo      -   NULL
  462. //      puArgErr        -   NULL
  463. //
  464. //  The parameters to this function are
  465. //
  466. //  pcp          - IConnectionPoint whose sinks should be Invoke()d.
  467. //                 If this parameter is NULL, the function does nothing.
  468. //  dispidMember - The DISPID to invoke.
  469. //  rgvarg       - Array of length cArgs.
  470. //                 It will be used to hold the parameters.
  471. //  cArgs        - Number of pairs of generic arguments (below).
  472. //
  473. //  ap           - va_list of parameters to package.  We package up the
  474. //                 first (2 * cArgs) of them.  See SHPackDispParams
  475. //                 for details.
  476. //
  477. LWSTDAPI IConnectionPoint_InvokeParamV(IConnectionPoint *pcp, DISPID dispidMember, 
  478.                                        VARIANTARG *rgvarg, UINT cArgs, va_list ap)
  479. {
  480.     HRESULT hr;
  481.     if (pcp)
  482.     {
  483.         DISPPARAMS dp;
  484.         hr = SHPackDispParamsV(&dp, rgvarg, cArgs, ap);
  485.         if (EVAL(SUCCEEDED(hr)))
  486.         {
  487.             hr = IConnectionPoint_SimpleInvoke(pcp, dispidMember, &dp);
  488.         }
  489.     }
  490.     else
  491.         hr = E_NOINTERFACE;
  492.     return hr;
  493. }
  494. //
  495. //  Worker function for many classes of IConnectionPoint_Invoke
  496. //  clients.  Just pass the parameters and we'll pack them up for you.
  497. //
  498. //  The parameters to the IDispatch::Invoke will be
  499. //
  500. //      dispidMember    -   dispidMember
  501. //      riid            -   IID_NULL
  502. //      lcid            -   0
  503. //      wFlags          -   DISPATCH_METHOD
  504. //      pdispparams     -   <parameters to this function>
  505. //      pvarResult      -   NULL
  506. //      pexcepinfo      -   NULL
  507. //      puArgErr        -   NULL
  508. //
  509. //  The parameters to this function are
  510. //
  511. //  pcp          - IConnectionPoint whose sinks should be Invoke()d.
  512. //                 If this parameter is NULL, the function does nothing.
  513. //
  514. //  dispidMember - The DISPID to invoke.
  515. //
  516. //  rgvarg       - Array of length cArgs.
  517. //                 It will be used to hold the parameters.
  518. //
  519. //  cArgs        - Number of pairs of generic arguments (below).
  520. //
  521. //  ...          - A collection of (VARENUM, blah) pairs of arguments.
  522. //                 See SHPackDispParams for details.
  523. //
  524. //  Example:
  525. //
  526. //      VARIANTARG args[1];
  527. //      IConnectionPoint_InvokeParam(pcp, DISPID_PROPERTYCHANGE,
  528. //                                   args, 1,
  529. //                                   VT_BSTR, bstrProperty);
  530. LWSTDAPIV IConnectionPoint_InvokeParam(IConnectionPoint *pcp, DISPID dispidMember, 
  531.                                        VARIANTARG *rgvarg, UINT cArgs, ...)
  532. {
  533.     HRESULT hr;
  534.     va_list ap;
  535.     va_start(ap, cArgs);
  536.     hr = IConnectionPoint_InvokeParamV(pcp, dispidMember, rgvarg, cArgs, ap);
  537.     va_end(ap);
  538.     return hr;
  539. }
  540. //
  541. //  Given a connection point that represents IPropertyNotifySink,
  542. //  call the IPropertyNotifySink::OnChanged for each connected sink.
  543. //
  544. //  Parameters:
  545. //
  546. //      pcp          -  IConnectionPoint whose sinks are to be notified.
  547. //                      If this parameter is NULL, the function does nothing.
  548. //      dispid       -  To pass to IPropertyNotifySink::OnChanged.
  549. HRESULT CALLBACK OnChangedCallback(IUnknown *psink, LPARAM lParam)
  550. {
  551.     IPropertyNotifySink *pns = (IPropertyNotifySink *)psink;
  552.     DISPID dispid = (DISPID)lParam;
  553.     pns->OnChanged(dispid);
  554.     return S_OK;
  555. }
  556. LWSTDAPI IConnectionPoint_OnChanged(IConnectionPoint *pcp, DISPID dispid)
  557. {
  558. #ifdef DEBUG
  559.     // Make sure it really is an IPropertyNotifySink connection point.
  560.     if (pcp)
  561.     {
  562.         IID iid;
  563.         HRESULT hr = pcp->GetConnectionInterface(&iid);
  564.         ASSERT(SUCCEEDED(hr) && iid == IID_IPropertyNotifySink);
  565.     }
  566. #endif
  567.     return EnumConnectionPointSinks(pcp, &IID_IPropertyNotifySink, NULL,
  568.                                     OnChangedCallback, (LPARAM)dispid);
  569. }
  570. //=============================================================================
  571. //
  572. //  IConnectionPointContainer helper functions
  573. //
  574. //  QI's for IConnectionPointContainer and then does the FindConnectionPoint.
  575. //
  576. //  Parameters:
  577. //
  578. //      punk         -  The object who might be an IConnectionPointContainer.
  579. //                      This parameter may be NULL, in which case the
  580. //                      operation fails.
  581. //      riidCP       -  The connection point interface to locate.
  582. //      pcpOut       -  Receives the IConnectionPoint, if any.
  583. LWSTDAPI IUnknown_FindConnectionPoint(IUnknown *punk, REFIID riidCP, 
  584.                                       IConnectionPoint **pcpOut)
  585. {
  586.     HRESULT hr;
  587.     *pcpOut = NULL;
  588.     if (punk)
  589.     {
  590.         IConnectionPointContainer *pcpc;
  591.         hr = punk->QueryInterface(IID_IConnectionPointContainer, (void **)&pcpc);
  592.         if (SUCCEEDED(hr))
  593.         {
  594.             hr = pcpc->FindConnectionPoint(riidCP, pcpOut);
  595.             pcpc->Release();
  596.         }
  597.     }
  598.     else
  599.         hr = E_NOINTERFACE;
  600.     return hr;
  601. }
  602. //
  603. //  Given an IUnknown, query for its connection point container,
  604. //  find the corresponding connection point, package up the
  605. //  invoke parameters, and call the IDispatch::Invoke for each
  606. //  connected sink.
  607. //
  608. //  See IConnectionPoint_InvokeParam for additional semantics.
  609. //
  610. //  Parameters:
  611. //
  612. //      punk         -  Object that might be an IConnectionPointContainer
  613. //      riidCP       -  ConnectionPoint interface to request
  614. //      pinv         -  Arguments for the Invoke.
  615. //
  616. LWSTDAPI IUnknown_CPContainerInvokeIndirect(IUnknown *punk, REFIID riidCP,
  617.                 SHINVOKEPARAMS *pinv)
  618. {
  619.     IConnectionPoint *pcp;
  620.     HRESULT hr = IUnknown_FindConnectionPoint(punk, riidCP, &pcp);
  621.     if (SUCCEEDED(hr))
  622.     {
  623.         hr = IConnectionPoint_InvokeIndirect(pcp, pinv);
  624.         pcp->Release();
  625.     }
  626.     return hr;
  627. }
  628. //
  629. //  This is the ultimate in one-stop shopping.
  630. //
  631. //  Given an IUnknown, query for its connection point container,
  632. //  find the corresponding connection point, package up the
  633. //  invoke parameters, and call the IDispatch::Invoke for each
  634. //  connected sink.
  635. //
  636. //  See IConnectionPoint_InvokeParam for additional semantics.
  637. //
  638. //  Parameters:
  639. //
  640. //      punk         -  Object that might be an IConnectionPointContainer
  641. //      riidCP       -  ConnectionPoint interface to request
  642. //      dispidMember -  The DISPID to invoke.
  643. //      rgvarg       -  Array of length cArgs.
  644. //                      It will be used to hold the parameters.
  645. //      cArgs        -  Number of pairs of generic arguments (below).
  646. //      ...          -  A collection of (VARNUM, LPVOID) pairs of arguments.
  647. //                      See SHPackDispParams for details.
  648. //
  649. //  Example:
  650. //
  651. //      IUnknown_CPContainerInvokeParam(punk, DIID_DShellFolderViewEvents,
  652. //                                      DISPID_SELECTIONCHANGED, NULL, 0);
  653. LWSTDAPIV IUnknown_CPContainerInvokeParam(
  654.     IUnknown *punk, REFIID riidCP,
  655.     DISPID dispidMember, VARIANTARG *rgvarg, UINT cArgs, ...)
  656. {
  657.     IConnectionPoint *pcp;
  658.     HRESULT hr = IUnknown_FindConnectionPoint(punk, riidCP, &pcp);
  659.     if (SUCCEEDED(hr))
  660.     {
  661.         va_list ap;
  662.         va_start(ap, cArgs);
  663.         hr = IConnectionPoint_InvokeParamV(pcp, dispidMember, rgvarg, cArgs, ap);
  664.         va_end(ap);
  665.         pcp->Release();
  666.     }
  667.     return hr;
  668. }
  669. //
  670. //  Given an IUnknown, query for its connection point container,
  671. //  find the corresponding connection point, and call the
  672. //  IPropertyNotifySink::OnChanged for each connected sink.
  673. //
  674. //  Parameters:
  675. //
  676. //      punk         -  Object that might be an IConnectionPointContainer
  677. //      dispid       -  To pass to IPropertyNotifySink::OnChanged.
  678. LWSTDAPI IUnknown_CPContainerOnChanged(IUnknown *punk, DISPID dispid)
  679. {
  680.     IConnectionPoint *pcp;
  681.     HRESULT hr = IUnknown_FindConnectionPoint(punk, IID_IPropertyNotifySink, &pcp);
  682.     if (SUCCEEDED(hr))
  683.     {
  684.         hr = IConnectionPoint_OnChanged(pcp, dispid);
  685.         pcp->Release();
  686.     }
  687.     return hr;
  688. }