呼吁通过OLE字为CentimetersToPoints时未指定的错误(Unspecified er

2019-09-01 09:05发布

下面的代码失败,对一个OLE 800040005“未指定”错误CentimetersToPoint当在Delphi(XE)执行呼叫时,类似的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);

默认情况下,德尔福只有通过浮动值作为双,所以我打过电话IDispatch.Invoke直接传递参数作为VT_R4 ,但没有更好的效果。

编辑:VB版的作品(保存到.VBS)

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

什么其他建议,可以去错了吗?

Answer 1:

I initially suspected that the issue is that the function expects Single and Delphi converts your float to something else. When I tracked it down in the debugger I find that the variant being passed to Invoke is has VType of varCurrency and a currency value of 2. Quite how that happens I'm not sure!

As I discovered, answering this question, it's surprisingly tricky to get a single precision float into a variant. I initially suspected that you can use the solution I presented there to solve your problem.

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)));

But this fails also, in the same way, for reasons I don't yet understand.

Like you, I tried calling the function using IDispatch.Invoke. This is what I came up with:

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.

I cannot call CentimetersToPoints this way, but can call the ProductCode function.

To add to your collection of success/failure indicators, when I call CentimetersToPoints function using PowerShell I have success. When I call using Python's win32com.client, I get E_FAIL.

There is clearly some special magic ingredient that we are missing. It seems that all the MS tools know about this magic.

I conclude that it is not possible to call CentimetersToPoints using variant dispatch as implemented in Delphi. It does not know about the magic, whatever that magic is.

It is clearly possible to call Invoke on the IDispatch and succeed. We can tell that because other environments manage to do so. So, what I do not know yet is what the missing magic is.

If you could use early bound COM, then you could sidestep this issue:

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

OK, with the help of Hans Passant, I have some Delphi code that manages to call this function:

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.

For reasons unknown, you need to include DISPATCH_PROPERTYGET as well as DISPATCH_METHOD.

The question that I asked probably makes this question a duplicate. So, I'm voting to close.



文章来源: Unspecified error when calling Word CentimetersToPoints via OLE