J’ai un exe natif de Delphi qui appelle en C # dll via COM interop. Voici le cas le plus simple qui démontre ce problème:
Delphes:
IClass1 = interface(IDispatch) ['{B29BAF13-E9E4-33D7-9C92-FE28416C662D}'] function Test(const aStr: WideSsortingng): WideSsortingng; safecall; end; var obj: IClass1; s: ssortingng; begin obj := CoClass1.Create as IClass1; s := obj.Test(''); // Returns '[null]' end;
C #:
[ComVisible(true)] public interface IClass1 { ssortingng Test(ssortingng aStr); } [ComVisible(true)] public class Class1 : IClass1 { public ssortingng Test(ssortingng aStr) { if (aStr == null) return "[null]"; if (aStr == "") return "[empty]"; return "Not empty: " + aStr; } }
Lorsque j’appelle la méthode Test avec une chaîne vide dans Delphi, la partie C # reçoit la valeur null en tant que valeur de paramètre. Pourquoi donc? Ne devrait-il pas être une chaîne vide également?
Dans Delphi, les valeurs AnsiSsortingng
, UnicodeSsortingng
et WideSsortingng
sont représentées par un pointeur nil
lorsqu’elles sont vides. COM utilise BSTR
pour les chaînes. Delphi enveloppe BSTR
avec WideSsortingng
. Donc, il n’y a aucun moyen de passer une chaîne “vide” non-nil à une méthode COM qui prend un WideSsortingng
tant que paramètre, ce sera nil
.
Dans Delphi, une chaîne nulle (c’est-à-dire nil
) et une chaîne vide sont traitées comme équivalentes. En tant que tel, passer ''
pour un paramètre ssortingng (ou WideSsortingng
) passe nil
interne –
program Project1; {$APPTYPE CONSOLE} {$R *.res} procedure Foo(const S: WideSsortingng); begin WriteLn(Pointer(S) = nil); end; begin Foo('Something'); //FALSE Foo(''); //TRUE ReadLn; end.
L’équation des chaînes nulles et vides a en fait été copiée à partir de COM … donc une bibliothèque COM n’est pas vraiment l’endroit pour insister sur une distinction de style C # entre les deux.
Pourquoi le passage à un paramètre WideSsortingng
a- WideSsortingng
résultat que l’autre côté reçoit la valeur null
? BSTR
comment Delphi représente un COM BSTR
vide. Si vous devez réellement transmettre une chaîne vide, vous devez modifier IClass1
dans le code Delphi pour transmettre TBStr
au lieu de WideSsortingng
et utiliser SysAllocSsortingng
ou SysAllocSsortingngLen
pour créer un TBStr
vide.
Modifiez la déclaration de la fonction dans le code Delphi en:
function Test(const aStr: TBStr): WideSsortingng; safecall;
Et transmettez SysAllocSsortingngLen('', 0)
lorsque vous devez transmettre une chaîne vide.
Voici une démonstration complète:
C #
using System; using System.Runtime.InteropServices; namespace ConsoleApplication1 { [ComVisible(true)] public interface IClass1 { ssortingng Test(ssortingng aStr); } [ComVisible(true)] public class Class1 : IClass1 { public ssortingng Test(ssortingng 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(ssortingng[] args) { IClass1 intf = new Class1(); Foo(intf); } } }
Delphes
uses Ole2; type IClass1 = interface(System.IDispatch) function Test(const aStr: TBStr): WideSsortingng; safecall; end; var EmptyBStr: TBStr; procedure Foo(const intf: IClass1); stdcall; begin Writeln(intf.Test(nil)); Writeln(intf.Test(EmptyBStr)); Writeln(intf.Test(SysAllocSsortingng('foo'))); end; exports Foo; begin EmptyBStr := SysAllocSsortingngLen('', 0); end.
Sortie
[nul] [vide] Pas vide: foo