{"id":357,"date":"2013-04-20T10:19:28","date_gmt":"2013-04-20T14:19:28","guid":{"rendered":"http:\/\/templesystems.com\/?page_id=357"},"modified":"2013-04-20T10:19:28","modified_gmt":"2013-04-20T14:19:28","slug":"variants-safearrays-and-bstrs","status":"publish","type":"page","link":"http:\/\/templesystems.com\/?page_id=357","title":{"rendered":"Variants, Safearrays, and BSTR&#8217;s"},"content":{"rendered":"<p>VARIANTs, SAFEARRAYs, and BSTRs, Oh My!<\/p>\n<form id=\"oletypes\" action=\"oletypes.aspx\" method=\"post\" name=\"oletypes\"><input type=\"hidden\" name=\"__VIEWSTATE\" value=\"dDwtNjU0MzcyMTk1Ozs+D+el8\/085oCRg3c7UtWGvPEbfYU=\" \/><\/p>\n<div style=\"text-align: center;\">(<a href=\"..\/whitepapers.aspx\">Click here<\/a> to<br \/>\ngo back to the index page for my white papers.)<\/div>\n<div style=\"text-align: center;\">\n<h2>VARIANTs, SAFEARRAYs, and BSTRs, Oh My!<\/h2>\n<h3>or, an Introduction to Common OLE Data Types for the C++ Programmer<\/h3>\n<h3>by <a href=\"mailto:rob@roblocher.com\">Rob Locher<\/a><\/h3>\n<\/div>\n<p>&nbsp;<\/p>\n<p>So far, all the textbooks I have seen that\u00a0talk about VARIANTs,<br \/>\nSAFEARRAYs, and BSTRs\u00a0tend to lead\u00a0the C++ programmer learning COM<br \/>\ndown the primrose path, by implying that wrapper classes such as <tt>CComVariant<\/tt><br \/>\nor <tt>_bstr_t<\/tt> are going to solve all your problems.\u00a0 Maybe such a<br \/>\nclass will, if you have a simple case, but what if you have to pass a VARIANT<br \/>\nholding a\u00a0SAFEARRAY of BSTRs?\u00a0 Well, then you need to understand<br \/>\nwhat\u00a0\u00a0VARIANTs, SAFEARRAYs, and BSTRs\u00a0really are, unless you<br \/>\nwant to wait around for somebody to create a wrapper class for VARIANTs<br \/>\nholding\u00a0SAFEARRAYs of BSTRs.<\/p>\n<p>For the rest of this article I will drop the capitalization of VARIANT,<br \/>\nSAFEARRAY, and BSTR for readability purposes most of the time, and I might call<br \/>\nthem &#8220;OLE types&#8221; or &#8220;VB types&#8221;.<\/p>\n<p>Why are variants, safearrays, and bstrs so hard to use?\u00a0 The problem, of<br \/>\ncourse, is that those OLE types aren&#8217;t simple to use at all, if you are a C++<br \/>\nprogrammer.\u00a0 If you are coding in Visual Basic, then the VB runtime takes<br \/>\ncare of that particular complexity for you.\u00a0 (Even then I&#8217;ve heard that<br \/>\nOLE types can be tricky sometimes.)\u00a0 If you as a C++ programmer are going<br \/>\nto use them, you will have to bear the same burden as the coders of the VB<br \/>\nruntime.\u00a0 In other words, you will have to understand what the VB types<br \/>\nactually\u00a0are, and follow the\u00a0poorly-documented rules about how to use<br \/>\nthem.\u00a0 Otherwise, you will find yourself causing memory leaks, or possibly<br \/>\neven using invalid pointers.<\/p>\n<p>In the discussion that follows, I will first talk about the fundamental types<br \/>\nand how to use them, and then I will discuss the shortcuts (helper classes) and<br \/>\ntheir limitations.<\/p>\n<p>&nbsp;<\/p>\n<h3>BSTRs<\/h3>\n<p>What is a BSTR?\u00a0 A bstr is a pointer to a string of wide characters (not <tt><br \/>\nchar<\/tt> ).\u00a0 The string is not terminated.\u00a0 Instead, the length of<br \/>\nthe string is stored\u00a0as an unsigned long (four bytes)\u00a0just before the<br \/>\nfirst character of the string.\u00a0 Note that this is not how you would do it<br \/>\nin C++; in C++, the pointer would be to the first member of the structure, the<br \/>\nunsigned long, and not to the second.\u00a0 You might think that you could<br \/>\ncreate a bstr the C++ way, by creating a structure and then returning a pointer<br \/>\nto the second element, cast to bstr, but you should never do this; the memory<br \/>\npointed to by a bstr is actually owned by Windows.\u00a0 Because a bstr&#8217;s<br \/>\nmemory is owned by Windows, you can safely pass a bstr (which is a pointer)<br \/>\nbetween processes.\u00a0 Instead, you can use the function <tt>SysAllocString()<\/tt><br \/>\nto create a bstr, and the function <tt>SysFreeString()<\/tt> to destroy it<br \/>\nproperly.\u00a0 In fact, you should use only platform SDK functions (or helper<br \/>\nclasses that use them internally)\u00a0to manipulate bstrs; see the platform<br \/>\nSDK help topic &#8220;<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/automat\/htm\/chap7_2xgz.asp\">String<br \/>\nManipulation Functions<\/a>&#8220;.<\/p>\n<p>It is a convention that a null pointer is a legal bstr, that represents an<br \/>\nempty string.\u00a0 A bstr must always either be a null pointer or point to an<br \/>\nactual allocated bstr; it should never be a random uninitialized pointer.<br \/>\nThere are also functions to reallocate or change a bstr. \u00a0As far as I can<br \/>\nsee, most of the string manipulation functions normally taken for granted, such<br \/>\nas finding a substring, comparing two strings,\u00a0and so on,\u00a0are<br \/>\nmissing.\u00a0 Most significantly, there seems to be no function to copy a<br \/>\nbstr.\u00a0 There are lots of functions to convert things to and from bstrs;<br \/>\nsee the platform SDK help topic &#8220;<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/automat\/htm\/chap7_9dir.asp?frame=true\">Data<br \/>\nType Conversion APIs<\/a>&#8220;, the help topic &#8220;<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/vclib\/html\/_atl_String_Conversion_Macros.asp?frame=true\">ATL<br \/>\nand MFC String Conversion Macros<\/a>&#8220;, and the help topics for the functions <tt><br \/>\n<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/vclang\/html\/vclrfConvertBSTRToString.asp\"><br \/>\nConvertBSTRToString()<\/a><\/tt> and <tt><a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/vclang\/html\/vclrfconvertstringtobstr.asp\"><br \/>\nConvertStringToBSTR()<\/a><\/tt>.<\/p>\n<p>When you pass a bstr across a COM boundary, you must give careful thought as<br \/>\nto whether the client or the server should be responsible for allocating and<br \/>\ndeallocating the bstr.\u00a0 Certain rules have been established by convention<br \/>\nthat determine who should create the bstr, and who should free it.<br \/>\nUnfortunately, these rules are poorly documented&#8211; see the platform SDK help<br \/>\ntopic &#8220;<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/vccore\/html\/vcconStringsAllocatingReleasingMemoryForBSTR.asp\">Allocating<br \/>\nand Releasing Memory for a BSTR<\/a>&#8221; for guidelines.\u00a0 Generally<br \/>\nspeaking, the client is responsible for the bstr.\u00a0 If the client is<br \/>\npassing a read-only string to the server, then usually it is passed as a<br \/>\nbstr.\u00a0 If the server is expected to change a string, then it might make<br \/>\nsense to have the client pass a pointer-to-bstr.\u00a0 If there is any<br \/>\nconfusion, you should probably test the server carefully against Visual Basic<br \/>\nto insure that the rules are being followed.\u00a0 (You can also import the<br \/>\ntype library into a C++ project and examine the wrapper classes generated.)<\/p>\n<p>There are two helper classes available when using\u00a0bstrs: the &#8220;native COM<br \/>\nsupport&#8221; class <tt>_bstr_t<\/tt>, and the ATL class <tt>CComBSTR<\/tt> .\u00a0 <tt>CComBSTR<\/tt><br \/>\nhas a helpful <tt>CopyTo()<\/tt> method that fills a pointer-to-bstr<br \/>\nproperly, which is very useful for <tt>[out]<\/tt> parameters.\u00a0 Otherwise,<br \/>\nthe two classes are very similar.\u00a0 Both will take care of allocating and<br \/>\ndeallocating the wrapped bstr in the class constructor and destructor.<br \/>\nThey also can be used to take charge of an existing bstr with the <tt>Attach()<\/tt><br \/>\nmethod, or can be made to abandon the bstr with the <tt>Detach()<\/tt> method.<br \/>\nThey have helpful operators and methods to compare two strings, check for<br \/>\nequality, copy a string, and so on.\u00a0 In many cases, <tt>_bstr_t<\/tt> and <tt><br \/>\nCComBSTR<\/tt> instances can be passed as bstr substitutes &#8212; please read the<br \/>\nhelp pages on those classes carefully, because improperly attempting to use the<br \/>\nclasses as bstr substitutes can cause memory leaks.<\/p>\n<h3>VARIANTs<\/h3>\n<p>What the heck is a variant?\u00a0 A variant is a structure containing a union<br \/>\nmember, and an unsigned integer member that describes which member of the union<br \/>\nis currently being used.\u00a0 (I&#8217;m oversimplifying a bit I think, but the<br \/>\noversimplification has gotten me through so\u00a0 far.)\u00a0 If you don&#8217;t know<br \/>\nwhat a union is, <a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/vclang\/html\/_pluslang_unions.asp?frame=true\"><br \/>\nread about it<\/a> first, and then come back.\u00a0 (But don&#8217;t feel bad,<br \/>\nbecause I&#8217;ve never used a union except as a variant and I don&#8217;t know of anybody<br \/>\nelse having done so either.)\u00a0 You might want to look up the system header<br \/>\nfile <tt>oaidl.h<\/tt> or the documentation topic &#8220;<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/automat\/htm\/chap6_7zdz.asp?frame=true\">VARIANT<br \/>\nand VARIANTARG<\/a>&#8220;, which shows the juicy bits of the header file.\u00a0 All<br \/>\nthe typedefs and conditional defines in the header file are quite confusing,<br \/>\nbut what really matters is that there is a member <tt>vt<\/tt> which shows what<br \/>\nmember of the union is being used, and then one of the union members (<tt>llVal<\/tt>,<br \/>\n<tt>lVal<\/tt>, <tt>bVal<\/tt>, <tt>iVal<\/tt>, etc.) is actually holding the<br \/>\ndata, or a pointer to the data.\u00a0 By the way, VARIANT and VARIANTARG are<br \/>\ninterchangeable.<\/p>\n<p>The unsigned integer member that tells you what type the variant is actually<br \/>\nholding, <tt>vt<\/tt>, is itself a bit confusing.\u00a0 The various legal values<br \/>\nthat it can hold are enumerated in the system header file <tt>wtypes.h<\/tt>.<br \/>\nIf you look at the enumeration <tt>VARENUM<\/tt> in that file, you will see that<br \/>\nit is possible to combine certain values. \u00a0(The bitwise or operator &#8220;<tt>|<\/tt><br \/>\n&#8221; is usually used to do the combining, but it seems to me that addition would<br \/>\nwork just as well.)\u00a0 The common values that I know of that are combined<br \/>\nwith other values are <tt>VT_ARRAY<\/tt> and <tt>VT_BYREF<\/tt>.\u00a0 If <tt>vt<\/tt><br \/>\nequals <tt>VT_ARRAY |<\/tt> (something), then it means that the variant contains<br \/>\na safearray of (something). For example, <tt>(vt == VT_ARRAY | VT_BSTR)<\/tt> means<br \/>\nthat you are passing a safearray of bstr.\u00a0 Similarly, if <tt>vt<\/tt> equals<br \/>\n<tt>VT_BYREF |<\/tt> (something), it means that you are passing (something) by<br \/>\nreference.\u00a0 In this case, you are explicitly passing a type-safe pointer,<br \/>\nwhere (something)\u00a0indicates the type pointed to.<\/p>\n<p>By now, hopefully we can infer that the idea of a variant is to provide Visual<br \/>\nBasic\u00a0with a generic variable type that doesn&#8217;t waste too much space, that<br \/>\ncan hold just about anything,\u00a0including a pointer to or an array of just<br \/>\nabout anything.\u00a0 Although a variant is general-purpose, it is still<br \/>\npossible for a variant to be marshalled, because it is always possible to<br \/>\ndetermine how much space it uses.\u00a0 Or, if the variant holds a pointer to<br \/>\nsomething, or a safearray of something, it is still possible to determine how<br \/>\nmuch space the value pointed to takes up, because that might have to be<br \/>\nmarshalled too.<\/p>\n<p>Thanks to the way variants support weakly-typed languages, it is legal to<br \/>\nchange the type of the variant when it is holding a value.\u00a0 This is known<br \/>\nas type coercion.\u00a0 For example, you could coerce a variant holding the<br \/>\nbstr &#8220;3.0&#8221; to be type\u00a0 <tt>VT_R8<\/tt> (double).\u00a0 When a variant is<br \/>\ncoerced, what is happening inside the variant is that <tt>vt<\/tt> is being<br \/>\nchanged, and also the data is being converted internally.\u00a0 In the example,<br \/>\nthe bstr pointed to by the <tt>pbstrVal<\/tt> element would be freed, and an<br \/>\neight byte floating point representation of the number 3.0 would be placed in<br \/>\nthe <tt>dblVal<\/tt> element.\u00a0 The reason I mention coercion is that if you<br \/>\nhave a COM object written in C++ that accepts a variant from a Visual Basic<br \/>\nclient, the variant may have to be coerced to the type that you expect.<br \/>\nThe functions <tt>VariantChangeType()<\/tt> and <tt>VariantChangeTypeEx()<\/tt> are<br \/>\nvery useful if you have to do type coercion.<\/p>\n<p>If a variant is holding a bstr, then the variant owns the bstr, and properly<br \/>\ndeallocating the variant will result in the bstr being deallocated.\u00a0 If a<br \/>\nvariant holds a pointer, that is to say <tt>vt<\/tt> is <tt>VT_BYREF |<\/tt> (something),<br \/>\nthen the variant is being used to explicitly pass the pointer, and the variant<br \/>\ndoes <em>not<\/em> own the memory pointed to.\u00a0 If a variant is holding a<br \/>\nsafearray, then the variant owns the safearray, and properly deallocating the<br \/>\nvariant will result in the safearray being deallocated.\u00a0 See the help<br \/>\ntopic &#8220;<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/automat\/htm\/chap7_0rn7.asp?frame=true\">Variant<br \/>\nManipulation API Functions<\/a>&#8220;.\u00a0 That topic also mentions the API<br \/>\nfunctions that are provided to use variants.<\/p>\n<p>If you want to create and manipulate a variant yourself, without benefit of a<br \/>\nhelper class, then here is how you do it (mostly adapted from the book <em>The<br \/>\nCOM and COM+ Programming Primer<\/em>, by Alan Gordon, published by Prentice<br \/>\nHall PTR):<\/p>\n<ol>\n<li>If your variant is going to\u00a0explicitly pass\u00a0a pointer to something,<br \/>\ndeclare and set the memory to which the pointer will point.<\/li>\n<li>Declare a variant, or use the <tt>new<\/tt><br \/>\noperator to create a pointer to a variant.<\/li>\n<li>Initialize the variant by calling <tt>VariantInit()<\/tt>.<\/li>\n<li>Set <tt>vt<\/tt><br \/>\nin the variant to be the proper value.<\/li>\n<li>Set the member variable in the variant that corresponds to the type in <tt>vt<\/tt>.<\/li>\n<li>Use or pass your variant.<\/li>\n<li>If the variant was used to explicitly pass a pointer, i.e. <tt>(vt &amp; VT_BYREF<br \/>\n!= 0)<\/tt>, then free the memory pointed to.<\/li>\n<li>Use <tt>VariantClear()<\/tt><br \/>\nto free any resources that the variant owns.<\/li>\n<li>Let the variant go out of scope, or use the <tt>delete<\/tt> operator on its<br \/>\npointer, the same as you would for any other variable.<\/li>\n<\/ol>\n<p>If that seems to you to be a lot of work, I agree with you.\u00a0 Fortunately,<br \/>\nthere are two helper classes, <tt>CComVariant<\/tt> and <tt>_variant_t<\/tt>,<br \/>\nthat you can use to make things easier.\u00a0 You can simply pass the pointer<br \/>\nor value that you wish to wrap to the constructor, and the class will create a<br \/>\nvariant to wrap the value or pointer, and take care of the variant.<br \/>\nPlease note though that since a variant that wraps a pointer is not responsible<br \/>\nfor the memory to which it points, neither is a <tt>_variant_t<\/tt> or <tt>CComVariant<\/tt><br \/>\ninstance.\u00a0 Both classes support <tt>Attach()<\/tt> and <tt>Detach()<\/tt> methods<br \/>\nsimilar to the bstr wrapper classes.\u00a0 <tt>Attach()<\/tt> lets an instance<br \/>\nof the class take charge of a pre-existing variant.\u00a0 <tt>Detach()<\/tt> forces<br \/>\nthe instance to abandon its variant.\u00a0 Both classes also have a <tt>ChangeType()<\/tt><br \/>\nmethod that can be used to coerce the wrapped variant.<\/p>\n<p>I would be remiss if I didn&#8217;t mention an <a href=\"http:\/\/msdn.microsoft.com\/archive\/default.asp?url=\/archive\/en-us\/dnarextvb\/html\/msdn_article4.asp\"><br \/>\nexcellent article<\/a> pointed out to me, written by Microsoft&#8217;s Bruce<br \/>\nMcKinney in 1996 and still appropriate, that talks about variants from both the<br \/>\nVisual C++ and the Visual Basic points of view.\u00a0 It apparently was written<br \/>\nbefore <tt>CComVariant<\/tt> and <tt>_variant_t<\/tt> came around, and does an<br \/>\nexcellent job of describing how variants really work.<\/p>\n<p>&nbsp;<\/p>\n<h3>SAFEARRAYs<\/h3>\n<p>If you understand bstrs and variants, then you shouldn&#8217;t have much\u00a0trouble<br \/>\nwith safearrays.\u00a0 SAFEARRAY was created to suit the needs of Visual Basic<br \/>\nand other weakly-typed languages\u00a0for a type-safe array of one or more<br \/>\ndimensions of arbitrary bounds.\u00a0 Note that it is not legal to pass a<br \/>\nsafearray by itself via an <tt>IDispatch<\/tt> interface; for automation<br \/>\npurposes, a safearray is only legal if it is wrapped by a variant.\u00a0 (The<br \/>\nATL wizard won&#8217;t let you do it.)\u00a0 If you are wrapping a safearray with a<br \/>\nvariant, then the member <tt>vt<\/tt> of the variant should be <tt>VT_ARRAY<\/tt><br \/>\nbitwise-or&#8217;ed (&#8220;<tt>|<\/tt>&#8220;) with (something), where (something) corresponds to<br \/>\nthe type of the elements of the array.\u00a0 A safearray <em>is<\/em> responsible<br \/>\nfor its contents; properly deallocating a safearray will result in its contents<br \/>\nbeing safely deallocated.\u00a0 Since a safearray should always be wrapped by a<br \/>\nvariant, and a variant is responsible for its contents as long as the variant<br \/>\ndoesn&#8217;t wrap a pointer, then\u00a0properly deallocating the variant holding the<br \/>\nsafearray will properly take care of the variant, the safearray, and the<br \/>\ncontents of the safearray.<\/p>\n<p>A safearray can only hold one type at a time, as you might<br \/>\nguess;\u00a0however, a safearray can hold variants, so that rule really isn&#8217;t<br \/>\nmuch of a restriction.\u00a0 In fact, if you wanted to, you could have a<br \/>\nmulti-dimensional safearray holding all different kinds of variants, some of<br \/>\nwhich could themselves be safearrays, which in turn could hold other things&#8230;<br \/>\nbut I recommend not making things that complicated if it can be avoided.<\/p>\n<p>So, by now you probably are wondering what a safearray really is.\u00a0Here is<br \/>\nsome code from <tt>oaidl.h<\/tt>:<\/p>\n<div class=\"code1\">typedef struct tagSAFEARRAYBOUND<\/p>\n<p>{<\/p>\n<p>ULONG cElements;<\/p>\n<p>LONG lLbound;<\/p>\n<p>} SAFEARRAYBOUND;<\/p>\n<p>typedef struct tagSAFEARRAY<\/p>\n<p>{<\/p>\n<p>USHORT cDims;<\/p>\n<p>USHORT fFeatures;<\/p>\n<p>ULONG cbElements;<\/p>\n<p>ULONG cLocks;<\/p>\n<p>PVOID pvData;<\/p>\n<p>SAFEARRAYBOUND rgsabound[ 1 ];<\/p>\n<p>} SAFEARRAY;<\/p><\/div>\n<p>The <tt>SAFEARRAYBOUND<\/tt> structure is simple: it describes how many elements<br \/>\nthere are, and what the lower bound is, for a dimension.\u00a0 (The first<br \/>\nelement of a VB array doesn&#8217;t have to be number zero.)\u00a0 As for the <tt>SAFEARRAY<\/tt><br \/>\nstructure itself, <tt>cDims<\/tt> has the number of dimensions, and <tt>rgsabound<\/tt><br \/>\nis an array of <tt>SAFEARRAYBOUND<\/tt>, where there is one element per<br \/>\ndimension in the safearray (contrary to the declaration above).\u00a0 <tt>pvData<\/tt><br \/>\nof course points to the actual data.\u00a0 <tt>cLocks<\/tt> holds a lock count,<br \/>\nand <tt>cbElements<\/tt> holds the size of an element.\u00a0 <tt>fFeatures<\/tt> somehow<br \/>\ntells how the data is being stored, and therefore how it can be freed.<br \/>\nSee the help topic &#8220;<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/automat\/htm\/chap7_9ntx.asp?frame=true\">SAFEARRAY<br \/>\nData Type<\/a>&#8221; to see more about the <tt>fFeatures<\/tt> member.<\/p>\n<p>There is a surprising number of API functions to deal with safearrays; see the<br \/>\nhelp topic &#8220;<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/automat\/htm\/chap7_5dyr.asp?frame=true\">Array<br \/>\nManipulation API Functions<\/a>&#8220;.\u00a0 You should not attempt to manipulate a<br \/>\nsafearray or access an element manually; you should instead either use the API<br \/>\nfunctions, or much better yet use the helper class <tt>CComSafeArray<\/tt>.<br \/>\n(There doesn&#8217;t seem to be a <tt>_safearray_t<\/tt> class.)\u00a0 <tt><\/tt><\/p>\n<p><tt>CComSafeArray<\/tt> is a template class; you provide the type of an element<br \/>\nas one of the template parameters.\u00a0\u00a0(The example code in <a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/vclib\/html\/vclrfCComSafeArray.asp?frame=true\"><br \/>\nthe MSDN documentation for <tt>CComSafeArray<\/tt><\/a> is lousy, if you ask<br \/>\nme.\u00a0\u00a0If you can&#8217;t figure it out, check out the\u00a0 <a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/vcsample\/html\/vcsamAtlSafeArraySampleDemonstratesCComSafeArrayPassingSAFEARRAYsToScript.asp\"><br \/>\nATLSafeArray sample<\/a>.)\u00a0 <tt>CComSafeArray<\/tt> provides <tt>Attach()<\/tt><br \/>\nand <tt>Detach()<\/tt> methods to take over an existing safearray or abandon the<br \/>\nwrapped safearray, respectively, as you would expect.\u00a0 Perhaps the best<br \/>\nfeature is the overridden <tt>operator[]<\/tt> method, which allows you to<br \/>\naccess an array element safely and easily.<\/p>\n<p>Well, that&#8217;s it for this article.\u00a0 I hope it helped you get pointed in<br \/>\nthe right direction.\u00a0 (Getting pointed there myself was the main reason I<br \/>\nwrote the article.)\u00a0 If it is incorrect anywhere, or you have any<br \/>\nsuggestions, please email me.<\/p>\n<\/form>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>VARIANTs, SAFEARRAYs, and BSTRs, Oh My! (Click here to go back to the index page for my white papers.) VARIANTs, SAFEARRAYs, and BSTRs, Oh My! or, an Introduction to Common OLE Data Types for the C++ Programmer by Rob Locher &hellip; <a href=\"http:\/\/templesystems.com\/?page_id=357\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":303,"menu_order":0,"comment_status":"open","ping_status":"open","template":"","meta":{"footnotes":""},"class_list":["post-357","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"http:\/\/templesystems.com\/index.php?rest_route=\/wp\/v2\/pages\/357","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/templesystems.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"http:\/\/templesystems.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"http:\/\/templesystems.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/templesystems.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=357"}],"version-history":[{"count":2,"href":"http:\/\/templesystems.com\/index.php?rest_route=\/wp\/v2\/pages\/357\/revisions"}],"predecessor-version":[{"id":359,"href":"http:\/\/templesystems.com\/index.php?rest_route=\/wp\/v2\/pages\/357\/revisions\/359"}],"up":[{"embeddable":true,"href":"http:\/\/templesystems.com\/index.php?rest_route=\/wp\/v2\/pages\/303"}],"wp:attachment":[{"href":"http:\/\/templesystems.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=357"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}