tags:

views:

1966

answers:

4

I'm trying to access an OleVariant in a callback that is coming from an ActiveX library.

Here's what the event handler is defined as in the TLB:

procedure(ASender: TObject; var structQSnap: {??structVTIQSnap}OleVariant) of object;

Here's the definition of structVTIQSnap in the TLB:

structVTIQSnap = packed record
  bstrSymbol: WideString;
  bstrListingExch: WideString;
  bstrLastExch: WideString;
  fLastPrice: Double;
  nLastSize: Integer;
  bstrBbo: WideString;
  bstrBidExch: WideString;
  fBidPrice: Double;
  nBidSize: Integer;
  bstrAskExch: WideString;
  fAskPrice: Double;
  nAskSize: Integer;
  fHighPrice: Double;
  fLowPrice: Double;
  fOpenPrice: Double;
  fClosePrice: Double;
  nCumVolume: Integer;
  bstrTradeCondition: WideString;
  nQuoteCondition: Integer;
  bstrCompanyName: WideString;
  f52WeekHigh: Double;
  f52WeekLow: Double;
  fEps: Double;
  nSharesOutstanding: Integer;
  nSpCode: Integer;
  fBeta: Double;
  bstrExDivDate: WideString;
  nDivFreq: Integer;
  fDivAmt: Double;
  nAvgVolume: Integer;
  bstrCusip: WideString;
  fVwap: Double;
  bstrUpdateTime: WideString;
  bstrExch: WideString;
  nSharesPerContract: Integer;
end;

It compiles fine, but everytime I try to access the bstrSymbol, I get an "Invalid Variant Operation":

 procedure TForm1.HandleVTIQuoteSnap(ASender: TObject; var structQSnap: OleVariant);
 var
    symbol: WideString;
 begin
    symbol := structQSnap.bstrSymbol; // this line causes the exception
 end;

How do I access structQSnap and its properties in Delphi?

In C#, this function works fine for the event handler:

    void vtiQ_OnVTIQSnap(ref vtiLib.structVTIQSnap structQSnap)
    {
        MessageBox.Show("Got qsnap for " + structQuoteSnap.bstrSymbol);            
    }

Any ideas?

A: 

try to look what returns in TVarData(structQSnap).VType ?

may be it will work:

 var
   symbol: WideString;
   rec: structVTIQSnap;
 begin
    rec := structVTIQSnap(structQSnap);
    symbol := rec.bstrSymbol; 
 end;
Jk
"VType: 3484" is what is returned when I do: ShowMessage('VType: ' + IntToStr(Integer(TVarData(structQSnap).VType)));
Dave
structVTIQSnap(structQSnap); says "Invalid Typecast"
Dave
some strange VType value.. try to look in system.pas declaration of TVarData.VType, my file(from Delphi6) doesn't contain this value..also you can try code like: rec := structQSnap;
Jk
rec := structQSnap; says "Incompatible types". I don't find anything close to that VType in system.pas.
Dave
A: 

I'm not sure why you are considering the "structVTIQSnap" to be an "OleVariant". Seems an odd translation to me. Could it be that it has been placed into an OleVariant in some form, either as an array or similar?

mj2008
That's how the TLB defines the procedure for the event handler. I'm just matching what was generated automatically when I imported the type library.
Dave
+1  A: 

You could try to typecast the structQSnap to a pointer to this struct. Something like

PstructVTIQSnap = ^structVTIQSnap;
structVTIQSnap = packed record
   bstrSymbol: WideString;
   // other stuff...
end;

and

procedure TForm1.HandleVTIQuoteSnap(ASender: TObject;
  var structQSnap: OleVariant);
var
  ps: PstructVTIQSnap;
  symbol: WideString;
begi
  ps := PstructVTIQSnap(structQSnap.VPointer^);
  symbol := ps.bstrSymbol;
  ...
end;

What I do not understand however is the following: I take it from the ref in the C# code that the structure should be marshalled twice, once from the library to the callback, second back to the library. This would mean that the varByRef flag ($4000) needs to be set in VType, but the value you gave in your comment (3484) is much too small?

mghie
I've run it a few more times and the VType is showing different values every time. Others are 32420, 13772, 55340 when I just ran it three different times.This line gives: Pointer Type Required ps := PstructVTIQSnap(structQSnap.VPointer^);
Dave
That's probably because it isn't a Variant at all; see my answer.
TOndrej
+7  A: 

I think that Delphi's ActiveX import wizard doesn't know how to handle the structVTIQSnap type (which seems to be a record) properly and just uses the default OleVariant. Is there a type declaration named structVTIQSnap or similar in the generated ..._TLB.pas file? Try using that instead of OleVariant, e.g.

procedure (ASender: TObject; var structQSnap: structVTIQSnap) of object;

The type will probably be declared as a "packed record".

TOndrej
Hmmm. I can access the bstrSymbol that way. Progress. I'm now accessing some other variables alongside bstrSymbol which is a double - I'm getting -1.97202464873328E111 and the number I'm expecting should be around 40 or so. Any ideas?
Dave
Maybe the ActiveX control uses different record alignment. Try using the "packed" directive, or if it's there try commenting it out and experiment with different values for record field alignment in the Compiler Options (or use directives {$A2}, {$A4}).
TOndrej
@TOndrej: +1, looks like you are right.
mghie
I just removed "packed" from the type definition and it appears to work. Thanks!!
Dave
I'm glad I could help, cheers.
TOndrej
@TOndrej: So basically, does Delphi not import the Type library correctly when it creates the TLB file when you import the component? Is this a deficiency in the type library importer? Is it common to have to edit the TLB file?
Dave
Yes, exactly. For some of the more complex types, the importer simply generates OleVariant and you have to manually correct the declarations.
TOndrej
@TOndrej: This mostly works, although the program that we're comm. with crashes consistently although not right away. Their support has narrowed it down to this section of their code. It seems like this might be the culprit, although this section of code executes dozens and dozens of times successfully before the program eventually crashes. Any ideas?
Dave
Without the source code, no ideas, sorry. How is structVTIQSnap declared?
TOndrej
The TLB importer imports it as a "packed record", but the values are weird unless I modify it to be a "record". BTW, my .NET app can access the data fine without crashes. Any way to examine how VS imported it and see how the type should be defined? I also thought about seeing if Delphi 2009 might import it properly - think that's worth it?
Dave
Sure, I think it's worth trying. I was hoping you would show the full Delphi source code declaration of the type, otherwise I'm afraid I can't tell you more. Or perhaps the other project is importing the type library incorrectly - it's hard to say.
TOndrej
I just updated the post and added the full definition of the structVTIQSnap from the _TLB.pas file. I assume that is what you mean?
Dave
Yes that's what I meant, but I don't see anything suspicious in the declaration.
TOndrej