{"id":203,"date":"2008-02-16T14:45:34","date_gmt":"2008-02-16T13:45:34","guid":{"rendered":"http:\/\/blog.m-ri.de\/index.php\/2008\/02\/16\/late-binding-und-schwache-performance-durch-getidsofnames\/"},"modified":"2008-02-29T20:13:31","modified_gmt":"2008-02-29T19:13:31","slug":"late-binding-und-schwache-performance-durch-getidsofnames","status":"publish","type":"post","link":"http:\/\/blog.m-ri.de\/index.php\/2008\/02\/16\/late-binding-und-schwache-performance-durch-getidsofnames\/","title":{"rendered":"Late Binding und schwache Performance durch GetIDsOfNames"},"content":{"rendered":"<p>Immer wieder sehe ich Entwickler, die \u00fcber Late Binding COM Komponenten ansprechen. Das ist an sich nur zu unterst\u00fctzen, denn <em>DISPIDs <\/em>k\u00f6nnen sich schnell mal \u00e4ndern, wenn sich ein Interface \u00e4ndert. Gerade bei den rasant\u00a0an Verbreitung zunehmenden .NET Komponenten, die man mal schnell\u00a0in die eigene Anwendung per COM einbindet, kann das Binding \u00fcber die Interfaces und <em>DISPIDs <\/em>schnell zum Frust werden. Gleiches gilt f\u00fcr Office Komponenten, bei denen man sich nicht zwingend an ein Interface binden will. Zudem ist es der von <a href=\"http:\/\/support.microsoft.com\/kb\/247579\/en-us\">Microsoft empfohlene Weg<\/a> die Office-Automation zu benutzen (siehe auch <a href=\"http:\/\/support.microsoft.com\/kb\/245115\/en-us\">hier<\/a>).<\/p>\n<p>OK! Also man nimmt Late Binding und verwendet z.B. die aktuellen Wrapper, die netterweise von der ATL zur Verf\u00fcgung gestellt werden, \u00a0z.B. die netten Funktionen aus der <a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/9yb5s0fk(VS.80).aspx\"><em>CComDispatchDriver<\/em><\/a> Klasse. <em>GetPropertyByName<\/em>, <em>PutPropertyByName <\/em>und auch die Funktionen <em>Invoke0<\/em>, <em>Invoke1 <\/em>und <em>InvokeN <\/em>haben entsprechende \u00dcberladungen, die die Verwendung von Funktions-\/Eigenschaftsnamen direkt erlauben.<\/p>\n<p>Der Nachteil liegt nicht gleich auf der Hand. Immer wenn solch eine Funktion aufgerufen wird, wird nicht durch der <em>IDispatch::Involke <\/em>ausgef\u00fchrt, sondern auch ein Aufruf von <em>IDispatch::GetIDsOfNames<\/em>. Bei einem Out-Of-Process-Server kann dieser Roundtrip einiges an Performance kosten. Dabei ist es so einfach es besser zu machen.<\/p>\n<p>Die Doku von <em><a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/ms221306(VS.85).aspx\">IDispatch::GetIDsOfNames<\/a><\/em> sagt folgendes:\u00a0\u00a0<\/p>\n<blockquote><p>The member and parameter DISPIDs must remain constant for the lifetime of the object. This allows a client to obtain the DISPIDs once, and cache them for later use.<\/p><\/blockquote>\n<p>Und\u00a0wer den oben genannten <a href=\"http:\/\/support.microsoft.com\/kb\/247579\/en-us\">KB-Artikel<\/a>\u00a0aufmerksam gelesen hat, der hat auch was von DISPID-Caching mitbekommen.<\/p>\n<p>Ich benutze gerne die die Wrapper, die in der MFC automatisch erzeugt werden, wenn man einen <em>Type-Library<\/em> \u00fcber den Class View importiert. Hier werden die Funktionen auch in eine nette kleine <em>COleDispatchDriver<\/em>-Klasse verpackt und man bekommt netterweise ein gutes Exception Handling den Fehlerfall geliefert. Leider werden hier aber auch wieder nur <em>DISPIDs <\/em>verwendet<em>.<br \/>\n<\/em>Aber mit einem kleinen Trick, kann man diese Klassen genial einfach f\u00fcr <em>Late Binding <\/em>nutzen. Ich gehe wie folgt vor:<\/p>\n<ul>\n<li>Ich importiere die <em>Type-Library<\/em> (tlb) mit dem Visual Studio Class View.<\/li>\n<li>D.h. ich habe jetzt alle Wrapper mit DISPIDs, Was ich eigentlich f\u00fcr Late Binding vermeiden will.<\/li>\n<li>Jetzt passe ich einfach die einzelnen Wrapper Funktionen in der folgenden Art und Weise an, ich ersetze die DISPID\u00a0durch eine statische Variable:<\/li>\n<\/ul>\n<pre line=\"1\" lang=\"cpp\">\r\nvoid CMyWrapper::DoSomething(){\r\n\u00a0 static CDispIdHolder dispid(m_lpDispatch,L\"DoSomething\");\r\n\u00a0 InvokeHelper(dispid, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);\r\n}<\/pre>\n<ul>\n<li>Die kleine Klasse die ich hier verwende macht nun den Rest und besorgt die <em>DISPID<\/em>\u00a0und cached sie damit weil die Variable statisch definiert ist:<\/li>\n<\/ul>\n<pre line=\"1\" lang=\"cpp\">\r\nclass CDispIdHolder\r\n{\r\npublic:\r\n\u00a0CDispIdHolder(IDispatch *pDispatch,LPCOLESTR pName)\r\n\u00a0 : m_dispid(DISPID_UNKNOWN)\r\n\u00a0 {\r\n\u00a0\u00a0 HRESULT hr = pDispatch-&gt;GetIDsOfNames(\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 IID_NULL,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &amp;const_cast&lt;LPOLESTR&gt;(pName),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 1,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 LOCALE_SYSTEM_DEFAULT,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &amp;m_dispid);\r\n\u00a0\u00a0 if (FAILED(hr))\r\n\u00a0\u00a0\u00a0 AfxThrowOleException(hr);\r\n\u00a0 }\r\n\u00a0 operator DISPID() { return m_dispid; }\r\nprivate:\r\n\u00a0DISPID m_dispid;\r\n};<\/pre>\n<p>Die Exception, die man verwendet ist nat\u00fcrlich Implementierungsfrage.<\/p>\n<p>\u2757 Der Performance Gain ist zum Teil betr\u00e4chtlich, besonders wnen bestimmte Funktionen sehr\u00a0oft aufgerufen werden m\u00fcssen!<\/p>\n<p>Anmerkung (f\u00fcr alle die es ganz genau nehmen):<br \/>\nWenn man die Doku genau liest, hei\u00dft es nat\u00fcrlich hier, dass die Implementierung nur f\u00fcr die Lebenszeit des Objektes konstante <em>DISPIDs<\/em> garantiert. Wenn man allerdings bei Early Binding schon <em>DISPIDs <\/em>als konstant annimmt, ist meine Methode f\u00fcr Late Binding sicherlich vertretbar.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Immer wieder sehe ich Entwickler, die \u00fcber Late Binding COM Komponenten ansprechen. Das ist an sich nur zu unterst\u00fctzen, denn DISPIDs k\u00f6nnen sich schnell mal \u00e4ndern, wenn sich ein Interface \u00e4ndert. Gerade bei den rasant\u00a0an Verbreitung zunehmenden .NET Komponenten, die man mal schnell\u00a0in die eigene Anwendung per COM einbindet, kann das Binding \u00fcber die Interfaces &hellip; <a href=\"http:\/\/blog.m-ri.de\/index.php\/2008\/02\/16\/late-binding-und-schwache-performance-durch-getidsofnames\/\" class=\"more-link\"><span class=\"screen-reader-text\">\u201eLate Binding und schwache Performance durch GetIDsOfNames\u201c <\/span>weiterlesen<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[25,30,4,3,2],"tags":[366,370,92,352],"class_list":["post-203","post","type-post","status-publish","format-standard","hentry","category-atl","category-c","category-mfc","category-programmieren","category-windows-api","tag-atl","tag-c","tag-com","tag-mfc"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"http:\/\/blog.m-ri.de\/index.php\/wp-json\/wp\/v2\/posts\/203","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/blog.m-ri.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.m-ri.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.m-ri.de\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.m-ri.de\/index.php\/wp-json\/wp\/v2\/comments?post=203"}],"version-history":[{"count":0,"href":"http:\/\/blog.m-ri.de\/index.php\/wp-json\/wp\/v2\/posts\/203\/revisions"}],"wp:attachment":[{"href":"http:\/\/blog.m-ri.de\/index.php\/wp-json\/wp\/v2\/media?parent=203"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.m-ri.de\/index.php\/wp-json\/wp\/v2\/categories?post=203"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.m-ri.de\/index.php\/wp-json\/wp\/v2\/tags?post=203"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}