1. When we use SafeArrayGetElement() to retrieve an element from a SAFEARRAY, special attention must be afforded to situations where the element is a User-Defined Type (UDT) (i.e. a structure in common programming terms).
2. More specifically, a UDT which contains member fields which are pointers to allocated memory (e.g. BSTRs).
3. Just before calling the API, the UDT structure that is to receive a copy of the array element must be cleared of random data (the example C++ code below demonstrates this by using the memset() function).
4. This is because as part of the SafeArrayGetElement() call, SysFreeString() will be invoked on BSTR member fields inside the UDT.
5. Now, if any BSTR member field contained random data, SysFreeString() will crash. If it is NULL, SysFreeString() will go through fine. In fact SysFreeString() may not even be called.
6. Also note that for such UDTs, when an instance is no longer required, it must once again be cleared. Note that this clearing is different from that which was mentioned previously using memset. This clearance is done to specifically free any members which are references to allocated memory (e.g. BSTRs).
7. This can be done manually by going through each memory reference member fields or (preferrably) by using the IRecordInfo::RecordClear() method. The reason this is necessary is because COM SAFEARRAYs use copy-semantics : all data retrieved from a SAFEARRAY are actually copies of corresponding elements. Hence each retrieved element must be subsequently cleared when no longer required.
8. The following is a sample code that demonstrates this :
long rgIndices; TestStruct value; // TestStruct is a UDT which contain a BSTR member field. // Note that we must set all fields // inside "value" to zero. // This is important because // SafeArrayGetElement() will first // clear all members of "value". // For BSTR member fields, // it will call the ::SysFreeString() // API. Now if the BSTR members contain // random data, it will result in // a crash. If it is NULL, it is OK. memset(&value, 0, sizeof(value)); // SafeArrayGetElement() will return // a copy of the relevant element in // the SAFEARRAY. Hence it is important // that when "value" is no longer required, // its members be cleared. This is especially // so for members which point to allocated // memory (e.g. BSTRs). rgIndices = i; SafeArrayGetElement ( pSafeArrayOfTestStruct, rgIndices, (void FAR*)&value ); ... ... ... // We clear the members of "value" // which includes the BSTR member fields. spIRecordInfo -> RecordClear((PVOID)&value);