Help with WinScard.dll

External libraries (GTK, GSL, SDL, Allegro, OpenGL, etc) questions.
Post Reply
Tolo68
Posts: 105
Joined: Mar 30, 2020 18:18
Location: Spain

Help with WinScard.dll

Post by Tolo68 »

Hello everyone

I have this code in Visual Basic 6.0 which reads a card, runs fine on both Win XP and higher versions.
WinScard.dll It already comes with the operating system, it is not an external library.

//////////////////////////////////////////////////////////

Code: Select all

Private Declare Function SCardEstablishContext Lib "WinScard.dll" ( _
    ByVal dwScope As Long, ByVal pvReserved1 As Long, _
    ByVal pvReserved2 As Long, ByRef phContext As Long) As Long

Private 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

Private Sub Form_Load()

Form1.AutoRedraw = True

Dim mszGroups As String
Dim szReaderLists As String * 256
Dim bConversion As Boolean
Dim lResult As Long
Dim hContext As Long
Dim nReaderCount As Long

Const SCARD_SCOPE_USER = 0
Const SCARD_S_SUCCESS = 0

bConversion = False
lResult = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, hContext)

If lResult <> SCARD_S_SUCCESS Then
    Form1.Print "Cannot establish connection"
    Form1.Print "ReturnCode(ESTACON):  " + Hex(lResult)
End If

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

Form1.Print "szReaderLists = " & szReaderLists
Form1.Print "nReaderCount = " & nReaderCount
Form1.Print "timeGetTime = " & timeGetTime

Form1.Refresh

End Sub
//////////////////////////////////////////////////////////

Now my idea is to do it in FreeBasic, with which the code looks like this, but it gives me an error .....

//////////////////////////////////////////////////////////

Code: Select all

#include "windows.bi"

extern "windows"
    Declare Function SCardEstablishContext Lib "WinScard.dll" ( _
        ByVal dwScope As Long, ByVal pvReserved1 As Long, ByVal pvReserved2 As Long, ByRef phContext As Long ) As Long
end extern

extern "windows"
    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
end Extern

Dim mszGroups As String
Dim szReaderLists As String * 256
dim bConversion As Boolean
dim lResult As Long
dim hContext As Long
dim nReaderCount As Long

Const SCARD_SCOPE_USER = 0
Const SCARD_S_SUCCESS = 0

bConversion = False
lResult = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, hContext)  <------ this line works fine
If lResult <> SCARD_S_SUCCESS Then
    print "Cannot establish connection"
    print "ReturnCode(ESTACON):  " + Hex(lResult)
End If

nReaderCount = 255
lResult = SCardListReaders(hContext, mszGroups, szReaderLists, nReaderCount) <------ in this line I have the error

print "szReaderLists = " & szReaderLists
print "nReaderCount = " & nReaderCount

sleep
//////////////////////////////////////////////////////////

I am compiling with FreeBasic 1.0.9.0 - Windows 32 bits

The error that the IDE FBIDE gives me is the following...

//////////////////////////////////////////////////////////

Command executed:
"D:\Programacion\FreeBasic_1090\fbc.exe" "D:\Programacion\Proyectos\FreeBasicNFC\FBIDETEMP.bas"

Compiler output:
D:\Programacion\FreeBasic_1090\bin\win32\ld.exe: D:\Programacion\Proyectos\FreeBasicNFC\FBIDETEMP.o:fake:(.text+0x138): undefined reference to `SCardListReadersA@32'

Results:
Compilation failed

System:
FBIde: 0.4.6
fbc: FreeBASIC Compiler - Version 1.09.0 (2021-12-31), built for win32 (32bit)
OS: Windows XP (build 2600, Service Pack 3)

//////////////////////////////////////////////////////////

Seeing that it only gave me an error in SCardListReaders, I realized that in the VB6 code it is as Alias

........ Private Declare Function SCardListReaders Lib "WinScard.dll" Alias "SCardListReadersA" (........

So in this function declaration in VB6, I removed the Alias

........ Private Declare Function SCardListReaders Lib "WinScard.dll" (........

And when executing in VB6, it also gave me an error with a MsgBox, just like in Freebasic

Cannot find entry point DLL SCardListReaders in WinScard.dll

//////////////////////////////////////////////////////////

So in FreeBasic, I put the Alias ​​the same as in VB6, but it keeps giving me the error, I've read that maybe it's because of the linker,
or because maybe I should call the DLL in another way.

The strange thing is that there is a function that works well and the other does not, being the 2 of the same Dll.

I have also seen that freebasic has the libraries.

FreeBasic_1090\inc\win\winscard.bi

and

FreeBasic_1090\lib\win32\libwinscard.dll.a

but I don't know how to use them, since there are no examples.

I hope you can help me, since I have been trying to solve this for 2 days, but there is no way.

Thank you very much and greetings to all !!!
Last edited by fxm on May 30, 2022 16:21, edited 1 time in total.
Reason: Formatting with code tags.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Help with WinScard.dll

Post by dodicat »

This compiles with your 2 functions

Code: Select all



'#include "windows.bi"

'#include "win\winscard.bi"

'

'declare function SCardEstablishContext(byval dwScope as DWORD, byval pvReserved1 as LPCVOID, byval pvReserved2 as LPCVOID, byval phContext as LPSCARDCONTEXT) as LONG
'declare function SCardListReadersA(byval hContext as SCARDCONTEXT, byval mszGroups as LPCSTR, byval mszReaders as LPSTR, byval pcchReaders as LPDWORD) as LONG


extern "windows"
    Declare Function SCardEstablishContext Lib "WinScard.dll" ( _
        ByVal dwScope As Long, ByVal pvReserved1 As any ptr, ByVal pvReserved2 As any ptr, ByRef phContext As uLong ptr ) As Long
end extern

extern "windows"
    Declare Function SCardListReadersA Lib "WinScard.dll" ( _
        ByVal hContext As Long, ByVal mzGroup As any ptr, ByVal ReaderList As any ptr, ByRef pcchReaders As uLong ptr) As Long
end Extern

Dim mszGroups As zstring ptr  'LPCSTR
Dim szReaderLists As zstring ptr   'LPSTR
dim bConversion As Boolean
dim lResult As Long
dim hContext As ulong              'SCARDCONTEXT
dim nReaderCount As ulong ptr      'LPDWORD


Const SCARD_SCOPE_USER = 0
Const SCARD_S_SUCCESS = 0

bConversion = False

lResult = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, @hContext)  '<------ this line works fine
If lResult <> SCARD_S_SUCCESS Then
    print "Cannot establish connection"
    print "ReturnCode(ESTACON):  " + Hex(lResult)
End If

dim as ulong x=255

nReaderCount = @x
lResult = SCardListReadersA(hContext, mszGroups, szReaderLists, nReaderCount)' <------ in this line I have the error
    
print "szReaderLists = " & szReaderLists
print "nReaderCount = " & nReaderCount

sleep
My results
Cannot establish connection
ReturnCode(ESTACON): 8010001D
szReaderLists = 0
nReaderCount = 1

using the win\winscard.bi this seems to work:

Code: Select all


'#include "windows.bi"
#inclib "WinScard.dll"
#include "win\winscard.bi"

Dim mszGroups As LPCSTR
Dim szReaderLists As LPSTR
dim bConversion As Boolean
dim lResult As Long
dim hContext As SCARDCONTEXT
dim nReaderCount As LPDWORD


Const SCARD_SCOPE_USER = 0
Const SCARD_S_SUCCESS = 0

bConversion = False

lResult = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, @hContext)  '<------ this line works fine
If lResult <> SCARD_S_SUCCESS Then
    print "Cannot establish connection"
    print "ReturnCode(ESTACON):  " + Hex(lResult)
End If

dim as ulong x=255

nReaderCount = @x
lResult = SCardListReadersA(*nReaderCount, mszGroups, szReaderLists, nReaderCount)' <------ in this line
    
print "szReaderLists = " & szReaderLists
print "nReaderCount = " & *nReaderCount

sleep 
But why the dll wasn't originally loaded in winscard.bi I have no idea.
Maybe in older windows it was automatic, like user32.dll, but I am not sure.
Tolo68
Posts: 105
Joined: Mar 30, 2020 18:18
Location: Spain

Re: Help with WinScard.dll

Post by Tolo68 »

Thank you very much dodicat.

Your code no longer gives me an error, I can compile it. I saw that you have made some modifications to the variables.

The thing that you do not get any device, is because it only lists them on the device on which I run it.

It is as if you wanted to list the audio devices, on a computer that does not have an audio card, which would give you 0.

Greetings and have a nice day.
Tolo68
Posts: 105
Joined: Mar 30, 2020 18:18
Location: Spain

Re: Help with WinScard.dll

Post by Tolo68 »

dodicat wrote: May 31, 2022 8:46
Hello again, the code no longer gives me an error, but the value of szReaderLists is equal to 0

In the VB6 example I see the following.....

Dim szReaderLists As String * 256

The result of szReaderLists is...

Proximity Based PCSC Reader 0

but between Proximity Based PCSC Reader and 0, there are some characters that look like carriage return or similar, I'll copy it and look at it in notepad++, to see if it tells me the characters.

But in the FreeBasic examples I see a String with no size.....

Dim szReaderLists As zstring ptr ' LPSTR

and I don't know if that's why it gives me a value of "0", what appears to be the character after the carriage return.

This value that it has to give me is a String, with the name of the hardware device, for example "Sound Blaster", something similar to what it gives in Device Manager in the Windows Control Panel

The value of nReaderCount if it works fine, it gives me a value of 41 in both VB6 and Freebasic.

Thank you very much in advance.
Greetings.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Help with WinScard.dll

Post by dodicat »

Maybe, as you say, the LPSTR needs some memory.
You could try something like

Code: Select all


#inclib "WinScard.dll"
#include "win\winscard.bi"
dim as zstring *256 tmp
Dim As LPCSTR mszGroups 
Dim As LPSTR szReaderLists=strptr(tmp) 
dim bConversion As Boolean
dim lResult As Long
dim hContext As SCARDCONTEXT
dim nReaderCount As LPDWORD


Const SCARD_SCOPE_USER = 0
Const SCARD_S_SUCCESS = 0

bConversion = False

lResult = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, @hContext)  '<------ this line works fine
If lResult <> SCARD_S_SUCCESS Then
    print "Cannot establish connection"
    print "ReturnCode(ESTACON):  " + Hex(lResult)
End If

dim as ulong x=255

nReaderCount = @x
lResult = SCardListReadersA(*nReaderCount, mszGroups, szReaderLists, nReaderCount)' <------ in this line
    
print "szReaderLists = " & szReaderLists
print "nReaderCount = " & *nReaderCount

sleep  
if strptr fails you could try @tmp or @tmp[0]
Tolo68
Posts: 105
Joined: Mar 30, 2020 18:18
Location: Spain

Re: Help with WinScard.dll

Post by Tolo68 »

Hello everyone.
I still do not read this fact, this is very strange.

With @tmp or @tmp[0] it doesn't give me 0, but it gives me a 6 or 7 digit number, and not a string with the name of the device.

Apart from Visual Basic 6, also try it with a free version of PowerBasic, which you had before it was acquired by another company, and read it. I will look to put the code in the forum.

I'll try it with OxygenBasic and ThinBasic. But I'd rather be able to do it with FreeBasic.

Best regards
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Help with WinScard.dll

Post by dodicat »

It is a pointer remember.
If I make temp="Hello"
then *szReaderLists gives Hello.

Cannot establish connection
ReturnCode(ESTACON): 8010001D
szReaderLists = Hello
nReaderCount = 255

If I got a connection then it should be something different I suppose.
adeyblue
Posts: 299
Joined: Nov 07, 2019 20:08

Re: Help with WinScard.dll

Post by adeyblue »

Not sure why this line was changed at some point but it was for the worse
lResult = SCardListReadersA(*nReaderCount, mszGroups, szReaderLists, nReaderCount)
The original is more like it
SCardListReaders(hContext, mszGroups, szReaderLists, nReaderCount)

Code: Select all

'#include "windows.bi"
#inclib "WinScard.dll"
#include "win\winscard.bi"

Dim mszGroups As LPCSTR
Dim szReaderLists As LPSTR
dim bConversion As Boolean
dim lResult As Long
dim hContext As SCARDCONTEXT

Const SCARD_SCOPE_USER = 0
Const SCARD_S_SUCCESS = 0

bConversion = False

lResult = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, @hContext)  '<------ this line works fine
If lResult <> SCARD_S_SUCCESS Then
    print "Cannot establish connection"
    print "ReturnCode(ESTACON):  " + Hex(lResult)
End If

'' don't bother guessing at how big this buffer needs to be, let Windows figure it out
dim as ulong nReaderCount=SCARD_AUTOALLOCATE

lResult = SCardListReadersA(hContext, mszGroups, CPtr(LPSTR, @szReaderLists), @nReaderCount)' <------ in this line
    
print "szReaderLists = " & szReaderLists
print "nReaderCount = " & *nReaderCount

'' when you've finished with szReaderLists
SCardFreeMemory(szReaderLists)

SCardReleaseContext(hContext)

sleep 
Tolo68
Posts: 105
Joined: Mar 30, 2020 18:18
Location: Spain

Re: Help with WinScard.dll

Post by Tolo68 »

Thank you Adeyblue.

This afternoon I realized this too, comparing the original VB6 code and the FreeBasic code.

I haven't tried it yet, but it could be because of this. And an example that I have in PowerBasic, is also with hcontext, and there if it works.

Thanks for the observation!!! 😀
Best regards
Tolo68
Posts: 105
Joined: Mar 30, 2020 18:18
Location: Spain

Re: Help with WinScard.dll

Post by Tolo68 »

adeyblue wrote: Jun 06, 2022 19:21 Not sure why this line was changed at some point but it was for the worse
lResult = SCardListReadersA(*nReaderCount, mszGroups, szReaderLists, nReaderCount)
The original is more like it
SCardListReaders(hContext, mszGroups, szReaderLists, nReaderCount)

Code: Select all

'#include "windows.bi"
#inclib "WinScard.dll"
#include "win\winscard.bi"

Dim mszGroups As LPCSTR
Dim szReaderLists As LPSTR
dim bConversion As Boolean
dim lResult As Long
dim hContext As SCARDCONTEXT

Const SCARD_SCOPE_USER = 0
Const SCARD_S_SUCCESS = 0

bConversion = False

lResult = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, @hContext)  '<------ this line works fine
If lResult <> SCARD_S_SUCCESS Then
    print "Cannot establish connection"
    print "ReturnCode(ESTACON):  " + Hex(lResult)
End If

'' don't bother guessing at how big this buffer needs to be, let Windows figure it out
dim as ulong nReaderCount=SCARD_AUTOALLOCATE

lResult = SCardListReadersA(hContext, mszGroups, CPtr(LPSTR, @szReaderLists), @nReaderCount)' <------ in this line
    
print "szReaderLists = " & szReaderLists
print "nReaderCount = " & *nReaderCount

'' when you've finished with szReaderLists
SCardFreeMemory(szReaderLists)

SCardReleaseContext(hContext)

sleep 

Yeahhhhhhh!!!!!

AdeyBlue it was for this!!!!!!!!

modify this line by changing nReaderCount

lResult = SCardListReadersA(*nReaderCount, mszGroups, szReaderLists, nReaderCount)

by hContext

lResult = SCardListReadersA(hContext, mszGroups, *szReaderLists, nReaderCount)

I had several days with this error, I was about to make the code with another compiler, less bad than comparing codes, and with your help I was able to solve it.

Then I try your example, with the lines...

SCardFreeMemory(szReaderLists)
SCardReleaseContext(hContext)

Now I have the other steps to read the card data, but without this step I could not continue.

Thank you all!!!!
I think I'm going to go have a beer!!! :D :D :D
Tolo68
Posts: 105
Joined: Mar 30, 2020 18:18
Location: Spain

Re: Help with WinScard.dll

Post by Tolo68 »

Translated text with Google.

Hello again everyone.

I have this new code reduced from the original in VB6, which I have only taken the code that interests me.
But I have a small error in a data, I have thought about it but I don't know how to solve it, in VB6 it works for me.
The code is somewhat long, so that you can better see where the error can come from.
The error line is bold and blue, near the end of the code.

Thank you all very much in advance.
Cheers

Code: Select all

'--------------------------------------------------------------

#inclib "WinScard.dll"
#include "win\winscard.bi"

dim hCard as long
dim lResult As Long
dim hContext As SCARDCONTEXT

Dim ret As Long
Dim pcchReaders As Long 
Dim mszReaders As String
dim MiDispositivo As String
dim szReaderName As String
dim szMessage As String
Const SCARD_SCOPE_USER As Integer = 0

ret = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, @hContext)
ret = SCardListReaders(hContext, 0, mszReaders, @pcchReaders)

mszReaders = String(pcchReaders, 0) ' < ---- pcchReaders = 41

ret = SCardListReaders(hContext, 0, mszReaders, @pcchReaders)
print "Device Name = " & mszReaders  ' < ---- get device name ok

MiDispositivo = mszReaders 

print "press a key...."
sleep

dim lMode As Long
dim lProtocol As Long

Dim lActiveProtocol As Long

lResult = 0

szReaderName = MiDispositivo

lMode = SCARD_SHARE_SHARED '<<<<<<<<<
lProtocol = SCARD_PROTOCOL_T0 Or SCARD_PROTOCOL_T1  '<<<<<<<<<

lResult = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, @hContext)

If lResult = SCARD_S_SUCCESS Then
    lResult = SCardConnect(hContext, szReaderName, lMode, lProtocol, @hCard, @lActiveProtocol)
    print "lActiveProtocol = " & lActiveProtocol ''' = 2 = T=1
End If

Type BytArray
    byBytes(601) As Byte
End Type
'---------------------------------------------
Function DecimalToString(byReadBuffer As BytArray, leng As Long, bSpace As Boolean) As String

    Dim i As Integer
    Dim iTemp As Integer
    Dim szTemp As String
    szTemp = ""
    i = 0
    While i < leng
        iTemp = byReadBuffer.byBytes(i)
        If (iTemp < 16) Then
            szTemp = szTemp + "0"
        End If
        szTemp = szTemp + Hex(iTemp)
        If bSpace Then
            szTemp = szTemp + " "
        End If
        i = i + 1
    Wend
    DecimalToString = szTemp
   
End Function
'---------------------------------------------
Dim lState As Long
Dim bAtr As BytArray
Dim lAtrLen As Long

lAtrLen = 255
Dim szReaderList As String * 255
       
Dim i As Integer
szMessage = szMessage + "ATR: "
i = 0
szMessage = szMessage + DecimalToString(bAtr, lAtrLen, True)
Dim szTemp As String

lProtocol = lActiveProtocol
    
print "press a key...."
sleep
'---------------------------------------------
Private Function StringToBytArray(mycommand As String) As BytArray

    Dim iLen As Integer
    Dim szTemp As String
    Dim btArrTemp As BytArray
    Dim iLower As Integer, iUpper As Integer
    
    szTemp = UCase(mycommand)
    iLen = Len(szTemp)
    If iLen Mod 2 <> 0 Or iLen = 0 Then
        print "Please Check the command that you have entered."
        Exit Function
    End If
    
    Dim i As Integer
    Dim count As Long
    
    i = 1
    count = 0
    While i <= iLen
        iLower = -1
        iUpper = -1
        '''On Error Resume Next
        iUpper = CInt(Mid(szTemp, i, 1))
        If iUpper = -1 Then
            iUpper = Asc(Mid(szTemp, i, 1)) - 55
            If iUpper >= 16 Then
                print "Invalid Charcter entered"
                '''bConversion = False
                Exit Function
            End If
        End If
        '''On Error Resume Next
        iLower = CInt(Mid(szTemp, i + 1, 1))
        If iLower = -1 Then
            iLower = Asc(Mid(szTemp, i + 1, 1)) - 55
            If iLower >= 16 Then
                print "Invalid Charcter entered"
                '''bConversion = False
                Exit Function
            End If
        End If
        i = i + 2
        btArrTemp.byBytes(count) = iUpper * 16 + iLower
        count = count + 1
    Wend
    
    StringToBytArray = btArrTemp
    
End Function
'---------------------------------------------

Dim bytCommand As BytArray
Dim byReadBuffer As BytArray
Dim lLen As Long
Dim iReturnlength As Long
bytCommand = StringToBytArray("AA0F")

' -----Error in this line
[b][color=#0000FF]lResult = SCardTransmit(hCard, 0, bytCommand, lLen, 0, byReadBuffer, iReturnlength)[/color][/b]
'---        Type mismatch, at parameter 3 of SCARDTRANSMIT() 
'---        in 'lResult = SCardTransmit(hCard, 0, bytCommand, lLen, 0, byReadBuffer, iReturnlength)'
' ---- Error End

If lResult <> SCARD_S_SUCCESS Then
    print "Transmit function failed"
    print "ReturnCode(transmit):  " + Hex(lResult)
End If

print DecimalToString(byReadBuffer, iReturnlength, True)

print "press a key...."
sleep
Last edited by Tolo68 on Jun 23, 2022 16:07, edited 2 times in total.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Help with WinScard.dll

Post by dodicat »

I reckon it should be this:
lResult = SCardTransmit(hCard, 0, @bytCommand.byBytes(0), lLen, 0, @byReadBuffer.byBytes(0), @iReturnlength)
To get the fb data types from the .bi file you can use something like

Code: Select all

#inclib "WinScard.dll"
#include "win\winscard.bi"
#print typeof(LPCBYTE)
#print typeof(LPDWORD) 
where the information is written to the compiler output or log.
and the pointer to an array is @array(lbound(array)), and since you are using zero based,@array(0).
Tolo68
Posts: 105
Joined: Mar 30, 2020 18:18
Location: Spain

Re: Help with WinScard.dll

Post by Tolo68 »

Hello dodicat!!

Thank you very much for the answer, I no longer get the error.

But I do not read well the buffer of the array, even change byte by ubyte in the array, since in vb6 byte it is from 0 to 255, and in freebasic byte it is from - 127 to 127

Reading the buffer gives me an error, which looking through google I have seen, which is a card reading failure.

But I found another VB code, which has a much simpler way of writing and reading the buffer, and it works, so I'll convert it to freebasic, to see if it goes well.

The truth is that I think it has been one of the codes, which has given me the most headache, and also the information on this subject is very scarce.

Thanks again.

Greetings to all, and have a good Sunday!!!
Post Reply