【问题标题】:Calling a C# .Net method that has ref String arguments from Delphi 7从 Delphi 7 调用具有 ref String 参数的 C# .Net 方法
【发布时间】:2014-03-17 13:46:59
【问题描述】:

修改后的问题:

我们使用 D7 调用 COM 对象中的 C# 方法。 C# 方法返回一个 WideString。

当我们在 Pascal 代码中使用返回的 WideString 时,.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 的帕斯卡代码

function Test(     var1: Integer; 
               var var2: Integer; 
               var var3: WideString): WordBool; dispid 5;

调用 COM 对象的 Pascal 代码。 var3 是否有被 GC-ed 的风险?

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;

【问题讨论】:

  • 请将C#Test方法中的代码和调用Test的Dephi客户端中的代码贴出来。 EOleSysError 是调用方法还是从方法返回?
  • 如果是 COM 则使用 COM 字符串。问题缺乏细节。
  • 您添加的代码不完整。显示完整的 C# 类声明。
  • Var3 (WideString) 被引用计数。它会在离开作用域时被释放,即当你的方法Button1Click() 结束时。
  • @LURD 只是为了确认一下,即使字符串最初是在 C# COM 对象中分配的,在我们的 Delphi Button1Click 方法中使用 var3 是安全的吗?这真是个好消息。我可以问是怎么发生的吗?当 COM 调用从 C# 返回时,var3 字符串是由 Delphi 创建的 C# 字符串的 copy 吗?

标签: c# .net delphi com


【解决方案1】:

使用BSTR 类型是最简单的方法,因为 COM 将处理内存(解除)分配。

例如:

德尔福:

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

【讨论】:

  • 我想你可能已经颠倒了我正在使用的调用者-被调用者场景。我已经修改了这个问题,希望它更清楚。
【解决方案2】:

您问题中的代码很好。 COM 编组器为您处理两个ref 参数的生命周期。 GC 没有危险,因为参数的语义被清楚地表达了。您在 Delphi 端使用的对象不是 .net 对象,因此不受 GC 影响。

确实,GC 不是一个因素,因为这是 COM。在另一端有一个 .net 对象实现了 COM 服务器这一事实既不存在也不存在。 .net COM 互操作层有责任向您提供有效的 COM 对象。

确认一下,即使字符串最初是在 C# COM 对象中分配的,在我们的 Delphi Button1Click 方法中使用 var3 是安全的吗?

是的。请记住,这是 COM,二进制互操作的标准。你不需要太在意细节。 FWIW,WideString 是围绕 COM BSTR 类型的 Delphi 包装器。该字符串类型是一个 UTF-16 编码的字符串,经过引用计数,并在共享 COM 堆外分配。最后一点很重要。 BSTR 实例来自共享 COM 堆这一事实允许它们在一个模块中分配,并在另一个模块中释放。

SubMan := nil 是释放 COM 对象的正确方法吗?

是的。尽管在这段代码中它有点毫无意义,因为局部变量即将离开作用域,这将与您的 nil 分配产生完全相同的效果。

【讨论】:

  • 谢谢你,@DavidHefferman!我非常感谢您抽出时间来回答。像往常一样,你的技术知识的广度和深度让我吃惊!再次感谢您的所有帮助,不仅在这个问题上,而且在很多方面!
  • 你真好。谢谢。我很惊讶没有其他人参与进来。如果你刺激我,我会尽快回答的!感谢@LURD 这样做。
  • @DavidHefferman:嘿,我一直在等待,希望你能来,大卫。而且,如果我知道如何刺激你,我会的!
  • 你刚刚用@NameOfProdee 刺激了我
猜你喜欢
  • 1970-01-01
  • 2015-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-08
  • 2015-04-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多