I'd like to know, if possible, how to pass a Variant Delphi 6 value to a C++
Builder 5 DLL.
Since COM uses Variant parameters all the time, there must be a way to do
it, possibly some VariantSomething type that's a Windows binary standard and
thus binary compatible between D6 and BCB5 ? Naive attempts to interpret a
D6 System.Variant as a BCB5 System::Variant fails in a strange way (see
below), perhaps due to VCL incompatibility. Suggestions ? (other than using
COM ;-)
Frédéric
- - ----------------- naive attempt:
Argument is passed as a reference/pointer to a System Variant : see precise
code below.
At execution time, Delphi assigns 1234 to the variant Result; in the DLL, at
DcvDll_Data_GetValue, the contents of out_value looks consistent with it
holding the same 1234 value. But the code bangs in "out_value =
value.asDouble" (type=dtDouble) : "access violation at 957DCCFC. read of
957DCCFC". Following execution at the asm level, I see the A.V. occurs in
System::Variant::Clear :
System::Variant::Clear (System::Variant * const):
push ebp
mov ebp,esp
call -0x6af86f6c <-- it fails here. strange address anyway, look like
this call should never succeed.
pop ebp
ret
The same thing happens if I declare Value as "out" in DcvDll_Data_GetValue
and if I do not assign it 1234.
The "call -0x6af86f6c" is so mysterious I wonder how it _ever_ works.
- - ----------naive attempt code:
----dll.h----
// 'DEMO_DLL_EXPORT' = stdcall + dllexport/dllimport declarations
DcvDllResult* DEMO_DLL_EXPORT DcvDll_Data_GetValue(
const DcvDataRow* row, const DcvDataField* col, System::Variant
& out_value);
----dll.cpp----
// somewhat simplified code : a try-catch block has been removed.
DcvDataValue is similar to a variant.
DcvDllResult* DEMO_DLL_EXPORT DcvDll_Data_GetValue(
const DcvDataRow* row, const DcvDataField* col, System::Variant
& out_value)
{
DcvDataValue value;
col->getValue(*row, value);
switch (value.type)
{
default:
throw DcvDllException("value has unexpected type");
break; case dtInteger: VariantAssignInt64(out_value,
value.asInt);
break; case dtDouble: out_value = value.asDouble;
break; case dtNull: out_value = Variant(); //?, or
Variant::Empty() ?
break; case dtText: out_value = value.asText.c_str();
break; case dtUnassigned: out_value.Clear(); //xxx could raise
exception instead
}
return 0;
}
----Delphi code calling the DLL----
function DcvDll_Data_GetValue(Row : TDataRow; Column : TDataColumn; var
Value : Variant) : TDllResult stdcall;
external 'd6v_dll.dll' name 'DcvDll_Data_GetValue'
function Data_GetValue(Row : TDataRow; Column : TDataColumn) : Variant;
begin
Result := 1234; //test only
HandleResult(DcvDll_Data_GetValue(Row, Column, Result));
end;
> I'd like to know, if possible, how to pass a Variant
> Delphi 6 value to a C++ Builder 5 DLL.
Yes, assuming the DLL is implementing a COM object and you want to pass the
Variant to an interface method as a parameter.
> Since COM uses Variant parameters all the time, there must
> be a way to do it, possibly some VariantSomething type that's
> a Windows binary standard and thus binary compatible between
> D6 and BCB5 ?
Variants are always binary compatible, otherwise such COM operations would
not work in Delphi at all. Borland/CodeGear's Variant, OleVariant and
TVariant types are all based on binary compatibility with the underlying COM
tagVARIANT structure.
> Naive attempts to interpret a D6 System.Variant as a
> BCB5 System::Variant fails in a strange way (see below)
You need to be more specific.
I would not recommend trying that anyway. The data may be binary compatible
at the COM layer, but the Variant class is implemented differently from
version to version.
> perhaps due to VCL incompatibility. Suggestions ?
> (other than using COM ;-)
Have the DLL accept a tagVARIANT directly, not a System::Variant. Use the
Win32 API's Variant functions, or the TVariant class in utilcls.h, to
process it. You should do that anyway, if you ever want your DLL to be
usable in non-Borland environments later on.
> Argument is passed as a reference/pointer to a System Variant
Don't do it that way. Try this instead:
--- dll.h ---
DcvDllResult* DEMO_DLL_EXPORT DcvDll_Data_GetValue(const DcvDataRow*
row, const DcvDataField* col, VARIANT *out_value);
--- dll.cpp ---
DcvDllResult* DEMO_DLL_EXPORT DcvDll_Data_GetValue(const DcvDataRow*
row, const DcvDataField* col, VARIANT *out_value)
{
// you should be validating parameters before using them...
if (!row || !out_value)
return NULL;
::VariantInit(out_value);
DcvDataValue value;
col->getValue(*row, value);
switch( value.type )
{
// DO NOT throw exceptions across DLL boundaries!
/*
default:
throw DcvDllException("value has unexpected type");
break;
*/
case dtInteger:
V_I4(out_value) = value.asInt;
break;
case dtDouble:
V_R8(out_value) = value.asDouble;
break;
case dtNull:
V_VT(out_value) = VT_NULL;
break;
case dtText:
V_BSTR(out_value) = WideString(value.asText).Detach();
break;
case dtUnassigned:
V_VT(out_value) = VT_EMPTY;
break;
}
return NULL;
}
--- Delphi code calling the DLL ---
function DcvDll_Data_GetValue(Row : TDataRow; Column : TDataColumn; var
Value : OleVariant) : TDllResult; stdcall; external 'd6v_dll.dll' name
'DcvDll_Data_GetValue'
function Data_GetValue(Row : TDataRow; Column : TDataColumn) :
OleVariant;
begin
HandleResult(DcvDll_Data_GetValue(Row, Column, Result));
end;
Gambit
Much thanks for your quick and complete answer !
It works fine ! (after I added V_VT(out_value) = VT_R8 before V_R8(value) =
..., etc. ) so I'll go and follow your design.
As for validating parameters and not letting exceptions escape the DLL,
don't worry, it was already done by macros I had removed from the original
code I sent. (DcvDllResult* is NULL on success and a custom error object on
failure.)
Frédéric vdP
"Remy Lebeau (TeamB)" <no....@no.spam.com> wrote in message
news:46a63048$1...@newsgroups.borland.com...
V_VT(out_value) = VT_I4;
> V_I4(out_value) = value.asInt;
> break;
> case dtDouble:
V_VT(out_value) = VT_R8;
> V_R8(out_value) = value.asDouble;
> break;
> case dtNull:
> V_VT(out_value) = VT_NULL;
> break;
> case dtText:
V_VT(out_value) = VT_BSTR;
> I added V_VT(out_value) = VT_R8 before V_R8(value) = ..., etc. )
Sorry about that. Yes, setting the appropriate VT value is an important
step.
Gambit