4

以下代码CentimetersToPoint在 Delphi (XE) 中执行时调用失败并出现 OLE 800040005“未指定”错误,类似的 VBS 或 VBA 版本通过

var w : OleVariant;

w := CreateOleObject('Word.Application');
w.Visible := true;
Writeln(w.CentimetersToPoints(2.0));

FWIW 类型库提供

/ [id(0x00000173), helpcontext(0x09700173)]
// single CentimetersToPoints([in] single Centimeters);

默认情况下,Delphi 只将浮点值作为 Double 传递,所以我尝试IDispatch.Invoke直接调用并将参数传递为VT_R4,但没有更好的结果。

编辑:有效的 VB 版本(保存到 .vbs)

set w = CreateObject("Word.Application")
w.Visible = true
msgbox w.CentimetersToPoints(2.0)

关于可能出错的任何其他建议?

4

1 回答 1

3

我最初怀疑问题在于该函数期望Single和 Delphi 将您的浮点数转换为其他内容。当我在调试器中对其进行跟踪时,我发现传递给的变体Invoke具有VTypeofvarCurrency且货币值为2. 我不确定这是怎么发生的!

正如我在回答这个问题时发现的那样,将单精度浮点数转换为变体非常棘手。我最初怀疑您可以使用我在那里提出的解决方案来解决您的问题。

function VarFromSingle(const Value: Single): Variant;
begin
  VarClear(Result);
  TVarData(Result).VSingle := Value;
  TVarData(Result).VType := varSingle;
end;

....

w := CreateOleObject('Word.Application');
w.Visible := true;
Writeln(w.CentimetersToPoints(VarFromSingle(2.0)));

但这也以同样的方式失败了,原因我还不明白。

像你一样,我尝试使用IDispatch.Invoke. 这就是我想出的:

program SO16279098;

{$APPTYPE CONSOLE}

uses
  SysUtils, Variants, Windows, ComObj, ActiveX;

function VarFromSingle(const Value: Single): Variant;
begin
  VarClear(Result);
  TVarData(Result).VSingle := Value;
  TVarData(Result).VType := varSingle;
end;

var
  WordApp: Variant;
  param: Variant;
  retval: HRESULT;
  disp: IDispatch;
  Params: TDispParams;
  result: Variant;

begin
  try
    CoInitialize(nil);
    WordApp := CreateOleObject('Word.Application');
    disp := IDispatch(WordApp);

    param := VarFromSingle(2.0);
    Params := Default(TDispParams);
    Params.cArgs := 1;
    Params.rgvarg := @param;
    retval := disp.Invoke(
      371,//CentimetersToPoints
      GUID_NULL,
      LOCALE_USER_DEFAULT,
      DISPATCH_METHOD,
      Params,
      @Result,
      nil,
      nil
    );
    // retval = E_FAIL

    Params := Default(TDispParams);
    retval := disp.Invoke(
      404,//ProductCode
      GUID_NULL,
      LOCALE_USER_DEFAULT,
      DISPATCH_METHOD,
      Params,
      @Result,
      nil,
      nil
    );
    // retval = S_OK
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

我不能这样调用CentimetersToPoints,但可以调用ProductCode函数。

要添加到您的成功/失败指标集合中,当我CentimetersToPoints使用我调用函数时,PowerShell我取得了成功。当我使用 Python 调用时win32com.client,我得到E_FAIL.

显然我们缺少一些特殊的魔法成分。似乎所有的 MS 工具都知道这个魔法。

我得出结论,不可能CentimetersToPoints使用 Delphi 中实现的变体调度来调用。它不知道魔法,不管魔法是什么。

显然可以呼吁InvokeIDispatch取得成功。我们可以看出这一点,因为其他环境可以做到这一点。所以,我还不知道缺失的魔法是什么。

如果您可以使用早期绑定的 COM,那么您可以回避这个问题:

Writeln((IDispatch(w) as WordApplication).CentimetersToPoints(2.0));

好的,在Hans Passant的帮助下,我有一些 Delphi 代码可以调用这个函数:

program SO16279098;

{$APPTYPE CONSOLE}

uses
  SysUtils, Variants, Windows, ComObj, ActiveX;

function VarFromSingle(const Value: Single): Variant;
begin
  VarClear(Result);
  TVarData(Result).VSingle := Value;
  TVarData(Result).VType := varSingle;
end;

var
  WordApp: Variant;
  param: Variant;
  retval: HRESULT;
  disp: IDispatch;
  Params: TDispParams;
  result: Variant;

begin
  try
    CoInitialize(nil);
    WordApp := CreateOleObject('Word.Application');
    disp := IDispatch(WordApp);

    param := VarFromSingle(2.0);
    Params := Default(TDispParams);
    Params.cArgs := 1;
    Params.rgvarg := @param;
    retval := disp.Invoke(
      371,//CentimetersToPoints
      GUID_NULL,
      LOCALE_USER_DEFAULT,
      DISPATCH_METHOD or DISPATCH_PROPERTYGET,
      Params,
      @Result,
      nil,
      nil
    );
    Writeln(Result);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

由于未知原因,您需要包括DISPATCH_PROPERTYGET以及DISPATCH_METHOD.

我问的问题可能使这个问题重复了。所以,我投票结束。

于 2013-04-29T13:33:58.687 回答