访问 VBA 和智能卡 - 有啥诀窍?

Posted

技术标签:

【中文标题】访问 VBA 和智能卡 - 有啥诀窍?【英文标题】:Access VBA and Smart Cards - what's the trick?访问 VBA 和智能卡 - 有什么诀窍? 【发布时间】:2021-04-21 15:39:28 【问题描述】:

我正在尝试使用 Windows 10/Access 2016 和 VBA 使用智能卡,并且我遇到了几乎所有可能找到的有关如何实现此目的的示例,包括:

https://www.utteraccess.com/forum/Acccess-Card-Readers-t1986575.html Getting started with smartcard & ISO 7816 in excel vba ( SCardEstablishContext ) https://docs.microsoft.com/en-us/windows/win32/api/winscard/(供函数参考),以及 Authentication Return Values 和 System Error Codes 只是为了弄清楚发生了什么。

我遇到的问题是 SCardEstablishContext 正在返回 0x0 - SCARD_S_SUCCESS,但是当我将 SCARDCONTEXT 传递给 SCardIsValidContext 时,它正在返回 0x6 - ERROR_INVALID_HANDLE

这是我正在使用的代码的相关部分(忽略 AuthDictSCardAuthCode,因为它们只是用于解码其他函数返回的任何状态的辅助函数)

Public AuthDict As Scripting.Dictionary
Public Const SCARD_SCOPE_USER As Long = &H0
Public Const SCARD_SCOPE_SYSTEM As Long = &H2
Public Const SCARD_SHARE_SHARED As Long = &H2
Public Const SCARD_SHARE_EXCLUSIVE As Long = &H1
Public Const SCARD_SHARE_DIRECT As Long = &H3
Public Const SCARD_PROTOCOL_T0 As Long = &H1
Public Const SCARD_PROTOCOL_T1 As Long = &H2
Public Const SCARD_DEFAULT_READERS As String = "SCard$DefaultReaders\000"
Public Const SCARD_ALL_READERS As String = "SCard$AllReaders\000"

Public Type SCARDCONTEXT
    CardContext1 As Long
    ReaderName As String
End Type

Public Declare PtrSafe Function SCardEstablishContext Lib "winscard.dll" ( _
    ByVal dwScope As Long, _
    ByVal pvReserved1 As Long, _
    ByVal pvReserved2 As Long, _
    ByRef phContext As SCARDCONTEXT _
    ) As Long

Public Declare PtrSafe Function SCardIsValidContext Lib "winscard.dll" ( _
    ByRef hContext As SCARDCONTEXT _
) As Long

Public Sub GetContext()
    Dim lreturn As Long
    Dim RSVD1 As Long, RSVD2 As Long
    Dim myContext As SCARDCONTEXT
    Set AuthDict = New Scripting.Dictionary

    Debug.Print "-----------------------------------------------------------------------------"
    lreturn = SCardEstablishContext(SCARD_SCOPE_USER, RSVD1, RSVD2, myContext)
    SCardAuthCode lreturn
    Debug.Print "SCardEstablishContext:" & vbCrLf & _
                "   Return = " & AuthDict("hr") & vbCrLf & _
                "   Value = " & AuthDict("hc") & vbCrLf & _
                "   Description = " & AuthDict("hd") & vbCrLf & _
                "   myContext.CardContext1 = " & myContext.CardContext1 & vbCrLf & _
                "   myContext.ReaderName = " & Chr(34) & myContext.ReaderName & Chr(34) & vbCrLf
    lreturn = SCardIsValidContext(myContext)
    SCardAuthCode lreturn
    Debug.Print "SCardIsValidContext:" & vbCrLf & _
                "   Return = " & AuthDict("hr") & vbCrLf & _
                "   Value = " & AuthDict("hc") & vbCrLf & _
                "   Description = " & AuthDict("hd") & vbCrLf
    If lreturn <> 0 Then GoTo GetContextExit
GetContextExit:
    Debug.Print "-----------------------------------------------------------------------------" & vbCrLf
End Sub

运行 Sub,输出如下:

-----------------------------------------------------------------------------
SCardEstablishContext:
   Return = 0x00000000
   Value = SCARD_S_SUCCESS
   Description = No error was encountered.
   myContext.CardContext1 = -855572480
   myContext.ReaderName = ""

SCardIsValidContext:
   Return = 0x00000006
   Value = ERROR_INVALID_HANDLE
   Description = The handle is invalid.

-----------------------------------------------------------------------------

我似乎没有正确设置myContext,但我不知道它应该是什么样子。

另外,如果你想要漂亮的回报,这里是SCardAuthCode 的代码:

'https://docs.microsoft.com/en-us/windows/win32/secauthn/authentication-return-values?redirectedfrom=MSDN#smart_card_return_values
'https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-?redirectedfrom=MSDN
Public Function SCardAuthCode(lreturn As Long)
    AuthDict.RemoveAll
    Dim hreturn As String, hc As String, hd As String
    hreturn = "0x" & Right("0000000" & Hex(lreturn), 8)
    Select Case hreturn
        Case "0x00000000": hc = "SCARD_S_SUCCESS": hd = "No error was encountered."
        Case "0x00000006": hc = "ERROR_INVALID_HANDLE": hd = "The handle is invalid."
        Case "0x00000109": hc = "ERROR_BROKEN_PIPE": hd = "The client attempted a smart card operation in a remote session, such as a client session running on a terminal server, and the operating system in use does not support smart card redirection."
        Case "0x80100001": hc = "SCARD_F_INTERNAL_ERROR": hd = "An internal consistency check failed."
        Case "0x80100002": hc = "SCARD_E_CANCELLED": hd = "The action was canceled by an SCardCancel request."
        Case "0x80100003": hc = "SCARD_E_INVALID_HANDLE": hd = "The supplied handle was not valid."
        Case "0x80100004": hc = "SCARD_E_INVALID_PARAMETER": hd = "One or more of the supplied parameters could not be properly interpreted."
        Case "0x80100005": hc = "SCARD_E_INVALID_TARGET": hd = "Registry startup information is missing or not valid."
        Case "0x80100006": hc = "SCARD_E_NO_MEMORY": hd = "Not enough memory available to complete this command."
        Case "0x80100007": hc = "SCARD_F_WAITED_TOO_LONG": hd = "An internal consistency timer has expired."
        Case "0x80100008": hc = "SCARD_E_INSUFFICIENT_BUFFER": hd = "The data buffer for returned data is too small for the returned data."
        Case "0x80100009": hc = "SCARD_E_UNKNOWN_READER": hd = "The specified reader name is not recognized."
        Case "0x8010000A": hc = "SCARD_E_TIMEOUT": hd = "The user-specified time-out value has expired."
        Case "0x8010000B": hc = "SCARD_E_SHARING_VIOLATION": hd = "The smart card cannot be accessed because of other outstanding connections."
        Case "0x8010000C": hc = "SCARD_E_NO_SMARTCARD": hd = "The operation requires a smart card, but no smart card is currently in the device."
        Case "0x8010000D": hc = "SCARD_E_UNKNOWN_CARD": hd = "The specified smart card name is not recognized."
        Case "0x8010000E": hc = "SCARD_E_CANT_DISPOSE": hd = "The system could not dispose of the media in the requested manner."
        Case "0x8010000F": hc = "SCARD_E_PROTO_MISMATCH": hd = "The requested protocols are incompatible with the protocol currently in use with the card."
        Case "0x80100010": hc = "SCARD_E_NOT_READY": hd = "The reader or card is not ready to accept commands."
        Case "0x80100011": hc = "SCARD_E_INVALID_VALUE": hd = "One or more of the supplied parameter values could not be properly interpreted."
        Case "0x80100012": hc = "SCARD_E_SYSTEM_CANCELLED": hd = "The action was canceled by the system, presumably to log off or shut down."
        Case "0x80100013": hc = "SCARD_F_COMM_ERROR": hd = "An internal communications error has been detected."
        Case "0x80100014": hc = "SCARD_F_UNKNOWN_ERROR": hd = "An internal error has been detected, but the source is unknown."
        Case "0x80100015": hc = "SCARD_E_INVALID_ATR": hd = "An ATR string obtained from the registry is not a valid ATR string."
        Case "0x80100016": hc = "SCARD_E_NOT_TRANSACTED": hd = "An attempt was made to end a nonexistent transaction."
        Case "0x80100017": hc = "SCARD_E_READER_UNAVAILABLE": hd = "The specified reader is not currently available for use."
        Case "0x80100018": hc = "SCARD_P_SHUTDOWN": hd = "The operation has been aborted to allow the server application to exit."
        Case "0x80100019": hc = "SCARD_E_PCI_TOO_SMALL": hd = "The PCI receive buffer was too small."
        Case "0x8010001A": hc = "SCARD_E_READER_UNSUPPORTED": hd = "The reader driver does not meet minimal requirements for support."
        Case "0x8010001B": hc = "SCARD_E_DUPLICATE_READER": hd = "The reader driver did not produce a unique reader name."
        Case "0x8010001C": hc = "SCARD_E_CARD_UNSUPPORTED": hd = "The smart card does not meet minimal requirements for support."
        Case "0x8010001D": hc = "SCARD_E_NO_SERVICE": hd = "The smart card resource manager is not running."
        Case "0x8010001E": hc = "SCARD_E_SERVICE_STOPPED": hd = "The smart card resource manager has shut down."
        Case "0x8010001F": hc = "SCARD_E_UNEXPECTED": hd = "An unexpected card error has occurred."
        Case "0x80100020": hc = "SCARD_E_ICC_INSTALLATION": hd = "No primary provider can be found for the smart card."
        Case "0x80100021": hc = "SCARD_E_ICC_CREATEORDER": hd = "The requested order of object creation is not supported."
        Case "0x80100022": hc = "SCARD_E_UNSUPPORTED_FEATURE": hd = "This smart card does not support the requested feature."
        Case "0x80100023": hc = "SCARD_E_DIR_NOT_FOUND": hd = "The specified directory does not exist in the smart card."
        Case "0x80100024": hc = "SCARD_E_FILE_NOT_FOUND": hd = "The specified file does not exist in the smart card."
        Case "0x80100025": hc = "SCARD_E_NO_DIR": hd = "The supplied path does not represent a smart card directory."
        Case "0x80100026": hc = "SCARD_E_NO_FILE": hd = "The supplied path does not represent a smart card file."
        Case "0x80100027": hc = "SCARD_E_NO_ACCESS": hd = "Access is denied to the file."
        Case "0x80100028": hc = "SCARD_E_WRITE_TOO_MANY": hd = "An attempt was made to write more data than would fit in the target object."
        Case "0x80100029": hc = "SCARD_E_BAD_SEEK": hd = "An error occurred in setting the smart card file object pointer."
        Case "0x8010002A": hc = "SCARD_E_INVALID_CHV": hd = "The supplied PIN is incorrect."
        Case "0x8010002B": hc = "SCARD_E_UNKNOWN_RES_MNG": hd = "An unrecognized error code was returned."
        Case "0x8010002C": hc = "SCARD_E_NO_SUCH_CERTIFICATE": hd = "The requested certificate does not exist."
        Case "0x8010002D": hc = "SCARD_E_CERTIFICATE_UNAVAILABLE": hd = "The requested certificate could not be obtained."
        Case "0x8010002E": hc = "SCARD_E_NO_READERS_AVAILABLE": hd = "No smart card reader is available."
        Case "0x8010002F": hc = "SCARD_E_COMM_DATA_LOST": hd = "A communications error with the smart card has been detected."
        Case "0x80100030": hc = "SCARD_E_NO_KEY_CONTAINER": hd = "The requested key container does not exist on the smart card."
        Case "0x80100031": hc = "SCARD_E_SERVER_TOO_BUSY": hd = "The smart card resource manager is too busy to complete this operation."
        Case "0x80100032": hc = "SCARD_E_PIN_CACHE_EXPIRED": hd = "The smart card PIN cache has expired."
        Case "0x80100033": hc = "SCARD_E_NO_PIN_CACHE": hd = "The smart card PIN cannot be cached."
        Case "0x80100034": hc = "SCARD_E_READ_ONLY_CARD": hd = "The smart card is read-only and cannot be written to."
        Case "0x80100065": hc = "SCARD_W_UNSUPPORTED_CARD": hd = "The reader cannot communicate with the card, due to ATR string configuration conflicts."
        Case "0x80100066": hc = "SCARD_W_UNRESPONSIVE_CARD": hd = "The smart card is not responding to a reset."
        Case "0x80100067": hc = "SCARD_W_UNPOWERED_CARD": hd = "Power has been removed from the smart card, so that further communication is not possible."
        Case "0x80100068": hc = "SCARD_W_RESET_CARD": hd = "The smart card was reset."
        Case "0x80100069": hc = "SCARD_W_REMOVED_CARD": hd = "The smart card has been removed, so further communication is not possible."
        Case "0x8010006A": hc = "SCARD_W_SECURITY_VIOLATION": hd = "Access was denied because of a security violation."
        Case "0x8010006B": hc = "SCARD_W_WRONG_CHV": hd = "The card cannot be accessed because the wrong PIN was presented."
        Case "0x8010006C": hc = "SCARD_W_CHV_BLOCKED": hd = "The card cannot be accessed because the maximum number of PIN entry attempts has been reached."
        Case "0x8010006D": hc = "SCARD_W_EOF": hd = "The end of the smart card file has been reached."
        Case "0x8010006E": hc = "SCARD_W_CANCELLED_BY_USER": hd = "The action was canceled by the user."
        Case "0x8010006F": hc = "SCARD_W_CARD_NOT_AUTHENTICATED": hd = "No PIN was presented to the smart card."
        Case "0x80100070": hc = "SCARD_W_CACHE_ITEM_NOT_FOUND": hd = "The requested item could not be found in the cache."
        Case "0x80100071": hc = "SCARD_W_CACHE_ITEM_STALE": hd = "The requested cache item is too old and was deleted from the cache."
        Case "0x80100072": hc = "SCARD_W_CACHE_ITEM_TOO_BIG": hd = "The new cache item exceeds the maximum per-item size defined for the cache."
        Case Else: hc = "UNKNOWN VALUE": hd = "Unknown value."
    End Select
    AuthDict.Add "hr", hreturn
    AuthDict.Add "hc", hc
    AuthDict.Add "hd", hd
End Function

【问题讨论】:

请不要标记与您的问题无关的语言 @AlanBirtles 我的错,我认为这有点有效,因为所有有关 winscard.h 的 MSDN 文档都使用 C++ 提及您正在使用的 Access 版本 - 以及您希望在其上运行的最低 Windows 版本。您是否需要在 Windows 10 之前的设备上运行。例如 Windows 7。即使您可能使用的是最新版本的 Access - 您可能希望它在较早的 Access 最低版本中运行,请提供该最低版本。跨度> @SqlSurfer 明白了。 Win 10/Access 2016。我的开发环境被很好地锁定了。 可能是 32 位与 64 位的问题,请参阅 docs.microsoft.com/en-us/office/vba/language/concepts/… 【参考方案1】:

SCARDCONTEXT 不是类型,而是句柄。我不知道那种类型是从哪里来的。

为该句柄使用LongPtr

SCardEstablishContext使用指向句柄的指针,所以ByRef

Public Declare PtrSafe Function SCardEstablishContext Lib "winscard.dll" ( _
    ByVal dwScope As Long, _
    ByVal pvReserved1 As LongPtr, _
    ByVal pvReserved2 As LongPtr, _
    ByRef phContext As LongPtr_
    ) As Long

注意pvReserveds 也是指针,所以LongPtr,而不是Long。只有在彻底检查了哪个Longs 实际上应该是LongPtr 之后,您才应该添加PtrSafe

而对于SCardIsValidContext,句柄是直接传递的,所以ByVal

Public Declare PtrSafe Function SCardIsValidContext Lib "winscard.dll" ( _
    ByVal hContext As LongPtr _
) As Long

然后,相应地调整您的代码,这应该是微不足道的。

当然,完成后不要忘记SCardReleaseContext,如果你打开上下文并且从不关闭它们,Windows 会变得很麻烦。

【讨论】:

您,先生,就是我一直在寻找的罗塞塔石碑。这是一个非常简单的修复,但这是我一直没有得到的一点信息。谢谢!

以上是关于访问 VBA 和智能卡 - 有啥诀窍?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过VBA覆盖获得智能感知

智能卡读卡器有啥实现方式吗?

在智能卡上下载 HCI 小程序有啥问题?

“智能客户端应用程序”和“富互联网应用程序”有啥区别?

联想 啥是智能卡,有啥用?

《编程集体智能》中的皮尔逊算法有啥问题?