I want to use the GlobalPlatform.dll from kaoh Karsten Ohme in Delphi. So i tried to translate the headers so i can use the GlobalPlatform.dll in Delphi.
The first i translated was Connection.h, i uploaded it on pastebin here.
The second i translated was Security.h i uploaded it on pastebin here.
First i establish a context with the OPGP_establish_context
function, that seems to go alright because the result is a OPGP_ERROR_STATUS_SUCCESS
and the message also states "success".
But then i try to list the readers with the OPGP_list_readers
function, which also returns a success - but when i try to read the returned names i get various access violations (mostly at adress 00000000 and trying to read 00000000
, but there are variations between my tries).
My code is assigned to a button click:
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;
When i use the above code i get no errors, but when i uncomment the messagebox - i get the access violations. I have been trying all day, and i modified everything .. but no luck. I can't see what im doing wrong. Maybe someone can help me out and point me in the right direction. I understand what a access violation on adress 00000000
means, but i dont know if i translated the headers the right way what might cause the error.
If someone could help me by checking, or testing it themselves - that would be highly appreciated.
I am using Delphi 10.4, and i have a internal smartcard reader (in the laptop), a Omnikey smartcard reader, and another unknown brand.
ps. Yes i am aware of the GPShell commandline utility, but i would like to avoid having to use that. I want to use smartcards for security, and the need for the commandline tool would make this a weak point - hence why i want to use the Library directly.
CodePudding user response:
In the 1st record you translated, OPGP_ERROR_STATUS
, the errorMessage
field is declared in the C code as:
TCHAR errorMessage[ERROR_MESSAGE_LENGTH 1];
where ERROR_MESSAGE_LENGTH
is defined as 256, thus this array has 257 chars max.
But your translation:
errorMessage : array [0..ERROR_MESSAGE_LENGTH 1] of Char;
has 258 chars max. This is because an array declaration in Delphi defines the indexes of the array, inclusive, so in your case you are declaring the array as having indexes 0..257
, but it should be 0..256
instead, so drop the 1
:
errorMessage : array [0..ERROR_MESSAGE_LENGTH] of Char;
You are making that same mistake in your translation of the OPGP_CARD_CONTEXT
record, too:
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;
You are declaring libraryName
as having 65 chars, and libraryVersion
as having 33 chars. They need to be 64 and 32 instead, respectively:
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;
Per the original C declaration:
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;
So, it makes sense why an AV could occur, since OPGP_list_readers()
internally accesses function pointers that are stored in the Context.connectionFunctions
field following the arrays, thus the pointers would be accessed at the wrong memory offsets.
Something else to watch out for is TCHAR
, which will map to either char
or wchar_t
depending on how the DLL is actually compiled. So that may or may not translate to Char
in Delphi, depending on what version you are using (which you didn't say). In general, char
-> AnsiChar
, wchar_t
-> WideChar
. The project's unicode.h
file suggests non-Windows builds are compiled to use char
. But the project makefile suggests the Windows build is compiled to use wchar_t
instead. It is not a good idea to use (P)Char
in interop code because of this. Use (P)AnsiChar
or (P)WideChar
as needed instead.
UPDATE
Also, try zeroing out the memory of the Context
before passing it to OPGP_establish_context()
. The first thing OPGP_establish_context()
does internally is call OPGP_release_context()
on the Context
, which means the Context
can't contain any garbage in it (particularly in the libraryHandle
and connectionFunctions.releaseContext
fields) or else it will be mishandled.