HanuSoftware
asked on
How to pass vector in a com method
Hi Experts,
I want to pass STL vector container in one of my com method. I also want to return that vector to my C++ client.
Thanks in advance
I want to pass STL vector container in one of my com method. I also want to return that vector to my C++ client.
Thanks in advance
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I want to pass a vector that can be used by both vb as well as MFC. The vector passed will return strings.
We need more information
- Is your COM object INPROC, i.e. in a dll?
- Are you using a custom interface, i.e. calling through Ixxx (Where Ixxx is not IDispatch)?
- Are you using ATL for the COM dll?
The short answer to your question is that you cannot pass STL vectors and for that matter any STL container in a COM method. The long answer is also a no but there are ways to help do what you intend to do, but that really depends upon the answers to these questions.
- Is your COM object INPROC, i.e. in a dll?
- Are you using a custom interface, i.e. calling through Ixxx (Where Ixxx is not IDispatch)?
- Are you using ATL for the COM dll?
The short answer to your question is that you cannot pass STL vectors and for that matter any STL container in a COM method. The long answer is also a no but there are ways to help do what you intend to do, but that really depends upon the answers to these questions.
>> I also want to return that vector to my C++ client.
>> I want to pass a vector that can be used by both vb as well as MFC.
Looks like a change of plans to me :)
In that case use SAFEARRAY as explained by mrblue. Ofcourse that means extra work!
>> I want to pass a vector that can be used by both vb as well as MFC.
Looks like a change of plans to me :)
In that case use SAFEARRAY as explained by mrblue. Ofcourse that means extra work!
> I want to pass a vector that can be used by both vb as well as MFC. The vector passed will return strings.
Derive your inteface from IDispatch (automation) & use VARIANT array (possibly array of VARIANTs)
VARIANT vData;
VARIANT *pvArray;
vData.parray = SafeArrayCreateVector(VT_V ARIANT, 0, 100); // 100 element array of VARIANT
if(vData.parray != NULL) {
vData.vt = VT_VARIANT| VT_ARRAY;
SafeArrayAccessData(vData. parray, (void **)&pvArray);
pvArray[0].vt = VT_R8; // or type you need (VT_R4, VT_I2, VT_I4, ...)
pvArray[0].dblVal = 1.0; // or fltVal, iVal, lVal, ...
// ...
SafeArrayUnaccessData(vDat a.parray);
};
Derive your inteface from IDispatch (automation) & use VARIANT array (possibly array of VARIANTs)
VARIANT vData;
VARIANT *pvArray;
vData.parray = SafeArrayCreateVector(VT_V
if(vData.parray != NULL) {
vData.vt = VT_VARIANT| VT_ARRAY;
SafeArrayAccessData(vData.
pvArray[0].vt = VT_R8; // or type you need (VT_R4, VT_I2, VT_I4, ...)
pvArray[0].dblVal = 1.0; // or fltVal, iVal, lVal, ...
// ...
SafeArrayUnaccessData(vDat
};
Go to this site and give these steps a try. I did this to return a vector<string> back to my program. I tested it using a C# and C++ client and it worked fine.
http://msdn2.microsoft.com/en-us/library/3stwxh95(VS.80).aspx
After you setup you COM app as above, your client will have to have these things in it:
-------------------------- ---------- ---------- ---------- ---------- ----
#include "YourDllLib_i.c"
#import "YourDllLib.tlb"
using namespace YourDllLibLib;
// Outputs all the items in a collection using the Count and Item properties
template <class T>
HRESULT OutputLoopedItems(T sp)
{
// Get the count of items
long lCount = sp->GetCount();
cout << "Rows Returned: " << lCount << endl << endl;
// The collection indexes are 1-based
/*for (long i = 1; i <= lCount; ++i)
{
_bstr_t bstrTemp = static_cast<_variant_t>(sp ->GetItem( i));
std::cout << static_cast<const char*>(bstrTemp) << "\n";
}*/
return S_OK;
}
// Outputs all the items in a collection using the enumerator
template <class T>
HRESULT OutputEnumeratedItems(T sp)
{
// Get the VARIANT enumerator from the collection
IEnumVARIANTPtr spEnum = sp->Get_NewEnum();
// nBatchSize is the number of items that we request in each call to IEnumVARIANT::Next.
// The actual number of items returned may not equal nBatchSize.
const ULONG nBatchSize = 5;
// nReturned will store the number of items returned by a call to IEnumVARIANT::Next
ULONG nReturned = 0;
// arrVariant is the array used to hold the returned items
VARIANT arrVariant[nBatchSize] = {0};
HRESULT hr = E_UNEXPECTED;
do
{
hr = spEnum->Next(nBatchSize, &arrVariant[0], &nReturned);
if (FAILED(hr))
return hr;
for (ULONG i = 0; i < nReturned; ++i)
{
_bstr_t bstrTemp = static_cast<_variant_t>(ar rVariant[i ]);
std::cout << static_cast<const char*>(bstrTemp) << "\n";
::VariantClear(&arrVariant [i]);
}
} while (hr != S_FALSE); // S_FALSE indicates end of collection
return hr;
}
int main(int argc, _TCHAR* argv[])
{
try
{
IYourDllPtr spDLLFunction(__uuidof(You FunctionNa me));
spDLLFunction->YouFunction Name(...wh at ever parameters you have ...);
//call this function to output the collection that was returned
OutputLoopedItems(spDLLFun ction);
}
catch (const _com_error& Err)
{
std::cout << "Error: " << Err.ErrorMessage() << "\n";
}
catch (...)
{
std::cout << "Unexpected Error\n";
}
}
Hope this helps.
http://msdn2.microsoft.com/en-us/library/3stwxh95(VS.80).aspx
After you setup you COM app as above, your client will have to have these things in it:
--------------------------
#include "YourDllLib_i.c"
#import "YourDllLib.tlb"
using namespace YourDllLibLib;
// Outputs all the items in a collection using the Count and Item properties
template <class T>
HRESULT OutputLoopedItems(T sp)
{
// Get the count of items
long lCount = sp->GetCount();
cout << "Rows Returned: " << lCount << endl << endl;
// The collection indexes are 1-based
/*for (long i = 1; i <= lCount; ++i)
{
_bstr_t bstrTemp = static_cast<_variant_t>(sp
std::cout << static_cast<const char*>(bstrTemp) << "\n";
}*/
return S_OK;
}
// Outputs all the items in a collection using the enumerator
template <class T>
HRESULT OutputEnumeratedItems(T sp)
{
// Get the VARIANT enumerator from the collection
IEnumVARIANTPtr spEnum = sp->Get_NewEnum();
// nBatchSize is the number of items that we request in each call to IEnumVARIANT::Next.
// The actual number of items returned may not equal nBatchSize.
const ULONG nBatchSize = 5;
// nReturned will store the number of items returned by a call to IEnumVARIANT::Next
ULONG nReturned = 0;
// arrVariant is the array used to hold the returned items
VARIANT arrVariant[nBatchSize] = {0};
HRESULT hr = E_UNEXPECTED;
do
{
hr = spEnum->Next(nBatchSize, &arrVariant[0], &nReturned);
if (FAILED(hr))
return hr;
for (ULONG i = 0; i < nReturned; ++i)
{
_bstr_t bstrTemp = static_cast<_variant_t>(ar
std::cout << static_cast<const char*>(bstrTemp) << "\n";
::VariantClear(&arrVariant
}
} while (hr != S_FALSE); // S_FALSE indicates end of collection
return hr;
}
int main(int argc, _TCHAR* argv[])
{
try
{
IYourDllPtr spDLLFunction(__uuidof(You
spDLLFunction->YouFunction
//call this function to output the collection that was returned
OutputLoopedItems(spDLLFun
}
catch (const _com_error& Err)
{
std::cout << "Error: " << Err.ErrorMessage() << "\n";
}
catch (...)
{
std::cout << "Unexpected Error\n";
}
}
Hope this helps.
1. VARIANT type (array of elements) - automation compliant version that can be used from script languages like VB or JS
Funct([out] VARIANT *pArray)
or
2. If you mark your custom (inheriting from IUnknown) interface as 'local' (marshalling will never be used) you can simply pass pointer to array or pointer to pointer to array
Funct([out] double *pdblArray)
Funct([out] double **pdblArray)
{
// allocate memory
*pdblArray = CoTaskMemAlloc(...);
}
In this case it works exactly like regular C/C++ function but cannot be called from script languages and cannot be called from another process or another machine (marshalling).