【问题标题】:Call Delphi XE6 unicode dll from C# asp.net从 C# asp.net 调用 Delphi XE6 unicode dll
【发布时间】:2015-06-10 12:10:45
【问题描述】:

在打开主题之前搜索了更多为我服务的回复,我的错误仍然存​​在,在 Delphi XE6 64x 中创建了一个 dll,如下所示:

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.

我的 C# .NET 代码如下所示:

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);

    }
}

当我调用 DLL 时出现错误“用户代码未处理 BadImageFormatException”

谢谢!

【问题讨论】:

  • "结果 := PAnsiChar(Dest);" - 这不会转换任何东西。你应该像Result := PAnsiChar(AnsiString(Dest));那样做,但即便如此,问题是谁以及何时从你刚刚分配的字符串中释放堆内存? C# 无法访问 Delphi 堆,我也没有看到您将 Delphi 堆管理器切换到某些库存窗口低堆。所以我认为这是另一条探索路线,我一般如何将字符串从 Delphi 函数返回到 C#
  • 看起来你错过了抑制名称修改 - msdn.microsoft.com/en-us/library/… 使用 CFF Explorer 检查你的函数从 DLL 导出的确切名称是什么?
  • @Arioch'The Nope。这导致EntryPointNotFoundException。无论如何,双方都没有进行任何名称修改。这不是 C++。
  • 大卫,DllImport 中有 win32/win64 开关吗?我认为 .Net 程序集与 CPU 无关,因此它们不能是 x86 或 x64 /// char* == PAnsiChar 的返回值似乎必须是 StringBuilrder,而不是字符串! msdn.microsoft.com/en-us/library/aa984739(v=vs.71).aspx
  • @Arioch ExactSpelling 抑制了这一点。但无论如何,如果未找到 A 或 W 变体,则会尝试确切的名称。

标签: c# delphi dll


【解决方案1】:

BadImageFormatException 通常表示位数不匹配。您的 Delphi 模块是 64 位的,而 C# 模块是 32 位的,反之亦然。

您还有其他问题。至少如下:

  • 您将 UTF-16 UnicodeString 字符串转换为 PAnsiChar。那个演员表不正确。
  • 您返回一个局部变量的地址。当函数返回时,局部变量超出范围。因此,您正在返回一个指向无效内存的指针。您需要找到另一种方法来返回该缓冲区。
  • 您的 C# 代码假定返回值是通过调用 CoTaskMemAlloc 分配的。因此它将在您返回的指针上调用CoTaskMemFree,这显然是错误的。但是,当您解决上述问题时,该问题就会消失。

我可能会将字符串作为 WideString 输出参数返回,并将其与 C# 端的 UnmanagedType.BStr 匹配:Why can a WideString not be used as a function return value for interop? 这允许被调用者分配字符串并允许调用者解除分配。

【讨论】:

  • 大卫,DllImport 中有 win32/win64 开关吗?我认为 .Net 程序集与 CPU 无关,因此它们不能是 x86 或 x64 /// char* == PAnsiChar 的返回值似乎必须是 StringBuilrder,而不是字符串! msdn.microsoft.com/en-us/library/aa984739(v=vs.71).aspx
  • 另一方面,BSTR 可能会消除繁琐的“谁管理堆”问题。这真的很有意义
  • @Arioch'The 没有这样的开关。处理可以是 32 位或 64 位的程序集的各种方法。 StringBuilder 对于返回值没有用处。对于 arg 要求调用者知道要分配多少空间。好的,如果来电者确实知道这一点。 BSTR 是最简单的。
  • “可以是 32 位或 64 位的程序集” 从未听说过。看起来像一个最终的失败,完全打败了 Java/CLR 背后的想法......
  • @arioch AnyCPU 程序集必须在运行时根据实际位数决定。很容易。如果您要允许与非托管代码互操作,那就必须如此。此外,.net 有很多目标。它比您介绍的要复杂。
【解决方案2】:

我是这样决定的:

德尔福

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;

在 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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-17
    • 1970-01-01
    相关资源
    最近更新 更多