Solved

How to pass vector in a com method

Posted on 2006-11-29
7
1,106 Views
Last Modified: 2013-12-14
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
0
Comment
Question by:HanuSoftware
7 Comments
 
LVL 8

Expert Comment

by:mrblue
ID: 18036083
You can use:

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).

0
 
LVL 8

Accepted Solution

by:
mrblue earned 500 total points
ID: 18036137
In case 1) you can use SafeArrayCreateVector(), SafeArrayAccessData() & SafeArrayUnaccessData()

for example:

VARIANT vData;
double *pdblArray;

vData.parray = SafeArrayCreateVector(VT_R8,  0,   100);        // 100 element array of doubles

if(vData.parray != NULL) {
  vData.vt = VT_R8 | VT_ARRAY;

  SafeArrayAccessData(vData.parray,   (void **)&pdblArray);
  pdblArray[0] = 1.0;
  // ...
  SafeArrayUnaccessData(vData.parray);
};

0
 

Author Comment

by:HanuSoftware
ID: 18036928
I want to pass a vector that can be used by both vb as well as MFC. The vector passed will return strings.
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 22

Expert Comment

by:ambience
ID: 18037188
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.
0
 
LVL 22

Expert Comment

by:ambience
ID: 18037219
>> 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!
0
 
LVL 8

Expert Comment

by:mrblue
ID: 18037493
> 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_VARIANT,  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(vData.parray);
};
0
 
LVL 2

Expert Comment

by:asmyatt
ID: 18039067
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>(arrVariant[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(YouFunctionName));
      spDLLFunction->YouFunctionName(...what ever parameters you have ...);
       
        //call this function to output the collection that was returned
      OutputLoopedItems(spDLLFunction);      
   }
   catch (const _com_error& Err)
   {
      std::cout << "Error: " << Err.ErrorMessage() << "\n";
   }
   catch (...)
  {
      std::cout << "Unexpected Error\n";
  }
}

Hope this helps.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
How to install Selenium IDE and loops for quick automated testing. Get Selenium IDE from http://seleniumhq.org Go to that link and select download selenium in the right hand columnThat will then direct you to their download page.From that page s…
The viewer will learn how to synchronize PHP projects with a remote server in NetBeans IDE 8.0 for Windows.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

895 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now