当从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中, AnsiString
, UnicodeString
和WideString
值在它们为空时由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
并使用SysAllocString
或SysAllocStringLen
来创建一个空的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