Watch, Follow, &
Connect with Us

Please visit our new home
community.embarcadero.com.


Welcome, Guest
Guest Settings
Help

Thread: code works in xe4, but not working in c++ 10.2.3


This question is answered. Helpful answers available: 1. Correct answers available: 1.


Permlink Replies: 5 - Last Post: Apr 19, 2018 11:06 AM Last Post By: Remy Lebeau (Te... Threads: [ Previous | Next ]
goksel erkan

Posts: 4
Registered: 1/19/06
code works in xe4, but not working in c++ 10.2.3  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 18, 2018 1:12 AM
Hello,

I have an active x component (loggernet sdk) returns me a variant data. The same code compiled with c++ xe4 is working fine but if I compile the code with c++ 10.2.3 it returns null.

void __fastcall TForm1::DSource1onAdviseRecord(TObject *Sender, LPDISPATCH myAdvisor,
LPDISPATCH myRecord)
{
indexclm.vt = VT_INT;
indexclm.intVal = 2;

IRecord *record = (IRecord *) myRecord;
value = record->get_Item(indexclm);
colname = value->get_columnName();

var = value->get_value();
::VariantChangeType(&var, &var, 0, VT_BSTR);

AdvMemo1->Lines->Insert(0,DateTimeToStr(record->timeStamp) + " - " + colname + ": " + var.bstrVal);
value->Release();
}

Binary produced from c++ 10.2.3 from code above returns the column name but var.bstrVal is empty. If I compile with xe4, var.bstrVal value gets the data as I expected. What am I supposed to do?

Best,

Göksel

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: code works in xe4, but not working in c++ 10.2.3
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 18, 2018 10:20 AM   in response to: goksel erkan in response to: goksel erkan
goksel erkan wrote:

IRecord *record = (IRecord *) myRecord;

You cannot type-cast raw interface pointers like that. You MUST use
the IUnknown::QueryInterface() and IUnknown:::Release() methods
instead, eg:

IRecord *record;
if (FAILED(myRecord->QueryInterface(IID_IRecord, (void**)&record)))
{
   ...
}
else
{
   ...
   record->Release();
}


Or, use a smart wrapper class that calls QueryInterface() (and
Release()) for you, eg:

IRecordPtr record = myRecord;
if (!record)
{
   ...
}
else
{
   ...
   record->Release();
}


TComInterface<IRecord> record = myRecord;
if (!record)
{
   ...
}
else
{
   ...
   record->Release();
}


CComPtr<IRecord> record = myRecord;
if (!record)
{
   ...
}
else
{
   ...
   record->Release();
}


Etc.

value = record->get_Item(indexclm);

You need to make sure get_item() returns a non-NULL pointer before you
use it, eg:

value = record->get_Item(indexclm);
if (!value)
{
    ...
}
else
{
    ...
    value->Release();
}


Or:

if (FAILED(record->get_Item(indexclm, &value)))
{
...
}
else
{
    ...
    value->Release();
}


This is also a situation where you really should be using a smart
wrapper for 'value' to call Release() for you.

Binary produced from c++ 10.2.3 from code above returns the column
name but var.bstrVal is empty.

What is 'var' declared as exactly? What does the declaration of
'get_value()' look like exactly? You are clearly using a wrapper for
get_value(), so what does that wrapper code actually look like in both
versions?

If I compile with xe4, var.bstrVal value gets the data as I expected.
What am I supposed to do?

There is not enough information to diagnose that yet.

--
Remy Lebeau (TeamB)
goksel erkan

Posts: 4
Registered: 1/19/06
Re: code works in xe4, but not working in c++ 10.2.3  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 19, 2018 3:17 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Hi Remy,

declarations:

in CSIDATASOURCELib_TLB.h;

interface IValue : public IDispatch
{
public:
// [1] The name of the column for this value.
virtual HRESULT STDMETHODCALLTYPE get_columnName(BSTR* pVal/*[out,retval]*/) = 0;
// [2] The value of the current column.
virtual HRESULT STDMETHODCALLTYPE get_value(VARIANT* pVal/*[out,retval]*/) = 0;

#if !defined(__TLB_NO_INTERFACE_WRAPPERS)

BSTR __fastcall get_columnName(void)
{
BSTR pVal = 0;
OLECHECK(this->get_columnName((BSTR*)&pVal));
return pVal;
}

VARIANT __fastcall get_value(void)
{
VARIANT pVal;
OLECHECK(this->get_value((VARIANT*)&pVal));
return pVal;
}

__property BSTR columnName = {read = get_columnName};

#endif // __TLB_NO_INTERFACE_WRAPPERS

};

// *********************************************************************//
// Interface: IRecord
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {0810BD25-EFED-11D5-BD9A-0060676668D7}
// *********************************************************************//
interface IRecord : public IDispatch
{
public:
// [-4] Visual Basic collection iterator function.
virtual HRESULT STDMETHODCALLTYPE get__NewEnum(LPUNKNOWN* ppUnk/*[out,retval]*/) = 0;
// [2] The number of values in this record.
virtual HRESULT STDMETHODCALLTYPE get_valuesCount(long* pVal/*[out,retval]*/) = 0;
// [3] The file mark number for this record.
virtual HRESULT STDMETHODCALLTYPE get_fileMarkNo(long* pVal/*[out,retval]*/) = 0;
// [0] The default function for this collection. This gives you a value by index.
virtual HRESULT STDMETHODCALLTYPE get_Item(VARIANT id,
Csidatasourcelib_tlb::IValue** ppIValue/*[out,retval]*/) = 0;
// [6] The timestamp for this record.
virtual HRESULT STDMETHODCALLTYPE get_timeStamp(DATE* pVal/*[out,retval]*/) = 0;
// [7] property recordNo
virtual HRESULT STDMETHODCALLTYPE get_recordNo(long* pVal/*[out,retval]*/) = 0;
// [8] property nanoSeconds
virtual HRESULT STDMETHODCALLTYPE get_nanoSeconds(long* pVal/*[out,retval]*/) = 0;

#if !defined(__TLB_NO_INTERFACE_WRAPPERS)

LPUNKNOWN __fastcall get__NewEnum(void)
{
LPUNKNOWN ppUnk;
OLECHECK(this->get__NewEnum((LPUNKNOWN*)&ppUnk));
return ppUnk;
}

long __fastcall get_valuesCount(void)
{
long pVal;
OLECHECK(this->get_valuesCount((long*)&pVal));
return pVal;
}

long __fastcall get_fileMarkNo(void)
{
long pVal;
OLECHECK(this->get_fileMarkNo((long*)&pVal));
return pVal;
}

Csidatasourcelib_tlb::IValue* __fastcall get_Item(VARIANT id)
{
Csidatasourcelib_tlb::IValue* ppIValue = 0;
OLECHECK(this->get_Item(id, (Csidatasourcelib_tlb::IValue**)&ppIValue));
return ppIValue;
}

DATE __fastcall get_timeStamp(void)
{
DATE pVal;
OLECHECK(this->get_timeStamp((DATE*)&pVal));
return pVal;
}

long __fastcall get_recordNo(void)
{
long pVal;
OLECHECK(this->get_recordNo((long*)&pVal));
return pVal;
}

long __fastcall get_nanoSeconds(void)
{
long pVal;
OLECHECK(this->get_nanoSeconds((long*)&pVal));
return pVal;
}

__property LPUNKNOWN _NewEnum = {read = get__NewEnum};
__property long valuesCount = {read = get_valuesCount};
__property long fileMarkNo = {read = get_fileMarkNo};
__property DATE timeStamp = {read = get_timeStamp};
__property long recordNo = {read = get_recordNo};
__property long nanoSeconds = {read = get_nanoSeconds};

#endif // __TLB_NO_INTERFACE_WRAPPERS

};

in my mainform header;

VARIANT indexclm,var;


Remy Lebeau (TeamB) wrote:
goksel erkan wrote:

IRecord *record = (IRecord *) myRecord;

You cannot type-cast raw interface pointers like that. You MUST use
the IUnknown::QueryInterface() and IUnknown:::Release() methods
instead, eg:

IRecord *record;
if (FAILED(myRecord->QueryInterface(IID_IRecord, (void**)&record)))
{
   ...
}
else
{
   ...
   record->Release();
}


Or, use a smart wrapper class that calls QueryInterface() (and
Release()) for you, eg:

IRecordPtr record = myRecord;
if (!record)
{
   ...
}
else
{
   ...
   record->Release();
}


TComInterface<IRecord> record = myRecord;
if (!record)
{
   ...
}
else
{
   ...
   record->Release();
}


CComPtr<IRecord> record = myRecord;
if (!record)
{
   ...
}
else
{
   ...
   record->Release();
}


Etc.

value = record->get_Item(indexclm);

You need to make sure get_item() returns a non-NULL pointer before you
use it, eg:

value = record->get_Item(indexclm);
if (!value)
{
    ...
}
else
{
    ...
    value->Release();
}


Or:

if (FAILED(record->get_Item(indexclm, &value)))
{
...
}
else
{
    ...
    value->Release();
}


This is also a situation where you really should be using a smart
wrapper for 'value' to call Release() for you.

Binary produced from c++ 10.2.3 from code above returns the column
name but var.bstrVal is empty.

What is 'var' declared as exactly? What does the declaration of
'get_value()' look like exactly? You are clearly using a wrapper for
get_value(), so what does that wrapper code actually look like in both
versions?

If I compile with xe4, var.bstrVal value gets the data as I expected.
What am I supposed to do?

There is not enough information to diagnose that yet.

--
Remy Lebeau (TeamB)

Edited by: goksel erkan on Apr 19, 2018 6:25 AM
goksel erkan

Posts: 4
Registered: 1/19/06
Re: code works in xe4, but not working in c++ 10.2.3  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 19, 2018 6:36 AM   in response to: goksel erkan in response to: goksel erkan
Hi Remy again,

I have solved my issue with your help. Reorganized the piece of code like this;


AdvMemo1->Clear();
VariantChangeType(&indexclm, &indexclm, 0, VT_INT);
IRecord *record;
IValue *value;
if(FAILED(myRecord->QueryInterface(IID_IRecord, (void**)&record)))
{
AdvMemo1->Lines->Add("error_record");
}
else
{
AdvMemo1->Lines->Insert(0,DateTimeToStr(record->timeStamp));
for(unsigned short i=0;i<record->valuesCount;i++)
{
indexclm.intVal = i;
if(FAILED(record->get_Item(indexclm, &value)))
{
AdvMemo1->Lines->Add("error_get_Item");
}
else
{
colname = value->get_columnName();
if (FAILED(value->get_value(&var)))
{
AdvMemo1->Lines->Add("error_get_value");
}
else
{
VariantChangeType(&var, &var, 0, VT_BSTR);
AdvMemo1->Lines->Add(colname + ": " + var.bstrVal);
value->Release();
}
}
}
record->Release();
}

Thank you again for your advices,

Best,

Göksel


goksel erkan wrote:
Hi Remy,

declarations:

in CSIDATASOURCELib_TLB.h;

interface IValue : public IDispatch
{
public:
// [1] The name of the column for this value.
virtual HRESULT STDMETHODCALLTYPE get_columnName(BSTR* pVal/*[out,retval]*/) = 0;
// [2] The value of the current column.
virtual HRESULT STDMETHODCALLTYPE get_value(VARIANT* pVal/*[out,retval]*/) = 0;

#if !defined(__TLB_NO_INTERFACE_WRAPPERS)

BSTR __fastcall get_columnName(void)
{
BSTR pVal = 0;
OLECHECK(this->get_columnName((BSTR*)&pVal));
return pVal;
}

VARIANT __fastcall get_value(void)
{
VARIANT pVal;
OLECHECK(this->get_value((VARIANT*)&pVal));
return pVal;
}

__property BSTR columnName = {read = get_columnName};

#endif // __TLB_NO_INTERFACE_WRAPPERS

};

// *********************************************************************//
// Interface: IRecord
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {0810BD25-EFED-11D5-BD9A-0060676668D7}
// *********************************************************************//
interface IRecord : public IDispatch
{
public:
// [-4] Visual Basic collection iterator function.
virtual HRESULT STDMETHODCALLTYPE get__NewEnum(LPUNKNOWN* ppUnk/*[out,retval]*/) = 0;
// [2] The number of values in this record.
virtual HRESULT STDMETHODCALLTYPE get_valuesCount(long* pVal/*[out,retval]*/) = 0;
// [3] The file mark number for this record.
virtual HRESULT STDMETHODCALLTYPE get_fileMarkNo(long* pVal/*[out,retval]*/) = 0;
// [0] The default function for this collection. This gives you a value by index.
virtual HRESULT STDMETHODCALLTYPE get_Item(VARIANT id,
Csidatasourcelib_tlb::IValue** ppIValue/*[out,retval]*/) = 0;
// [6] The timestamp for this record.
virtual HRESULT STDMETHODCALLTYPE get_timeStamp(DATE* pVal/*[out,retval]*/) = 0;
// [7] property recordNo
virtual HRESULT STDMETHODCALLTYPE get_recordNo(long* pVal/*[out,retval]*/) = 0;
// [8] property nanoSeconds
virtual HRESULT STDMETHODCALLTYPE get_nanoSeconds(long* pVal/*[out,retval]*/) = 0;

#if !defined(__TLB_NO_INTERFACE_WRAPPERS)

LPUNKNOWN __fastcall get__NewEnum(void)
{
LPUNKNOWN ppUnk;
OLECHECK(this->get__NewEnum((LPUNKNOWN*)&ppUnk));
return ppUnk;
}

long __fastcall get_valuesCount(void)
{
long pVal;
OLECHECK(this->get_valuesCount((long*)&pVal));
return pVal;
}

long __fastcall get_fileMarkNo(void)
{
long pVal;
OLECHECK(this->get_fileMarkNo((long*)&pVal));
return pVal;
}

Csidatasourcelib_tlb::IValue* __fastcall get_Item(VARIANT id)
{
Csidatasourcelib_tlb::IValue* ppIValue = 0;
OLECHECK(this->get_Item(id, (Csidatasourcelib_tlb::IValue**)&ppIValue));
return ppIValue;
}

DATE __fastcall get_timeStamp(void)
{
DATE pVal;
OLECHECK(this->get_timeStamp((DATE*)&pVal));
return pVal;
}

long __fastcall get_recordNo(void)
{
long pVal;
OLECHECK(this->get_recordNo((long*)&pVal));
return pVal;
}

long __fastcall get_nanoSeconds(void)
{
long pVal;
OLECHECK(this->get_nanoSeconds((long*)&pVal));
return pVal;
}

__property LPUNKNOWN _NewEnum = {read = get__NewEnum};
__property long valuesCount = {read = get_valuesCount};
__property long fileMarkNo = {read = get_fileMarkNo};
__property DATE timeStamp = {read = get_timeStamp};
__property long recordNo = {read = get_recordNo};
__property long nanoSeconds = {read = get_nanoSeconds};

#endif // __TLB_NO_INTERFACE_WRAPPERS

};

in my mainform header;

VARIANT indexclm,var;


Remy Lebeau (TeamB) wrote:
goksel erkan wrote:

IRecord *record = (IRecord *) myRecord;

You cannot type-cast raw interface pointers like that. You MUST use
the IUnknown::QueryInterface() and IUnknown:::Release() methods
instead, eg:

IRecord *record;
if (FAILED(myRecord->QueryInterface(IID_IRecord, (void**)&record)))
{
   ...
}
else
{
   ...
   record->Release();
}


Or, use a smart wrapper class that calls QueryInterface() (and
Release()) for you, eg:

IRecordPtr record = myRecord;
if (!record)
{
   ...
}
else
{
   ...
   record->Release();
}


TComInterface<IRecord> record = myRecord;
if (!record)
{
   ...
}
else
{
   ...
   record->Release();
}


CComPtr<IRecord> record = myRecord;
if (!record)
{
   ...
}
else
{
   ...
   record->Release();
}


Etc.

value = record->get_Item(indexclm);

You need to make sure get_item() returns a non-NULL pointer before you
use it, eg:

value = record->get_Item(indexclm);
if (!value)
{
    ...
}
else
{
    ...
    value->Release();
}


Or:

if (FAILED(record->get_Item(indexclm, &value)))
{
...
}
else
{
    ...
    value->Release();
}


This is also a situation where you really should be using a smart
wrapper for 'value' to call Release() for you.

Binary produced from c++ 10.2.3 from code above returns the column
name but var.bstrVal is empty.

What is 'var' declared as exactly? What does the declaration of
'get_value()' look like exactly? You are clearly using a wrapper for
get_value(), so what does that wrapper code actually look like in both
versions?

If I compile with xe4, var.bstrVal value gets the data as I expected.
What am I supposed to do?

There is not enough information to diagnose that yet.

--
Remy Lebeau (TeamB)

Edited by: goksel erkan on Apr 19, 2018 6:25 AM

Edited by: goksel erkan on Apr 19, 2018 6:37 AM
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: code works in xe4, but not working in c++ 10.2.3  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 19, 2018 10:55 AM   in response to: goksel erkan in response to: goksel erkan
goksel erkan wrote:
declarations:

in CSIDATASOURCELib_TLB.h;
<snip>

Based on those declarations, even if you get the code "working", you still have a couple of memory leaks. The BSTR returned by value->get_columnName() needs to be freed with SysFreeString(). You can use WideString to handle that for you. And the VARIANT returned by value->get_value() needs to be freed with VariantClear(), especially since you are changing its data type to a BSTR dynamically. You can use TVariant to handle that for you.

For your original code, I would use something more like this:

#include <utilcls.h>
 
void __fastcall TForm1::DSource1onAdviseRecord(TObject *Sender, LPDISPATCH myAdvisor, LPDISPATCH myRecord)
{
    TComInterface<IRecord> record = myRecord;
    TComInterface<IValue> value = record->get_Item(TVariant(2));
 
    WideString colname;
    colname.Attach(value->get_columnName());
    /* or:
    OLECHECK(value->get_columnName(&colname));
    */
 
    TVariant var;
    // avoid making a copy of the VARIANT, take ownership instead...
    // var = value->get_value(); 
    *(var.GetBaseVariant()) = value->get_value();
    /* or:
    OLECHECK(value->get_value(&var));
    */
 
    AdvMemo1->Lines->Insert(0, DateTimeToStr(record->timeStamp) + _D(" - ") + colname + _D(": ") + static_cast<String>(var));
}


Which can then be applied to your newer code:

void __fastcall TForm1::DSource1onAdviseRecord(TObject *Sender, LPDISPATCH myAdvisor, LPDISPATCH myRecord)
{
    TComInterface<IRecord> record = myRecord;
 
    AdvMemo1->Clear();
    AdvMemo1->Lines->Add(DateTimeToStr(record->timeStamp));
 
    long count = record->valuesCount;
    for(long i = 0; i < count; ++i)
    {
        TComInterface<IValue> value = record->get_Item(TVariant(i));
 
        WideString colname;
        colname.Attach(value->get_columnName());
        /* or:
        OLECHECK(value->get_columnName(&colname));
        */
 
        TVariant var;
        // avoid making a copy of the VARIANT, take ownership instead...
        // var = value->get_value(); 
        *(var.GetBaseVariant()) = value->get_value();
        /* or:
        OLECHECK(value->get_value(&var));
        */
 
        AdvMemo1->Lines->Add(colname + _D(": ") + static_cast<String>(var));
    }
}


--
Remy Lebeau (TeamB)
goksel erkan

Posts: 4
Registered: 1/19/06
Re: code works in xe4, but not working in c++ 10.2.3  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 19, 2018 6:37 AM   in response to: goksel erkan in response to: goksel erkan
with the help of remy, I have changed the piece of code to pass the values by referance. than its working in 10.2.3 too.
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02