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

时间:2014-02-23 22:24:15

标签: c# delphi com-interop

我有一个原生的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作为参数值。这是为什么?它不应该是一个空字符串吗?

4 个答案:

答案 0 :(得分:3)

在Delphi中,AnsiStringUnicodeStringWideString值在它们为空时由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并使用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);
        }
    }
}

<强>的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)的空字符,而不是发送返回空值的''。