我有一个原生的Delphi exe,它通过COM互操作调用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作为参数值。这是为什么?它不应该是一个空字符串吗?
答案 0 :(得分:3)
在Delphi中,AnsiString
,UnicodeString
和WideString
值在它们为空时由nil
指针表示。 COM使用BSTR
表示字符串。 Delphi用BSTR
包装WideString
。因此,无法将“空”非零字符串传递给以WideString
作为参数的COM方法,而是nil
。
答案 1 :(得分: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.
null和空字符串的等式实际上是从COM中复制的......所以COM库并不是真正坚持两者之间C#式区别的地方。
答案 2 :(得分:1)
为什么将''
传递给WideString
参数导致另一方接收null
?好吧,这就是Delphi如何表示空COM BSTR
。如果您确实需要传递空字符串,则需要更改Delphi代码中的IClass1
以传递TBStr
而不是WideString
并使用SysAllocString
或SysAllocStringLen
创建一个空的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);
}
}
}
<强>的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.
<强>输出强>
[null] [empty] Not empty: foo
答案 3 :(得分:0)
为避免空指针导致错误,您可以发送带有Chr(#0)或AnsiChar(#0)的空字符,而不是发送返回空值的''。