Windowsで.NETを利用しPaSoRi RC-S380を使ってNFCタグからIDmを読み取る



# TL;DR（要約）

- 目的は、簡易的な入退館システムとして、NFCタグのIDmを読み取りたい。
- ここでは、Windowsで、開発言語として.NET、リーダとして「PaSoRi RC-S380」を利用し、NFCタグのIDmを読み取る方法について記載している。

# 「PC/SCを利用して実装する」に基づいたソース

参考：https://log.windows78.net/2015/03/1295/

> このラッパークラスを使えばTypeA(Mifare), TypeB, FeliCa の ID を読み取ることが出来ます。VB.netでの実装ノウハウがネット上に少なく苦労しましたので、メモ書きとしてここに残したいと思います

上記サイトを参考に整理したソースを記載いたします。
ほとんどの内容は、上記サイト製作者様による成果となります。

## 環境

- Windows 10 64bit
- PaSoRi RC-S380
- Visual Studio 2019
- .Net Framework 3.5
- [NFCポートソフトウェア](https://www.sony.co.jp/Products/felica/consumer/download/felicaportsoftware.html)

## 注意点

- Felicaのみ動作確認
    - Sugoca、および購入した[NFCタグ](https://www.amazon.co.jp/NFCステッカー-mm%EF%BC%881インチ%EF%BC%89円形-144バイトメモリ-すべてのNFC電話機との互換-TimesKey/dp/B07JRHL743)では動作確認済み
- ソース内でカードリーダーの名称をバイト配列から文字列に変換するところがうまくいかなかったため、以下のように埋め込みとしています。ぜひ間違いをご指摘いただけますと助かります。

```vb:修正前
        Me.CardReaderName = System.Text.Encoding.Unicode.GetString(mszReaders)
        Me.CardReaderName = Me.CardReaderName.Substring(0, Me.CardReaderName.IndexOf(vbNullChar))

        Me.CardReaderName = "Sony FeliCa Port/PaSoRi 3.0 0"
```

- コメントにて、上記の解決方法をご指摘いただきました！@anchanBiz さんありがとうございます。

    関連：https://www.pinvoke.net/default.aspx/winscard.scardlistreaders

```vb:修正後
Me.CardReaderName = Encoding.ASCII.GetString(mszReaders).Split(vbNullChar.ToCharArray)(0)
```

- `SCardConnect`での指定`WinSCardWrapper.SCARD_PROTOCOL_T0 Or WinSCardWrapper.SCARD_PROTOCOL_T1`は、`WinSCardWrapper.SCARD_PROTOCOL_T1`でよい可能性があります。

## ソース

```vb:Formt1.vb
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim cardNoNumeric = ""

        If IDCardUtility.TryGetCardNoNumeric(cardNoNumeric) Then
            txtICtag.Text = cardNoNumeric.ToLower
        Else
            txtICtag.Text = ""
        End If
    End Sub
```

```vb:IDCardUtility.vb
'https://log.windows78.net/2015/03/1295/

Public Class IDCardUtility
    Private Sub New()
    End Sub

    Public Shared Function TryGetCardNo(ByRef cardNo As String) As Boolean
        cardNo = ""

        Dim c = New WinSCard
        If Not c.TrySCardEstablishContext() Then Return False
        If Not c.TrySCardListReaders() Then Return False
        c.ProcessState = WinSCard.EnumProcessState.State3
        If Not c.TrySCardConnect() Then Return False
        If Not c.TrySCardTransmit() Then Return False
        If Not c.TrySCardDisconnect() Then Return False

        cardNo = c.CardNo
        Return True
    End Function

    Public Shared Function TryGetCardNoNumeric(ByRef cardNoNumeric As String) As Boolean
        cardNoNumeric = ""

        Dim c = New WinSCard
        If Not c.TrySCardEstablishContext() Then Return False
        If Not c.TrySCardListReaders() Then Return False
        c.ProcessState = WinSCard.EnumProcessState.State3
        If Not c.TrySCardConnect() Then Return False
        If Not c.TrySCardTransmit() Then Return False
        If Not c.TrySCardDisconnect() Then Return False

        cardNoNumeric = c.CardNoNumeric
        Return True
    End Function
End Class
```

```vb:WinSCard.vb
'-----------------------------------------------------------
' All Rights Reserved , Copyright (C) 2014
'https://log.windows78.net/2015/03/1295/
'-----------------------------------------------------------

Imports System.Runtime.InteropServices
Imports System.Text

Public Class WinSCard

    Public Enum EnumProcessState
        State0 = 0
        State1 = 1
        State2 = 2
        State3 = 3
        State4 = 4
        State5 = 5
    End Enum

    Public ProcessState As EnumProcessState
    Public CardReaderName As String
    Public SerialNumber As String
    ''' <summary>
    ''' FelicaのIDmやMifareのUID
    ''' </summary>
    Public CardNo As String
    Public CardType As Integer     '0:Felica 1:Mifare
    Private hCard As IntPtr
    Private hContext As IntPtr
    Private ReadOnly intPolling As Integer = 500 'ICカードを読み込むポーリング間隔

    Private readerState As WinSCardWrapper.SCARD_READERSTATE = New WinSCardWrapper.SCARD_READERSTATE


    Public ReadOnly Property CardNoNumeric As String
        Get
            Return Me.CardNo.Replace("-"c, "")
        End Get
    End Property

    Public Sub New()
        Me.ProcessState = EnumProcessState.State0
        hContext = IntPtr.Zero
        CardReaderName = vbNullString
        SerialNumber = vbNullString
        hCard = IntPtr.Zero
        CardNo = String.Empty
        'state.dwCurrentState = WinSCardWrapper.SCARD_STATE_EMPTY
        readerState.dwCurrentState = WinSCardWrapper.SCARD_STATE_UNAWARE
    End Sub

    Public Function TrySCardEstablishContext() As Boolean
        If Me.ProcessState <> EnumProcessState.State0 Then Return False

        '変数定義
        'SmartCardサービスの確認
        Dim URtn As UInteger = WinSCardWrapper.SCardEstablishContext(WinSCardWrapper.SCARD_SCOPE_USER,
                                                     IntPtr.Zero,
                                                     IntPtr.Zero,
                                                     Me.hContext)
        If URtn <> WinSCardWrapper.SCARD_S_SUCCESS Then
            'エラー内容をスロー
            Throw New Exception($"{URtn}:{WinSCardWrapper.GetErrorMessage(URtn)}")
        End If

        Me.ProcessState = EnumProcessState.State1

        Return True
    End Function

    Public Function TrySCardListReaders() As Boolean
        If Me.ProcessState <> WinSCard.EnumProcessState.State1 Then Return False

        Dim pcchReaders As UInteger = 0
        Dim mszReaders As Byte()

        '変数定義
        '接続されているカードリーダーを確認
        'ここで取得できるカードリーダーの名前はNull文字で区切られた文字数
        Dim URtn As UInteger = WinSCardWrapper.SCardListReaders(Me.hContext, Nothing, Nothing, pcchReaders)
        If URtn <> WinSCardWrapper.SCARD_S_SUCCESS Then
            'エラー内容をスロー
            Throw New Exception($"{URtn}:{WinSCardWrapper.GetErrorMessage(URtn)}")
        End If

        '接続されているカードリーダの名前を取得
        'ここで取得できるカードリーダーの名前はNull文字で区切られたバイト配列(2バイト1文字)
        mszReaders = New Byte(Convert.ToInt32(pcchReaders) * 2 - 1) {}

        URtn = WinSCardWrapper.SCardListReaders(Me.hContext, Nothing, mszReaders, pcchReaders)
        If URtn <> WinSCardWrapper.SCARD_S_SUCCESS Then
            'エラー内容をスロー
            Throw New Exception($"{URtn}:{WinSCardWrapper.GetErrorMessage(URtn)}")
        End If

        ' 例："Sony FeliCa Port/PaSoRi 3.0 0" 
        Me.CardReaderName = Encoding.ASCII.GetString(mszReaders).Split(vbNullChar.ToCharArray)(0)

        '状態を1→2に遷移
        Me.ProcessState = EnumProcessState.State2

        Return True
    End Function


    Public Function TrySCardGetStatusChange() As Boolean
        If Me.ProcessState <> WinSCard.EnumProcessState.State2 Then Return False

        readerState.szReader = Me.CardReaderName

        '変数定義
        'SmartCardステータス遷移
        Dim URtn As UInteger = WinSCardWrapper.SCardGetStatusChange(Me.hContext, -1, readerState, 1)

        If URtn <> WinSCardWrapper.SCARD_S_SUCCESS Then
            'エラー内容をスロー
            Throw New Exception($"{URtn}:{WinSCardWrapper.GetErrorMessage(URtn)}")
        End If

        '状態遷移
        If readerState.dwEventState = WinSCardWrapper.SCARD_STATE_PRESENT Then
            '状態を2→3に遷移
            Me.ProcessState = EnumProcessState.State3
            Return True
        Else
            Return False
        End If

    End Function

    Public Function TrySCardConnect() As Boolean
        If Me.ProcessState <> WinSCard.EnumProcessState.State3 Then Return False

        Dim pdwActiveProtocol As IntPtr = IntPtr.Zero

        '変数定義
        'カードを確認
        Dim URtn As UInteger = WinSCardWrapper.SCardConnect(Me.hContext,
                                            Me.CardReaderName,
                                            WinSCardWrapper.SCARD_SHARE_SHARED,
                                            WinSCardWrapper.SCARD_PROTOCOL_T0 Or WinSCardWrapper.SCARD_PROTOCOL_T1,
                                            Me.hCard, pdwActiveProtocol)
        If URtn = WinSCardWrapper.SCARD_S_SUCCESS Then
            '続行
        ElseIf URtn = WinSCardWrapper.GetUInteger(WinSCardWrapper.SCARD_W_REMOVED_CARD) Then
            'continue
            System.Threading.Thread.Sleep(intPolling)
            Return False
        Else
            'エラー内容をスロー
            Throw New Exception($"{URtn}:{WinSCardWrapper.GetErrorMessage(URtn)}")
        End If

        '※以下のカードシリアル番号取得は別になくても構わない・・・たぶん

        Dim controlCode As Integer = &H3136B0
        Dim sendBuffer As Byte() = New Byte() {&HC0, &H8}
        Dim recvBuffer As Byte() = New Byte(64) {}
        Dim bytesReturned As Integer = 0

        'カードシリアル番号取得
        URtn = WinSCardWrapper.SCardControl(
                    Me.hCard, controlCode, sendBuffer,
                    sendBuffer.Length,
                    recvBuffer,
                    recvBuffer.Length,
                    bytesReturned)
        If URtn <> WinSCardWrapper.SCARD_S_SUCCESS Then
            'エラー内容をスロー
            Throw New Exception($"{URtn}:{WinSCardWrapper.GetErrorMessage(URtn)}")
        End If

        Me.SerialNumber = (New ASCIIEncoding).GetString(recvBuffer, 0, bytesReturned - 1)

        Me.ProcessState = EnumProcessState.State4

        Return True
    End Function

    Public Function TrySCardTransmit() As Boolean
        If Me.ProcessState <> WinSCard.EnumProcessState.State4 Then Return False

        Dim sendBuffer As Byte() = New Byte() {&HFF, &HCA, &H0, &H0, &H0}    'Felica IDm取得コマンド
        'Dim sendBuffer As Byte() = New Byte() {&HFF, &HCA, &H0, &H0, &H4}   'Mifare UID取得コマンド
        Dim recvBuffer As Byte() = New Byte(511) {}
        Dim recvBufferLen As Integer = recvBuffer.Length

        '動的にライブラリのポインタを取得・・・
        Dim hLoader As IntPtr = WinSCardWrapper.LoadLibrary("winscard.dll")
        Dim SCARD_PCI_T1 As IntPtr = WinSCardWrapper.GetProcAddress(hLoader, "g_rgSCardT1Pci")
        WinSCardWrapper.FreeLibrary(hLoader)

        Dim pioRecvRequest As WinSCardWrapper.SCARD_IO_REQUEST = Nothing
        '変数定義
        Dim URtn As UInteger = WinSCardWrapper.SCardTransmit(Me.hCard, SCARD_PCI_T1, sendBuffer, sendBuffer.Length, pioRecvRequest, recvBuffer, recvBufferLen)
        If URtn <> WinSCardWrapper.SCARD_S_SUCCESS Then
            'エラー内容をスロー
            Throw New Exception($"{URtn}:{WinSCardWrapper.GetErrorMessage(URtn)}")
        End If

        'Me.mCardNo = BitConverter.ToString(recvBuffer, 0, recvBufferLen - 2).Replace("-", "")
        Me.CardNo = BitConverter.ToString(recvBuffer, 0, recvBufferLen - 2)

        Me.ProcessState = EnumProcessState.State5

        Return True
    End Function

    Public Function TrySCardDisconnect() As Boolean
        If Me.ProcessState <> WinSCard.EnumProcessState.State5 Then Return False

        '変数定義
        'カード解放
        Dim URtn As UInteger = WinSCardWrapper.SCardDisconnect(hCard, WinSCardWrapper.SCARD_LEAVE_CARD)
        If URtn <> WinSCardWrapper.SCARD_S_SUCCESS Then
            'カードの解放に失敗
        End If

        Return True
    End Function

    Public Sub SCardFreeMemory()

        '変数定義
        Dim URtn As UInteger = WinSCardWrapper.SCardFreeMemory(hContext, CardReaderName)

        If URtn <> WinSCardWrapper.SCARD_S_SUCCESS Then
            'カードリーダーの解放に失敗
        End If

    End Sub

    Public Sub SCardReleaseContext()

        '変数定義
        Dim URtn As UInteger = WinSCardWrapper.SCardReleaseContext(hContext)

        If URtn <> WinSCardWrapper.SCARD_S_SUCCESS Then
            'カードリーダーの解放に失敗
        End If

    End Sub


    Private Class WinSCardWrapper

        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_AIRMATCH 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



        <DllImport("winscard.dll")>
        Public Shared Function SCardEstablishContext(ByVal dwScope As UInteger,
                                                     ByVal pvReserved1 As IntPtr,
                                                     ByVal pvReserved2 As IntPtr,
                                                     ByRef phContext As IntPtr) As UInteger
        End Function

        <DllImport("winscard.dll")>
        Public Shared Function SCardListReaders(ByVal hContext As IntPtr,
                                                ByVal mszGroups As Byte(),
                                                ByVal mszReaders As Byte(),
                                                ByRef pcchReaders As UInt32) As UInteger
        End Function


        <DllImport("winscard.dll")>
        Public Shared Function SCardReleaseContext(ByVal phContext As IntPtr) As UInteger
        End Function


        <DllImport("winscard.dll")>
        Public Shared 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


        <DllImport("winscard.dll")>
        Public Shared Function SCardDisconnect(ByVal hCard As IntPtr,
                                               ByVal Disposition As Integer) As UInteger
        End Function

        Public Class SCARD_IO_REQUEST
            Friend dwProtocol As UInteger
            Friend cbPciLength As Integer
            Public Sub New()
                dwProtocol = 0
            End Sub
        End Class


        <DllImport("winscard.dll")>
        Public Shared Function SCardStatus(ByVal hCard As IntPtr,
                                           ByVal szReaderName As String,
                                           ByVal SendBuff As Byte(),
                                           ByVal SendBuffLen As Integer,
                                           ByVal pioRecvRequest As SCARD_IO_REQUEST,
                                           ByVal RecvBuff As Byte(),
                                           ByRef RecvBuffLen As Integer) As UInteger
        End Function


        <DllImport("winscard.dll")>
        Public Shared Function SCardTransmit(ByVal hCard As IntPtr,
                                             ByVal pioSendRequest As IntPtr,
                                             ByVal SendBuff As Byte(),
                                             ByVal SendBuffLen As Integer,
                                             ByVal pioRecvRequest As SCARD_IO_REQUEST,
                                             ByVal RecvBuff As Byte(),
                                             ByRef RecvBuffLen As Integer) As UInteger
        End Function


        <DllImport("winscard.dll")>
        Public Shared Function SCardControl(ByVal hCard As IntPtr,
                                            ByVal controlCode As Integer,
                                            ByVal inBuffer As Byte(),
                                            ByVal inBufferLen As Integer,
                                            ByVal outBuffer As Byte(),
                                            ByVal outBufferLen As Integer,
                                            ByRef bytesReturned As Integer) As UInteger
        End Function


        Public Structure SCARD_READERSTATE
            Friend szReader As String
            Friend pvUserData As IntPtr
            Friend dwCurrentState As UInt32
            Friend dwEventState As UInt32
            Friend cbAtr As UInt32

            Friend rgbAtr As Byte()
        End Structure


        <DllImport("winscard.dll")>
        Public Shared Function SCardGetStatusChange(ByVal hContext As IntPtr,
                                                    ByVal dwTimeout As Integer,
                                                    ByVal rgReaderStates As SCARD_READERSTATE,
                                                    ByVal cReaders As Integer) As UInteger
        End Function


        <DllImport("winscard.dll")>
        Public Shared Function SCardFreeMemory(ByVal hContext As IntPtr,
                                               ByVal szReader As String) As UInteger
        End Function


        <DllImport("kernel32.dll")>
        Public Shared Function LoadLibrary(ByVal lpFileName As String) As IntPtr
        End Function


        <DllImport("kernel32.dll")>
        Public Shared Sub FreeLibrary(ByVal handle As IntPtr)
        End Sub


        <DllImport("kernel32.dll")>
        Public Shared Function GetProcAddress(ByVal handle As IntPtr,
                                              ByVal procName As String) As IntPtr
        End Function

        Public Shared Function GetErrorMessage(ByVal errNo As UInteger) As String

            Dim strMessage As String

            Select Case errNo
                Case WinSCardWrapper.GetUInteger(SCARD_F_INTERNAL_ERROR)
                    strMessage = errNo.ToString + " INTERNAL ERROR"
                Case WinSCardWrapper.GetUInteger(SCARD_E_CANCELLED)
                    strMessage = errNo.ToString + " CANCELLED"
                Case WinSCardWrapper.GetUInteger(SCARD_E_INVALID_HANDLE)
                    strMessage = errNo.ToString + " INVALID HANDLE"
                Case WinSCardWrapper.GetUInteger(SCARD_E_INVALID_PARAMETER)
                    strMessage = errNo.ToString + " INVALID PARAMETER"
                Case WinSCardWrapper.GetUInteger(SCARD_E_INVALID_TARGET)
                    strMessage = errNo.ToString + " INVALID TARGET"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NO_MEMORY)
                    strMessage = errNo.ToString + " NO MEMORY"
                Case WinSCardWrapper.GetUInteger(SCARD_F_WAITED_TOO_LONG)
                    strMessage = errNo.ToString + " WAITED TOO LONG"
                Case WinSCardWrapper.GetUInteger(SCARD_E_INSUFFICIENT_BUFFER)
                    strMessage = errNo.ToString + " INSUFFICIENT BUFFER"
                Case WinSCardWrapper.GetUInteger(SCARD_E_UNKNOWN_READER)
                    strMessage = errNo.ToString + " UNKNOWN READER"
                Case WinSCardWrapper.GetUInteger(SCARD_E_TIMEOUT)
                    strMessage = errNo.ToString + " TIMEOUT"
                Case WinSCardWrapper.GetUInteger(SCARD_E_SHARING_VIOLATION)
                    strMessage = errNo.ToString + " SHARING VIOLATION"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NO_SMARTCARD)
                    strMessage = errNo.ToString + " NO SMARTCARD"
                Case WinSCardWrapper.GetUInteger(SCARD_E_UNKNOWN_CARD)
                    strMessage = errNo.ToString + " UNKNOWN CARD"
                Case WinSCardWrapper.GetUInteger(SCARD_E_CANT_DISPOSE)
                    strMessage = errNo.ToString + " CANT DISPOSE"
                Case WinSCardWrapper.GetUInteger(SCARD_E_PROTO_MISMATCH)
                    strMessage = errNo.ToString + " PROTO MISMATCH"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NOT_READY)
                    strMessage = errNo.ToString + " NOT READY"
                Case WinSCardWrapper.GetUInteger(SCARD_E_INVALID_VALUE)
                    strMessage = errNo.ToString + " INVALID VALUE"
                Case WinSCardWrapper.GetUInteger(SCARD_E_SYSTEM_CANCELLED)
                    strMessage = errNo.ToString + " SYSTEM CANCELLED"
                Case WinSCardWrapper.GetUInteger(SCARD_E_COMM_ERROR)
                    strMessage = errNo.ToString + " COMM ERROR"
                Case WinSCardWrapper.GetUInteger(SCARD_F_UNKNOWN_ERROR)
                    strMessage = errNo.ToString + " UNKNOWN ERROR"
                Case WinSCardWrapper.GetUInteger(SCARD_E_INVALID_ATR)
                    strMessage = errNo.ToString + " INVALID ATR"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NOT_TRANSACTED)
                    strMessage = errNo.ToString + " NOT TRANSACTED"
                Case WinSCardWrapper.GetUInteger(SCARD_E_READER_UNAVAILABLE)
                    strMessage = errNo.ToString + " READER UNAVAILABLE"
                Case WinSCardWrapper.GetUInteger(SCARD_P_SHUTDOWN)
                    strMessage = errNo.ToString + " SHUTDOWN"
                Case WinSCardWrapper.GetUInteger(SCARD_E_PCI_TOO_SMALL)
                    strMessage = errNo.ToString + " PCI TOO SMALL"
                Case WinSCardWrapper.GetUInteger(SCARD_E_READER_UNSUPPORTED)
                    strMessage = errNo.ToString + " READER UNSUPPORTED"
                Case WinSCardWrapper.GetUInteger(SCARD_E_DUPLICATE_READER)
                    strMessage = errNo.ToString + " DUPLICATE READER"
                Case WinSCardWrapper.GetUInteger(SCARD_E_CARD_UNSUPPORTED)
                    strMessage = errNo.ToString + " CARD UNSUPPORTED"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NO_SERVICE)
                    strMessage = errNo.ToString + " NO SERVICE"
                Case WinSCardWrapper.GetUInteger(SCARD_E_SERVICE_STOPPED)
                    strMessage = errNo.ToString + " SERVICE STOPPED"
                Case WinSCardWrapper.GetUInteger(SCARD_E_UNEXPECTED)
                    strMessage = errNo.ToString + " UNEXPECTED"
                Case WinSCardWrapper.GetUInteger(SCARD_E_ICC_INSTALLATION)
                    strMessage = errNo.ToString + " ICC INSTALLATION"
                Case WinSCardWrapper.GetUInteger(SCARD_E_ICC_CREATEORDER)
                    strMessage = errNo.ToString + " ICC CREATEORDER"
                Case WinSCardWrapper.GetUInteger(SCARD_E_UNSUPPORTED_FEATURE)
                    strMessage = errNo.ToString + " UNSUPPORTED FEATURE"
                Case WinSCardWrapper.GetUInteger(SCARD_E_DIR_NOT_FOUND)
                    strMessage = errNo.ToString + " DIR NOT FOUND"
                Case WinSCardWrapper.GetUInteger(SCARD_E_FILE_NOT_FOUND)
                    strMessage = errNo.ToString + " FILE NOT FOUND"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NO_DIR)
                    strMessage = errNo.ToString + " NO DIR"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NO_FILE)
                    strMessage = errNo.ToString + " NO FILE"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NO_ACCESS)
                    strMessage = errNo.ToString + " NO ACCESS"
                Case WinSCardWrapper.GetUInteger(SCARD_E_WRITE_TOO_MANY)
                    strMessage = errNo.ToString + " WRITE TOO MANY"
                Case WinSCardWrapper.GetUInteger(SCARD_E_BAD_SEEK)
                    strMessage = errNo.ToString + " BAD SEEK"
                Case WinSCardWrapper.GetUInteger(SCARD_E_INVALID_CHV)
                    strMessage = errNo.ToString + " INVALID CHV"
                Case WinSCardWrapper.GetUInteger(SCARD_E_UNKNOWN_RES_MNG)
                    strMessage = errNo.ToString + " UNKNOWN RES MNG"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NO_SUCH_CERTIFICATE)
                    strMessage = errNo.ToString + " NO SUCH CERTIFICATE"
                Case WinSCardWrapper.GetUInteger(SCARD_E_CERTIFICATE_UNAVAILABLE)
                    strMessage = errNo.ToString + " CERTIFICATE UNAVAILABLE"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NO_READERS_AVAILABLE)
                    strMessage = errNo.ToString + " NO READERS AVAILABLE"
                Case WinSCardWrapper.GetUInteger(SCARD_E_COMM_DATA_LOST)
                    strMessage = errNo.ToString + " COMM DATA LOST"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NO_KEY_CONTAINER)
                    strMessage = errNo.ToString + " NO KEY CONTAINER"
                Case WinSCardWrapper.GetUInteger(SCARD_E_SERVER_TOO_BUSY)
                    strMessage = errNo.ToString + " SERVER TOO BUSY"
                Case WinSCardWrapper.GetUInteger(SCARD_E_PIN_CACHE_EXPIRED)
                    strMessage = errNo.ToString + " PIN CACHE EXPIRED"
                Case WinSCardWrapper.GetUInteger(SCARD_E_NO_PIN_CACHE)
                    strMessage = errNo.ToString + " NO PIN CACHE"
                Case WinSCardWrapper.GetUInteger(SCARD_E_READ_ONLY_CARD)
                    strMessage = errNo.ToString + " READ ONLY CARD"
                Case WinSCardWrapper.GetUInteger(SCARD_W_UNSUPPORTED_CARD)
                    strMessage = errNo.ToString + " UNSUPPORTED CARD"
                Case WinSCardWrapper.GetUInteger(SCARD_W_UNRESPONSIVE_CARD)
                    strMessage = errNo.ToString + " UNRESPONSIVE CARD"
                Case WinSCardWrapper.GetUInteger(SCARD_W_UNPOWERED_CARD)
                    strMessage = errNo.ToString + " UNPOWERED CARD"
                Case WinSCardWrapper.GetUInteger(SCARD_W_RESET_CARD)
                    strMessage = errNo.ToString + " RESET CARD"
                Case WinSCardWrapper.GetUInteger(SCARD_W_REMOVED_CARD)
                    strMessage = errNo.ToString + " REMOVED CARD"
                Case WinSCardWrapper.GetUInteger(SCARD_W_SECURITY_VIOLATION)
                    strMessage = errNo.ToString + " SECURITY VIOLATION"
                Case WinSCardWrapper.GetUInteger(SCARD_W_WRONG_CHV)
                    strMessage = errNo.ToString + " WRONG CHV"
                Case WinSCardWrapper.GetUInteger(SCARD_W_CHV_BLOCKED)
                    strMessage = errNo.ToString + " CHV BLOCKED"
                Case WinSCardWrapper.GetUInteger(SCARD_W_EOF)
                    strMessage = errNo.ToString + " EOF"
                Case WinSCardWrapper.GetUInteger(SCARD_W_CANCELLED_BY_USER)
                    strMessage = errNo.ToString + " CANCELLED BY USER"
                Case WinSCardWrapper.GetUInteger(SCARD_W_CARD_NOT_AUTHENTICATED)
                    strMessage = errNo.ToString + " CARD NOT AUTHENTICATED"
                Case WinSCardWrapper.GetUInteger(SCARD_W_CACHE_ITEM_NOT_FOUND)
                    strMessage = errNo.ToString + " CACHE ITEM NOT FOUND"
                Case WinSCardWrapper.GetUInteger(SCARD_W_CACHE_ITEM_STALE)
                    strMessage = errNo.ToString + " CACHE ITEM STALE"
                Case WinSCardWrapper.GetUInteger(SCARD_W_CACHE_ITEM_TOO_BIG)
                    strMessage = errNo.ToString + " CACHE ITEM TOO BIG"
                Case Else
                    strMessage = errNo.ToString + " OTHER ERROR"
            End Select
            Return strMessage
        End Function

        Public Shared Function GetUInteger(ByVal p_Value As Integer) As UInteger
            Return CUInt(p_Value + 2 ^ 32)
        End Function
    End Class


End Class
```

# 用語説明

## PC/SC

http://developers.orangetags.jp/words/pc-sc
> PC/SC (Personal Computer/Smart Card)は、Windows環境でICカードを利用するための標準アプリケーションインターフェイス仕様である。

> SDKを購入せずにMicrosoft社が無償で提供する標準インターフェース仕様PC/SC APIを利用すれば、サブライセンスフィー等の追加費用も不要。

### IDm

http://developers.orangetags.jp/words/idm

> IDmは、FeliCaのICチップ製造時に、ICチップに記録され書き換えができない固有のID番号である。IDmは8byte（16桁）の数字で、トレーサビリティと個体識別を目的としてソニーが製造するICチップに書き込まれ同じ番号が流通しないようにソニーが管理をおこなっている。編集は不可能であり比較的簡単に利用できることから、応用範囲が広く、このID番号を使ったアプリケーション・システムが広く普及している。

> 近年FeliCaの上位規格NFCで簡単に読み取ることが可能となっている。NFCでもこのIDmを簡単にリードできるためIDmを利用したアプリケーションが広がっている。一方、ソニーは、高いセキュリティが必要とされるサービスについてはこのIDmのみに依存しないよう推奨しており、高いセキュリティ利用にはFeliCaの暗号領域を利用したり、Mifareを利用して高セキュリティを実現する場合は内部の暗号鍵エリアでの読み書きなどが使用されている。公共の電子マネーサービスではこのIDmやUIDは利用されていない。IDmと混同しがちな番号の中には、サービスのＩＤ番号(Edy番号など）や携帯電話のPIN番号、各会社向けに発行されたシステムコードなどIDチップの中に格納されている各種番号が存在する。なおEdyやSuiCaなどのFeliCaカードやおサイフケータイもこのIDmを保有しており読み取ることができる。

### カードのプロトコル

http://eternalwindows.jp/security/scard/scard05.html

#### SCARD_PROTOCOL_T0

> T=0はキャラクタベースのプロトコルであり、SCARD_PROTOCOL_T0で表されます

#### SCARD_PROTOCOL_T1

> T=1はブロックベースのプロトコルであり、SCARD_PROTOCOL_T1で表されます

#### SCARD_PROTOCOL_T0 or SCARD_PROTOCOL_T1

> この場合、実際にカードのプロトコルを特定するには、pdwActiveProtocolを参照します。 これが、たとえばSCARD_PROTOCOL_T0であるならば、 セットされているカードのプロトコルはT=0ということになります。 基本的に、カードベンダーは、カードの仕様説明の箇所にプロトコルを記述しています。 

### IDm/UIDの桁数

http://e-svc.net/felica/index.html


## TypeA、TypeB、Felica

https://proengineer.internous.co.jp/content/columnfeature/2947

> 非接触ICカードの規格には、世界標準規格である「Type A」と「Type B」、そしてほぼ日本独自の規格である「FeliCa（フェリカ）」の3種類があります。多くの交通系ICカードやおサイフケータイなどは、このFeliCaを利用しています。
> ■Type A
> オランダのフィリップス社で開発された「Mifare」など、欧米を中心に広く使用されています。
コストを抑えて作ることができ、日本では社員証に埋め込み、企業の入退室管理などに利用されています。
> ■Type B
> アメリカのモトローラ社で開発されました。
セキュリティレベルが高く、運転免許証や住基カード、キャッシュカードなどに利用されています。
> ■FeliCa
> ソニー社によって開発され、処理速度の速さには海外でも定評があります。
> Suicaへ採用する方式の決定時に出された条件「処理速度0.1秒以内」をクリアすることができたのは、FeliCaだけだったらしいという逸話も。

# リソース

https://www.softech.co.jp/mm_140205_pc.htm