Link to home
Start Free TrialLog in
Avatar of riteshtonk
riteshtonk

asked on

Problems with VT_BYREF and VARIANTS

Hi,

I have a method inside a COM+ DLL which I invoke from a C++ application.
This is a stripped down version of the code.
The parameter repeatData is an out parameter which is an array of structures.

The VARIANT in the structure contains a SAFEARRAY of BSTRs.

--------------------------------------

//Just FYI. This declaration is in IDL file
typedef _BstrStruct
{
    int i;
    VARIANT name;
} BstrStruct;

STDMETHODIMP CTestVariant::GetData(SAFEARRAY** repeatData)
{
      ITypeLib* pTypelib = NULL;
      ITypeInfo *pTypeInfo;
      HRESULT hr = LoadTypeLib(L".\\DllData.tlb", &pTypelib);
      pTypelib->GetTypeInfoOfGuid(ID_MYBSTRSTRUCT, &pTypeInfo);
      IRecordInfo* pRecInfo = NULL;
      ::GetRecordInfoFromTypeInfo(pTypeInfo, &pRecInfo);

       //Allocate memory for the safe data
      *repeatData = ::SafeArrayCreateEx(VT_RECORD, 1, &bound, pRecInfo);

      BstrStruct *pBstrR = NULL;
      SafeArrayAccessData(*repeatData, (void HUGEP**)&pBstrR);
      int count = 0;

      for(int i=0; i<1; i++)
      {
            LPSAFEARRAY lpsa;      
            lpsa = SafeArrayCreate(VT_BSTR, 1, &bound);

            pBstrR[i].i = i;
            
            for(int j=0; j<COUNT; j++)
            {
                  long tempL = j;
                  BSTR pBstrTemp = ::SysAllocString(L"Test");
                  lpsa->fFeatures ^= FADF_BSTR;
                  SafeArrayPutElement(lpsa, &tempL, &pBstrTemp);
                  lpsa->fFeatures |= FADF_BSTR;
            }
       
            //PROBLEM CODE
            pBstrR[i].name.vt = VT_ARRAY|VT_RECORD;
            pBstrR[i].name.parray = lpsa;
      }
      
      SafeArrayUnaccessData(*repeatData);
}

The code works fine but when I change the lines with the following:

            pBstrR[i].name.vt = VT_ARRAY|VT_RECORD|VT_BYREF;
            pBstrR[i].name.pparray = &lpsa;

I get the following error:

First-chance exception at 0x771399ca in DllData.exe: 0xC0000005: Access violation reading location 0x00000003.
First-chance exception at 0x7c812a5b in DllData.exe: 0x000003E6: Invalid access to memory location.
HEAP[DllData.exe]: Invalid Address specified to RtlFreeHeap( 00150000, 0012EFF8 )

Any idea on what am I doing wrong here?

Any help regarding this will be highly appreciated!

Avatar of jkr
jkr
Flag of Germany image

>>Any idea on what am I doing wrong here?

In

      for(int i=0; i<1; i++)
      {
            LPSAFEARRAY lpsa;      
       
            //...

            pBstrR[i].name.vt = VT_ARRAY|VT_RECORD|VT_BYREF;
            pBstrR[i].name.pparray = &lpsa;
      }

'lpsa' is a local variable that goes out of scope when the loop terminates. If you now store the address of that variable - which will cease to exist after it goes out of scope - anywhere, an access violation is preassigned.
Avatar of riteshtonk
riteshtonk

ASKER

lpsa is a pointer to a SAFEARRAY which has been allocated from the Heap.
Here, since pparray is a pointer to a pointer of SAFEARRY, I am assigning the address of lpsa.

Had this been the case, even the previous solution would not have worked where I am doing

pBstrR[i].name.parray = lpsa;

Thanks,
Ritesh
No. The previous version using

pBstrR[i].name.parray = lpsa;

is OK, since you are storing the allocated *content*

Using

pBstrR[i].name.parray = &lpsa;

you are storing the *address* of the allocated memory, which makes little sense, since it is local.
I forgot to mention in my previous post that even when the 'lpsa' is in the global scope, the problem still occurs.

I basically picked up some parts of the code form here:
http://vb.mvps.org/tips/vb5dll.asp

Thanks,
Ritesh
Oops. my apologies!

The error text changes when  'lpsa' is in global scope.

HEAP[DllData.exe]: Invalid Address specified to RtlFreeHeap( 00150000, 0043AB68 )
Unhandled exception at 0x7c901230 in DllData.exe: User breakpoint.
Here is the client code which calls the dll.

      HRESULT hr = ::CoInitialize(NULL);
      if (SUCCEEDED(hr))
      {
            ITestVariant *pTestVariant = NULL;
            HRESULT hr = ::CoCreateInstance(CLSID_TestVariant, NULL, CLSCTX_LOCAL_SERVER, IID_ITestVariant, (LPVOID*)(&pTestVariant));
            if(SUCCEEDED(hr))
            {
                  SAFEARRAY*  repeatData = NULL;

                  hr = pTestVariant->GetData(&repeatData);
                  if(SUCCEEDED(hr))
                        printf("Hello World !!!");
                  else
                        printf("Bad World !!!");
            }
      }
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
In this case the loop runs only once

for(int i=0; i<1; i++)

The answer didnt help me directly, but I got some pointers and was able to proceed further.
Thanks for all the efforts!