NFCカードのUIDを取得したい
自分の夢に近づくためのステップの一つとして、PCからFeliCa、Mifare等のNFCカードに読み書きする技術の習得があります。
最低限、UIDが取得できれば、あとはアプリケーション側で時間情報や場所情報を付加し、データベースに読み書きすることで、かなりのことができると考えています。
NFCカードに読み書きするために、PC/SC(Personal Computer/Smart Card)というインタフェースがあります。PC/SCを使えばSDKを購入する必要がないようです。
C言語などでは、PC/SCのソースコードが出回っているようです。でも、あいにく僕には理解できません。
僕が使えるのはExcelのVBAとVisual Basicです。
ググってみると、こちらの記事でVisual Basicのコードを見つけました。でも、そのまま使っても、うまく動きませんでした。
そこで、コードを自分流に書き換えながら、動かないところに手を入れてみたところ、UIDが取れるようになりました。
ちなみに、開発環境はVisual Basic 2019です。
Visual Basic 2019で作成したコードと使用手順
使用手順は次の通りです。コードは使用手順の下に示します。
- カードリーダのドライバをインストールします
- カードリーダをつなぎます
- 「PC/SC」が有効になるようにドライバを設定します
- フォームにボタンを一つ置いて、Form1.vbのコードをコピペします
- モジュールを1つ追加して、Module1.vbをコピペします
(ドキュメントコメントのタグは、WordPressのタグと干渉してしまうようなので、丸カッコにしました。ご了承ください) - デバッグモードで実行します。
- ボタンをクリックしてからカードをかざすと、出力ウィンドウにUIDが表示されます。
動作が確認出来たら、UIDをテキストボックスに表示させるとか、無限ループを作って次々とUIDを取得できるようにするとかして、改造してください。
Form1.vb
Imports System.Runtime.InteropServices
Public Class Form1
''' --------------------------------------------------------------------
''' (summary) Button1をクリックしたときの処理 (/summary)
''' --------------------------------------------------------------------
Private Sub Button1_Click(sender As Object, e As EventArgs) _
Handles Button1.Click
' --- リソースマネージャコンテキストを確立する ---
Dim hContext As IntPtr ' コンテキストを識別するハンドル
Dim URet As UInteger = SCardEstablishContext(
SCARD_SCOPE_USER, IntPtr.Zero, IntPtr.Zero, hContext)
If URet <> SCARD_S_SUCCESS Then GoTo ErrorHandler
' --- カードリーダのリストを取得する ---
' バッファ長を取得する
Dim pcchReaders As UInteger ' mszReadersバッファ長
URet = SCardListReaders(hContext, Nothing, Nothing, pcchReaders)
If URet <> SCARD_S_SUCCESS Then GoTo ErrorHandler
' Byteの配列でバッファを用意し、リストを取得する
Dim mszReaders As Byte() =
New Byte(Convert.ToInt32(pcchReaders) * 2 - 1) {}
URet = SCardListReaders(hContext, Nothing, mszReaders, pcchReaders)
If URet <> SCARD_S_SUCCESS Then GoTo ErrorHandler
' Byte配列を文字に変換して1つ目のリーダ名を取得する
Dim szReader As String =
System.Text.Encoding.ASCII.GetString(mszReaders).
Split(vbNullChar.ToCharArray)(0)
' --- リーダにカードがかざされるのを待つ ---
Dim dwTimeout As Integer = System.Threading.Timeout.Infinite
Dim rgReaderStates(0) As SCARD_READERSTATE ' 監視対象リーダ用構造体
rgReaderStates(0).szReader = szReader ' 監視対象のリーダ名
' すぐに初回のレポートを受信する設定
rgReaderStates(0).dwCurrentState = SCARD_STATE_UNAWARE
' リーダにカードがかざされるまで待機
Do
' 監視する
URet = SCardGetStatusChange(hContext, dwTimeout,
rgReaderStates(0), rgReaderStates.Count)
If URet <> SCARD_S_SUCCESS Then GoTo ErrorHandler
' カードが確認されたらループを抜ける
If (rgReaderStates(0).dwEventState And
SCARD_STATE_PRESENT) <> 0 Then Exit Do
Loop
' --- アプリケーションとカードの接続を確立する ---
Dim hCard As IntPtr
Dim pdwActiveProtocol As IntPtr = IntPtr.Zero
URet = SCardConnect(hContext, szReader,
SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 Or SCARD_PROTOCOL_T1,
hCard, pdwActiveProtocol)
If URet <> SCARD_S_SUCCESS Then GoTo ErrorHandler
' --- サービスリクエストを送信しデータバックを受信する ---
' 命令プロトコルヘッダへのポインタを作成する
Dim pioSendPci As IntPtr ' ヘッダへのポインタ
' winscard.dllのハンドラを取得する
Dim hModule As IntPtr = LoadLibrary("winscard.dll")
' プロトコルに応じて分岐
If pdwActiveProtocol = SCARD_PROTOCOL_T0 Then
' 調歩式半二重キャラクタ伝送プロトコルの場合
pioSendPci = GetProcAddress(hModule, "g_rgSCardT0Pci")
ElseIf pdwActiveProtocol = SCARD_PROTOCOL_T1 Then
' 調歩式半二重ブロック伝送プロトコルの場合
pioSendPci = GetProcAddress(hModule, "g_rgSCardT1Pci")
End If
' DLLモジュールを解放する
FreeLibrary(hModule)
' カードUIDの取得コマンド
Dim pbSendBuffer As Byte() = New Byte() {&HFF, &HCA, &H0, &H0, &H0}
' 受信プロトコルヘッダのポインタ
Dim pioRecvRequest As SCARD_IO_REQUEST = Nothing
' 返信データ用バッファ
Dim pbrecvBuffer As Byte() = New Byte(511) {}
' 返信データ用バッファサイズ
Dim pcbRecvLength As Integer = pbrecvBuffer.Length
' サービスリクエストを送信しデータバックを受信する
URet = SCardTransmit(
hCard, pioSendPci, pbSendBuffer, pbSendBuffer.Length,
pioRecvRequest, pbrecvBuffer, pcbRecvLength)
If URet <> SCARD_S_SUCCESS Then GoTo ErrorHandler
' 結果出力
Console.WriteLine(BitConverter.ToString(pbrecvBuffer, 0, pcbRecvLength - 2))
' --- 接続を終了する ---
URet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)
If URet <> SCARD_S_SUCCESS Then GoTo ErrorHandler
Exit Sub
ErrorHandler:
' エラーの場合の処理
Throw New Exception($"{URet}:{GetErrMsg(URet)}")
End Sub
End Class
Module1vb
Imports System.Runtime.InteropServices
Module Module1
' ───────────────────────────────────
' 定数の宣言
' ───────────────────────────────────
''' --------------------------------------------------------------------
''' (summary) 定数 (/summary)
''' --------------------------------------------------------------------
Public Const SCARD_S_SUCCESS As Integer = 0
Public Const SCARD_F_INTERNAL_ERROR As Integer = &H80100001
Public Const SCARD_E_CANCELLED As Integer = &H80100002
Public Const SCARD_E_INVALID_HANDLE As Integer = &H80100003
Public Const SCARD_E_INVALID_PARAMETER As Integer = &H80100004
Public Const SCARD_E_INVALID_TARGET As Integer = &H80100005
Public Const SCARD_E_NO_MEMORY As Integer = &H80100006
Public Const SCARD_F_WAITED_TOO_LONG As Integer = &H80100007
Public Const SCARD_E_INSUFFICIENT_BUFFER As Integer = &H80100008
Public Const SCARD_E_UNKNOWN_READER As Integer = &H80100009
Public Const SCARD_E_TIMEOUT As Integer = &H8010000A
Public Const SCARD_E_SHARING_VIOLATION As Integer = &H8010000B
Public Const SCARD_E_NO_SMARTCARD As Integer = &H8010000C
Public Const SCARD_E_UNKNOWN_CARD As Integer = &H8010000D
Public Const SCARD_E_CANT_DISPOSE As Integer = &H8010000E
Public Const SCARD_E_PROTO_MISMATCH As Integer = &H8010000F
Public Const SCARD_E_NOT_READY As Integer = &H80100010
Public Const SCARD_E_INVALID_VALUE As Integer = &H80100011
Public Const SCARD_E_SYSTEM_CANCELLED As Integer = &H80100012
Public Const SCARD_E_COMM_ERROR As Integer = &H80100013
Public Const SCARD_F_UNKNOWN_ERROR As Integer = &H80100014
Public Const SCARD_E_INVALID_ATR As Integer = &H80100015
Public Const SCARD_E_NOT_TRANSACTED As Integer = &H80100016
Public Const SCARD_E_READER_UNAVAILABLE As Integer = &H80100017
Public Const SCARD_P_SHUTDOWN As Integer = &H80100018
Public Const SCARD_E_PCI_TOO_SMALL As Integer = &H80100019
Public Const SCARD_E_READER_UNSUPPORTED As Integer = &H8010001A
Public Const SCARD_E_DUPLICATE_READER As Integer = &H8010001B
Public Const SCARD_E_CARD_UNSUPPORTED As Integer = &H8010001C
Public Const SCARD_E_NO_SERVICE As Integer = &H8010001D
Public Const SCARD_E_SERVICE_STOPPED As Integer = &H8010001E
Public Const SCARD_E_UNEXPECTED As Integer = &H8010001F
Public Const SCARD_E_ICC_INSTALLATION As Integer = &H80100020
Public Const SCARD_E_ICC_CREATEORDER As Integer = &H80100021
Public Const SCARD_E_UNSUPPORTED_FEATURE As Integer = &H80100022
Public Const SCARD_E_DIR_NOT_FOUND As Integer = &H80100023
Public Const SCARD_E_FILE_NOT_FOUND As Integer = &H80100024
Public Const SCARD_E_NO_DIR As Integer = &H80100025
Public Const SCARD_E_NO_FILE As Integer = &H80100026
Public Const SCARD_E_NO_ACCESS As Integer = &H80100027
Public Const SCARD_E_WRITE_TOO_MANY As Integer = &H80100028
Public Const SCARD_E_BAD_SEEK As Integer = &H80100029
Public Const SCARD_E_INVALID_CHV As Integer = &H8010002A
Public Const SCARD_E_UNKNOWN_RES_MNG As Integer = &H8010002B
Public Const SCARD_E_NO_SUCH_CERTIFICATE As Integer = &H8010002C
Public Const SCARD_E_CERTIFICATE_UNAVAILABLE As Integer = &H8010002D
Public Const SCARD_E_NO_READERS_AVAILABLE As Integer = &H8010002E
Public Const SCARD_E_COMM_DATA_LOST As Integer = &H8010002F
Public Const SCARD_E_NO_KEY_CONTAINER As Integer = &H80100030
Public Const SCARD_E_SERVER_TOO_BUSY As Integer = &H80100031
Public Const SCARD_E_PIN_CACHE_EXPIRED As Integer = &H80100032
Public Const SCARD_E_NO_PIN_CACHE As Integer = &H80100033
Public Const SCARD_E_READ_ONLY_CARD As Integer = &H80100034
Public Const SCARD_W_UNSUPPORTED_CARD As Integer = &H80100065
Public Const SCARD_W_UNRESPONSIVE_CARD As Integer = &H80100066
Public Const SCARD_W_UNPOWERED_CARD As Integer = &H80100067
Public Const SCARD_W_RESET_CARD As Integer = &H80100068
Public Const SCARD_W_REMOVED_CARD As Integer = &H80100069
Public Const SCARD_W_SECURITY_VIOLATION As Integer = &H8010006A
Public Const SCARD_W_WRONG_CHV As Integer = &H8010006B
Public Const SCARD_W_CHV_BLOCKED As Integer = &H8010006C
Public Const SCARD_W_EOF As Integer = &H8010006D
Public Const SCARD_W_CANCELLED_BY_USER As Integer = &H8010006E
Public Const SCARD_W_CARD_NOT_AUTHENTICATED As Integer = &H8010006F
Public Const SCARD_W_CACHE_ITEM_NOT_FOUND As Integer = &H80100070
Public Const SCARD_W_CACHE_ITEM_STALE As Integer = &H80100071
Public Const SCARD_W_CACHE_ITEM_TOO_BIG As Integer = &H80100072
Public Const SCARD_PROTOCOL_T0 As Integer = 1
Public Const SCARD_PROTOCOL_T1 As Integer = 2
Public Const SCARD_PROTOCOL_RAW As Integer = 4
Public Const SCARD_SCOPE_USER As UInteger = 0
Public Const SCARD_SCOPE_TERMINAL As UInteger = 1
Public Const SCARD_SCOPE_SYSTEM As UInteger = 2
Public Const SCARD_STATE_UNAWARE As Integer = &H0
Public Const SCARD_STATE_IGNORE As Integer = &H1
Public Const SCARD_STATE_CHANGED As Integer = &H2
Public Const SCARD_STATE_UNKNOWN As Integer = &H4
Public Const SCARD_STATE_UNAVAILABLE As Integer = &H8
Public Const SCARD_STATE_EMPTY As Integer = &H10
Public Const SCARD_STATE_PRESENT As Integer = &H20
Public Const SCARD_STATE_ATRMATCH As Integer = &H40
Public Const SCARD_STATE_EXCLUSIVE As Integer = &H80
Public Const SCARD_STATE_INUSE As Integer = &H100
Public Const SCARD_STATE_MUTE As Integer = &H200
Public Const SCARD_STATE_UNPOWERED As Integer = &H400
Public Const SCARD_SHARE_EXCLUSIVE As Integer = &H1
Public Const SCARD_SHARE_SHARED As Integer = &H2
Public Const SCARD_SHARE_DIRECT As Integer = &H3
Public Const SCARD_LEAVE_CARD As Integer = 0
Public Const SCARD_RESET_CARD As Integer = 1
Public Const SCARD_UNPOWER_CARD As Integer = 2
Public Const SCARD_EJECT_CARD As Integer = 3
' ───────────────────────────────────
' 構造体の宣言
' ───────────────────────────────────
''' --------------------------------------------------------------------
''' (summary) スマートカードトラッキング用構造体 (/summary)
''' --------------------------------------------------------------------
Public Structure SCARD_READERSTATE
Friend szReader As String ' モニタしているリーダへのポインタ
Friend pvUserData As IntPtr ' 不使用
Friend dwCurrentState As UInt32 ' アプリケーションから見た状態
Friend dwEventState As UInt32 ' リソースマネージャから見た状態
Friend cbAtr As UInt32 ' ATRのバイト数
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=36)>' 配列サイズの固定
Friend rgbAtr() As Byte ' カードのATR
End Structure
' ───────────────────────────────────
' クラスの宣言
' ───────────────────────────────────
''' --------------------------------------------------------------------
''' (summary) プロトコル制御情報クラス (/summary)
''' --------------------------------------------------------------------
Public Class SCARD_IO_REQUEST
Friend dwProtocol As UInteger ' 使用中のプロトコル
Friend cbPciLength As Integer ' クラスのサイズとPCI固有の情報
End Class
' ───────────────────────────────────
' 変数の宣言
' ───────────────────────────────────
' ───────────────────────────────────
' API関数の宣言
' ───────────────────────────────────
''' --------------------------------------------------------------------
''' (summary) リソースマネージャコンテキストを確立する (/summary)
''' (param name="dwScope"> コンテキストのスコープ (/param)
''' (param name="pvReserved1"> 予約変数。NULLにする (/param)
''' (param name="pvReserved2"> 予約変数。NULLにする (/param)
''' (param name="phContext"> コンテキストへのハンドル (/param)
''' (returns) エラーコード (/returns)
''' --------------------------------------------------------------------
<DllImport("winscard.dll")>
Public Function SCardEstablishContext(
ByVal dwScope As UInteger,
ByVal pvReserved1 As IntPtr,
ByVal pvReserved2 As IntPtr,
ByRef phContext As IntPtr) _
As UInteger
End Function
''' --------------------------------------------------------------------
''' (summary) リーダーグループ中のリーダリストを提供する (/summary)
''' (param name="hContext"> コンテキストを識別するハンドル (/param)
''' (param name="mszGroups"> リーダーグループの名前 (/param)
''' (param name="mszReaders"> カードリーダー一覧 (/param)
''' (param name="pcchReaders"> mszReadersバッファ長 (/param)
''' (returns) エラーコード (/returns)
''' --------------------------------------------------------------------
<DllImport("winscard.dll")>
Public Function SCardListReaders(
ByVal hContext As IntPtr,
ByVal mszGroups As Byte(),
ByVal mszReaders As Byte(),
ByRef pcchReaders As UInt32) _
As UInteger
End Function
''' --------------------------------------------------------------------
''' (summary) リーダが特定の状態になるまで実行を待機する (/summary)
''' (param name="hContext"> コンテキストを識別するハンドル(/param)
''' (param name="dwTimeout"> 待ち時間[ms]。INFINITEなら∞ (/param)
''' (param name="rgReaderStates"> 監視するリーダ構造体の配列 (/param)
''' (param name="cReaders"> rgReaderStatesの配列サイズ (/param)
''' (returns) エラーコード (/returns)
''' --------------------------------------------------------------------
<DllImport("winscard.dll")>
Public Function SCardGetStatusChange(
ByVal hContext As IntPtr,
ByVal dwTimeout As Integer,
ByRef rgReaderStates As SCARD_READERSTATE,
ByVal cReaders As Integer) _
As UInteger
End Function
''' --------------------------------------------------------------------
''' (summary) アプリケーションとカードの接続を確立する (/summary)
''' (param name="hContext"> コンテキスト識別ハンドル(/param)
''' (param name="szReader"> リーダ名 (/param)
''' (param name="dwShareMode"> 他アプリ排除フラグ (/param)
''' (param name="dwPreferredProtocols"> 接続プロトコル (/param)
''' (param name="phCard"> カードへのハンドル (/param)
''' (param name="pdwActiveProtocol"> 確立されたプロトコル (/param)
''' (returns) エラーコード (/returns)
''' --------------------------------------------------------------------
<DllImport("winscard.dll")>
Public Function SCardConnect(
ByVal hContext As IntPtr,
ByVal szReader As String,
ByVal dwShareMode As UInteger,
ByVal dwPreferredProtocols As UInteger,
ByRef phCard As IntPtr,
ByRef pdwActiveProtocol As IntPtr) _
As UInteger
End Function
''' --------------------------------------------------------------------
''' (summary) リーダの直接制御 (/summary)
''' (param name="hCard">SCardConnectから返される参照値 (/param)
''' (param name="dwControlCode"> 操作の制御コード (/param)
''' (param name="lpInBuffer"> データバッファへのポインタ (/param)
''' (param name="cbInBufferSize"> lpInBufferのサイズ (/param)
''' (param name="lpOutBuffer"> 出力バッファへのポインタ (/param)
''' (param name="cbOutBufferSize"> lpOutBufferのサイズ (/param)
''' (param name="lpBytesReturned"> lpOutBufferに格納されたサイズ(/param)
''' (returns) エラーコード (/returns)
''' --------------------------------------------------------------------
<DllImport("winscard.dll")>
Public Function SCardControl(
ByVal hCard As IntPtr,
ByVal dwControlCode As Integer,
ByVal lpInBuffer As Byte(),
ByVal cbInBufferSize As Integer,
ByVal lpOutBuffer As Byte(),
ByVal cbOutBufferSize As Integer,
ByRef lpBytesReturned As Integer) _
As UInteger
End Function
''' --------------------------------------------------------------------
''' (summary) カードの状態 (/summary)
''' (param name="hCard"> SCardConnectから返される参照値(/param)
''' (param name="mszReaderNames"> リーダから返された表示名 (/param)
''' (param name="pcchReaderLen"> szReaderName bufferのサイズ (/param)
''' (param name="pdwState"> リーダ内のカードの状態 (/param)
''' (param name="pdwProtocol"> 現在プロトコル (/param)
''' (param name="pbAtr"> ATR文字列へのポインタ (/param)
''' (param name="pcbAtrLen"> 用意したサイズと受信したサイズ(/param)
''' (returns) エラーコード (/returns)
''' --------------------------------------------------------------------
<DllImport("winscard.dll")>
Public Function SCardStatus(
ByVal hCard As IntPtr,
ByVal mszReaderNames As String,
ByVal pcchReaderLen As Integer,
ByVal pdwState As Integer,
ByVal pdwProtocol As Integer,
ByVal pbAtr As Byte,
ByVal pcbAtrLen As Integer) _
As UInteger
End Function
''' --------------------------------------------------------------------
''' (summary) サービスリクエストを送信しデータバックを受信する(/summary)
''' (param name="hCard"> SCardConnectから返される参照値(/param)
''' (param name="pioSendPci"> 命令プロトコルヘッダのポインタ(/param)
''' (param name="pbSendBuffer"> 送信データへのポインタ (/param)
''' (param name="cbSendLength"> pbSendBufferパラメーターの長さ(/param)
''' (param name="pioRecvPci"> 受信プロトコルヘッダのポインタ(/param)
''' (param name="pbRecvBuffer"> 返信データへのポインタ (/param)
''' (param name="pcbRecvvLength"> pbRecvBufferパラメーターの長さ(/param)
''' (returns) エラーコード (/returns)
''' --------------------------------------------------------------------
<DllImport("winscard.dll")>
Public Function SCardTransmit(
ByVal hCard As IntPtr,
ByVal pioSendPci As IntPtr,
ByVal pbSendBuffer As Byte(),
ByVal cbSendLength As Integer,
ByVal pioRecvPci As SCARD_IO_REQUEST,
ByVal pbRecvBuffer As Byte(),
ByRef pcbRecvvLength As Integer) _
As UInteger
End Function
''' --------------------------------------------------------------------
''' (summary) 接続を終了する (/summary)
''' (param name="hCard"> SCardConnectから返される参照値 (/param)
''' (param name="dwDisposition"> 実行時のカードへのアクション (/param)
''' (returns) エラーコード (/returns)
''' --------------------------------------------------------------------
<DllImport("winscard.dll")>
Public Function SCardDisconnect(
ByVal hCard As IntPtr,
ByVal dwDisposition As Integer) _
As UInteger
End Function
''' --------------------------------------------------------------------
''' (summary)コンテクストを閉じ、リソースを解放する (/summary)
''' (param name="hContext"> コンテキスト識別ハンドル (/param)
''' (returns) エラーコード (/returns)
''' --------------------------------------------------------------------
<DllImport("winscard.dll")>
Public Function SCardReleaseContext(
ByVal hContext As IntPtr) _
As UInteger
End Function
' ───────────────────────────────────
''' --------------------------------------------------------------------
''' (summary) DLLモジュールを解放する (/summary)
''' (param name="handle"> ロードされたライブラリへのハンドル (/param)
''' --------------------------------------------------------------------
<DllImport("kernel32.dll")>
Public Sub FreeLibrary(ByVal handle As IntPtr)
End Sub
''' --------------------------------------------------------------------
''' (summary) DLLからの関数や変数へのアドレスを取得する (summary)
''' (param name="hModule"> DLLモジュールのハンドル (/param)
''' (param name="lpProcName"> 関数または変数名、または関数の序数(/param)
''' (returns) 関数または変数のアドレス (/returns)
''' --------------------------------------------------------------------
<DllImport("kernel32.dll")>
Public Function GetProcAddress(
ByVal hModule As IntPtr,
ByVal lpProcName As String) _
As IntPtr
End Function
''' --------------------------------------------------------------------
''' (summary) 指定したモジュールをアドレス空間にロードする (/summary)
''' (param name="lpLibFileName"> モジュール名 (/param)
''' (returns) 成功したらモジュールのハンドル、失敗したらNULL (/returns)
''' --------------------------------------------------------------------
<DllImport("kernel32.dll")>
Public Function LoadLibrary(
ByVal lpLibFileName As String) _
As IntPtr
End Function
' ───────────────────────────────────
' 本体
' ───────────────────────────────────
''' --------------------------------------------------------------------
''' (summary) エラーコードからエラーメッセージを取得する (/summary)
''' (param name="uintErr"> エラーコード (/param)
''' (returns) エラーメッセージ (/returns)
''' --------------------------------------------------------------------
Public Function GetErrMsg(ByVal uintErr As UInteger) As String
Select Case Uint2Int(uintErr)
Case SCARD_S_SUCCESS
Return "SUCCESS"
Case SCARD_F_INTERNAL_ERROR
Return "INTERNAL_ERROR"
Case SCARD_E_CANCELLED
Return "CANCELLED"
Case SCARD_E_INVALID_HANDLE
Return "INVALID_HANDLE"
Case SCARD_E_INVALID_PARAMETER
Return "INVALID_PARAMETER"
Case SCARD_E_INVALID_TARGET
Return "INVALID_TARGET"
Case SCARD_E_NO_MEMORY
Return "NO_MEMORY"
Case SCARD_F_WAITED_TOO_LONG
Return "WAITED_TOO_LONG"
Case SCARD_E_INSUFFICIENT_BUFFER
Return "INSUFFICIENT_BUFFER"
Case SCARD_E_UNKNOWN_READER
Return "UNKNOWN_READER"
Case SCARD_E_TIMEOUT
Return "TIMEOUT"
Case SCARD_E_SHARING_VIOLATION
Return "SHARING_VIOLATION"
Case SCARD_E_NO_SMARTCARD
Return "NO_SMARTCARD"
Case SCARD_E_UNKNOWN_CARD
Return "UNKNOWN_CARD"
Case SCARD_E_CANT_DISPOSE
Return "CANT_DISPOSE"
Case SCARD_E_PROTO_MISMATCH
Return "PROTO_MISMATCH"
Case SCARD_E_NOT_READY
Return "NOT_READY"
Case SCARD_E_INVALID_VALUE
Return "INVALID_VALUE"
Case SCARD_E_SYSTEM_CANCELLED
Return "SYSTEM_CANCELLED"
Case SCARD_E_COMM_ERROR
Return "COMM_ERROR"
Case SCARD_F_UNKNOWN_ERROR
Return "UNKNOWN_ERROR"
Case SCARD_E_INVALID_ATR
Return "INVALID_ATR"
Case SCARD_E_NOT_TRANSACTED
Return "NOT_TRANSACTED"
Case SCARD_E_READER_UNAVAILABLE
Return "READER_UNAVAILABLE"
Case SCARD_P_SHUTDOWN
Return "SHUTDOWN"
Case SCARD_E_PCI_TOO_SMALL
Return "PCI_TOO_SMALL"
Case SCARD_E_READER_UNSUPPORTED
Return "READER_UNSUPPORTED"
Case SCARD_E_DUPLICATE_READER
Return "DUPLICATE_READER"
Case SCARD_E_CARD_UNSUPPORTED
Return "CARD_UNSUPPORTED"
Case SCARD_E_NO_SERVICE
Return "NO_SERVICE"
Case SCARD_E_SERVICE_STOPPED
Return "SERVICE_STOPPED"
Case SCARD_E_UNEXPECTED
Return "UNEXPECTED"
Case SCARD_E_ICC_INSTALLATION
Return "ICC_INSTALLATION"
Case SCARD_E_ICC_CREATEORDER
Return "ICC_CREATEORDER"
Case SCARD_E_UNSUPPORTED_FEATURE
Return "UNSUPPORTED_FEATURE"
Case SCARD_E_DIR_NOT_FOUND
Return "DIR_NOT_FOUND"
Case SCARD_E_FILE_NOT_FOUND
Return "FILE_NOT_FOUND"
Case SCARD_E_NO_DIR
Return "NO_DIR"
Case SCARD_E_NO_FILE
Return "NO_FILE"
Case SCARD_E_NO_ACCESS
Return "NO_ACCESS"
Case SCARD_E_WRITE_TOO_MANY
Return "WRITE_TOO_MANY"
Case SCARD_E_BAD_SEEK
Return "BAD_SEEK"
Case SCARD_E_INVALID_CHV
Return "INVALID_CHV"
Case SCARD_E_UNKNOWN_RES_MNG
Return "UNKNOWN_RES_MNG"
Case SCARD_E_NO_SUCH_CERTIFICATE
Return "NO_SUCH_CERTIFICATE"
Case SCARD_E_CERTIFICATE_UNAVAILABLE
Return "CERTIFICATE_UNAVAILABLE"
Case SCARD_E_NO_READERS_AVAILABLE
Return "NO_READERS_AVAILABLE"
Case SCARD_E_COMM_DATA_LOST
Return "COMM_DATA_LOST"
Case SCARD_E_NO_KEY_CONTAINER
Return "NO_KEY_CONTAINER"
Case SCARD_E_SERVER_TOO_BUSY
Return "SERVER_TOO_BUSY"
Case SCARD_E_PIN_CACHE_EXPIRED
Return "PIN_CACHE_EXPIRED"
Case SCARD_E_NO_PIN_CACHE
Return "NO_PIN_CACHE"
Case SCARD_E_READ_ONLY_CARD
Return "READ_ONLY_CARD"
Case SCARD_W_UNSUPPORTED_CARD
Return "UNSUPPORTED_CARD"
Case SCARD_W_UNRESPONSIVE_CARD
Return "UNRESPONSIVE_CARD"
Case SCARD_W_UNPOWERED_CARD
Return "UNPOWERED_CARD"
Case SCARD_W_RESET_CARD
Return "RESET_CARD"
Case SCARD_W_REMOVED_CARD
Return "REMOVED_CARD"
Case SCARD_W_SECURITY_VIOLATION
Return "SECURITY_VIOLATION"
Case SCARD_W_WRONG_CHV
Return "WRONG_CHV"
Case SCARD_W_CHV_BLOCKED
Return "CHV_BLOCKED"
Case SCARD_W_EOF
Return "EOF"
Case SCARD_W_CANCELLED_BY_USER
Return "CANCELLED_BY_USER"
Case SCARD_W_CARD_NOT_AUTHENTICATED
Return "CARD_NOT_AUTHENTICATED"
Case SCARD_W_CACHE_ITEM_NOT_FOUND
Return "CACHE_ITEM_NOT_FOUND"
Case SCARD_W_CACHE_ITEM_STALE
Return "CACHE_ITEM_STALE"
Case SCARD_W_CACHE_ITEM_TOO_BIG
Return "CACHE_ITEM_TOO_BIG"
Case Else
Return "UNKENOWN"
End Select
End Function
''' --------------------------------------------------------------------
''' (summary) UIntegerをIntegerに変換する (/summary)
''' (param name="uint"> UInteger (/param)
''' (returns) Integer (/returns)
''' --------------------------------------------------------------------
Private Function Uint2Int(ByVal uint As UInteger) As Integer
Dim lng As Long = uint - 2 ^ 32
Return CInt(lng)
End Function
End Module
苦労したところ
SCardGetStatusChangeの戻り値がSCARD_S_SUCCESSにならない
MicrosoftのDocumentationには、引数であるrgReaderStatesの説明として、「An array of SCARD_READERSTATE structures」と書いてあります。つまり、rgReaderStatesは配列です。でも、APIの呼び出しを配列にすると、戻り値がSCARD_S_SUCCESSになりません。
そこで、配列にせず、「ByRef rgReaderStates As SCARD_READERSTATE」として定義し、呼び出し元でrgReaderStates(0)としたら、カードを置かないときの戻り値がSCARD_S_SUCCESSになりました。
どうしてSCARD_S_SUCCESSが戻るようになるのか、理由はよくわかりません。
カードを置くとエラーが発生する
次に、カードを置いてみたら、「System.Runtime.InteropServices.SafeArrayTypeMismatchException: ‘配列のランタイム型とメタデータに記録されている sub 型が一致していません。’」というエラーが出て止まりました。
ウォッチ式で、引数であるrgAtrを確認するとNothingでした。
MicrosoftのDocumentationを確認すると、「BYTE rgbAtr[36];」とあります。つまり、固定バイト長が必要なようです。
そこで、<MarshalAs(UnmanagedType.ByValArray, SizeConst:=36)>というおまじないをかけたら、エラーを生じなくなりました。
実施例
フォームのコマンドボタンをクリックし、随分前に秋葉原で買ってきたMifare Ultralight Cの白紙カードをかざしてみたら、出力ウィンドウに「04-53-86-DA-1B-5C-80」などと、UIDが取れました。
Suicaやwaonカードでは、8桁のUIDが取れました。
まとめ
Visual Basic 2019からPC/SCインタフェースを介して、NFCカードのUIDを取得することができました。
UIDが取れれば、SCardTransmitのpbSendBufferを自分の好きなコマンドに換えることで、読み書きもできるようになると思います。
コメント