2014-02-23 21 views
2

我有一个本地的Delphi exe,它通过COM interop调用到C#dll中。下面是这证明这个问题最简单的例子:当从Delphi传递给C#作为函数参数时,空字符串变为空

德尔福:

IClass1 = interface(IDispatch) 
    ['{B29BAF13-E9E4-33D7-9C92-FE28416C662D}'] 
    function Test(const aStr: WideString): WideString; safecall; 
end; 

var 
    obj: IClass1; 
    s: string; 
begin 
    obj := CoClass1.Create as IClass1; 
    s := obj.Test(''); // Returns '[null]' 
end; 

C#:

[ComVisible(true)] 
public interface IClass1 
{ 
    string Test(string aStr); 
} 

[ComVisible(true)] 
public class Class1 : IClass1 
{ 
    public string Test(string aStr) 
    { 
     if (aStr == null) return "[null]"; 
     if (aStr == "") return "[empty]"; 
     return "Not empty: " + aStr; 
    } 
} 

当我打电话的方法测试与德尔福一个空字符串,在C#部分接收null作为一个参数值。这是为什么?它不应该是一个空的字符串吗?

回答

3

在Delphi中,AnsiString,UnicodeStringWideString值在空时为nil指针。 COM使用BSTR作为字符串。德尔福包装BSTRWideString。所以没有办法将一个“空”的非零字符串传递给以WideString作为参数的COM方法,取而代之的是nil

+0

是的,但是您不必强制使用WideString,因为我在答案中指出了这一点。 –

2

在Delphi中,null(即nil)和空字符串被视为等价。因此,通过''一个字符串(或WideString)参数传递nil内部 -

program Project1; 

{$APPTYPE CONSOLE} 

{$R *.res} 

procedure Foo(const S: WideString); 
begin 
    WriteLn(Pointer(S) = nil); 
end; 

begin 
    Foo('Something'); //FALSE 
    Foo(''); //TRUE 
    ReadLn; 
end. 

空和空字符串的公式实际上是从COM复制......所以COM库是不是真的到位坚持两者之间的C#风格区分。

+1

COM确实有区别。 'BSTR'可以是NULL或0长度的非NULL。德尔福没有区分。 –

+1

@RemyLebeau - 为确保它可以在技术上给予许多COM约定(比如)C++没有得到编译器支持。然而,码是(或至少是)应该以治疗无效和零长度'BSTR'等同(请参见http://blogs.msdn。com/b/ericlippert/archive/2003/09/12/52976.aspx) –

1

为什么通过''WideString参数导致对方收到null?那么,德尔福如何代表一个空的COM BSTR。如果确实需要传递空字符串,则需要在Delphi代码中更改IClass1以通过TBStr而不是WideString,并使用SysAllocStringSysAllocStringLen创建空的TBStr

改变功能在Delphi代码的声明:

function Test(const aStr: TBStr): WideString; safecall; 

又通SysAllocStringLen('', 0)当你需要传递一个空字符串。

这里是一个完整的演示:

C#

using System; 
using System.Runtime.InteropServices; 

namespace ConsoleApplication1 
{ 
    [ComVisible(true)] 
    public interface IClass1 
    { 
     string Test(string aStr); 
    } 

    [ComVisible(true)] 
    public class Class1 : IClass1 
    { 
     public string Test(string aStr) 
     { 
      if (aStr == null) return "[null]"; 
      if (aStr == "") return "[empty]"; 
      return "Not empty: " + aStr; 
     } 
    } 

    class Program 
    { 
     [DllImport(@"Project1.dll")] 
     static extern void Foo(IClass1 intf); 

     static void Main(string[] args) 
     { 
      IClass1 intf = new Class1(); 
      Foo(intf); 
     } 
    } 
} 

德尔福

uses 
    Ole2; 

type 
    IClass1 = interface(System.IDispatch) 
    function Test(const aStr: TBStr): WideString; safecall; 
    end; 

var 
    EmptyBStr: TBStr; 

procedure Foo(const intf: IClass1); stdcall; 
begin 
    Writeln(intf.Test(nil)); 
    Writeln(intf.Test(EmptyBStr)); 
    Writeln(intf.Test(SysAllocString('foo'))); 
end; 

exports 
    Foo; 

begin 
    EmptyBStr := SysAllocStringLen('', 0); 
end. 

输出

 
[null] 
[empty] 
Not empty: foo 
+0

我用接口声明更新了我的问题。首先我没有添加它,因为它是从类型库中自动生成的。 – Max

+0

@Max谢谢。我的最新更新向您展示了如何传递一个空字符串。 –

相关问题