{"id":5443,"date":"2013-01-25T07:00:00","date_gmt":"2013-01-25T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/01\/25\/when-you-have-a-safearray-you-need-to-know-what-it-is-a-safearray-of\/"},"modified":"2013-01-25T07:00:00","modified_gmt":"2013-01-25T07:00:00","slug":"when-you-have-a-safearray-you-need-to-know-what-it-is-a-safearray-of","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130125-00\/?p=5443","title":{"rendered":"When you have a SAFEARRAY, you need to know what it is a SAFEARRAY *of*"},"content":{"rendered":"<p><P>\nA customer had a problem with <CODE>SAFEARRAY<\/CODE>,\nor more specifically, with\n<CODE>CComSafeArray<\/CODE>.\n<\/P>\n<PRE>\nCComSafeArray&lt;VARIANT&gt; sa;\nGetAwesomeArray(&amp;sa);<\/p>\n<p>LONG lb = sa.GetLowerBound();\nLONG ub = sa.GetUpperBound();<\/p>\n<p>for (LONG i = lb; i &lt;= ub; i++) {\n CComVariant item = sa.GetAt(i);\n &#8230; use the item &#8230;\n}\n<\/PRE>\n<P>\nThe <CODE>GetAt<\/CODE> method returns a\n<CODE>VARIANT&amp;<\/CODE>,\nand when it is copy-constructed into\n<CODE>item<\/CODE>,\nthe\n<CODE><A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2009\/08\/28\/9887637.aspx\">DISP_E_<\/A>BAD&shy;VAR&shy;TYPE<\/CODE>\nexception is raised.\n<\/P>\n<P>\nOn the other hand, if the offending line is changed to\n<\/P>\n<PRE>\nCComQIPtr&lt;IAwesome&gt; pAwesome = sa.GetAt(i).punkVal;\n<\/PRE>\n<P>\nthen the problem goes away.\n<\/P>\n<P>\nYour initial reaction to this code would be that\nthere is an off-by-one error in the loop control,\nbut it turns out that there isn&#8217;t because\n<CODE>SAFEARRAY<\/CODE> uses inclusive upper bounds\nrather than exclusive.\n<\/P>\n<P>\nThe first step in debugging this is seeing what is\nin the bad variant that makes the copy constructor\nthink it&#8217;s not a valid variant type.\n<\/P>\n<P>\nInspecting in the debugger shows that the variant\nreturned by <CODE>GetAt<\/CODE> has a valid\n<CODE>punk<\/CODE>, but the <CODE>vt<\/CODE> is <CODE>0x1234<\/CODE>.\nWell, that&#8217;s not a valid variant type, so that&#8217;s the proximate\ncause of the problem.\n<\/P>\n<P>\nHow did an invalid variant type get into your <CODE>SAFEARRAY<\/CODE>?\n<\/P>\n<P>\nAt this point the customer realized that maybe their code to create\nthe array was faulty,\nso they offered to share it.\n<\/P>\n<PRE>\nvoid GetAwesomeArray(SAFEARRAY **ppsa)\n{\n SAFEARRAY *psa = SafeArrayCreateVector(VT_UNKNOWN, 0, m_count);\n for (LONG i = 0; i &lt; m_count; i++) {\n  CComPtr&lt;IAwesome&gt; spAwesome;\n  CreateAwesomeThing(i, &amp;spAwesome);\n  SafeArrayPutElement(psa, &amp;i, spAwesome);\n }\n *ppsa = psa;\n}\n<\/PRE>\n<P>\nOkay, now all the pieces fell into place.\n<\/P>\n<P>\nThe <CODE>Get&shy;Awesome&shy;Array<\/CODE>\nfunction is creating an array of <CODE>VT_UNKNOWN<\/CODE>,\nbut the code fragment that calls\n<CODE>Get&shy;Awesome&shy;Array<\/CODE>\ntreats it as an array of <CODE>VT_VARIANT<\/CODE>.\n<\/P>\n<P>\nYour array of <CODE>IUnknown*<\/CODE> is being misinterpreted\nas an array of <CODE>VARIANT<\/CODE>.\nThat explains all the symptoms:\nThe <CODE>vt<\/CODE> is wrong, because it&#8217;s really just the\nlow-order word of the first <CODE>IUnknown*<\/CODE>.\nIgnoring the <CODE>vt<\/CODE> and going straight for the\n<CODE>punk<\/CODE> seems to work because that&#8217;s where\nthe <I>second<\/I> <CODE>IUnknown*<\/CODE> happens to be.\n(Or third, if you are compiling as 32-bit.)\n<\/P>\n<P>\nIn other words, it&#8217;s as if you did a\n<CODE>reinterpret_cast&lt;VARIANT&amp;&gt;(punkArray[0])<\/CODE>.\n<\/P>\n<P>\nIf you had used regular C-style arrays\nor a C++ collection,\nthen the compile-time type checking would have told you that\nyou mismatched the producer and consumer.\nBut since you went through a <CODE>SAFEARRAY<\/CODE>,\nthat compile-time type information is lost,\nsince a\n<CODE>SAFEARRAY<\/CODE> is a polymorphic array.\nIt now becomes your job to keep track of what you have an array <I>of<\/I>,\nand its dimensions and bounds.\n<\/P>\n<P>\nYou can keep track of this information via documentation,\n&#8220;This function returns a 1-dimensional\n<CODE>SAFEARRAY<\/CODE> of <CODE>VT_IUNKNOWN<\/CODE>,\nwith lower bound 0 and variable upper bound.&#8221;\nOr you can check at runtime, by calling\n<CODE>Safe&shy;Array&shy;Get&shy;Vartype<\/CODE>\nto see what the base type is,\nand\n<CODE>Safe&shy;Get&shy;Dim<\/CODE> to see how many\ndimensions the array has,\nand\n<CODE>Safe&shy;Array&shy;Get&shy;LBound<\/CODE>\nand\n<CODE>Safe&shy;Array&shy;Get&shy;UBound<\/CODE>\nto obtain the upper and lower bounds for those dimensions.\n<\/P>\n<P>\nThe code above seemed not to be sure which model it wanted\nto use.\nIt trusted the base type and the dimension,\nbut checked the upper and lower bounds.\n<\/P>\n<P>\nAnyway,\nassuming we are going with the &#8220;keep track via documentation&#8221;\napproach,\nthe solution for the original problem is to have\nthe producer and consumer agree on exactly what kind of\n<CODE>SAFEARRAY<\/CODE> is being handed around.\nEither produce an array of <CODE>VT_UNKNOWN<\/CODE> and\nconsume it as a <CODE>CComSafeArray&lt;IUnknown*&gt;<\/CODE>\nor produce an array of <CODE>VT_VARIANT<\/CODE> and consume\nit as a\n<CODE>CComSafeArray&lt;VARIANT&gt;<\/CODE>.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A customer had a problem with SAFEARRAY, or more specifically, with CComSafeArray. CComSafeArray&lt;VARIANT&gt; sa; GetAwesomeArray(&amp;sa); LONG lb = sa.GetLowerBound(); LONG ub = sa.GetUpperBound(); for (LONG i = lb; i &lt;= ub; i++) { CComVariant item = sa.GetAt(i); &#8230; use the item &#8230; } The GetAt method returns a VARIANT&amp;, and when it is copy-constructed into [&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-5443","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A customer had a problem with SAFEARRAY, or more specifically, with CComSafeArray. CComSafeArray&lt;VARIANT&gt; sa; GetAwesomeArray(&amp;sa); LONG lb = sa.GetLowerBound(); LONG ub = sa.GetUpperBound(); for (LONG i = lb; i &lt;= ub; i++) { CComVariant item = sa.GetAt(i); &#8230; use the item &#8230; } The GetAt method returns a VARIANT&amp;, and when it is copy-constructed into [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/5443","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=5443"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/5443\/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=5443"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=5443"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=5443"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}