Delphi OleVariant来自COM库的字符串数组

时间:2015-05-25 15:54:49

标签: string delphi com variant safearray

我有Delphi 2006客户端应用程序。此客户端从COM服务器接收Olevariant类型数据。功能是:

procedure OnLimitsChanged(var SymbolsChanged: {??PSafeArray}OleVariant);

此函数返回一个字符串数组。我无法从delphi中读取OleVariant类型的数据。

从Excel VBA开始工作:

Private Sub g_Realtime_OnLimitsChanged(SymbolsChanged() As String)
    Dim i%
    Dim Salir As Boolean
    If UBound(SymbolsChanged) <> -1 Then
        i = 0: Salir = False
        While Not Salir
            If SymbolsChanged(i) = Simbolo Then
                LlamarALimites
                Salir = True
            Else
                i = i + 1
                If i > UBound(SymbolsChanged) Then Salir = True
            End If
        Wend
    End If
End Sub

我尝试将OleVariant转换为Psafearray ......

procedure TfmConfiguracion.RecibirNuevosTicks(ASender: TObject;
  var ArrayTicks : Olevariant);    
var 
  Data : pSafeArray;
  i,iLow, iHigh : Integer;  
  value : wideString;
begin
  Data:=PSafeArray(TVarData(ArrayTicks).VArray);
  SafeArrayGetLBound(Data,1,iLow);
  SafeArrayGetUBound(Data,1,iHigh);
  for i:=iLow to iHigh do
  begin
    SafeArrayGetElement(Data,i,Value);
    Showmessage(Value);
  end; 

但我收到的除了这一行:

 SafeArrayGetLBound(Data,1,iLow);
  

调试器故障通知
  项目...出现错误消息:'0x751de18c的访问冲突:读取地址0xabababab'。进程停止器。使用步骤或运行继续

非常感谢任何建议和意见。

2 个答案:

答案 0 :(得分:4)

RTL具有VarArrayAsPSafeArray()功能,可以从PSafeArray正确提取(Ole)Variant

procedure TfmConfiguracion.RecibirNuevosTicks(ASender: TObject; var ArrayTicks : OleVariant);    
var 
  Data : PVarArray; // RTL's version of PSafeArray
  //...
begin
  Data := VarArrayAsPSafeArray(ArrayTicks);
  //...
end; 

如果(Ole)Variant不包含数组,则会引发异常。或者您可以使用VarIsArray()手动检查它:

procedure TfmConfiguracion.RecibirNuevosTicks(ASender: TObject; var ArrayTicks : OleVariant);    
var 
  Data : PVarArray;
  //...
begin
  if not VarIsArray(ArrayTicks) then Exit;
  Data := VarArrayAsPSafeArray(ArrayTicks);
  //...
end; 

话虽如此,(Ole)Variant内置了对访问PSafeArray元素数据的支持,因此您不需要直接访问PSafeArray(除非您需要额外的性能提升,在这种情况下,您需要在访问其数据之前自己验证PSafeArray

procedure TfmConfiguracion.RecibirNuevosTicks(ASender: TObject; var ArrayTicks : Olevariant);    
var 
  i : Integer;  
  value : String;
begin
  if not VarIsArray(ArrayTicks) then Exit;
  for i := VarArrayLowBound(ArrayTicks, 1) to VarArrayHighBound(ArrayTicks, 1) do
  begin
    Value := ArrayTicks[i];
    ShowMessage(Value);
  end; 

答案 1 :(得分:2)

RTL让function访问Varant数组作为SAFEARRAY

function VarArrayAsPSafeArray(const V: Variant): PSafeArray;

我想记录如何反过来。

Variant是一个结构

在Delphi中,Variant是一个不透明的blob。但在内部它实际上是TVarData结构(又称Windows VARIANT结构)。变体可以包含不同类型的数据。您可以通过VType成员指明哪种类型。 VType成员的值告诉您如何解释结构的其余部分:

32位整数(VT_I4

  • 变体
    • VType: Word = VT_I4; // 3
    • VInteger: Integer;

IUnknown界面(VT_UNKNOWN

  • 变体
    • VType: Word = VT_UNKNOWN; // 13
    • VUnknown: Pointer; //实际上是IUnknown

BSTR(Delphi中的WideString)

  • 变体
    • VType: Word = VT_BSTR; // 8
    • VOleStr: PWideChar;

在变量是32位整数的SAFEARRAY的情况下:

  • 变体
    • VType: Word = (VT_ARRAY or VT_I4);
    • VArray: PVarArray;

然后VArray指向SAFEARRAY结构:

  • 变体
    • VType: Word = (VT_ARRAY or VT_I4);
    • VArray: PVarArray;
      • cDims: Word;
      • fFeatures: Word;
      • cbElements: LongWord;
      • cLocks: LongWord;
      • pvData: Pointer;
      • rgsabound: array[0..0] of TSafeArrayBound;

如果我们从SAFEARRAY

开始怎么办?

有时候,特别是在与COM或.NET交互的时候:

  • 必须提供PSafeArray
  • 或获得PSafeArray

如果使用Delphi的函数创建变量数组,则可以轻松地构造SafeArray。 Delphi负责创建你的“变体数组”实际存在的底层SafeArray。

但我们想要走另一条路;我们得到一个PSafeArray,我们希望将它包含在Delphi Variant 变量中,以便它处理所有的丑陋和生命周期。

assemblies: PSafeArray;

assemblies := DefaultAppDomain.GetAssemblies;

我们如何处理指向SAFEARRAY

的指针
function PSafeArrayToVariant(psa: PSafeArray): OleVariant;
begin
   TVarData(v).VType = (VT_ARRAY or VT_xxxx); 
   TVarData(v).VArray := PVarArray(psa);
end;

除了我们需要知道SafeArray包含的内容之外;我们需要在上面的代码中填写 VT_xxxx

幸运的是,SAFEARRAY结构的其中一个成员告诉VType数组的成员是什么:

  • fFeatures: Word;
    • FADF_BSTR :这是一个BSTR数组(VT_BSTR
    • FADF_UNKNOWN :这是一个IUnknown(VT_UNKNOWN)数组
    • FADF_DISPATCH :这是一个IDispatch数组(VT_DISPATCH
    • FADF_VARIANT :这是一个变种数组(VT_VARIANT
    • FADF_HAVEVARTYPE :您可以使用SafeArrayGetVartype
    • 获取类型

最终功能

function SafeArrayGetVartype(psa: PSafeArray): TVarType; safecall; external 'OleAut32.dll';

function PSafeArrayToVariant(psa: PSafeArray): OleVariant;
var
    features: Word;
    vt: TVarType;
const
    FADF_HAVEVARTYPE = $80;
begin
    features := psa^.fFeatures;
    if (features and FADF_UNKNOWN) = FADF_UNKNOWN then
        vt := VT_UNKNOWN
    else if (features and FADF_DISPATCH) = FADF_DISPATCH then
        vt := VT_DISPATCH
    else if (features and FADF_VARIANT) = FADF_VARIANT then
        vt := VT_VARIANT
    else if (features and FADF_BSTR) <> 0 then
        vt := VT_BSTR
    else if (features and FADF_HAVEVARTYPE) <> 0 then
        vt := SafeArrayGetVartype(psa)
    else
        vt := VT_UI4; //assume 4 bytes of *something*

    TVarData(Result).VType := VT_ARRAY or vt;
    TVarData(Result).VArray := PVarArray(psa);
end;