当从Delphi传递给C#作为函数参数时,空字符串变为null

我有一个原生的Delphi exe,它通过COM interop调用C#dll。 以下是演示此问题的最简单案例:

delphi:

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

当我在Delphi中使用空字符串调用方法Test时,C#部分接收null作为参数值。 这是为什么? 它不应该是一个空字符串吗?

在Delphi中, AnsiStringUnicodeStringWideString值在它们为空时由nil指针表示。 COM使用BSTR作为字符串。 Delphi使用WideString包装BSTR 。 因此,没有办法将“空”非零字符串传递WideString作为参数的COM方法,它将为nil

在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#式区别的地方。

为什么将''传递给WideString参数会导致另一方接收null ? 好吧,这就是Delphi如何代表一个空的COM BSTR 。 如果你真的需要传递一个空字符串,你需要在Delphi代码中更改IClass1以传递TBStr而不是WideString并使用SysAllocStringSysAllocStringLen来创建一个空的TBStr

将Delphi代码中的函数声明更改为:

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

当需要传递空字符串时SysAllocStringLen('', 0)传递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); } } } 

delphi

 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. 

产量

 [空值]
 [空]
不是空的:foo