问题
The following code fails with an OLE 800040005 "unspecified" error on the CentimetersToPoint
call when executed in Delphi (XE), the similar VBS or VBA version passes
var w : OleVariant;
w := CreateOleObject('Word.Application');
w.Visible := true;
Writeln(w.CentimetersToPoints(2.0));
FWIW the type library gives
/ [id(0x00000173), helpcontext(0x09700173)]
// single CentimetersToPoints([in] single Centimeters);
By default, Delphi only passes the floating values as Double, so I tried calling IDispatch.Invoke
directly and passing the argument as VT_R4
, but without better results.
edit: VB version that works (save to .vbs)
set w = CreateObject("Word.Application")
w.Visible = true
msgbox w.CentimetersToPoints(2.0)
Any other suggestions of what could be going wrong?
回答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.
来源:https://stackoverflow.com/questions/16279098/unspecified-error-when-calling-word-centimeterstopoints-via-ole