【问题标题】:How to test if a OleVariant contains an interface如何测试 OleVariant 是否包含接口
【发布时间】:2015-06-03 13:35:11
【问题描述】:

通过其自动化界面使用 Excel 的一个烦人的事情是弱输入。
返回值可以包含 anything 不同的类型。
如何测试caller返回的变体是否是ExcelRange接口?

function TAddInModule.InBank: boolean;
var
  ExcelAppAsVariant: OleVariant;
  test: string;
  Caller: OleVariant;
begin  //Exception handling omitted for brevity. 
  Result:= false;
  ExcelAppAsVariant:= ExcelApp.Application;
  Caller:= ExcelApp.Application.Caller[EmptyParam, 0];
  if IDispatch(Caller) is ExcelRange then begin //E2015 Operator not applicable 
    Result:= lowercase(Caller.Parent.Name) = 'bank' 
  end;
end;

(很奇怪,as 运算符可以正常工作,(IDispatch(Caller) as ExcelRange).Parent; 编译得很好)。

以下代码有效,但似乎过于冗长:

if VarIsType(Caller, varDispatch) then begin 
  IUnknown(Caller).QueryInterface(ExcelRange, ICaller) 
  if Assigned(ICaller) then ICaller......

也没有内置函数VarIsInterface(Variant, Interface)
如何测试 OleVariant 是否包含给定接口?

另请参阅:How to cast OleVariant to IDispatch derived?

编辑
谢谢大家,我使用以下方法进行测试,因为 Excel 混合了接口和 OleStrings 作为可能的返回值。

if VarIsType(Caller, varDispatch) and Supports(Caller, ExcelRange) then begin

【问题讨论】:

  • 接口一直支持as进行类型转换。奇怪的是is 不起作用,而不是as 起作用。

标签: delphi variant office-automation


【解决方案1】:

我可能会为此使用Supports

if Supports(Caller, ExcelRange) then
  ....

这解析为与@Stijn 给出的相同代码,但Supports 调用更简洁。

【讨论】:

  • supports 最酷的地方在于它有一个重载,可以返回转换后的接口。
  • 这里当然不需要,但在很多情况下,这些重载确实非常有用
  • Supports 也取代了Johan 的VarIsType 支票,还是只是QueryInterface + Assigned 电话?
  • @Rob 最接近if IDispatch(Caller) is ExcelRange then begin。因此,如果Caller 没有IDispatch,则会引发变体转换异常。我对代码的阅读是可以安全地假设Caller 中有一个IDispatch
  • 是的,我想这也是我对 Johan 的第一个代码块的解释。不过,第二个代码块似乎并没有保持同样的假设。它首先检查变体类型,然后类型转换为IDispatch
【解决方案2】:

System.Variants 单元具有VarSupports() 函数,用于从(Ole)Variant 测试/提取接口:

function VarSupports(const V: Variant; const IID: TGUID; out Intf): Boolean; overload;
function VarSupports(const V: Variant; const IID: TGUID): Boolean; overload;

例如:

Caller := ExcelApp.Application.Caller[EmptyParam, 0];
if VarSupports(Caller, ExcelRange) then
  Result := LowerCase(Caller.Parent.Name) = 'bank';

if VarSupports(Caller, ExcelRange, ICaller) then 
  ICaller.DoSomething;

【讨论】:

  • 与普通支持相比,它提供了哪些功能?
  • 在这种特殊情况下,不多。但它确实有助于确保Variant 包含一个可获取的接口,即使来自自定义 Variant 类型也是如此。此外,它是 Variant 特定的 RTL 函数。你有一个Variant,你想要一个来自它的接口,那么为什么不使用一个专门为此目的设计的函数呢?如果您直接调用Supports(),您并没有真正调用本机Variant 功能,而是首先将Variant 隐式转换为IInterface,然后查询它以获得所需的接口。那么为什么不让 RTL 为您处理呢?
  • rtl 会通过 Supports 为您处理。我怀疑这些在这里是一样的。
  • 在这种情况下没有什么可以选择的。 VarSupports 仅使用 IVarInstanceReference(如果可用)。它不会在这里。否则只需致电Supports。当您谈论自定义变体类型时,您指的是什么。当你有一个变体时使用VarSupports 是有意义的,作为一般规则,我猜,即使在这种情况下,行为是相同的。
  • @Johan:没有varInterface,你想的是varUnknown(意思是IUnknown,不是未知类型)。无论哪种方式,VarIsType() 不考虑 varTypeMaskvarByRef,但 (Ole)Variant 在转换期间会考虑。在这种特殊情况下,这无关紧要,因为 varByRef 不应该出现,但在其他情况下可能很重要,在这种情况下,您需要 if VarIsType(AVariant, [varDispatch, varDispatch or varByRef, varUnknown, varUnknown or varByRef]) then,或者 if (VarType(AVariant) and varTypeMask) in [varDispatch, varUnknown] then
【解决方案3】:

遗憾的是,代码确实很冗长,但与我通常使用的代码接近:

if IUnknown(Caller).QueryInterface(ExcelRange, ICaller)=S_OK then

【讨论】:

    猜你喜欢
    • 2011-04-09
    • 1970-01-01
    • 2013-12-09
    • 2017-11-21
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 2019-08-16
    相关资源
    最近更新 更多