通过 OLE 调用 Word CentimetersToPoints 时出现未指定的错误 [英] Unspecified error when calling Word CentimetersToPoints via OLE
问题描述
以下代码在 Delphi (XE) 中执行时在 CentimetersToPoint
调用中失败,并出现 OLE 800040005未指定"错误,类似的 VBS 或 VBA 版本通过
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 类型库提供
/ [id(0x00000173), helpcontext(0x09700173)]
// single CentimetersToPoints([in] single Centimeters);
默认情况下,Delphi 仅将浮动值作为 Double 传递,因此我尝试直接调用 IDispatch.Invoke
并将参数作为 VT_R4
传递,但没有更好的结果.
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.
有效的 VB 版本(保存到 .vbs)
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?
推荐答案
我最初怀疑问题是函数需要 Single
并且 Delphi 将您的浮点数转换为其他内容.当我在调试器中跟踪它时,我发现传递给 Invoke
的变体具有 VType
的 varCurrency
和 的货币值>2
.我不确定这是怎么发生的!
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.
和您一样,我尝试使用 IDispatch.Invoke
调用该函数.这是我想出的:
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.
我无法以这种方式调用 CentimetersToPoints
,但可以调用 ProductCode
函数.
I cannot call CentimetersToPoints
this way, but can call the ProductCode
function.
要添加到您的成功/失败指标集合中,当我使用 PowerShell
调用 CentimetersToPoints
函数时,我成功了.当我使用 Python 的 win32com.client
调用时,我得到 E_FAIL
.
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
.
显然我们缺少一些特殊的魔法成分.似乎所有 MS 工具都知道这个魔法.
There is clearly some special magic ingredient that we are missing. It seems that all the MS tools know about this magic.
我的结论是不可能使用 Delphi 中实现的变体调度来调用 CentimetersToPoints
.它不知道魔法是什么,不管那个魔法是什么.
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.
显然可以在 IDispatch
上调用 Invoke
并成功.我们可以这么说,因为其他环境也能做到这一点.所以,我还不知道缺失的魔法是什么.
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.
如果您可以使用早期绑定 COM,那么您就可以避开这个问题:
If you could use early bound COM, then you could sidestep this issue:
Writeln((IDispatch(w) as WordApplication).CentimetersToPoints(2.0));
<小时>
好的,使用 Hans Passant 的 >help,我有一些 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
.
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.
这篇关于通过 OLE 调用 Word CentimetersToPoints 时出现未指定的错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!