Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations wOOdy-Soft on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Convert a C++ array to SAFEARRAY

Status
Not open for further replies.

irocz

IS-IT--Management
Oct 28, 2001
25
US
Hi All,
I have a C++ DLL which receives an array parameter as follows:
typedef const char* pStr;
typedef const pStr* Strarry;// the declaration of the function:
__declspec(dllexport) int _stdcall filem(pStr filename,Strarry fielddesc), I already handled the BSTR string for the first parameter, that works.
(we are talking about the second parameter), I cannot use LPSAFEARRAY FAR *, for the second parameter in the declaration, I have to manipulate the array in the code itself. I am passing the array from VB(which is a safearray).
My question is ; is there a way I can convert the array that I receive from VB to a C++ style array in the body of the function, bear in mind that what is passed is a pointer to the array, and I am not sending the array back to VB.
Thanks.
 
No you can't; A C array is nothing more than some variables at contigeous memory locations (pardon the English, can't find the right words...). The structure of a SAFEARRAY is completely different. You'll have to use the SAFEARRAY functions, or access it directly at the correct locations, and build a new C++ array out of the variables found in the SAFEARRAY.
Greetings,
Rick
 
Thanks Rick,
I guess I was not clear on how to ask the question. In simple words;The array is passed from VB which makes it by default a safearray, but since the DLL receives a pointer to that array, I was wondering if I can take that pointer to the array, then build a C++ array out of the variables(in this case variable length strings), found in the SAFEARRAY as you stated above.How do I do that, i.e take the variables from the safearray and build a c++ array?
Thanks
Eddie
 
There are APIs for that: SafeArrayGetElement is one of them, others oare SafeArrayGetDim, SafeArrayGetLBound/UBound etc. You can then access the BSTR variables and create _TCHARS of them. Be prepared to wirte a lot of code though (compared to a VB programmer). Here's some code I once wrote, it contains a lot of rubbish you probably won't need, but it should give you an idea:

STDMETHODIMP CSelectionList::ShowList(BSTR sTitle, VARIANT vProperties, VARIANT vValues)
{
//Before we start doing any fancy stuff, let's perform some pre-checking of the parameters:

//What we expect for the properties is a 2-dimensional array of variants, the first dimension containing 3 elements, the second an unknown number of elements:
if(!(vProperties.vt&VT_VARIANT && vProperties.vt&VT_ARRAY)) return ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
//The same goes for the values (except that here we also do not know the number of elements in the first dimension):
if(!(vValues.vt&VT_VARIANT && vValues.vt&VT_ARRAY)) return ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);

SAFEARRAY* parrProperties = NULL, * parrValues = NULL;
if(!(parrProperties=vProperties.parray) || !(parrValues=vValues.parray)) return ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);

//Check if we've got arrays of the correct dimensions (should be 2):
if(SafeArrayGetDim(parrProperties)!=2 || SafeArrayGetDim(parrValues)!=2) return ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);

//Get the number of elements in both arrays:
HRESULT hRes = S_OK;
long nLBoundProp1=0, nLBoundProp2=0, nLBoundVal1=0, nLBoundVal2=0, nUBoundProp1=0, nUBoundProp2=0, nUBoundVal1=0, nUBoundVal2=0, nColumns=0, nRows=0;

//The properties (3 elements in the first dimension, unknown (but minimal 1) number of elements in the second dimension):
if(!SUCCEEDED(hRes=SafeArrayGetLBound(parrProperties, 1, &nLBoundProp1)) || !SUCCEEDED(hRes=SafeArrayGetUBound(parrProperties, 1, &nUBoundProp1))) { //First dimension.
g_Log.WriteErrorEx(IDS_LOG_E_GETSAFEARRAYBOUNDS, hRes);
return ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
}else if(nUBoundProp1-nLBoundProp1!=2) return ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);

if(!SUCCEEDED(hRes=SafeArrayGetLBound(parrProperties, 2, &nLBoundProp2)) || !SUCCEEDED(hRes=SafeArrayGetUBound(parrProperties, 2, &nUBoundProp2))) { //Second dimension.
g_Log.WriteErrorEx(IDS_LOG_E_GETSAFEARRAYBOUNDS, hRes);
return ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
}else nColumns=nUBoundProp2-nLBoundProp2+1;

//The values (nColumns+1 elements in the first dimension, unknown (but minimal 1) number of elements in the second dimension):
if(!SUCCEEDED(hRes=SafeArrayGetLBound(parrValues, 1, &nLBoundVal1)) || !SUCCEEDED(hRes=SafeArrayGetUBound(parrValues, 1, &nUBoundVal1))) { //First dimension.
g_Log.WriteErrorEx(IDS_LOG_E_GETSAFEARRAYBOUNDS, hRes);
return ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
}else if(nUBoundVal1-nLBoundVal1!=nColumns) return ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);

if(!SUCCEEDED(hRes=SafeArrayGetLBound(parrValues, 2, &nLBoundVal2)) || !SUCCEEDED(hRes=SafeArrayGetUBound(parrValues, 2, &nUBoundVal2))) { //Second dimension.
g_Log.WriteErrorEx(IDS_LOG_E_GETSAFEARRAYBOUNDS, hRes);
return ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
}else nRows=nUBoundVal2-nLBoundVal2+1;

//Ok, we've done all the checking that could be done without dissecting the arrays completely, let's do some real work now:
long nIndex[2] = {0};
VARIANT* pvValue = CreateVariant_VT_EMPTY();

m_szSize.cx = (SELLIST_FRAMETHICKNESS*2) + 16 + ((nColumns-1)*SELLIST_COLSPACE); //The width of the window will be reset here.

//Let's loop through the properties array first:
int* pnColumnWidth = new int[nColumns];
_TCHAR** ppszColumnHdr = new _TCHAR*[nColumns];
posListColumnTypes* pColumnType = new posListColumnTypes[nColumns];

ZeroMemory((void*)ppszColumnHdr, sizeof(_TCHAR*)*nColumns);

//These should be VT_I4 types, specifying the types of the columns:
nIndex[0] = nLBoundProp1;
for(long nCounter=nLBoundProp2; nCounter<=nUBoundProp2; nCounter++) {
nIndex[1] = nCounter;
if(!SUCCEEDED(hRes=SafeArrayGetElement(parrProperties, nIndex, (void*)pvValue))) {
g_Log.WriteErrorEx(IDS_LOG_E_EXTRACTSAFEARRAYELEMENT, hRes);
hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
break;
}else if(pvValue->vt!=VT_I4) {
VariantClear(pvValue);
hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
break;
}
pColumnType[nCounter-nLBoundProp2] = (posListColumnTypes)pvValue->lVal;
VariantClear(pvValue);
}

if(hRes==S_OK) {
//And these should be VT_BSTR types, specifying the headers of the columns:
nIndex[0] = nLBoundProp1 + 1;
for(nCounter=nLBoundProp2; nCounter<=nUBoundProp2; nCounter++) {
nIndex[1] = nCounter;
if(!SUCCEEDED(hRes=SafeArrayGetElement(parrProperties, nIndex, (void*)pvValue))) {
g_Log.WriteErrorEx(IDS_LOG_E_EXTRACTSAFEARRAYELEMENT, hRes);
hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
break;
}else if(pvValue->vt!=VT_BSTR) {
VariantClear(pvValue);
hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
break;
}
BSTR2TCHAREx(&pvValue->bstrVal, &ppszColumnHdr[nCounter-nLBoundProp2], FALSE);
VariantClear(pvValue);
}
}

if(hRes==S_OK) {
//And these should be VT_I4 types again, specifying the width of the columns:
nIndex[0] = nUBoundProp1;
for(long nCounter=nLBoundProp2; nCounter<=nUBoundProp2; nCounter++) {
nIndex[1] = nCounter;
if(!SUCCEEDED(hRes=SafeArrayGetElement(parrProperties, nIndex, (void*)pvValue))) {
g_Log.WriteErrorEx(IDS_LOG_E_EXTRACTSAFEARRAYELEMENT, hRes);
hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
break;
}else if(pvValue->vt!=VT_I4) {
VariantClear(pvValue);
hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
break;
}
pnColumnWidth[nCounter-nLBoundProp2] = (int)pvValue->lVal;
//While we're looping, we might as well set the width of the window:
m_szSize.cx += pnColumnWidth[nCounter-nLBoundProp2];
VariantClear(pvValue);
}

//Now loop through the actual values:
if(hRes==S_OK) {
ROW* pRows = new ROW[nRows];
ITEM* pItems = NULL;
_TCHAR* pszValue = NULL;

//Create new list object:
if(m_pList && m_pList->nCacheID==-1) delete m_pList; //Current list is not from cache, destroy it.
BSTR2TCHAREx(&sTitle, &pszValue, FALSE);
m_pList = new LIST(0, nColumns, nRows, pRows, ppszColumnHdr, pszValue);
pszValue = NULL;

for(long nRow=nLBoundVal2; nRow<=nUBoundVal2; nRow++) {
nIndex[1] = nRow;
pItems = new ITEM[nColumns];
pRows[nRow-nLBoundVal2].nItems = nColumns;
pRows[nRow-nLBoundVal2].pItems = pItems;
for(long nCol=nLBoundVal1; nCol<=nUBoundVal1; nCol++) {
nIndex[0] = nCol;
if(!SUCCEEDED(hRes=SafeArrayGetElement(parrValues, nIndex, (void*)pvValue))) {
g_Log.WriteErrorEx(IDS_LOG_E_EXTRACTSAFEARRAYELEMENT, hRes);
hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
break;
}else {
if(nCol==nUBoundVal1) { //Actually this not a column, it contains the ID number of the ROW.
if(pvValue->vt!=VT_I4)
hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
else
pRows[nRow-nLBoundVal2].nID = pvValue->lVal;
}else { //These certainly are columns!
switch(pvValue->vt) {
case VT_I4:
if(pColumnType[nCol-nLBoundVal1]!=posListColumnNumber) hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
break;
case VT_BSTR:
if(pColumnType[nCol-nLBoundVal1]!=posListColumnText)
hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
else {
pszValue = NULL; //First set to NULL (otherwise it will be destroyed in the BSTR2TCHAREx).
BSTR2TCHAREx(&pvValue->bstrVal, &pszValue, FALSE);
}
break;
default:
hRes = ATLSYSERROR(E_INVALIDARG, E_INVALIDARG_RES, IID_ICABSelectionList);
break;
}
pItems[nCol-nLBoundVal1].nColWidth = pnColumnWidth[nCol-nLBoundVal1];
pItems[nCol-nLBoundVal1].iValue = pColumnType[nCol-nLBoundVal1]==posListColumnNumber ? pvValue->lVal : (int)pszValue;
pItems[nCol-nLBoundVal1].nType = pColumnType[nCol-nLBoundVal1];
}

VariantClear(pvValue);
if(hRes!=S_OK) break;
}
}
if(hRes!=S_OK) break;
}
}
}

delete pvValue;
delete[] pColumnType;
delete[] pnColumnWidth;

ZeroMemory((void*) m_szBuffer, sizeof(m_szBuffer)); //Clear input buffer.

//Set current height:
m_szSize.cy = (m_nMaxLines>nRows ? (nRows+3)*SELLIST_LINEHEIGHT : (m_nMaxLines+3)*SELLIST_LINEHEIGHT) + (SELLIST_FRAMETHICKNESS*2) + 4;

if(hRes==S_OK) ShowScreen();

return hRes;
}

Greetings,
Rick
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top