如何从COM服务器返回WideString?

3
这是_TLB.pas文件中的接口。
// *********************************************************************//
// Interface: ITMyCOM
// Flags: (256) OleAutomation
// GUID: {D94769D0-F4AF-41E9-9111-4D8BC2F42D69}
// *********************************************************************//
ITMyCOM = interface(IUnknown)
['{D94769D0-F4AF-41E9-9111-4D8BC2F42D69}']
function MyDrawWS(a: Integer; b: Integer): WideString; stdcall;
end;

这篇文章涉及到Windows操作系统。

[
odl,
uuid(D94769D0-F4AF-41E9-9111-4D8BC2F42D69),
version(1.0),
helpstring("Interface for TMyCOM Object"),
oleautomation
]
interface ITMyCOM : IUnknown {
BSTR _stdcall MyDrawWS(
[in] long a, 
[in] long b);
};

COM服务器中的函数如下:

function TTMyCOM.MyDrawWS(a, b: Integer): WideString;
begin
Result := WideString(IntToStr(a+b));
end;

在COM客户端中,我会像这样调用这个函数:
Edit1.Text := String(MyCOM.MyDrawWS(1,1));

当返回整数时,它可以正常工作。如何返回WideString?出现以下错误:在$75A9FBAE处第一次遇到异常。异常类别为EAccessViolation,消息为“在模块'RPCRT4.dll'中的地址75A409A4处访问冲突。读取地址FFFFFFFF8”。进程Project1.exe(2296)

5个回答

2

让Delphi自动执行转换,不要强制类型转换。你可以将(ansi)string强制转换为PChar,因为它们的内存布局是兼容的,但你不能将string强制转换为widestring或反之亦然。当你将一个赋值给另一个时,Delphi会执行转换。

在Delphi < 2009中

var
S: string;
W: WideString;
...
S := W;  // Conversion, WideString -> AnsiString;
W := S; // Conversion, AnsiString -> WideString

应用于您的代码,一个简单的 Result:=IntToStr(a+b); 就可以了。 - Stijn Sanders
另一个解决方案是直接使用正确的代码页来调用MultiByteToWideChar()和WideCharToMultiByte()函数,但是除非需要它们的灵活性,否则最好让Delphi调用适当的转换函数。 - user160694

2

正确的处理方式如下:

[ 
odl, 
uuid(D94769D0-F4AF-41E9-9111-4D8BC2F42D69), 
version(1.0), 
helpstring("Interface for TMyCOM Object"), 
oleautomation 
] 
interface ITMyCOM : IUnknown { 
HRESULT _stdcall MyDrawWS( 
[in] long a,  
[in] long b,
[out, retval] BSTR* ret); 
}; 

ITMyCOM = interface(IUnknown) 
  ['{D94769D0-F4AF-41E9-9111-4D8BC2F42D69}'] 
  function MyDrawWS(a: Integer; b: Integer; out ret: WideString): HResult; stdcall; 
end; 

function TTMyCOM.MyDrawWS(a, b: Integer; out ret: WideString): HRESULT; 
begin 
  ret := IntToStr(a+b);
  Result := S_OK;
end; 

var
  W: WideString;
begin
  OleCheck(MyCOM.MyDrawWS(1, 1, W));
  Edit1.Text := W;
end;

使用Delphi声明接口时(而不是在TypeLibrary本身中),可以通过使用Delphi的safecall调用约定来简化这个过程:

ITMyCOM = interface(IUnknown) 
  ['{D94769D0-F4AF-41E9-9111-4D8BC2F42D69}'] 
  function MyDrawWS(a: Integer; b: Integer): WideString; safecall;
end; 

function TTMyCOM.MyDrawWS(a, b: Integer): WideString;
begin 
  Result := IntToStr(a+b);
end; 

Edit1.Text := MyCOM.MyDrawWS(1, 1);

1

不要使用除 HRESULT 以外的返回值。相反,将您的返回值作为输出参数放入参数列表中。

function MyDrawWS(a: Integer; b: Integer; out str : WideString): HRESULT; stdcall;

这样做,您也被迫使用COM内存管理器IMalloc(对于纯COM使用CoTaskMemAlloc,对于自动化使用SysAllocString)。


不是这样的。当你在类型库编辑器中声明一个 out reval 参数时,Delphi 会生成一个函数,使用 safecall 约定。正是 safecall 处理 HRESULT 值并在不是 S_OK 的情况下生成异常,同时让你将调用视为普通函数。 - user160694
1
但是这并不涉及到 safecall,所以我们应该遵循COM规则。 - ChristianWimmer
1
safecall是Delphi中COM函数的包装。如果一个COM函数在TypeLibrary中声明返回一个HRESULT返回值并具有“retval”参数,则可以安全地使用safecallsafecall函数的返回值需要与'retval'参数数据类型相同,并且'retval'参数本身不应在safecall函数中声明。当在应用程序代码中调用safecall时,它将分配一个临时变量,在'retval'参数中调用COM函数,进行HRESULT检查,并返回变量的值。 - Remy Lebeau
1
好的,但是这里没有使用safecall,因此需要使用HRESULT、stdcall和out参数。如果有人不使用safecall,则应该返回HRESULT。 - ChristianWimmer

0

2
或者你可以直接使用 WideString,让 RTL 为你完成所有工作。 - Rob Kennedy

0

在$75A9FBAE处发生第一次异常。异常类别为EAccessViolation,消息为“模块'RPCRT4.dll'中地址为75A409A4的访问冲突”

  1. 错误来自于RPCRT4.dll

  2. EAccessViolation通常是由于访问空对象引起的,请逐步检查您的代码,确保所有对象都是有效的对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接