将 GlobalPlatform 从 C 翻译成 Delphi - 访问冲突错误

Translating GlobalPlatform from C to Delphi - Access violation errors

我想在 Delphi 中使用 kaoh Karsten Ohme 中的 GlobalPlatform.dll。所以我尝试翻译 headers 以便我可以在 Delphi.

中使用 GlobalPlatform.dll

我翻译的第一个是Connection.h,i uploaded it on pastebin here
我翻译的第二个是 Security.h i uploaded it on pastebin here.

首先,我使用 OPGP_establish_context 函数建立了一个上下文,这似乎没问题,因为结果是 OPGP_ERROR_STATUS_SUCCESS 并且消息还显示“成功”。

但随后我尝试使用 OPGP_list_readers 函数列出 readers,这也 returns 成功 - 但是当我尝试读取返回的名称时,我获得了各种访问权限违规(主要在 adress 00000000 and trying to read 00000000,但我的尝试之间存在差异)。

我的代码被分配给一个按钮点击:

procedure TfrmFormatCard.Button1Click(Sender: TObject);
const
  BUFLEN = 1024;
var
  Status,
  Status2 : OPGP_ERROR_STATUS;
  Context : OPGP_CARD_CONTEXT;
  Names   : array [0..BUFLEN +1] of Char;
  Len     : DWord;
begin
  Context.libraryName    := 'gppcscconnectionplugin';
  Context.libraryVersion := '211';
  Status := OPGP_establish_context(Context);
  if Status.errorStatus = OPGP_ERROR_STATUS_SUCCESS then
  begin
    Len := 1024;
    Status2 := OPGP_list_readers(Context, Names, Len);
    if Status2.errorStatus = OPGP_ERROR_STATUS_SUCCESS then
    begin
      // Messagebox(application.Handle, names, '', 0);
    end;
    OPGP_release_context(Context);
  end;
end;

当我使用上面的代码时,我没有收到任何错误,但是当我取消注释消息框时 - 我遇到了访问冲突。我整天都在尝试,我修改了所有内容..但没有运气。我看不出我做错了什么。也许有人可以帮助我并指出正确的方向。我了解 adress 00000000 上的访问冲突意味着什么,但我不知道我是否以正确的方式翻译了 headers 可能导致错误的原因。

如果有人可以帮助我检查或自行测试 - 将不胜感激。

我正在使用 Delphi 10.4,并且我有一个内部智能卡 reader(在笔记本电脑中)、一个 Omnikey 智能卡 reader 和另一个未知品牌。

ps。是的,我知道 GPSshell 命令行实用程序,但我想避免使用它。我想使用智能卡来确保安全,而对命令行工具的需求会使这成为一个弱点 - 因此我想直接使用库。

在您翻译的第一条记录中,OPGP_ERROR_STATUSerrorMessage 字段在 C 代码中声明为:

TCHAR errorMessage[ERROR_MESSAGE_LENGTH+1];

其中 ERROR_MESSAGE_LENGTH 定义为 256,因此该数组最多有 257 个字符

但是你的翻译:

errorMessage : array [0..ERROR_MESSAGE_LENGTH + 1] of Char;

最多 258 个字符。这是因为 array declaration in Delphi 定义了数组的 indexes,所以在您的情况下,您声明数组具有索引 0..257,但它应该是0..256 相反,所以删除 +1:

errorMessage : array [0..ERROR_MESSAGE_LENGTH] of Char;

您在翻译 OPGP_CARD_CONTEXT 记录时也犯了同样的错误:

OPGP_CARD_CONTEXT = record
  librarySpecific     : Pointer;
  libraryName         : array [0..64] of Char; // <--
  libraryVersion      : array [0..32] of Char; // <--
  libraryHandle       : Pointer;
  connectionFunctions : OPGP_CONNECTION_FUNCTIONS;
end;

您声明 libraryName65 个字符,而 libraryVersion33 个字符。它们需要分别为 6432

OPGP_CARD_CONTEXT = record
  librarySpecific     : Pointer;
  libraryName         : array [0..63] of Char;
  libraryVersion      : array [0..31] of Char;
  libraryHandle       : Pointer;
  connectionFunctions : OPGP_CONNECTION_FUNCTIONS;
end;

根据原始 C 声明:

typedef struct {
    PVOID librarySpecific; //!< Library specific data.
    TCHAR libraryName[64]; //!< The name of the connection library to use.
    TCHAR libraryVersion[32]; //!< The version of the connection library to use.
    PVOID libraryHandle; //!< The handle to the library.
    OPGP_CONNECTION_FUNCTIONS connectionFunctions; //!< Connection functions of the connection library. Is automatically filled in if the connection library can be loaded correctly.
} OPGP_CARD_CONTEXT;

因此,为什么会出现 AV 是有道理的,因为 OPGP_list_readers() 在内部访问存储在数组后面的 Context.connectionFunctions 字段中的函数指针,因此指针将被错误地访问内存偏移量。

其他需要注意的是 TCHAR,它将映射到 charwchar_t,具体取决于 DLL 的实际编译方式。因此 可能会或可能不会 转换为 Delphi 中的 Char,具体取决于您使用的版本(您没有说)。一般来说,char -> AnsiCharwchar_t -> WideChar。该项目的 unicode.h 文件建议将 non-Windows 构建编译为使用 char。但是项目 makefile 建议将 Windows 构建编译为使用 wchar_t。因此,在互操作代码中使用 (P)Char 不是一个好主意。根据需要使用 (P)AnsiChar(P)WideChar

更新

此外,尝试在将 Context 的内存传递给 OPGP_establish_context() 之前将其清零。 OPGP_establish_context() 在内部做的第一件事是在 Context 上调用 OPGP_release_context(),这意味着 Context 不能包含任何垃圾(特别是在 libraryHandleconnectionFunctions.releaseContext 字段),否则将被错误处理。