1

before opening the topic searched any more responses served me, my error remains, have created a dll in Delphi XE6 64x which is as follows:

function GeraChave(Action : PAnsiChar; StrCrip : PAnsiChar; Cnpj : PAnsiChar; idPC: PAnsiChar): PAnsiChar; stdcall;
var
 KeyLen : Integer;
 KeyPos : Integer;
 OffSet : Integer;
 Dest, Key : String;
 SrcPos : Integer;
 SrcAsc : Integer;
 TmpSrcAsc : Integer;
 Range, I : Integer;
 matKey : Array[0..7] of String;
begin
  if (StrCrip = '') Then
  begin
    Result:= '';
    Exit;
  end;

  for I := 0 to 8 do
    matKey[I] := Copy(idPC, I, 1);

  Key := 'A#%tgNh*'+ matKey[0] +'UJklo'+ matKey[1] + 'O' + cnpj + 'O' +
         '798DS'+ matKey[2] +'Cea'+ matKey[3] +'SEc%7*I'+ matKey[4] +'u@Ed#'+
         matKey[5] +'f$f'+ matKey[6] +'%g&j-9'+ matKey[7] +'sc';

  Dest := '';
  KeyLen := Length(Key);
  KeyPos := 0;
  SrcPos := 0;
  SrcAsc := 0;
  Range := 256;

  if (Action = UpperCase('C')) then
  begin
    Randomize;
    OffSet := Random(Range);
    Dest := Format('%1.2x',[OffSet]);
    for SrcPos := 1 to Length(StrCrip) do
    begin
      SrcAsc := (Ord(StrCrip[SrcPos]) + OffSet) Mod 255;
      if KeyPos < KeyLen then KeyPos := KeyPos + 1 else KeyPos := 1;

      SrcAsc := SrcAsc Xor Ord(Key[KeyPos]);
      Dest := Dest + Format('%1.2x',[SrcAsc]);
      OffSet := SrcAsc;
    end;
  end
  Else if (Action = UpperCase('D')) then
  begin
    OffSet := StrToInt('$' + copy(StrCrip,1,2));
    SrcPos := 3;
    repeat
      SrcAsc := StrToInt('$' + copy(StrCrip,SrcPos,2));
      if (KeyPos < KeyLen) Then KeyPos := KeyPos + 1 else KeyPos := 1;
      TmpSrcAsc := SrcAsc Xor Ord(Key[KeyPos]);
      if TmpSrcAsc <= OffSet then TmpSrcAsc := 255 + TmpSrcAsc - OffSet
      else TmpSrcAsc := TmpSrcAsc - OffSet;
      Dest := Dest + Chr(TmpSrcAsc);
      OffSet := SrcAsc;
      SrcPos := SrcPos + 2;
    until (SrcPos >= Length(StrCrip));
  end;

 Result := PAnsiChar(Dest);
end;

 Exports
 GeraChave;

 begin
end.

My C # .NET code looks like this:

public partial class _Default : System.Web.UI.Page
{

    [DllImport(@"H:\FTP\HOME\Web\testes\dllDelphi\Bin\DllChave.dll", 
               CallingConvention = CallingConvention.StdCall,
               CharSet = CharSet.Ansi)]

    public static extern string GeraChave(string xAction, string StrCrip, string Cnpj, string idPC);

    protected void Page_Load(object sender, EventArgs e)
    {
        string str = "A75BD3FD" + "|15/12/2016|" + "04564521020004";
        string xTp = "C";
        string Cnpj = "04564521020004";
        string IDPC = "A75BD3FD";
        string xRes = "";

        xRes = GeraChave(xTp, str, Cnpj, IDPC);

    }
}

and when I call the DLL is the error "BadImageFormatException was unhandled by user code"

thanks!

Dark Ducke
  • 145
  • 1
  • 3
  • 12
  • "Result := PAnsiChar(Dest);" - this does not convert a thing. You should make like `Result := PAnsiChar(AnsiString(Dest));` But even then the question is who and when would free the heap memory from the string you just allocated ? C# has no access to Delphi heap and I do not see you switch Delphi Heap Manager to some stock windows low heap either. So I think that is another route to explore, how I general can string be returned from Delphi function to C# – Arioch 'The Jun 10 '15 at 12:16
  • looks like u missed to suppress name mangling - https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute.exactspelling(v=vs.71).aspx Check with CFF Explorer what is EXACTLY the name your function is exported from the DLL with ? – Arioch 'The Jun 10 '15 at 12:19
  • @Arioch'The Nope. That leads to `EntryPointNotFoundException`. In any case, neither side is doing any name mangling. This is not C++. – David Heffernan Jun 10 '15 at 12:21
  • David, is there win32/win64 switch in DllImport? I thought .Net assemblies are CPU-agnostic thus they can not be x86 or x64 /// Also return value of `char*` == `PAnsiChar` seems to have to be StringBuilrder, not string! https://msdn.microsoft.com/en-us/library/aa984739(v=vs.71).aspx – Arioch 'The Jun 10 '15 at 12:23
  • msdn docs claim that using `CharSet =` attribute makes either "A" or "W" added to dll function names – Arioch 'The Jun 10 '15 at 12:32
  • 1
    @Arioch ExactSpelling suppresses that. But in any case, if A or W variant not found, the exact name is tried. – David Heffernan Jun 10 '15 at 13:52

2 Answers2

5

BadImageFormatException typically indicates a bitness mismatch. Your Delphi module is 64 bit and the C# module is 32 bit, or vice versa.

You have other problems. At least the following:

  • You cast a UTF-16 UnicodeString string to PAnsiChar. That cast is not correct.
  • You return a the address of a local variable. The local variable goes out of scope when the function returns. Thus you are returning a pointer to invalid memory. You need to find another way to return that buffer.
  • Your C# code assumes that the returned value was allocated by a call to CoTaskMemAlloc. It will thus call CoTaskMemFree on the pointer you returned, which is clearly wrong. That issue will dissolve though when you solve the above issue.

I'd probably return the string as a WideString out parameter and match that with UnmanagedType.BStr on the C# side: Why can a WideString not be used as a function return value for interop? That allows the callee to allocate the string and the caller to deallocate.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • David, is there win32/win64 switch in DllImport? I thought .Net assemblies are CPU-agnostic thus they can not be x86 or x64 /// Also return value of `char*` == `PAnsiChar` seems to have to be StringBuilrder, not string! msdn.microsoft.com/en-us/library/aa984739(v=vs.71).aspx – Arioch 'The Jun 10 '15 at 12:31
  • BSTR on another hand would probably remove the tedious "who manages the heap" problem. That really makes sense – Arioch 'The Jun 10 '15 at 12:33
  • 1
    @Arioch'The No such switch. Various ways to deal with an assembly that can be either 32 or 64 bit. StringBuilder not useful for a return value. And for an arg requires caller to know how much space to allocate. Fine if the caller does know that. BSTR is the simplest. – David Heffernan Jun 10 '15 at 12:35
  • "assembly that can be either 32 or 64 bit" Never heard of. Looks like an ultimate fail, totally defeating the very idea behind Java/CLR... – Arioch 'The Jun 10 '15 at 12:38
  • @Arioch'The Assemblies are x86, x64 or AnyCPU. I don't think it's "ultimate fail". I'm not sure what your objection is. – David Heffernan Jun 10 '15 at 12:41
  • the goal of JVM/CLR is to be CPU-agnostic JIT-optimized. Yes, there is n-something service that adds the optimized CPU-bound code to the cache, but that is the secondary function. So what DLL would AnyCPU load, win32 or win64 ? or perhaps winrt-ARM ? – Arioch 'The Jun 10 '15 at 12:51
  • 1
    @arioch AnyCPU assembly has to decide at runtime depending on actual bitness. Quite easy. If you are going to allow interop to unmanaged code then that's how it must be. Also, there are many goals of .net. It's more complex than you present. – David Heffernan Jun 10 '15 at 12:54
0

I decided this way:

On Delphi

function GeraChave(xAction : PChar; StrCrip : PChar; Cnpj : PChar; idPC: PChar; Saida : PChar; len : Integer): Integer; stdcall;
begin
 StrLCopy(Saida, PChar(Dest), len);
 Result := 1;
end;

On C#

public static extern int GeraChave(string xAction, string StrCrip, string Cnpj, string idPC, StringBuilder Saida, int len);
    StringBuilder str = new StringBuilder(256);
    int retval = GeraChave(xTp, strCrip, Cnpj, IDPC, str, str.Capacity);
Dark Ducke
  • 145
  • 1
  • 3
  • 12