So, do you mean create a type with a record structure that matches how "structVTIQSnap = packed record" is defined and then try to cast the OleVariant as that new type?
Main Topics
Browse All TopicsI'm trying to access an olevariant object type using Delphi. The object is passed through an event handler in the Active X library that I've imported.
Here's what the event handler is defined as in the TLB:
procedure(ASender: TObject; var structQSnap: {??structVTIQSnap}OleVaria
Ican't seem to set it up so I can properly access the structQSnap object in my event handler.
Also in the TLB, structVTIQSnap is defined as a packed record.
structVTIQSnap = packed record
bstrSymbol: WideString;
// other stuff...
end;
Everytime I try to access the bstrSymbol, I get an "Invalid Variant Operation":
procedure TForm1.HandleVTIQuoteSnap(
var
symbol: WideString;
begin
symbol := structQSnap.bstrSymbol; // this line causes the exception
end;
How do I access structQSnap to get the symbol string?
This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.
Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.
If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.
Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.
Access the answers to your technology questions today.
30-day free trial. Register in 60 seconds.
Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Try it out and discover for yourself.
30-day free trial. Register in 60 seconds.
Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.
Well, I've only ever used simple (intrinsic) types in parameter variants and olevariants. I don't see how the code in the function HandleVTIQuoteSnap can know to automatically typecast the incoming olevariant to a structVTIQSnap.
If it wasn't an OleVariant, you would get a compile error on this line (instead of a runtime exception):
symbol := structQSnap.bstrSymbol; // this line causes the exception
If TForm1 can reference the TLB in its uses clause, and if the "structVTIQSnap" is in the interface section of the TLB, then you shouldn't need to redeclare the structure.
Otherwise, yes, declare the structure locally. Either way, as long as the function itself can know what a structVTIQSnap is, the following line might work:
symbol := (structQSnap as structVTIQSnap).bstrSymbol
I have doubts about this, but this is all I've got. :( Good luck.
I never understood Borland's half implementation of structures in COM... eg you can define them in the type library editor, but there is no IRecordInfo support in the varaint routines.
So, the problem is this: using Ole automation (IDispatch based), you cannot pass structures as direct params; they must be handled as IRecordInfo interfaces. What you have (I believe) in the OleVariant parameter is actually a variant holding the record data and IRecordInfo interface. To start off with, add the following declarations in your source code (in the form unit, below the form declaration is fine).
---
//////////////////////////
// Record type identifier
//////////////////////////
const
VT_RECORD = 36;
//////////////////////////
// IRecordInfo UUID
//////////////////////////
const
IID_IRecordInfo: TGUID = '{0000002F-0000-0000-C000-
//////////////////////////
// IRecordInfo interface
//////////////////////////
type
IRecordInfo = interface(IUnknown)
['{0000002F-0000-0000-C000
function RecordInit(out pvNew): HResult; stdcall;
function RecordClear(pvExisting: Pointer): HResult; stdcall;
function RecordCopy(pvExisting: Pointer; out pvNew: Pointer): HResult; stdcall;
function GetGuid(out pGuid: TGUID): HResult; stdcall;
function GetName(out pbstrName: POleStr): HResult; stdcall;
function GetSize(out pcbSize: ULONG): HResult; stdcall;
function GetTypeInfo(out ppTypeInfo: ITypeInfo): HResult; stdcall;
function GetField(pvData: Pointer; szFieldName: POleStr; out pvarField: TVariantArg): HResult; stdcall;
function GetFieldNoCopy(pvData: Pointer; szFieldNamd: POleStr; out pvarField: TVariantArg; out ppvData: Pointer): HResult; stdcall;
function PutField(wFlags: ULONG; pvData: Pointer; szFieldName: POleStr; pvarField: PVariantArg): HResult; stdcall;
function PutFieldNoCopy(wFlags: ULONG; pvData: Pointer; szFieldName: POleStr; pvarField: TVariantArg): HResult; stdcall;
function GetFieldNames(var pcNames: ULONG; rgBstrNames: Pointer): HResult; stdcall;
function IsMatchingType(pRecordInfo
function RecordCreate: Pointer; stdcall;
function RecordCreateCopy(pvSource:
function RecordDestroy(pvRecord: Pointer): HResult; stdcall;
end;
//////////////////////////
// RecordInfo structure
//////////////////////////
type
PRecordInfo = ^TRecordInfo;
TRecordInfo = packed record
lpRecord: Pointer;
pvRecInfo: IRecordInfo;
end;
---
Next, add the following support routines:
---
//////////////////////////
// Helper functions
//////////////////////////
function OleIsRecordType(V: OleVariant): Boolean;
function OleGetRecord(V: OleVariant; out RecordInfo: TRecordInfo): HResult;
procedure OleCleanupRecord(var RecordInfo: TRecordInfo);
function OleGetRecordField(RecordIn
---
The implementation for these routines is as follows:
---
function OleIsRecordType(V: OleVariant): Boolean;
var lpVar: PVariantArg;
begin
// Cast as pointer to variant arg
lpVar:=PVariantArg(@V);
// Resource protection
try
// Check for by ref variant type
if (lpVar^.vt = (VT_VARIANT or VT_BYREF)) then
begin
// Get byref variant pointer
lpVar:=PVariantArg(lpVar.p
end;
finally
// Check for VT_RECORD type
result:=(lpVar^.vt = VT_RECORD) or (lpVar^.vt = (VT_RECORD or VT_BYREF));
end;
end;
function OleGetRecord(V: OleVariant; out RecordInfo: TRecordInfo): HResult;
var lpVar: PVariantArg;
lpRecord: PRecordInfo;
begin
// Clear pointer
lpRecord:=nil;
// Resource protection
try
// Cast as pointer to variant arg
lpVar:=PVariantArg(@V);
// Check vt type
case lpVar^.vt of
// Straight record
VT_RECORD : lpRecord:=PRecordInfo(@lpV
// ByRef record
VT_VARIANT or
VT_BYREF :
begin
// Get byref variant pointer
lpVar:=PVariantArg(lpVar.p
// Byref is no longer an option at this level
if (lpVar.vt = VT_RECORD) then
begin
// Get the record
lpRecord:=PRecordInfo(@lpV
end;
end;
VT_RECORD or
VT_BYREF :
begin
// Get pointer to the record struture
lpRecord:=lpVar.byRef;
end;
else
// Not a record type
lpRecord:=nil;
end;
// Set result record
if Assigned(lpRecord) then
begin
// We need to make sure the ref count is updated
RecordInfo.pvRecInfo:=lpRe
// Create record pointer copy
RecordInfo.pvRecInfo.Recor
end;
finally
// Check record pointer
if Assigned(lpRecord) then
// Success
result:=S_OK
else
// Return failure
result:=E_NOINTERFACE;
end;
end;
function OleGetRecordField(RecordIn
var lpwszField: PWideChar;
begin
// Init the outbound param
Value:=Unassigned;
// Check record structure to ensure we have the interface and pointer to the data
if (Assigned(RecordInfo.lpRec
begin
// Convert string to wide char
lpwszField:=StringToOleStr
// Resource protection
try
// Attempt to get the field value
result:=RecordInfo.pvRecIn
finally
// Free the allocated string
SysFreeString(lpwszField);
end;
end
else
// No pointer / interface
result:=E_NOINTERFACE;
end;
procedure OleCleanupRecord(var RecordInfo: TRecordInfo);
begin
// Resource protection
try
// Check the interface pointer
if Assigned(RecordInfo.pvRecI
begin
// Check data pointer
if Assigned(RecordInfo.lpReco
begin
// Destroy our copy of the record
RecordInfo.pvRecInfo.Recor
end;
end;
finally
// Release interface
RecordInfo.pvRecInfo:=nil;
// Clear data pointer
RecordInfo.lpRecord:=nil;
end;
end;
---
The next thing that you want to do is test your structQSnap parameter to ensure that is is indeed holding a recordinfo interface. This can be done with the following check:
---
procedure TForm1.HandleVTIQuoteSnap(
begin
// Determine if record type
if OleIsRecordType(structQSna
begin
... // Ok to proceed...
---
If the check comes back as true, then continue oniwards with the code below. Otherwise, I have NO idea what the data is being passed as.
To extract the field information from the interface (and record data pointer), you need to extract the interface and record data, and create a new copy of the record data This is done for you in the OleGetRecord routine. You can then use the OleGetRecordField to extract the desired field data from the record. When done, make sure you cleanup the interface pointer and record data by using the OleCleanupRecord.
--- eg ---
procedure TForm1.HandleVTIQuoteSnap(
var ovSymbol: OleVariant;
recInfo: TRecordInfo;
szSymbol: String;
begin
// Determine if record type
if OleIsRecordType(structQSna
begin
// Get record data
if (OleGetRecord(structQSnap,
begin
// Resource protection
try
// Get desired field
if (OleGetRecordField(recInfo
begin
// Get as string
szSymbol:=ovSymbol;
// Display field value
ShowMessage(szSymbol);
end;
finally
// Cleanup record
OleCleanupRecord(recInfo);
end;
end;
end;
end;
---
Please note that this was compiled and tested on Delphi 5 with a sample AX object that returned a record structure in an ole variant. Let me know if you have problems compiling, or with any of the PVariantArg casts.
Hope this helps,
Russell
Its Delphi that is throwing the exception.... most likely in the conversion of the byref (var) to the actual OleVariant. Do me a favor and check the .vt type directly using the following code:
procedure TForm1.HandleVTIQuoteSnap(
var ovSymbol: OleVariant;
recInfo: TRecordInfo;
szSymbol: String;
begin
ShowMessage(IntToStr(PVari
Let me know what the integral value is
---
Russell
I actually tracked down the answer here:
http://stackoverflow.
The TLB has to be modified.
Business Accounts
Answer for Membership
by: JosephGloszPosted on 2009-01-26 at 11:06:23ID: 23469677
I think by default, olevariants can't contain structured types. An OleVariant is a kind of Variant and both are discussed by CodeGear here:
ocs/radstu dio/radstu dio2007/ RS 2007_helpu pdates/HUp date4/EN/h tml/devcom mon/ varian ttypes_xml .html
http://docs.codegear.com/d
Look at the 2nd paragraphi in the link above. The OleVariant is specifically discussed near the end of the article. Specifically:
"OleVariant can only contain the data types defined as compatible with OLE Automation"
but you may also be able to declare your own specialized (record structure) type of variant according to the article.