{"id":4113,"date":"2013-06-11T07:00:00","date_gmt":"2013-06-11T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/06\/11\/an-introduction-to-com-connection-points\/"},"modified":"2013-06-11T07:00:00","modified_gmt":"2013-06-11T07:00:00","slug":"an-introduction-to-com-connection-points","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130611-00\/?p=4113","title":{"rendered":"An introduction to COM connection points"},"content":{"rendered":"<p><P>\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/06\/10\/10424662.aspx\">\nLast time<\/A>,\nwe saw how to enumerate all the Internet Explorer and\nExplorer Windows and see what they are viewing.\nBut that program printed static information.\nIt didn&#8217;t track the changes to the windows if the user\nclicked to another Web page or navigated to a different folder.\n<\/P>\n<P>\nIn order to hook that up, we\nneed to understand the connection point model and the\nway events are expressed in dispatch interfaces.\nFirst, let&#8217;s look at the connection point model.\nThese topics confused me when I first met them\n(in part because I didn&#8217;t do a good job of mentally\nseparating them into two topics and instead treated it\nas one big topic),\nso I&#8217;m going to spend a few days\ntrying to explain how it works,\nand then later this week, we&#8217;ll actually hook things up.\n(And actually hooking it up is a lot easier than explaining it.)\n<\/P>\n<P>\nToday, the connection point model.\n<\/P>\n<P>\nSuppose you have a widget which can have multiple clients.\nThe clients can communicate with the widget by invoking methods\non the widget, like\n<CODE>IWidget::Set&shy;Color<\/CODE>.\nbut how does the widget communicate with its clients?\nWell, since this is COM, the first thing you need is an interface,\nsay,\n<CODE>IWidget&shy;Client<\/CODE>.\nThe idea is that each client implements\n<CODE>IWidget&shy;Client<\/CODE>,\nand when the widget needs to, say, notify each client that\nthe color changed,\nit can invoke\n<CODE>IWidget&shy;Client::On&shy;Color&shy;Changed<\/CODE>\non each one.\nEach client can register with the widget for notifications.\n<\/P>\n<P>\nThe COM interface for standardizing the registration mechanism\nis <CODE>IConnection&shy;Point<\/CODE>.\nA <I>connection point<\/I>\nacts as a middle-man between\nthe widget and all its clients:\nWhenever the widget needs to notify all its clients,\nit tells the connection point to do it.\n<\/P>\n<TABLE>\n<TR>\n<TABLE BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\" SUMMARY=\"To the left is the widget. An arrow goes from the widget to the connection point in the center. Three arrows go from the connection point to clients A, B, and C on the right.\">\n<TR>\n    <TD ROWSPAN=\"5\" ALIGN=\"center\" STYLE=\"border: solid black .75pt;padding: 3pt\">\n    Widget<\/TD>\n    <TD><\/TD>\n    <TD ROWSPAN=\"5\" ALIGN=\"center\" STYLE=\"border: solid black .75pt;padding: 3pt\">\n    Connection<BR>Point<\/TD>\n    <TD>&rarr;<\/TD>\n    <TD ALIGN=\"center\" STYLE=\"border: solid black .75pt;padding: 3pt\">\n    Client&nbsp;A<\/TD>\n<\/TR>\n<TR STYLE=\"height: 1ex\">\n    <TD><\/TD>\n    <TD><\/TD>\n    <TD><\/TD>\n<\/TR>\n<TR>\n    <TD>&rarr;<\/TD>\n    <TD>&rarr;<\/TD>\n    <TD ALIGN=\"center\" STYLE=\"border: solid black .75pt;padding: 3pt\">\n    Client&nbsp;B<\/TD>\n<\/TR>\n<TR STYLE=\"height: 1ex\">\n    <TD><\/TD>\n    <TD><\/TD>\n    <TD><\/TD>\n<\/TR>\n<TR>\n    <TD><\/TD>\n    <TD>&rarr;<\/TD>\n    <TD ALIGN=\"center\" STYLE=\"border: solid black .75pt;padding: 3pt\">\n    Client&nbsp;C<\/TD>\n<\/TR>\n<\/TABLE>\n<P>\nA client registers with a connection point by calling\n<CODE>IConnection&shy;Point::Advise<\/CODE>,\nand it unregisters by calling\n<CODE>IConnection&shy;Point::Unadvise<\/CODE>.\n<\/P>\n<P>\nOkay, that&#8217;s great,\nbut how do clients find the connection point so they can register with it?\n<\/P>\n<P>\nThe widget exposes an interface known as\n<CODE>IConnection&shy;Point&shy;Container<\/CODE>\nwhich provides access to an object&#8217;s connection points.\nThe client can call the\n<CODE>IConnection&shy;Point&shy;Container::Find&shy;Connection&shy;Point<\/CODE> method\nto get access to a specific connection point.\n<\/P>\n<P>\nHere&#8217;s how the pieces fit together:\n<\/P>\n<PRE>\n\/\/ error checking elided for expository purposes<\/p>\n<p>void IUnknown_FindConnectionPoint(IUnknown *punk,\n                                  REFIID riid,\n                                  IConnectionPoint **ppcp)\n{\n \/\/ get the IConnectionPointContainer interface\n CComQIPtr&lt;IConnectionPointContainer&gt; spcpc(punk);<\/p>\n<p> \/\/ Locate the connection point\n spcpc-&gt;FindConnectionPoint(riid,  ppcp);\n}<\/p>\n<p>class CClient : public IWidgetClient\n{\n&#8230;\n IWidget *m_pWidget;\n DWORD m_dwCookie;\n};<\/p>\n<p>CClient::RegisterWidgetClient()\n{\n \/\/ Find the IWidgetClient connection point\n CComPtr&lt;IConnectionPoint&gt; spcp;\n IUnknown_FindConnectionPoint(m_pWidget,\n                              IID_IWidgetClient, &amp;spcp);<\/p>\n<p> \/\/ register with it\n spcp-&gt;Advise(this, &amp;m_dwCookie);\n}<\/p>\n<p>CClient::UnregisterWidgetClient()\n{\n \/\/ Find the IWidgetClient connection point\n CComPtr&lt;IConnectionPoint&gt; spcp;\n IUnknown_FindConnectionPoint(m_pWidget,\n                              IID_IWidgetClient, &amp;spcp);<\/p>\n<p> \/\/ unregister from it\n spcp-&gt;Unadvise(m_dwCookie);\n}\n<\/PRE>\n<P>\nAfter registering as a widget client,\nthe <CODE>CClient<\/CODE> object will receive\nmethod calls on its <CODE>IWidget&shy;Client<\/CODE>\nuntil it unregisters.\n<\/P>\n<P>\nNow the widget and clients have two-way communication.\nIf the clients want to initiate the communuication,\nit can call a method on <CODE>IWidget<\/CODE>.\nIf the widget wants to initiate the communication,\nit can call a method on <CODE>IWidget&shy;Client<\/CODE>.\n<\/P>\n<P>\nNote that we&#8217;ve created a giant circular reference.\nThe widget has a reference to its connection point\n(so it can tell it to fire a notification to all its clients),\nand the connection point has a reference to the widget\nclient\n(so it can forward the notification along),\nand the widget client has a reference to the widget\nin its <CODE>m_pWidget<\/CODE> member.\nIn order to break this cycle,\nyou have to remember to explicitly call\n<CODE>Unregister&shy;Widget&shy;Client<\/CODE>\nwhen you are no longer interested in receiving\nwidget notifications.\n<\/P>\n<P>\nNote that even though the arrows in the diagram above\nflow from left to right (from widget to clients),\nthat doesn&#8217;t mean that the\ninformation flow is strictly left-to-right.\nYou can pass information in the other direction\nvia return values or output parameters.\n<\/P>\n<P>\nFor example, there might be a method on\nthe <CODE>IWidget&shy;Client<\/CODE> interface\ncalled <CODE>Get&shy;Color<\/CODE>:\n<\/P>\n<PRE>\ninterface IWidgetClient : IUnknown\n{\n &#8230;\n HRESULT GetColor([out] COLORREF *pclr);\n &#8230;\n};\n<\/PRE>\n<P>\nSince there can be multiple clients, the widget\nneeds to have some sort of rule for deciding which\nclient gets to choose the color.\nIt might decide to ask each client in turn for a color,\nuntil one of them returns <CODE>S_OK<\/CODE>,\nand that client&#8217;s color is used and no further clients\nare notified.\n<\/P>\n<P>\nOr maybe there&#8217;s a method called\n<CODE>On&shy;Save<\/CODE>:\n<\/P>\n<PRE>\ninterface IWidgetClient : IUnknown\n{\n &#8230;\n HRESULT OnSave([in] IPropertyStorage *pps);\n &#8230;\n};\n<\/PRE>\n<P>\nThe convention here might be that all clients\nwill be notified of the Save operation and they\ncan write any additional information to the\n<CODE>IProperty&shy;Storage<\/CODE> while handing\nthe notification.\n<\/P>\n<P>\nThose are just examples.\nFeel free to make up your own.\nThe point is that just because the arrows go from the\nwidget to the clients doesn&#8217;t mean that information\ncan&#8217;t flow back the other way.\n<\/P>\n<P>\nMost of the time, you have the simple case where\na widget will expose a single connection point.\nIn that case, the generality of the\n<CODE>IConnection&shy;Point&shy;Container<\/CODE>\nmay seem unnecessary.\nBut it allows you to add new connection points later.\nFor example, you might have multiple client interfaces\nfor different types of clients.\nYou could have\n<CODE>IWidget&shy;Color&shy;Client<\/CODE>\nfor clients that are interested only in color changes,\nand\n<CODE>IWidget&shy;Network&shy;Client<\/CODE>\nfor clients that are interested only in monitoring the\nwidget&#8217;s network activity.\n<\/P>\n<P>\nOr maybe you didn&#8217;t plan on having multiple connection points\noriginally,\nbut in the second version of your product,\nyou want to add additional methods to\n<CODE>IWidget&shy;Client<\/CODE>,\nso you need to create\n<CODE>IWidget&shy;Client2<\/CODE>,\nwhich means that you also need a new connection point for it.\n<\/P>\n<P>\nNext time, a look at the special case where\nthe client interface is a dispatch interface.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last time, we saw how to enumerate all the Internet Explorer and Explorer Windows and see what they are viewing. But that program printed static information. It didn&#8217;t track the changes to the windows if the user clicked to another Web page or navigated to a different folder. In order to hook that up, we [&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-4113","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Last time, we saw how to enumerate all the Internet Explorer and Explorer Windows and see what they are viewing. But that program printed static information. It didn&#8217;t track the changes to the windows if the user clicked to another Web page or navigated to a different folder. In order to hook that up, we [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4113","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=4113"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4113\/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=4113"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=4113"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=4113"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}