修订问题:
我们使用D7来调用COM对象中的C#方法。 C#方法返回一个WideString。
当我们在Pascal代码中使用它时,.NET是否存在从我们脚下垃圾收集返回的WideString的风险?如果是这样,我们有什么替代方法可以安全地从C#COM对象返回一个字符串?
另外,我们的最终Delphi行SubMan:= nil是否正确释放COM对象?
C#代码
[DispId(5)]
bool Test(int var1, ref int var2, ref string var3);
public bool Test(int var1, ref int var2, ref string var3)
{
bool result;
if (var1 == 0)
{
var2 = 0;
var3 = "zero";
result = true;
}
else
{
var2 = -1;
var3 = "minus one";
result = false;
}
return result;
}
D7中生成的TLB的Pascal代码
function Test( var1: Integer;
var var2: Integer;
var var3: WideString): WordBool; dispid 5;
调用COM对象的Pascal代码。风险变量是否会被GC编辑?
procedure TForm1.Button1Click(Sender: TObject);
var
SubMan: TSubMan;
Var1: Integer;
Var2: Integer;
Var3: WideString;
FunctionResult: Boolean;
begin
SubMan := COTSubMan.Create;
Var1 := 1;
FunctionResult := SubMan.Test(Var1, Var2, Var3);
// Will Var3 above be persistent in the code
// or might it be garbage collected by .NET before we can use it below or later?
ShowMessage( BoolToStr(FunctionResult, TRUE) +
' Var2 = ' + IntToStr(Var2) +
' Var3 = ' + Var3);
SubMan := nil; // Is this the right way to release the COM object?
end;
答案 0 :(得分:3)
你问题中的代码很好。 COM marshaller为您处理两个ref
参数的生命周期。 GC没有危险,因为参数的语义清楚地表达出来了。在Delphi端使用的对象不是.net对象,因此不受GC的约束。
确实,GC不是因素,因为这是COM。另一方面有.net对象实现COM服务器的事实既不在这里也不在那里。 .net COM互操作层负责向您呈现有效的COM对象。
要确认,即使字符串最初是在C#COM对象中分配的,var3在我们的Delphi Button1Click方法中也可以安全使用吗?
是。请记住,这是COM,二进制互操作的标准。您不必过于关注细节。 FWIW,WideString
是围绕COM BSTR
类型的Delphi包装器。该字符串类型是UTF-16编码的字符串,引用计数,并从共享COM堆分配。最后一点很重要。 BSTR
实例来自共享COM堆的事实是允许它们在一个模块中分配,并在另一个模块中解除分配。
SubMan := nil
是释放COM对象的正确方法吗?
是的。虽然在此代码中有点无意义,因为局部变量即将离开范围,并且与nil
赋值具有完全相同的效果。
答案 1 :(得分:0)
使用BSTR类型是最简单的方法,因为COM将处理内存(de)分配。
例如:
的Delphi:
function ReturnString(out TheString: WideString): Boolean; stdcall;
begin
try
TheString := 'Hello World';
finally
Result := True;
end;
end;
C#:
[DllImport("mydll.dll", CallingConvention=CallingConvention.StdCall)]
public static extern bool ReturnString([MarshalAs(UnmanagedType.BStr)]out string TheString);