Empty string becomes null when passed from Delphi to C# as a function argument

后端 未结 4 363
南笙
南笙 2021-01-14 14:43

I have a native Delphi exe which calls into C# dll via COM interop. Here\'s the simplest case which demonstrate this issue:

Delphi:

IClass1 = interfa         


        
相关标签:
4条回答
  • 2021-01-14 15:14

    In Delphi, a null (i.e., nil) and empty string are treated as equivalent. As such, passing '' for a string (or WideString) parameter passes nil internally -

    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.
    

    The equation of null and empty strings was in fact copied from COM... so a COM library isn't really the place to insist on a C#-style distinction between the two.

    0 讨论(0)
  • 2021-01-14 15:16

    To avoid errors by null pointers you can send an empty character with Chr (#0) or AnsiChar(#0) instead of '' that returns a null.

    0 讨论(0)
  • 2021-01-14 15:26

    In Delphi, AnsiString, UnicodeString, and WideString values are represented by a nil pointer when they are empty. COM uses BSTR for strings. Delphi wraps BSTR with WideString. So there is no way to pass an "empty" non-nil string to a COM method that takes a WideString as a parameter, it will be nil instead.

    0 讨论(0)
  • 2021-01-14 15:35

    Why does passing '' to a WideString parameter result in the other side receiving null? Well, that's just how Delphi represents an empty COM BSTR. If you really need to pass an empty string, you need to change IClass1 in the Delphi code to pass TBStr instead of WideString and use SysAllocString or SysAllocStringLen to create an empty TBStr.

    Change the declaration of the function in the Delphi code to:

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

    And pass SysAllocStringLen('', 0) when you need to pass an empty string.

    Here is a complete demonstration:

    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.
    

    Output

    [null]
    [empty]
    Not empty: foo
    
    0 讨论(0)
提交回复
热议问题