Lettura Smart Card CNS

Salvo ho la necessit di leggere i dati dalle tessere CNS, da un programma in VB6 ho ricavato le funzioni per le chiamate a WinScard.dll il problema che non riesco a tradurre esattamente come passare i parametri alle chiamate.
Ho riportato solo due chiamate perch le altre sono con la stessa impostazione.

Il sorgente VB6

Public Declare Function SCardEstablishContext Lib “WinScard.dll” ( _
ByVal dwScope As Long, _
ByVal pvReserved1 As Long, _
ByVal pvReserved2 As Long, _
ByRef phContext As Long _
) As Long

Public Declare Function SCardListReaders Lib “WinScard.dll” Alias “SCardListReadersA” ( _
ByVal hContext As Long, _
ByVal mzGroup As String, _
ByVal ReaderList As String, _
ByRef pcchReaders As Long _
) As Long

che ho tradotto in Xojo cos

Declare Function SCardEstablishContext Lib “WinScard.dll” ( _
ByVal dwScope As Integer, _
ByVal pvReserved1 As Integer, _
ByVal pvReserved2 As Integer, _
ByRef phContext As Integer _
) As Integer

Declare Function SCardListReaders Lib “WinScard.dll” Alias “SCardListReadersA” ( _
ByVal hContext As Integer, _
ByVal mzGroup As CString, _
ByVal ReaderList As Cstring, _
ByRef pcchReaders As Integer _
) As Integer

la chiamata in VB6

Dim mszGroups As String
Dim szReaderLists As String * 256

lResult = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, hContext) ’ SCARD_SCOPE_USER = 0
If lResult <> SCARD_S_SUCCESS Then ’ SCARD_S_SUCCESS deve essere 0
MsgBox “Cannot establish connection”
MsgBox "ReturnCode(ESTACON): " + Hex(lResult)
End If

nReaderCount = 255
lResult = SCardListReaders(hContext, mszGroups, szReaderLists, nReaderCount)

Dim sTemp As String
Dim Index As Integer

Index = 1
sTemp = “”
Ctrl.Clear
While (Mid(ReaderList, Index, 1) <> vbNullChar)
While (Mid(ReaderList, Index, 1) <> vbNullChar)
sTemp = sTemp + Mid(ReaderList, Index, 1)
Index = Index + 1
Wend
Index = Index + 1
Ctrl.AddItem sTemp
sTemp = “”
Wend ’ in sTemp ho il nome del lettore di Card che poi serve nelle altre chiamate

che ho tradotto in Xojo cos:

retval = SCardEstablishContext(SCARD_SCOPE_USER, &h0, &h0, hContext)
If (retval <> 0) Then
MsgBox ("Errore: " + Str(retval))
return
end if

Dim mzGroup As String = “”
Dim r As New MemoryBlock(256)
Dim reder As String
Dim sTemp As String
Dim Indice As Integer

reder = r
readerlen = 255

’ FACCIO UNA LISTA DEI LETTORI DISPONIBILI
retval = SCardListReaders(hContext, mzGroup, reder, readerlen)
If (retval <> 0) Then
MsgBox(“Errore " + hex(retval) + " Lettore non trovato!”)
return
end if

Indice = 1
sTemp =""

While mid(reder, Indice, 1) <> Str(&h0)
While mid(reder, Indice, 1) <> Str(&h0)
sTemp = sTemp +mid(reder, Indice, 1)
MsgBox(" indice " + Str(Indice) + " Carattere " + hex(val(mid(reder, indice, 1))))
Indice = Indice + 1
Wend
Indice = Indice + 1
sTemp = “”
Wend

la prima chiamata SCardEstablishContext funziona perfettamente verificato con il run del programma in VB6 i valori ritornati sono gli stessi

la seconda chiamata SCardListReaders mentre in VB6 ritorna la lista dei lettori collegati in Xojo ritorna sempre una stringa Null.
Tutte le altre chiamate per leggere scrivere le Smart Card sono impostate come SCardListReaders quindi risolta una avrei risolto tutto.

Qualcuno pu aiutarmi perch sono completamente a corto di idee, grazie

Esiste gi un MBS Plugin per SmartCard:

http://www.monkeybreadsoftware.de/xojo/plugin-smartcard.shtml
e
http://www.monkeybreadsoftware.net/pluginpart-smartcard.shtml

Forse lo provate?

L’ho gia scaricato e lo sto provando prima di comperarlo, preferirei per se possibile utilizzare le chiamate di sistema anche perch ho gi tutte le routine di decodifica dei campi pronte devo solo risolvere il problema di come fare le chiamate, grazie

Ho modificato la definizione dei parametri basandomi sulla tabella di conversione “Windows APIs to Xojo data type conversion”
nuova definizione:

Declare Function SCardListReaders Lib “WinScard.dll” Alias “SCardListReadersA” ( _
ByVal hContext As Integer, _
ByVal mzGroup As WString, _ // invece di Cstring perch nella definizione originale LPCTSTR
ByVal ReaderList As Wstring, _ // pu essere sia Cstring che Wstring ma non ho capito la differenza originale LPTSTR
ByRef pcchReaders As Uint32 _ // Uint32 definizione originale LPDWORD
) As Integer

ma il risultato finale non cambia.

il parametro hContext mi ritorna il valore corretto come spiegato sopra.
in ReaderList dovrei avere la lista dei lettori installati ma mi ritorna Null.

La definizione e’ SCardListReaders.
Come tante funzioni Microsoft anche questa e’ presente in due versioni:

SCardListReadersA SCardListReadersW

La versione SCardListReadersA utilizza stringhe CString (ASCII), mentre la versione SCardListReadersW utilizza stringhe WString ovvero codificate UTF16.
Modifica quindi la definizione utilizzando CString e non WString in quanto si sta facendo riferimento a SCardListReadersA.
Nota che ogni stringa e’ terminata con un byte pari a 0 se CString oppure con 2 bytes pari a 0 se WString.

Non sono nella possibilita’ di verificare quanto segue quindi eventuali verifiche saranno a tuo carico utilizzando il debugger.
Il parametro ReaderList (mszReaders nella documentazione sopra riportata) e’ inteso come un insieme di stringhe, quindi possibilmente piu’ di una stringa: non so se nel tuo caso sia possibile che il valore contenga una o piu’ stringhe.
Da quanto vedo sarebbe tua intenzione utilizzare un buffer di 256 bytes dove salvare queste stringhe ma non e’ detto che il buffer sia abbastanza capiente e che possa contenere tutti i valori che la funzione vorrebbe ritornare.

Modifica la definizione del parametro ReaderList in: ByRef ReaderList As Ptr.
Modifica la chiamata della funzione in:

retval = SCardListReaders(hContext, mzGroup, r, readerlen)

Essendo r un MemoryBlock per definizione nelle Declare viene passato come Ptr.
A questo punto in r dovresti avere una o piu’ stringhe contenenti i valori richiesti: utilizza il debugger per vedere il contenuto di r e verificare che contenga una o piu’ stringhe ognuna terminata con un byte a 0.
Per accedere alla prima stringa puoi utilizzare r.CString(0).
La stringa seguente e’ all’offset (il valore tra le parentesi) pari alla lunghezza dalla stringa + 1 dove “+1” e’ il fatidico byte a 0 che indica il termine della stringa stessa e che non viene conteggiato dal metodo Len.

Il metodo per determinare quanto grande deve essere il buffer necessario a contenere tutte le stringhe, prevede di chiamare la funzione due volte.
La prima volta con:

retval = SCardListReaders(hContext, mzGroup, Nil, readerlen)

In readerlen sara’ ritornata la dimensione del buffer adeguato a contenere tutte le stringhe.
La seconda volta con:

Dim r As New MemoryBlock(readerlen) retval = SCardListReaders(hContext, mzGroup, r, readerlen)

Spero possa essere utile.
Saluti.

Ti ringrazio molto per i tuoi consigli ho risolto il problema, la dichiarazione sbagliata era sia sulla variabile “mzGroup” che sulla variabile “reder” perch le avevo configurate come String invece andavano configurate come Cstring modificando la dichiarazione della funzione con le variabile Cstring, l’analisi del programma non mi aveva segnalato alcun errore solo dopo due modifiche spuntato l’errore e allora ho capito dove sbagliavo cio la forma corretta di utilizzo la seguente

Declare Function SCardListReaders Lib “WinScard.dll” Alias “SCardListReadersA” ( _
ByVal hContext As Integer, _
ByVal mzGroup As Cstring, _
ByVal ReaderList As Cstring, _
ByRef pcchReaders As Uint32 _
) As Integer

Dim mzGroup As String
Dim r As New MemoryBlock(256)
Dim ReaderList As Cstring = r

retval = SCardListReaders(hContext, mzGroup, ReaderList, readerlen)

ora cos funziona, nel caso di errore proprio un errore di lettura e non un errore di forma della chiamata.
Comunque grazie ancora per la tua spiegazione in particolar modo sulla differenza degli Alias che terminano in “A” e in “W” era una cosa che non avevo capito.