careta
asked on
Problem returning a VARIANT with a SAFEARRAY of VARIANTs
Hello all!
I'm having trouble returning a VARIANT holding a SAFEARRAY of VARIANTs, and I suspect the
problem is with the ATL macros that define the array. Its like this:
in idl file:
HRESULT RV( [in] ULONG dwCount, [out, retval] VARIANT* pValues);
in server side:
SAFEARRAY * pRetSA;
pRetSA = SafeArrayCreate(VT_VARIANT , 2, bounds);
...
SafeArrayPutElement(pRetSA , arr, &varData);
...
VariantClear(pValues);
V_VT(pValues) = VT_ARRAY | VT_VARIANT;
V_ARRAY(pValues) = pRetSA;
and in client:
VARIANT pVariant;
pVariant.vt = VT_BYREF | VT_VARIANT;
hr = pIMME->RV(n, &pVariant);
When I execute this, the server side receives the variant as VT_EMPTY, and
as this it returns it, with error "Invalid access to memory location". What
am I doing wrong?
Thanks in advance,
careta
I'm having trouble returning a VARIANT holding a SAFEARRAY of VARIANTs, and I suspect the
problem is with the ATL macros that define the array. Its like this:
in idl file:
HRESULT RV( [in] ULONG dwCount, [out, retval] VARIANT* pValues);
in server side:
SAFEARRAY * pRetSA;
pRetSA = SafeArrayCreate(VT_VARIANT
...
SafeArrayPutElement(pRetSA
...
VariantClear(pValues);
V_VT(pValues) = VT_ARRAY | VT_VARIANT;
V_ARRAY(pValues) = pRetSA;
and in client:
VARIANT pVariant;
pVariant.vt = VT_BYREF | VT_VARIANT;
hr = pIMME->RV(n, &pVariant);
When I execute this, the server side receives the variant as VT_EMPTY, and
as this it returns it, with error "Invalid access to memory location". What
am I doing wrong?
Thanks in advance,
careta
ASKER
I'm not calling SafeArrayAccessData() or SafeArrayUnaccessData(), since I'm accessing the array contents with safeArrayGet/PutElement.
As for the macros, I meant to
V_VT(pValues) = VT_ARRAY | VT_VARIANT;
V_ARRAY(pValues) = pRetSA;
By now, I removed the line
pVariant.vt = VT_BYREF | VT_VARIANT;
from client code, and changed in the idl file the definition of the VARIANT*
to be [in,out] (instead of [out,retval]), but now, on server return, client error is "Memory is locked".
careta
As for the macros, I meant to
V_VT(pValues) = VT_ARRAY | VT_VARIANT;
V_ARRAY(pValues) = pRetSA;
By now, I removed the line
pVariant.vt = VT_BYREF | VT_VARIANT;
from client code, and changed in the idl file the definition of the VARIANT*
to be [in,out] (instead of [out,retval]), but now, on server return, client error is "Memory is locked".
careta
> By now, I removed the line
Oh. Those aren't ATL macros. They predate ATL by about six years.
> I'm accessing the array contents with safeArrayGet/PutElement.
Oh! Indeed, you are. I misread your code.
..B ekiM
Oh. Those aren't ATL macros. They predate ATL by about six years.
> I'm accessing the array contents with safeArrayGet/PutElement.
Oh! Indeed, you are. I misread your code.
..B ekiM
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Adjusted points from 100 to 200
ASKER
I'm afraid it doesn't help. I already removed the call to VariantClear at server side, and changed in idl file to [in,out] VARIANT*. So now the error I get at client side (on server return) is that the "Memory is locked". Since I'm passing back a safearray, where it should be allocated, at the client side to be filled by the server or created and filled by the server?
thanks,
careta
thanks,
careta
in IDL it is OK to have SAFEARRAY packed in VARIANt as [out, retval] parameter
in client
you need only to supply the space for output
thus
VARIANT var;
Zeromemory(&var, sizeof(var));
pInterface->CallFunction(& var);
in server side
try to use COleSafeArray MFC class
it make all neccessary preparation work for you
HRESULT CallFunction(VARIANT * pVar)
{
COleSafeArray arr;
arr.Create(...);
arr.PutElement(...);
*Var = arr.Detach();
}
hope this helps
also be sure you do not place in safe array any locked data.
What is in VARIANTs in safearray?
in client
you need only to supply the space for output
thus
VARIANT var;
Zeromemory(&var, sizeof(var));
pInterface->CallFunction(&
in server side
try to use COleSafeArray MFC class
it make all neccessary preparation work for you
HRESULT CallFunction(VARIANT * pVar)
{
COleSafeArray arr;
arr.Create(...);
arr.PutElement(...);
*Var = arr.Detach();
}
hope this helps
also be sure you do not place in safe array any locked data.
What is in VARIANTs in safearray?
Hi try using
hres = VariantCopy(pValues, pRetSA);
check for hres
Hope this helps
inpras
hres = VariantCopy(pValues, pRetSA);
check for hres
Hope this helps
inpras
Since I'm passing back a safearray, where it should be allocated, at the client side to be filled by the server or created and filled by the server?
===================
it depends on your needs and the architecture you select for your application.
I prefer to allocate arrays at server side and free them at client side
===================
it depends on your needs and the architecture you select for your application.
I prefer to allocate arrays at server side and free them at client side
ASKER
For this code
HRESULT CallFunction(VARIANT * pVar)
{
COleSafeArray arr;
arr.Create(...);
arr.PutElement(...);
*Var = arr.Detach();
}
since I'm not using MFC (I hope people in this forum don't mind :-) ), what is the Detach() equivalent using SAFEARRAY?
careta
HRESULT CallFunction(VARIANT * pVar)
{
COleSafeArray arr;
arr.Create(...);
arr.PutElement(...);
*Var = arr.Detach();
}
since I'm not using MFC (I hope people in this forum don't mind :-) ), what is the Detach() equivalent using SAFEARRAY?
careta
since COleSafeArray is the wrapper for SAFEARRAY Detach() return VARIANT with SAFEARRAY packed into it.
You do not need Detach() for Pure API calls.
You do not need Detach() for Pure API calls.
ASKER
For this code
HRESULT CallFunction(VARIANT * pVar)
{
COleSafeArray arr;
arr.Create(...);
arr.PutElement(...);
*Var = arr.Detach();
}
since I'm not using MFC (I hope people in this forum don't mind :-) ), what is the Detach() equivalent using SAFEARRAY?
careta
HRESULT CallFunction(VARIANT * pVar)
{
COleSafeArray arr;
arr.Create(...);
arr.PutElement(...);
*Var = arr.Detach();
}
since I'm not using MFC (I hope people in this forum don't mind :-) ), what is the Detach() equivalent using SAFEARRAY?
careta
could you send the complete implementation of server function and the piece of code from client where that function called?
ASKER
> since COleSafeArray is the wrapper for SAFEARRAY Detach() return VARIANT with SAFEARRAY packed into it.
Then I really don't see what am I doing wrong and what is locking the memory. All I need is 4 lines of atl code for server side and 4 lines of atl code for client side to show how to return a variant with a safearray of variants (doubles).
Thanks,
Aaron
Then I really don't see what am I doing wrong and what is locking the memory. All I need is 4 lines of atl code for server side and 4 lines of atl code for client side to show how to return a variant with a safearray of variants (doubles).
Thanks,
Aaron
ASKER
Sure, I'll be glad to, but just tomorrow (I'm at home now, without access to the files).
Aaron
Aaron
okay - I knocked up a quick example - based on your information
and I had no trouble I will post the relevent bits of code for you to compare
- it may help - its okay using the MFC wrappers but as you may know MFC drags along a lot of other garbage with it as well and is not conducive to compact code - NOTE my array is 1D with 1 element see bounds definition
server code
in my idl I declare
interface IIMME : IDispatch
{
[id(1), helpstring("method RV")] HRESULT RV([in]ULONG dwCount, [in, out] VARIANT * var);
};
in my .h file
STDMETHOD(RV)(/*[in]*/ULON G dwCount, /*[in,out]*/ VARIANT * var);
and in my .cpp file
STDMETHODIMP CIMME::RV(ULONG dwCount, VARIANT *pValues)
{
SAFEARRAY * pRetSA;
SAFEARRAYBOUND bounds[]={ {1, 0} };
long ar[]= {0};
// create our array
pRetSA = SafeArrayCreate(VT_VARIANT , 1, bounds);
// insert our data
VARIANT data;
VariantInit(&data);
V_VT(&data) = VT_I4;
V_I4(&data) = 2000;
HRESULT hr=SafeArrayPutElement(pRe tSA,ar,&da ta); // 0x8002000b DISP_E_BADINDEX
// add to our out value
VariantClear(pValues);
V_VT(pValues) = VT_ARRAY | VT_VARIANT;
V_ARRAY(pValues) = pRetSA;
return S_OK;
}
for the client side
IIMMEPtr ptrIMME;
if (S_OK==ptrIMME.CreateInsta nce(CLSID_ IMME))
{
// create value to store data
VARIANT var;
VariantInit(&var);
// call our method
ptrIMME->RV(22,&var);
// extract our returned data
SAFEARRAY *psa=V_ARRAY(&var);
long ar[]={0};
VARIANT data;
VariantInit(&data);
HRESULT hr=SafeArrayGetElement(psa ,ar,&data) ;
// destroy the array
SafeArrayDestroy(psa);
ptrIMME.Release();
}
Okay it isn't perfect as it requires error checking - (and I my use of [in,out] is open to dubious practice charges)- I can send the whole sample project if you want.
If you are only returning the safearray then maybe you only want an [out] or [out,retval] parameter.
May 1999 MSJ Wicked Code article by Jeff Prosise covers just this topic
and I had no trouble I will post the relevent bits of code for you to compare
- it may help - its okay using the MFC wrappers but as you may know MFC drags along a lot of other garbage with it as well and is not conducive to compact code - NOTE my array is 1D with 1 element see bounds definition
server code
in my idl I declare
interface IIMME : IDispatch
{
[id(1), helpstring("method RV")] HRESULT RV([in]ULONG dwCount, [in, out] VARIANT * var);
};
in my .h file
STDMETHOD(RV)(/*[in]*/ULON
and in my .cpp file
STDMETHODIMP CIMME::RV(ULONG dwCount, VARIANT *pValues)
{
SAFEARRAY * pRetSA;
SAFEARRAYBOUND bounds[]={ {1, 0} };
long ar[]= {0};
// create our array
pRetSA = SafeArrayCreate(VT_VARIANT
// insert our data
VARIANT data;
VariantInit(&data);
V_VT(&data) = VT_I4;
V_I4(&data) = 2000;
HRESULT hr=SafeArrayPutElement(pRe
// add to our out value
VariantClear(pValues);
V_VT(pValues) = VT_ARRAY | VT_VARIANT;
V_ARRAY(pValues) = pRetSA;
return S_OK;
}
for the client side
IIMMEPtr ptrIMME;
if (S_OK==ptrIMME.CreateInsta
{
// create value to store data
VARIANT var;
VariantInit(&var);
// call our method
ptrIMME->RV(22,&var);
// extract our returned data
SAFEARRAY *psa=V_ARRAY(&var);
long ar[]={0};
VARIANT data;
VariantInit(&data);
HRESULT hr=SafeArrayGetElement(psa
// destroy the array
SafeArrayDestroy(psa);
ptrIMME.Release();
}
Okay it isn't perfect as it requires error checking - (and I my use of [in,out] is open to dubious practice charges)- I can send the whole sample project if you want.
If you are only returning the safearray then maybe you only want an [out] or [out,retval] parameter.
May 1999 MSJ Wicked Code article by Jeff Prosise covers just this topic
ASKER
Ok, I solved my problem, thanks to vachooho and ShaunWilde.
Hmmm... I'm not so familiar with points distribution. Who should get them? Certainly both answers helped me a lot.
Hmmm... I'm not so familiar with points distribution. Who should get them? Certainly both answers helped me a lot.
post you problem on the http://www1.experts-exchange.com/Customer_Service/Experts_Exchange/ forum and they will hopefully sort it out
say how you want the split done
because there has been a lot in this topic they may wish for you to post what you final answer was.
say how you want the split done
because there has been a lot in this topic they may wish for you to post what you final answer was.
Community Support has reduced points from 200 to 100
Hi careta,
I have reduced the points on this question to one half. Please accept one of the Experts comments as an answer. Remember, the Accept Comment as Answer button is in the header of the comment.
For the second Expert, post a question in this topic area. The new question title should be 'For ExpertName - 10325852' and it should be for 100 points.
For your convenience, you can use this link to create the new question:
http://www1.experts-exchange.com/bin/NewQForm?ta=44
Please take a moment to post your solution as the Expert recommended.
darinw
Customer Service
I have reduced the points on this question to one half. Please accept one of the Experts comments as an answer. Remember, the Accept Comment as Answer button is in the header of the comment.
For the second Expert, post a question in this topic area. The new question title should be 'For ExpertName - 10325852' and it should be for 100 points.
For your convenience, you can use this link to create the new question:
http://www1.experts-exchange.com/bin/NewQForm?ta=44
Please take a moment to post your solution as the Expert recommended.
darinw
Customer Service
ASKER
Following is the code I made, after all responses I got from you. I removed error handling and other stuff for simplicity. If somebody still sees a problem, pls tell me.
// idl
[id(3)] HRESULT ReadWriteVariant([in] ULONG dwCount, [in, out] VARIANT* pValues);
// server code
STDMETHODIMP CMyClass::ReadWriteVariant (UINT dwCount, VARIANT* pValues) {
HRESULT hr = S_OK;
DWORD dwArgs;
CComBSTR* pVar = NULL;
DWORD i, j;
double dblValue = 0.0;
CComBSTR bstr;
// we receieve the variables to read in a safearray, so first of all
// unpack it and get back the names of the variables
hr = UnpackSafearray(*pValues, &pVar, &dwArgs);
// create bidimensional vector
SAFEARRAY* pRetSA;
SAFEARRAYBOUND appBounds[] = {{dwArgs, 0}, {dwCount * 2, 0}};
long ix[2];
pRetSA = SafeArrayCreate(VT_VARIANT , 2, appBounds);
VARIANT varData;
// for dwCount (times to read each variable)
for(i = 0; i < dwCount; i++) {
// for each variable
for(j = 0; j < dwArgs; j++) {
// read variable from target
hr = ReadSingleVariable(pVar[j] , &dblValue, &bstr);
ix[0] = j; ix[1] = i;
VariantInit(&varData);
varData.vt = VT_R8;
varData.dblVal = dblValue;
hr = SafeArrayPutElement(pRetSA , ix, &varData);
ix[0] = j; ix[1] += 1;
VariantInit(&varData);
varData.vt = VT_BSTR;
varData.bstrVal = bstr.Detach();
hr = SafeArrayPutElement(pRetSA , ix, &varData);
}
}
delete[] pVar;
V_VT(pValues) = VT_ARRAY | VT_VARIANT;
V_ARRAY(pValues) = pRetSA;
return S_OK;
}
STDMETHODIMP CMyClass::UnpackSafearray( VARIANT aVars, DWORD** pdwVars, DWORD* pdwArgs) {
long lBound;
bool bFailed = false;
DWORD i;
HRESULT hr;
if (!(aVars.vt & VT_ARRAY) || ((aVars.vt & VT_TYPEMASK) != VT_R8))
return E_INVALIDARG;
SAFEARRAY* pSa = aVars.parray;
if(SafeArrayGetDim(pSa) != 1) return E_INVALIDARG;
hr = SafeArrayGetLBound(pSa, 1, &lBound);
if(lBound != 0) return E_INVALIDARG;
hr = SafeArrayGetUBound(pSa, 1, &lBound);
*pdwArgs = lBound + 1;
DWORD* pDwArr = new DWORD[*pdwArgs];
for(i = 0; i < *pdwArgs; i++) {
hr = SafeArrayGetElement(pSa, (long*)(&i), &pDwArr[i]);
if(FAILED(hr)) {
delete[] pDwArr;
SafeArrayDestroy(pSa);
VariantClear(&aVars);
return hr;
}
}
SafeArrayDestroy(pSa);
VariantClear(&aVars);
*pdwVars = pDwArr;
return S_OK;
}
// example client code
SAFEARRAY FAR* psa;
SAFEARRAYBOUND rgsabound[1];
DWORD i, n = 3;
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = n;
psa = SafeArrayCreate(VT_BSTR, 1, rgsabound);
CComBSTR vars[] = {"LRST", "DSP_FRZB_EN", "AUTO_SYNC_STATE"};
for(i = 0; i < n; ++i){
SafeArrayPutElement(psa, (long*)&i, vars[i]);
vars[i].Detach();
}
VARIANT pVariant;
VariantInit(&pVariant);
pVariant.parray = psa;
pVariant.vt = VT_ARRAY | VT_BSTR;
hr = pIMME->ReadWriteVariant(n, &pVariant);
// extract out returned data
SAFEARRAY *pra=V_ARRAY(&pVariant);
long ix[2];
VARIANT data;
VariantInit(&data);
ix[0] = 1; ix[1] = 1;
hr=SafeArrayGetElement(pra ,ix,&data) ;
SafeArrayDestroy(pra);
// idl
[id(3)] HRESULT ReadWriteVariant([in] ULONG dwCount, [in, out] VARIANT* pValues);
// server code
STDMETHODIMP CMyClass::ReadWriteVariant
HRESULT hr = S_OK;
DWORD dwArgs;
CComBSTR* pVar = NULL;
DWORD i, j;
double dblValue = 0.0;
CComBSTR bstr;
// we receieve the variables to read in a safearray, so first of all
// unpack it and get back the names of the variables
hr = UnpackSafearray(*pValues, &pVar, &dwArgs);
// create bidimensional vector
SAFEARRAY* pRetSA;
SAFEARRAYBOUND appBounds[] = {{dwArgs, 0}, {dwCount * 2, 0}};
long ix[2];
pRetSA = SafeArrayCreate(VT_VARIANT
VARIANT varData;
// for dwCount (times to read each variable)
for(i = 0; i < dwCount; i++) {
// for each variable
for(j = 0; j < dwArgs; j++) {
// read variable from target
hr = ReadSingleVariable(pVar[j]
ix[0] = j; ix[1] = i;
VariantInit(&varData);
varData.vt = VT_R8;
varData.dblVal = dblValue;
hr = SafeArrayPutElement(pRetSA
ix[0] = j; ix[1] += 1;
VariantInit(&varData);
varData.vt = VT_BSTR;
varData.bstrVal = bstr.Detach();
hr = SafeArrayPutElement(pRetSA
}
}
delete[] pVar;
V_VT(pValues) = VT_ARRAY | VT_VARIANT;
V_ARRAY(pValues) = pRetSA;
return S_OK;
}
STDMETHODIMP CMyClass::UnpackSafearray(
long lBound;
bool bFailed = false;
DWORD i;
HRESULT hr;
if (!(aVars.vt & VT_ARRAY) || ((aVars.vt & VT_TYPEMASK) != VT_R8))
return E_INVALIDARG;
SAFEARRAY* pSa = aVars.parray;
if(SafeArrayGetDim(pSa) != 1) return E_INVALIDARG;
hr = SafeArrayGetLBound(pSa, 1, &lBound);
if(lBound != 0) return E_INVALIDARG;
hr = SafeArrayGetUBound(pSa, 1, &lBound);
*pdwArgs = lBound + 1;
DWORD* pDwArr = new DWORD[*pdwArgs];
for(i = 0; i < *pdwArgs; i++) {
hr = SafeArrayGetElement(pSa, (long*)(&i), &pDwArr[i]);
if(FAILED(hr)) {
delete[] pDwArr;
SafeArrayDestroy(pSa);
VariantClear(&aVars);
return hr;
}
}
SafeArrayDestroy(pSa);
VariantClear(&aVars);
*pdwVars = pDwArr;
return S_OK;
}
// example client code
SAFEARRAY FAR* psa;
SAFEARRAYBOUND rgsabound[1];
DWORD i, n = 3;
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = n;
psa = SafeArrayCreate(VT_BSTR, 1, rgsabound);
CComBSTR vars[] = {"LRST", "DSP_FRZB_EN", "AUTO_SYNC_STATE"};
for(i = 0; i < n; ++i){
SafeArrayPutElement(psa, (long*)&i, vars[i]);
vars[i].Detach();
}
VARIANT pVariant;
VariantInit(&pVariant);
pVariant.parray = psa;
pVariant.vt = VT_ARRAY | VT_BSTR;
hr = pIMME->ReadWriteVariant(n,
// extract out returned data
SAFEARRAY *pra=V_ARRAY(&pVariant);
long ix[2];
VARIANT data;
VariantInit(&data);
ix[0] = 1; ix[1] = 1;
hr=SafeArrayGetElement(pra
SafeArrayDestroy(pra);
> the ATL macros that define the array
What macros are you talking about?
..B ekiM