【问题标题】:Accessing OleVariant containing VT_ARRAY of VT_RECORD from Delphi从 Delphi 访问包含 VT_ARRAY 的 VT_RECORD 的 OleVariant
【发布时间】:2013-04-30 10:05:14
【问题描述】:

使用 Delphi,我需要访问一个包含一个或多个数组记录的 OleVariant。

我调用的方法返回一个VT_RECORD的VT_ARRAY,记录本身定义为:

struct StreamTimeInfo {
  unsigned int PID;
  LONGLONG PTS;
  LONGLONG TimeStamp;
}; 

我的代码是这样的:

procedure Test;
type
  TStreamInfo = record
    PID: Cardinal;
    PTS: Int64;
    TimeStamp: Int64;  
  end;
var
  Value: OleVariant
  StreamTime: TStreamInfo;
begin
  GetValue(Value); // Value holds a VT_ARRAY of VT_RECORD

  // How should I access the array of records in Delphi?
  // I've tried this to get to the first element:
  StreamTime := TStreamInfo(TVarData(Value).VPointer^);
end;

我不明白如何从 Delphi 访问记录。

非常感谢任何输入。

【问题讨论】:

  • 打包/非打包记录我都试过了,没区别。此外,代码是 32 位的(Delphi 和我正在调用的 DLL)。
  • 原代码没有使用#pragma pack 1所以我把打包的去掉了。

标签: delphi


【解决方案1】:

我以前从未这样做过,但我认为这应该可行。

type 
  TStreamInfoArray = array [0..MaxArrayCount-1] of TStreamInfo; 
  PStreamInfoArray = ^TStreamInfoArray; 
var 
  Value: Variant;
  p: PStreamInfoArray;
  StreamInfo: TStreamInfo;
begin 
  GetValue(Value);
  p := PStreamInfoArray(VarArrayLock(Value)); 
  try
    StreamInfo := p^[Index]; 
  finally
    VarArrayUnlock(Value); 
  end;
end; 

【讨论】:

  • 大卫,太棒了!您的代码运行良好。我有点尴尬,我自己没有弄清楚这个。谢谢!
  • 我和你一样惊讶!实际上,包装问题对 32 位也很重要,不是吗。记录在PID 之后有填充。
  • 我想用我的最终代码更新这个问题,以供将来参考和其他人参考。我应该编辑现有帖子还是自己添加答案?这样做的首选方法是什么?
【解决方案2】:

为了将来参考和其他人,这是最终的工作代码:

// Original C-Source definition of StreamTimeInfo
// import "oaidl.idl";
// import "ocidl.idl";
// [uuid(A5AA2ACD-BEA0-4570-9232-D8301A6DAE0F)] 
// struct StreamTimeInfo {
//   unsigned int    PID;       
//   LONGLONG        PTS;       
//   LONGLONG        TimeStamp; 
// };
// cpp_quote("typedef struct StreamTimeInfo StreamTimeInfo;")

procedure GetStreamTimes;
type
  TStreamTimeInfo = record
    PID: Cardinal;
    PTS: Int64;
    TimeStamp: Int64;
  end;
  TStreamTimeInfoArray = array[0..31] of TStreamTimeInfo;
  PStreamTimeInfoArray = ^TStreamTimeInfoArray;
var
  Value: OleVariant;
  SizeOfArray: Integer;
  PtrToArray: PStreamTimeInfoArray;
begin
  GetValue(EMPGPDMX_STREAMTIMES, Value);
  if VarArrayDimCount(Value) = 1 then
  begin
    SizeOfArray := 1 + VarArrayHighBound(Value, 1) - VarArrayLowBound(Value, 1);
    PtrToArray := PStreamTimeInfoArray(VarArrayLock(Value));
    try
     for I := 0 to SizeOfArray - 1 do
     begin
       StreamTimeInfo := PtrToArray^[I];
       // Usage Sample:
       // FStatus.StreamTimePID[I] := StreamTimeInfo.PID;
       // FStatus.StreamTimePTS[I] := StreamTimeInfo.PTS;
       // FStatus.StreamTimeTS[I] := StreamTimeInfo.TimeStamp;
     end;
   finally
     VarArrayUnlock(Value);
   end;
  end;
end;

【讨论】:

  • 嗯,这实际上是错误的。你不想那样使用VarArrayDimCountVarArrayDimCount 返回维数。所以,0 如果不是数组,1 如果是一维数组,2 是二维数组,比如矩阵。您想检查VarArrayDimCount 是否返回1。然后用1+VarArrayHighBound(Value, 1)-VarArrayLowBound(Value, 1)求元素个数。
  • 噢!你是绝对正确的。我已经更新了代码。无需测试 VarArrayDimCount,因为我已经在使用 VarIsArray 进行测试,并且 GetValue() 调用永远不会返回多维数组。谢谢!
  • 我想我会将VarIsArray 转换为VarArrayDimCount(...)=1 以断言您没有多维数组。无论如何,我想你发布这个答案很有价值!
  • 最后一条评论。如果您启用了范围检查,如果数组中有超过 32 个元素,您将收到运行时错误。在这种情况下,您可以在数组类型上使用非常大的上索引。或者不打扰数组类型并使用指针算术,例如inc(p)。如果您没有启用范围检查,则可以将数组设置为 [0..0]!
猜你喜欢
  • 1970-01-01
  • 2010-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多