6

我有 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'。进程停止器。使用 Step 或 Run 继续

任何意见和建议将不胜感激。

4

2 回答 2

5

RTL 具有从 中正确VarArrayAsPSafeArray()提取 a 的功能: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; 
于 2015-05-25T17:55:41.897 回答
3

RTL 具有让访问数组的功能VarantSAFEARRAY

function VarArrayAsPSafeArray(const V: Variant): PSafeArray;

我想记录如何做相反的事情。

变体是一种结构

在 Delphi 中,aVariant是一个不透明的 blob。但在内部,它实际上是TVarData结构(又名 WindowsVARIANT结构)。一个变体可以保存不同类型的数据。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.

SafeArray如果你使用 Delphi 的函数来创建一个变体数组,你可以很容易地构造一个。Delphi 为创建您的“变体数组”实际上是的底层 SafeArray 做了繁重的工作。

但我们想走另一条路;我们得到一个PSafeArray,我们想把它包装在一个 Delphi Variant变量中,这样它就可以处理所有的丑陋和生命周期。

assemblies: PSafeArray;

assemblies := DefaultAppDomain.GetAssemblies;

我们如何处理这个指向 a 的指针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;
于 2017-03-27T21:47:17.490 回答