{"id":4103,"date":"2013-06-12T07:00:00","date_gmt":"2013-06-12T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/06\/12\/dispatch-interfaces-as-connection-point-interfaces\/"},"modified":"2013-06-12T07:00:00","modified_gmt":"2013-06-12T07:00:00","slug":"dispatch-interfaces-as-connection-point-interfaces","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130612-00\/?p=4103","title":{"rendered":"Dispatch interfaces as connection point interfaces"},"content":{"rendered":"<p><P>\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/06\/11\/10424940.aspx\">\nLast time<\/A>,\nwe learned about how connection points work.\nOne special case of this is where the connection interface\nis a dispatch interface.\n<\/P>\n<P>\nDispatch interfaces are, as the name suggests,\nCOM interfaces based on <CODE>IDispatch<\/CODE>.\nThe <CODE>IDispatch<\/CODE> interface is the base interface\nfor OLE automation objects,\nand if you want your connection point interface to be usable\nfrom script,\nyou probably should make it a dispatch interface.\n<\/P>\n<P>\nI&#8217;m assuming you know how <CODE>IDispatch<\/CODE> works.\nThe short version is that script that wants to invoke\na method or property calls\n<CODE>Get&shy;IDs&shy;Of&shy;Names<\/CODE> to get the\n<I>dispatch ID<\/I> for the method or property it wants\nto access,\nand it uses the type library to figure out\nthings like the parameters and return value.\nOnce the scripting engine figures out how the method or property\nexpects to be called,\nit can call\n<CODE>IDispatch::Invoke<\/CODE>\npassing the dispatch ID and a <CODE>DISPPARAMS<\/CODE> structure\nthat holds the parameters.\n<\/P>\n<P>\nNowadays, this sort of thing goes by the fancy name of <I>reflection<\/I>,\nbut back in the OLE Automation days,\nit was simply all in a day&#8217;s work.\n<I>You kids think you invented everything<\/I>.\n<\/P>\n<P>\nJust like as with regular connection point interfaces,\na dispatch interface used as a connection point interface\nconsists of events which are formally implemented as methods.\n<\/P>\n<PRE>\ndispinterface DWidgetEvents\n{\n [id(WDISPID_RENAMED)]\n HRESULT Renamed([in] BSTR oldName, [in] BSTR newName);\n&#8230;\n};\n<\/PRE>\n<P>\nYou declare that your object is a source of events for this interface\nby noting it in your object declaration.\n(Thanks, Medinoc for\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/06\/12\/10425215.aspx#10425408\">\nnoting the error in the original version of this article<\/A>.)\n<\/P>\n<PRE>\ncoclass Widget\n{\n [default] interface IWidget;\n <FONT COLOR=\"blue\">[default, source] dispinterface DWidgetEvents;<\/FONT>\n}\n<\/PRE>\n<P>\nA client registers with the connection point with the\n<CODE>DIID_DWidget&shy;Events<\/CODE> interface.\nBy convention, dispatch interfaces usually end\nwith the word <CODE>Events<\/CODE>\nand are often prefixed with the letter <CODE>D<\/CODE>,\nand the interface ID symbol begins with <CODE>DIID<\/CODE>\nrather than simply <CODE>IID<\/CODE>.\nThese conventions are not universally adhered-to,\nso don&#8217;t freak out if you see people who don&#8217;t follow them.\n(If you declare your dispatch interface in an IDL file,\nthen the MIDL compiler will\ngenerate the dispatch interface ID with\nthe <CODE>DIID<\/CODE> prefix for you.)\n<\/P>\n<P>\nNow, formally, when the connection point wants to invoke\nthe <CODE>Renamed<\/CODE> method, it calls\n<CODE>Get&shy;IDs&shy;Of&shy;Names<\/CODE> to get the ID\nfor the method called <CODE>L&#8221;Renamed&#8221;<\/CODE>, and asks\nfor the type library to figure out what the parameters are.\nBut this is frequently just pointless busy-work:\nThe connection point often already knows the answer,\nsince the connection point already knows what interface\nit is talking to.\nIt doesn&#8217;t need to do any &#8220;reflection&#8221; since the connection\npoint already knows what the IDs and calling conventions are.\nIn the same way, your C# code doesn&#8217;t need to use reflection\nto call a method on an object whose assembly you already have\nreferenced in your project.\n(The <CODE>Get&shy;IDs&shy;Of&shy;Names<\/CODE> exists not for\nconnection points, but rather to assist\ndynamically-typed languages, where you can try to invoke any method\non any object, and the method is looked up at run time.)\n<\/P>\n<P>\nIn other words, the connection point already knows that\nthe ID for the method <CODE>Rename<\/CODE> is\n<CODE>WDISPID_RENAMED<\/CODE>, and that it takes two\n<CODE>BSTR<\/CODE> parameters,\nbecause that was part of the contract for registering with\nthe connection point in the first place.\n<\/P>\n<P>\nThis means that in practice, the only method on the\nclient that is ever called is\n<CODE>IDispatch::Invoke<\/CODE>.\n<\/P>\n<P>\nHere is a template base class that I use for my connection point interface\nimplementations of dispatch interfaces.\nWe&#8217;ll discuss the pieces afterwards:\n<\/P>\n<PRE>\ntemplate&lt;typename DispInterface&gt;\nclass CDispInterfaceBase : public DispInterface\n{\npublic:\n CDispInterfaceBase() : m_cRef(1), m_dwCookie(0) { }<\/p>\n<p> \/* IUnknown *\/\n IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)\n {\n  *ppv = nullptr;\n  HRESULT hr = E_NOINTERFACE;\n  if (riid == IID_IUnknown || riid == IID_IDispatch ||\n      riid == __uuidof(DispInterface))\n  {\n   *ppv = static_cast&lt;DispInterface *&gt;\n          (static_cast&lt;IDispatch*&gt;(this));\n   AddRef();\n   hr = S_OK;\n  }\n  return hr;\n }<\/p>\n<p> IFACEMETHODIMP_(ULONG) AddRef()\n {\n  return InterlockedIncrement(&amp;m_cRef);\n }<\/p>\n<p> IFACEMETHODIMP_(ULONG) Release()\n {\n  LONG cRef = InterlockedDecrement(&amp;m_cRef);\n  if (cRef == 0) delete this;\n  return cRef;\n }<\/p>\n<p> \/\/ *** IDispatch ***\n IFACEMETHODIMP GetTypeInfoCount(UINT *pctinfo)\n {\n  *pctinfo = 0;\n  return E_NOTIMPL;\n }<\/p>\n<p> IFACEMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid,\n                            ITypeInfo **ppTInfo)\n {\n  *ppTInfo = nullptr;\n  return E_NOTIMPL;\n }<\/p>\n<p> IFACEMETHODIMP GetIDsOfNames(REFIID, LPOLESTR *rgszNames,\n                              UINT cNames, LCID lcid,\n                              DISPID *rgDispId)\n {\n  return E_NOTIMPL;\n }<\/p>\n<p> IFACEMETHODIMP Invoke(\n    DISPID dispid, REFIID riid, LCID lcid, WORD wFlags,\n    DISPPARAMS *pdispparams, VARIANT *pvarResult,\n    EXCEPINFO *pexcepinfo, UINT *puArgErr)\n {\n  if (pvarResult) VariantInit(pvarResult);\n  return SimpleInvoke(dispid, pdispparams, pvarResult);\n }<\/p>\n<p> \/\/ Derived class must implement SimpleInvoke\n virtual HRESULT SimpleInvoke(DISPID dispid,\n    DISPPARAMS *pdispparams, VARIANT *pvarResult) = 0;<\/p>\n<p>public:\n HRESULT Connect(IUnknown *punk)\n {\n  HRESULT hr = S_OK;\n  CComPtr&lt;IConnectionPointContainer&gt; spcpc;\n  if (SUCCEEDED(hr)) {\n   hr = punk-&gt;QueryInterface(IID_PPV_ARGS(&amp;spcpc));\n  }\n  if (SUCCEEDED(hr)) {\n  hr = spcpc-&gt;FindConnectionPoint(__uuidof(DispInterface), &amp;m_spcp);\n  }\n  if (SUCCEEDED(hr)) {\n  hr = m_spcp-&gt;Advise(this, &amp;m_dwCookie);\n  }\n  return hr;\n }<\/p>\n<p> void Disconnect()\n {\n  if (m_dwCookie) {\n   m_spcp-&gt;Unadvise(m_dwCookie);\n   m_spcp.Release();\n   m_dwCookie = 0;\n  }\n }<\/p>\n<p>private:\n LONG m_cRef;\n CComPtr&lt;IConnectionPoint&gt; m_spcp;\n DWORD m_dwCookie;\n};\n<\/PRE>\n<P>\nFirst, a distraction: Our <CODE>Query&shy;Interface<\/CODE>\nimplementation performs a double-cast of <CODE>this<\/CODE>\nto <CODE>IDispatch<\/CODE>, then to the templated interface.\nThis ensures that the templated interface pointer\nand <CODE>IDispatch<\/CODE> are compatible.\nIt would be bad if somebody tried to use this\n<CODE>Query&shy;Interface<\/CODE> implementation\nwith something unrelated to <CODE>IDispatch<\/CODE>.\n(Yes, I could&#8217;ve used <CODE>std::is_base_of<\/CODE>,\nbut I&#8217;m an old-timer who grew up before TR1.)\n<\/P>\n<P>\nThe bulk of the class merely stubs out all the methods\nof <CODE>IDispatch<\/CODE>,\nsave for\n<CODE>IDispatch::Invoke<\/CODE>, which does a little\ngrunt work (initializing the result <CODE>VARIANT<\/CODE>)\nand then leaves the derived class to do the heavy lifting.\n<\/P>\n<P>\nFinally, there are two public methods\n<CODE>Connect<\/CODE> and <CODE>Disconnect<\/CODE>.\nThese perform the <CODE>Advise<\/CODE> and\n<CODE>Unadvise<\/CODE> calls we saw yesterday.\nTo simplify things for our caller,\nwe save the <CODE>IConnection&shy;Pointer<\/CODE>\nwe registered against so that the caller doesn&#8217;t\nhave to pass it back in when disconnecting.\n<\/P>\n<P>\n<B>Exercise<\/B>:\nIs the <CODE>m_spcp.Release()<\/CODE> call\nin <CODE>Disconnect<\/CODE>\nreally necessary?\n(Assuming that <CODE>Connect<\/CODE> is called at most once.)\n<\/P>\n<P>\nThis helper template class makes writing dispatch interface\nconnection point clients really simple,\nsince all you have to do is implement <CODE>Simple&shy;Invoke<\/CODE>\nin the form of a <CODE>switch<\/CODE> statement on the\ndispatch IDs you care about:\n<\/P>\n<PRE>\nclass CWidgetClient : public CDispInterfaceBase\n{\npublic:\n CWidgetClient() { }<\/p>\n<p> HRESULT SimpleInvoke(\n    DISPID dispid, DISPPARAMS *pdispparams, VARIANT *pvarResult)\n{\n switch (dispid) {\n case WDISPID_RENAMED:\n  HeyLookItGotRenamed(pdispparams-&gt;rgvarg[0].bstrVal,\n                      pdispparams-&gt;rgvarg[1].bstrVal);\n  break;\n }\n return S_OK;\n};\n<\/PRE>\n<P>\nIn the <CODE>Simple&shy;Invoke<\/CODE> method,\nwe switch on the dispatch ID,\nand if we see one we like, we extract the parameters from the\n<CODE>pdispparams<\/CODE>.\n<\/P>\n<P>\n<B>Update<\/B>: Commenter parkrrr\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/06\/12\/10425215.aspx#10425376\">\npoints out<\/A> a huge gotcha with the\n<CODE>DISP&shy;PARAMS<\/CODE> structure:\nThe parameters are passed in <I>reverse<\/I> order.\nI don&#8217;t know why.\nThey just are.\n<\/P>\n<P>\nNext time, we&#8217;ll start hooking up events to our Little Program\nso it can update when the user navigates an Explorer or\nInternet Explorer window.\n<\/P>\n<P>\n<B>Warning! Managed code!<\/B>\nThe CLR\nunderstands the connection point\/dispatch interface\nconvention and exposes a dispatch event\nto managed code\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/66ahbe6y\">\nin the form of a CLR event and corresponding delegate<\/A>.\nFor example, our <CODE>Renamed<\/CODE> event is\nexposed as an event called <CODE>Renamed<\/CODE>,\nwith delegate type\n<CODE>DWidget&shy;Events_Renamed&shy;Event&shy;Handler<\/CODE>.\nYou can listen on the event the way you listen\nto any other CLR event:\n<CODE>widget.Renamed += this.OnRenamed;<\/CODE>.\n<\/P>\n<P>\n<B>Note<\/B>: I completely ignored the subject of dual interfaces.\nYou can read about those if you like,\nbut we won&#8217;t need to know about them for the job at hand.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last time, we learned about how connection points work. One special case of this is where the connection interface is a dispatch interface. Dispatch interfaces are, as the name suggests, COM interfaces based on IDispatch. The IDispatch interface is the base interface for OLE automation objects, and if you want your connection point interface to [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-4103","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Last time, we learned about how connection points work. One special case of this is where the connection interface is a dispatch interface. Dispatch interfaces are, as the name suggests, COM interfaces based on IDispatch. The IDispatch interface is the base interface for OLE automation objects, and if you want your connection point interface to [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4103","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=4103"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4103\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=4103"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=4103"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=4103"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}