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

问题描述 投票:2回答:4

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

德尔福:

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

c# delphi com-interop
4个回答
3
投票

在Delphi中,AnsiStringUnicodeStringWideString值在它们为空时由nil指针表示。 COM使用BSTR作为字符串。德尔福用BSTR包裹WideString。所以没有办法将一个“空”非零字符串传递给一个以WideString作为参数的COM方法,它将改为nil


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
投票

为什么将''传递给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
投票

为避免空指针出错,您可以使用Chr(#0)或AnsiChar(#0)而不是返回null的空字符发送空字符。

© www.soinside.com 2019 - 2024. All rights reserved.