Cannot convert ByRef VARIANT array to SAFEARRAY - c++

I'm writing a C++ DLL to be accessed from Excel VBA (it is only completing mathematical equations and doesn't need to be accessed from the worksheet). The DLL is intended to replace current VBA code and I want to write it in C/C++ for performance.
Since I'm am using existing code and simply want to replace the current VBA function with the new DLL I have to use VBA Variants containing single vector arrays. The following is an abbreviated example of the VBA calling code:
Sub VBACaller()
Dim InputR0(1 To 5) As Variant, vResult As Variant
InputR0(1) = 26.8
InputR0(2) = 27.8
InputR0(3) = 28.8
InputR0(4) = 29.8
vResult = ReadArrayVBA(InputR0)
End Sub
I can pass this Variant array to my C++ function either ByVal or ByRef as a VARIANT data type and it appears to be received correctly. My problems occur when I try to read the contents of the array after converting it into a SAFEARRAY using SafeArrayAccessData. The SAFEARRAY conversion seems to work but the contents of the array aren't correct. The following is my C++ code:
VARIANT __stdcall ReadArrayByRef(VARIANT *input0)
{
//Variable to assign value from Array
double d_m = 0;
//Check if this is a variant type or not
if (V_VT(input0) & VT_ARRAY)
{
SAFEARRAY* pSafeArrayInput0 = NULL;
pSafeArrayInput0 = V_ARRAY(input0);
double* pVals;
HRESULT hr = SafeArrayAccessData(pSafeArrayInput0, (void **)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lLBound = -1, lUBound = 1; // get array bounds
SafeArrayGetLBound(pSafeArrayInput0, 1, &lLBound);
SafeArrayGetUBound(pSafeArrayInput0, 1, &lUBound);
if (lLBound > -1 && lUBound > -1)
{
d_m = pVals[1];
}
SafeArrayUnaccessData(pSafeArrayInput0);
}
}
//Output
VARIANT v;
VariantInit(&v);
v.vt = VT_R8;
v.dblVal = d_m;
return v;
}
The examples I've found seem to indicate that the .parray should hold the data, however inspection of input0 indicates that it is in .pparray. If I try to assign pSafeArrayInput0 = input0.pparray it gives an error. The value d_m returns is along the lines of 1.05319234616515E-307.
If I change the input to ByVal then I can access the elements of the array correctly using the following C++ code (the only difference being the address of input0 is being accessed by the SAFEARRAY.
VARIANT __stdcall ReadArrayByVal(VARIANT input0)
{
//Variable to assign value from Array
double d_m = 0;
//Check if this is a variant type or not
if (V_VT(&input0) & VT_ARRAY)
{
SAFEARRAY* pSafeArrayInput0 = NULL;
pSafeArrayInput0 = V_ARRAY(&input0);
double* pVals;
HRESULT hr = SafeArrayAccessData(pSafeArrayInput0, (void **)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lLBound = -1, lUBound = 1; // get array bounds
SafeArrayGetLBound(pSafeArrayInput0, 1, &lLBound);
SafeArrayGetUBound(pSafeArrayInput0, 1, &lUBound);
if (lLBound > -1 && lUBound > -1)
{
d_m = pVals[1];
}
SafeArrayUnaccessData(pSafeArrayInput0);
}
}
VARIANT v;
VariantInit(&v);
v.vt = VT_R8;
v.dblVal = d_m;
return v;
}
All the examples I've found indicate that passing the VARIANT input by a pointer should works per my ReadArrayByRef function (Ie http://support.microsoft.com/kb/167668 which is slightly different but the same at the points I'm after).
What am I doing wrong in my ByRef function?

You should check the VT_BYREF flag, and if it is set, dereference the pointer to access the array like so:
pSafeArrayInput0 = *V_ARRAYREF(input0);

Related

How to directly access to what's in VARIANT variables in C++?

My program uses an external ocx library and receives data through it. The code below shows how it works.
VARIANT varArrItem, varArrData;
ocx_instance.GetItemArr(real, &varArrItem); // the library provides GetItemArr
// 1) receives data
long lLBound, lUBound;
VARIANT varItem, varData;
long index[2];
index[0] = 0;
index[1] = 0;
COleSafeArray* pSafeItemArr = (COleSafeArray*)&varArrItem; // 2) casts varArrItem to COleSafeArray
CString strItem;
CStringArray arrItem;
pSafeItemArr->GetLBound(1, &lLBound);
pSafeItemArr->GetUBound(1, &lUBound);
int nItemCnt = (lUBound - lLBound + 1);
for (int i = 0; i < nItemCnt; i++)
{
index[0] = i;
VariantInit(&varItem);
pSafeItemArr->GetElement(index, (void *)&varItem); // 3) gets its values using GetElement
strItem = varItem.bstrVal;
arrItem.Add(strItem);
VariantClear(&varItem);
}
A big problem of the program is that this code is run whenever new data arrives, which is quite often, and it consumes a lot of resources. So, I'd like to simplify the code and get just contents of varArrItem, as strings or an array of structs of strings, for example.
varArrItem.vt gives me 8204 and it's said that it consists of 8192(VT_ARRAY) and 12(VT_VARIANT). I'm stuck here and don't know what to do after this. How can I simply get what's in them? Is it possible to access to them without using COleSafeArray?
You don't NEED to use COleSafeArray, it is just a wrapper for convenience. You could just extract the SAFEARRAY* pointer directly from varArrItem and then use the SafeArray APIs directly: SafeArrayGet(L|U)Bound(), SafeArrayGetElement(), etc, though if performance is important then consider using SafeArrayAccessData() to access the VARIANT[] array directly, and thus its BSTR pointers. The less copying of data you do, the faster the code will run. The only copy of data this code actually needs to make is the assignment of the initial VARIANT and each CString you add to the CStringArray:
VARIANT varArray;
ocx_instance.GetItemArr(real, &varArray);
LPSAFEARRAY psa = varArrar.parray;
LONG lLBound, lUBound;
SafeArrayGetLBound(psa, 1, &lLBound);
SafeArrayGetUBound(psa, 1, &lUBound);
CStringArray arrItem;
VARIANT *varArrayData;
if (SUCCEEDED(SafeArrayAccessData(psa, (void**) &varArrayData)))
{
int nItemCnt = (lUBound - lLBound + 1);
for (int i = 0; i < nItemCnt; i++)
{
CString strItem = varArrayData[i].bstrVal;
arrItem.Add(strItem);
}
SafeArrayUnaccessData(psa);
}

Arduino - what's a correct way to handle an array of structs?

I've got a problem while trying to fill an array with structs of Strings. Probably, I'm missing something basic in working with pointers or memory allocation rules on Arduino.
See my code below.
Data struct to be filled:
struct SMSData {
String id;
String status;
String from;
String date;
String text;
};
String parser routine:
SMSData* readSMS(String reply) {
debugSerial.println(reply);
// declare a pointer to result array
SMSData* smsArray = NULL;
const String startPattern = F("+CMGL: ");
int index = -1;
// calculate result array length
byte count = 0;
do {
index = reply.indexOf(startPattern, (index + 1));
if(index < 0) {
break;
}
count++;
} while(true);
if(count == 0) {
return NULL;
}
debugSerial.println(count);
// allocate memory to store result array
smsArray = malloc(count * sizeof(SMSData*));
if(smsArray == NULL) {
return NULL;
}
// start parsing input String
index = reply.indexOf(startPattern);
int fromIndex = 0;
while(true) {
debugSerial.println();
if(index < 0) {
break;
}
// init data for the next element of result array
SMSData smsData = {"", "", "", "", ""};
// start filling result array element
// get id
fromIndex = index + startPattern.length();
index = reply.indexOf(F(","), fromIndex);
smsData.id = reply.substring(fromIndex, index);
debugSerial.println(smsData.id);
// get status
fromIndex = reply.indexOf(F("\""), index) + 1;
index = reply.indexOf(F("\""), fromIndex);
smsData.status = reply.substring(fromIndex, index);
debugSerial.println(smsData.status);
// get phone
fromIndex = reply.indexOf(F("\""), index + 1) + 1;
index = reply.indexOf(F("\""), fromIndex);
smsData.from = reply.substring(fromIndex, index);
debugSerial.println(smsData.from);
// get date
fromIndex = reply.indexOf(F("\""), index + 1) + 1;
index = reply.indexOf(F("\""), fromIndex);
smsData.date = reply.substring(fromIndex, index);
debugSerial.println(smsData.date);
// get text
fromIndex = index + 1;
index = reply.indexOf(startPattern, fromIndex);
if(index < 0) {
smsData.text = reply.substring(fromIndex);
} else {
smsData.text = reply.substring(fromIndex, index);
}
smsData.text.trim();
debugSerial.println(smsData.text);
// add filled element to result array
smsArray[count - 1] = smsData;
}
return smsArray;
}
Output parsed data:
SMSData* smsArray = readSMS(reply);
int count = sizeof(smsArray);
debugSerial.print(F("SMS count:"));
debugSerial.println(count);
for(int i = 0; i < count; i++) {
SMSData smsData = smsArray[i];
debugSerial.print(F("id: "));
debugSerial.println(smsData.id);
debugSerial.print(F("status: "));
debugSerial.println(smsData.status);
debugSerial.print(F("from: "));
debugSerial.println(smsData.from);
debugSerial.print(F("date: "));
debugSerial.println(smsData.date);
debugSerial.print(F("text: "));
debugSerial.println(smsData.text);
}
free(smsArray);
Dummy String to parse:
String reply = "+CMGL: 1,\"REC READ\",\"+123456789012\",,\"2017/09/26,18:31:25+03\"\r\nHi\r\n+CMGL: 2,\"REC READ\",\"+123456789012\",,\"2017/09/26,18:34:25+03\"\r\nHello\r\n";
When I run the sketch it's output is often different, but always broken and incomplete, e.g.
+CMGL: 1,"REC READ","+123456789012",,"2017/09/26,18:31:25+03"
Hi
+CMGL: 2,"REC READ","+123456789012",,"2017/09/26,18:34:25+03"
Hello
2
1
REC READ
+12345678905+03 017/09/26,18:31:25+03
Hi
2
REC REA
As you can see according to the output, it logs out the whole input string, starts parsing it, goes through the first loop iteration (mixing strings from struct fields), starts the second iteration filling struct with heavily mixed Strings once again and then stops responding in the middle of it.
Right now I see no reason for such behaviour except the problems with memory allocation, but I can't find out what I'm doing wrong.
Any your help is appreciated.
First, your code is c++, not strictly c, that's okay, but the tag should be changed. Here are some problems I found in your code...
///// Passing nothing, println requires one or two parameters
debugSerial.println();
See documentation on println
// init data for the next element of result array
////// You are creating a local buffer, it will go out of scope
////// when you leave the function. And you are trying to store
////// it in an array that you return from your function.
////// And you are changing the data with pass through your loop
////// (2 passes).
SMSData smsData = {"", "", "", "", ""};
//...
////// You should be assigning an allocated struct pointer
////// count is not changing, you are only assigning the last
////// element of the array.
smsArray[count - 1] = smsData;
///// This will not work. smsArray is a pointer and you have lost
///// the information about how much memory was allocated and assigned to
///// the pointer.
SMSData* smsArray = readSMS(reply);
int count = sizeof(smsArray);
One trick we use to use with C style pointers as arrays, was to allocate a block of pointers one bigger than we needed, and make sure they were all set to NULL (using calloc() instead of malloc()). Then, we would set eacch pointer in the array except the last one. Finally, we would iterate through the array until the pointer was NULL, indicating the end of the data.
You should be storing pointers to allocated data in your smsArray, and you should free (destroy) the that data as well as the array.
So, your code might look something like...
SMSData** smsArray = NULL;
smsArray = (SMSData **)calloc(count+1, sizeof(SMSData *));
int idx = 0;
//...
SMSData* smsData = new smsData();
smsData->id = ""; //etc. for the rest of the fields
//...
smsArray[idx++] = smsData;
//..
After return...
SMSData ** smsArray = readSMS(reply);
SMSData ** ptr = smsArray;
while (ptr != NULL) {
//do stuff with ptr->whatever
destroy(ptr); //destroy since allocated with new
ptr++;
}
free(smsArray); //free since allocated with malloc
This is not the best code (and may have bugs, I don't have access to my compiler right now). But it tries to stick with your approach.

When i pass an array of strings from VBA to c++ and use them as keys in a map, why am i not able to retrieve data (using the keys) from the map?

I am trying to pass 2 arrays of strings from vba to c++ and use them as keys to a nested map. The data corresponding to the keys is passed as an array of doubles. First array of strings (VolArrayTenor_Destination) contains "ON", "1W", "1M", "3M", "6M" and the other array of strings (VolArrayData_Destination) contains "ATMVOL", "25RR", "10FLY". I am trying to get the data using keys in the code and it returns a 0 value, my guess is that it cannot find the keys from the map.
double convert2Maps(double* VolArray_destination, BSTR* VolArrayTenor_destination, BSTR* VolArrayData_destination,
long VolArrayTenorLen, long VolArrayDataLen )
{
double a ,x;
BSTR* b;
BSTR* c;
map<BSTR, map<BSTR, double> >* nestedMap = new map<BSTR, map<BSTR, double> >;
map<BSTR, map<BSTR, double> > &nestedMapAlais = *nestedMap;
for (int i=0; i<VolArrayTenorLen;++i)
for (int j=0; j<VolArrayDataLen;++j)
{
{
a = *(VolArray_destination + (i * VolArrayDataLen) + (j));
b = ((VolArrayTenor_destination + i));
c = ((VolArrayData_destination + j));
nestedMapAlais[*b][*c] = a;
}
}
BSTR ON=L"1Y";
BSTR VOLATM=L"VOLATM";
x=nestedMapAlais[ON][VOLATM];
return (x);
}
The type BSTR is a pointer, so you use the address stored in the pointer as a key in the map, not the string value pointed to.
Trying to do a lookup with an identical string value, but stored at a different address will not find the original entry in the map.
In C++ it is common to use a map<std::string, double> to avoid this problem.
When handling BSTRs, keep in mind that a BSTR is not a normal string. BSTR values must be allocated using Win32 API calls like SysAllocString(), and freed using SysFreeString(). In addition, BSTR can be NULL (which is a valid empty string).
Some suggestions:
Make sure your VBA arrays are fixed-sized. Otherwise, they are stored as SAFEARRAY, not a C-style array.
Use proper API to allocate and free BSTRs:
BSTR sOn = SysAllocStringW(L"1Y");
...
SysFreeString(sOn);
Instead of dealing with BSTRs directly, convert them into strings of wide chars. (Note that a BSTR encoding an empty string can be (but not necessarily is) a NULL pointer). Here's one example of such conversion function:
std::wstring BstrToString(BSTR s)
{
return std::wstring(s, s + SysStringLen(sMyHello));
}
To summarize:
map<std::wstring, map<std::wstring, double> > nestedMap;
for (int i=0; i<VolArrayTenorLen;++i)
{
for (int j=0; j<VolArrayDataLen;++j)
{
double a = *(VolArray_destination + (i * VolArrayDataLen) + (j));
BSTR b = *(VolArrayTenor_destination + i);
BSTR c = *(VolArrayData_destination + j);
nestedMapAlais[BstrToString(b)][BstrToString(c)] = a;
}
}
x=nestedMapAlais[L"1Y"][L"VOLATM"];

Value getting changed after end of function

I am trying to set data which needs to be used in another file (layer) of the application. During debugging , I see that the value gets correctly set for the first time . But when I try to use this set variable after the function , the value is changed. I think the memory is getting released causing the variable to reset . Can anyone please help me with what I am trying to do.
void SetExpectedTabsData(_In_ PCWSTR tabUrls[], _In_ PCWSTR tabTitles[], _In_ UINT tabsCount, _In_ FakeCourier* courier)
{
wchar_t jsonPerTab[256];
wchar_t tabsDataJSON[c_jsonTabDataSize];
// tabsDataJSON should be large enough to hold any data.
StringCchPrintf(tabsDataJSON, c_jsonTabDataSize, L"\"tabs\":[");
bool isActiveTab = true;
// tabId starts from 1 and the tabIndex starts with 0. Manipulated the json string generation accordingly.
for (unsigned int i = 1; i <= tabsCount; ++i)
{
StringCchPrintf(jsonPerTab, ARRAYSIZE(jsonPerTab), L"{\"id\":%i,\"index\":%i,\"windowId\":1,\"active\":%s,\"status\":\"complete\",\"title\":\"%s\",\"url\":\"%s\"}", i, (i - 1), isActiveTab ? L"true" : L"false", tabTitles[i - 1], tabUrls[i - 1]);
StringCchCat(tabsDataJSON, c_jsonTabDataSize, jsonPerTab);
isActiveTab = false;
if (i != tabsCount)
{
StringCchCat(tabsDataJSON, c_jsonTabDataSize, L",");
}
}
StringCchCat(tabsDataJSON, c_jsonTabDataSize, L"],");
VERIFY_SUCCEEDED(courier->SetExpectedTabsData(tabsDataJSON));
}
The courier file where the data needs to be set is as
HRESULT FakeCourier::SetExpectedTabsData(_In_ wchar_t* tabsData)
{
m_tabsData = tabsData;
return S_OK;
}
Please suggest the correct approach to do this.
The variable tabsDataJSON is local in the function SetExpectedTabsData, so it can be overwritten after finishing of this function. And since it can happen, it happens.
When you call FakeCourier::SetExpectedTabsData(tabsDataJSON), this function just remembers pointer to that local variable tabsDataJSON. But right after finishing of the function SetExpectedTabsData this pointer becomes invalid.
To fix it you need to modify the function FakeCourier::SetExpectedTabsData: replace copying of the pointer be copying of the pointed data. Since your m_tabsData is a wchar_t*, just allocate this string and copy the data from tabsData into m_tabsData (read about wcsncpy and similar functions).
Try something like this:
HRESULT FakeCourier::SetExpectedTabsData(_In_ wchar_t* tabsData)
{
const size_t len = wcslen(tabsData);
if (m_tabsData)
delete m_tabsData;
m_tabsData = new wchar_t[len + 1];
wcsncpy(m_tabsData, tabsData, len + 1);
return S_OK;
}
And also you need to free this memory in the destructor of the FakeCourier (to avoid memory leak). And to initialize m_tabsData in the constructor (m_tabsData = 0;)

An issue with memory allocations of arrays of the same size

I'm having a weird behaviour with my C++ code. Here it is.
OI_Id * Reqlist = 0;
int * Idlist = 0;
int Reqsize = listcount; // we calculate listcount somehow earlier.
Idlist = new int [Reqsize];
if (Idlist == 0)
{
return;
}
printf ("Idlist = %0x",Idlist);
Reqlist = new OI_Id [Reqsize]; // OI_Id is a 3rd party lib simple struct.
if (Reqlist == 0)
{
return;
}
printf ("Reqlist = %0x",Reqlist);
So the problem is that in both cases it prints the same value - the same pointer is returned by the new operator. BUT! If we change the length of second allocated array to another value (Reqsize+ 1, for example), everything is OK.
Did anybody meet any similar behaviour? I have no idea what's the reason of the problem.