Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: How to convert the following DELPHI code into C++ BUILDER code?



Permlink Replies: 7 - Last Post: Jan 26, 2018 9:41 AM Last Post By: Remy Lebeau (Te...
huang weifeng

Posts: 8
Registered: 2/24/18
How to convert the following DELPHI code into C++ BUILDER code?
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 24, 2018 5:27 AM
function TADOTools.saveToStream2(
pvDataSet: TADODataSet): TMemoryStream;
var
AStream:_Stream;
V:OLEVariant;
P:Pointer;
begin
AStream:=CoStream.Create;
OLEVariant(pvDataSet.Recordset).Save(AStream, adPersistADTG);
AStream.Position:=0;
V:=AStream.Read(AStream.Size);
result:=TMemoryStream.Create;
try
P:=VarArrayLock(V);
try
result.Size:=VarArrayHighBound(V,1)+1;
Move(P^,result.Memory^, result.Size);
finally
VarArrayUnLock(V);
end;
except
result.Free();
result := nil;
raise;
end;
end;
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: How to convert the following DELPHI code into C++ BUILDER code?
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 24, 2018 10:53 AM   in response to: huang weifeng in response to: huang weifeng
huang weifeng wrote:

function TADOTools.saveToStream2(
pvDataSet: TADODataSet): TMemoryStream;

Try something like this:

#include <Winapi.ADOInt.hpp>
#include <Data.Win.ADODB.hpp>
 
TMemoryStream* __fastcall TADOTools::saveToStream2(TADODataSet *pvDataSet)
{
    _di__Stream AStream = CoStream::Create();
    pvDataSet->Recordset->Save(AStream, /*adPersistADTG*/0);
    AStream->Position = 0;
 
    OleVariant V;
    AStream->Read(AStream->Size, V); // or: Read(/*adReadAll*/-1, V);
 
    TMemoryStream *result = new TMemoryStream;
    try
    {
        void *P = V.ArrayLock();
        try
        {
            result->Size = V.ArrayHighBound() + 1;
            Move(P, result->Memory, result->Size);
        }
        __finally {
            V.ArrayUnlock();
        }
    }
    catch (const Exception &)
    {
        delete result;
        throw;
    }
    return result;
}


However, I would suggest using a generic TStream instead, so you can save to any kind of streamable destination (file, memory, etc) that the caller wants:

void __fastcall TADOTools::saveToStream2(TADODataSet *pvDataSet, TStream *pvStream)
{
    _di__Stream AStream = CoStream::Create();
    pvDataSet->Recordset->Save(AStream, /*adPersistADTG*/0);
    AStream->Position = 0;
 
    // no need to copy the whole recordset stream into memory, just read it in chunks...
    while (!AStream->EOS)
    {
        OleVariant V;
        AStream->Read(1024, V);
 
        void *P = V.ArrayLock();
        try {
            pvStream->WriteBuffer(P, V.ArrayHighBound() + 1);
        }
        __finally {
            V.ArrayUnlock();
        }
    }
}


--
Remy Lebeau (TeamB)
huang weifeng

Posts: 8
Registered: 2/24/18
Re: How to convert the following DELPHI code into C++ BUILDER code?
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 24, 2018 8:31 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Try something like this:

#include <Winapi.ADOInt.hpp>
#include <Data.Win.ADODB.hpp>
 
TMemoryStream* __fastcall TADOTools::saveToStream2(TADODataSet
*pvDataSet)
{
    _di__Stream AStream = CoStream::Create();
    pvDataSet->Recordset->Save(AStream, adPersistADTG);
    AStream->Position = 0;
    OleVariant V = AStream->Read(AStream->Size);
    TMemoryStream *result = new TMemoryStream;
    try
    {
        void *P = VarArrayLock(V);
        try
        {
            result->Size = VarArrayHighBound(V, 1) + 1;
            Move(P, result->Memory, result->Size);
        }
        __finally {
            VarArrayUnLock(V);
        }
    }
    catch (const Exception &)
    {
        delete result;
        throw;
    }
    return result;
}


--
Remy Lebeau (TeamB)

Thank you very much. There are 9 errors when compiling the code. I made some modifications, the code is as follows.

TMemoryStream* __fastcall saveToStream2(TADODataSet
*pvDataSet)
{
	_di__Stream AStream = CoStream::Create();
	pvDataSet->Recordset->Save(OleVariant(AStream), 0);
	AStream->Position = 0;
	OleVariant V;
	AStream->Read(AStream->Size,V);
	Variant s=V.AsType(0x0011+0x2000);
	TMemoryStream *result = new TMemoryStream;
	try
	{
		void *P = VarArrayLock(s);
		try
		{
			result->Size = VarArrayHighBound(s, 1) + 1;
			Move(P, result->Memory, result->Size);
		}
		__finally {
			VarArrayUnlock(s);
		}
	}
	catch (const Exception &)
	{
		delete result;
		throw;
	}
	return result;
}
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: How to convert the following DELPHI code into C++ BUILDER code?
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 25, 2018 9:39 AM   in response to: huang weifeng in response to: huang weifeng
huang weifeng wrote:

pvDataSet->Recordset->Save(OleVariant(AStream), 0);

You don't need to explicitly cast the Stream to OleVariant. Save()
takes an OleVariant as input, OleVariant has a constructor that takes
an IDispatch* pointer as input, _di__Stream is implicitly convertible
to _Stream*, which in turn is implicitly convertible to IDispatch*.
Explicitly constructing an object that the compiler can implicitly
construct is redundant, just let the compiler do the work for you:

pvDataSet->Recordset->Save(AStream, 0);


Good catch on the second parameter, though. I didn't see that the
parameter was translated to a generic TOleEnum instead of
PersistFormatEnum like in MSDN's documentation.

OleVariant V;
AStream->Read(AStream->Size,V);

Good catch on the output parameter.

Just an FYI, the size parameter can optionally be set to adReadAll (-1)
so you don't have to query the stream's Size separately.

AStream->Read(-1, V);


Variant s=V.AsType(0x0011+0x2000);

Don't use "magic numbers" like that. Use their synbolic names instead:

Variant s = V.AsType(VT_UI | VT_ARRAY);


However, the OleVariant is already a byte array, so there is no reason
to use the AsType() method, just assign the OleVariant as-is:

Variant s = V;


Either way will make a new copy of the byte array.

On the other hand, you don't actually need an explicit Variant at all,
since OleVariant derives from Variant, so you can use OleVariant with
RTL functions that operate on Variant objects.

--
Remy Lebeau (TeamB)
huang weifeng

Posts: 8
Registered: 2/24/18
Re: How to convert the following DELPHI code into C++ BUILDER code?
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 26, 2018 8:17 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thank you. I've learned a lot.
If I don't cast the Stream to OleVariant. System prompt error."2010" and "XE7" are not compiled.
"Variant s = V;" I can't compile it, but the reverse is ok."OleVariant V = s;"
Remy Lebeau (TeamB) wrote:
huang weifeng wrote:

pvDataSet->Recordset->Save(OleVariant(AStream), 0);

You don't need to explicitly cast the Stream to OleVariant. Save()
takes an OleVariant as input, OleVariant has a constructor that takes
an IDispatch* pointer as input, _di__Stream is implicitly convertible
to _Stream*, which in turn is implicitly convertible to IDispatch*.
Explicitly constructing an object that the compiler can implicitly
construct is redundant, just let the compiler do the work for you:

pvDataSet->Recordset->Save(AStream, 0);


Good catch on the second parameter, though. I didn't see that the
parameter was translated to a generic TOleEnum instead of
PersistFormatEnum like in MSDN's documentation.

OleVariant V;
AStream->Read(AStream->Size,V);

Good catch on the output parameter.

Just an FYI, the size parameter can optionally be set to adReadAll (-1)
so you don't have to query the stream's Size separately.

AStream->Read(-1, V);


Variant s=V.AsType(0x0011+0x2000);

Don't use "magic numbers" like that. Use their synbolic names instead:

Variant s = V.AsType(VT_UI | VT_ARRAY);


However, the OleVariant is already a byte array, so there is no reason
to use the AsType() method, just assign the OleVariant as-is:

Variant s = V;


Either way will make a new copy of the byte array.

On the other hand, you don't actually need an explicit Variant at all,
since OleVariant derives from Variant, so you can use OleVariant with
RTL functions that operate on Variant objects.

--
Remy Lebeau (TeamB)
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: How to convert the following DELPHI code into C++ BUILDER code? [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 26, 2018 9:41 AM   in response to: huang weifeng in response to: huang weifeng
huang weifeng wrote:

If I don't cast the Stream to OleVariant. System prompt error.

Then cast it to the interface type instead, eg:

pvDataSet->Recordset->Save((_Stream*)AStream, 0);


"Variant s = V;" I can't compile it

The OleVariant holds a byte array (in a SAFEARRAY). OleVariant has a
conversion operator for SAFEARRAY*, and Variant has constructors that
accept a SAFEARRAY as input, eg:

SAFEARRAY *sa = V;
Variant s = &sa;
s.VType |= VT_UI1; // <-- bug fix!


There is a bug in the Variant class when assigning it a SAFEARRAY**
pointer. The VType field is correctly set to VT_ARRAY|VT_BYREF|_vt
(where _vt is the underlying element type of the array), but then is
immediately re-assigned to just VT_ARRAY|VT_BYREF, losing the element
type. This mistake doesn't manifest itself until the Variant is
destroyed (or copied to another variant), as it will throw an "invalid
variant type" exception (a destructor should NEVER throw an exception!).

Alternatively, you can do this instead (which doesn't have the same
bug):

Variant s = (VARIANT*) V;


or:

Variant s = (Variant*) &V;


This allows the Variant to hold a pointer to the OleVariant, rather
than the array directly.

Either way, the idea is to get the Variant to hold a reference to the
original array/OleVariant, not a copy of it.

But, this is moot, since OleVariant has its own methods for (un)locking
an array, so there is no need to convert the OleVariant to a Variant
just for purposes of array access.

--
Remy Lebeau (TeamB)
huang weifeng

Posts: 8
Registered: 2/24/18
Re: How to convert the following DELPHI code into C++ BUILDER code?
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 24, 2018 8:35 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB)

OleVariant must be converted to Variant, otherwise the VarArrayLock function cannot be used. DELPHI does not need to transform, why?
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: How to convert the following DELPHI code into C++ BUILDER code?
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 25, 2018 9:46 AM   in response to: huang weifeng in response to: huang weifeng
huang weifeng wrote:
OleVariant must be converted to Variant

No, it doesn't.

otherwise the VarArrayLock function cannot be used.

Yes, it can.

In any case, an alternative is to call the OleVariant's own Array(Lock|Unlock) methods instead of the RTL's standalone VarArray(Lock|Unlock) functions.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02