Access VBAでNFCを使った社内アプリを作ってみる
以前、Accessで社内向けの書籍貸し出し管理用アプリを作ろうとした時、社内のOfficeが64bit版であったが為に、SheepSmartCard.dllが32bit版のみであったので、作成を断念しElectronでアプリを作成しました。
しかし、つい先日DLL作者様のページを見た所、64bit版がリリースされていました!!
Electronは開発環境の整備やNode.jsを使う為、少々敷居が高い。そこで今回手軽なVBA環境でNFCカードを使った書籍管理アプリを改めて作成してみました。NFCが使えると出退勤のタイムレコーダの作成や認証処理にも使えるので社内向けアプリでは活躍の場が広がります。
※64bit版Officeを使っているので、32bit版の場合はDLLの呼び出しと利用するDLLが異なるので注意です。
目次
今回使用するものとライブラリ
カードリーダーとドライバー
- 今回使用するAccessファイル
- SheepSmartCard64.dll
- Sony RC-S380 カードリーダー
- NFCポートソフトウェア
- ISBNが読み込めるバーコードリーダー
DLLについては作者様のサイトよりダウンロードし、今回使用するAccessファイルと同じ場所に入れてください。書籍検索機能も装備しているため、利用する場合には事前準備が必要です。また、バーコードリーダですが適当なものでもISBNコード(13桁)はだいたい読めるはずなので、安いものを用意してもらえればと思います。
また、NFCのカードではなく、スマフォ搭載のNFCでも行けるかもしれません。運転免許証やマイナンバーカード等のType-Bカードは使えないので注意が必要です。SuicaやPasmoは利用可能です。会社側でカードを用意せずとも、手持ちのNFCのidm番号を登録しておいて、利用することでカードのコストをゼロに出来ますね。
※Electronとは違い、カードをかざしただけではNFC読み取りはなされません。
使用する外部サービス
事前準備と仕様
楽天ブックスAPIの準備
今回、国会図書館APIおよび楽天ブックスAPIを使っての書籍検索を装備しています。書籍登録時に国会図書館APIでまず検索し、見つからない場合には、楽天ブックスAPIで続けて検索をする仕様になっています(ただ、国会図書館APIは反応が鈍いので、検索する順番は逆の方が良いかもしれません)。
参照設定
今回のAccessアプリで追加してる参照設定は以下の通り。新規で作成する場合には参照設定に追加しておきましょう。遅延バインディングで良いのであれば、1x.0 Object Library以外を参照させない方法も可能です。
- Microsoft Office 1x.0 Object Library - 独自のリボンを使う場合に必要
- Microsoft Scripting Runtime - FSOなどのサービスを利用するのに必要
- Microsoft XML v6.0 - 国会図書館APIの返り値であるXMLファイルの解析に必要
図:今回は3個の設定を追加しています。
仕様
登録窓口フォームについて
貸出フォームである登録窓口フォームは、今回隠しオブジェクトになっています。また、以下のコードにより起動時に最大化されてフレームレスになっているので、画面いっぱいまでフルスクリーンになります。
1 2 3 |
'最大化表示 DoCmd.Maximize DoCmd.RunCommand acCmdAppMinimize |
隠しオブジェクトを表示する方法は以下の通りです。
- Accessのサイドバー上の▼の部分を右クリック
- ナビゲーションオプションをクリック
- 表示オプションの「隠しオブジェクトの表示」をチェックを入れて、OKにする
- 登録窓口フォームがサイドバーに表示されます
図:隠しオブジェクトは普段は非表示にしています
database.accdbについて
データを保存してるdatabase.accdbには、本体のアプリであるooteboook.accdbより起動時にリンクテーブルにて接続させています。また、このdatabase.accdbは暗号化を施しています(パスワードはhogehoge2020)。以下のコードでリンクテーブルを起動時にクリアして接続し直しています。また、このファイルは本体アプリ起動時に自動的に日付時間でバックアップファイルが5世代で作成されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
'リンクテーブルを削除する Function deleteLink() As Boolean On Error Resume Next Dim mdb As DAO.Database Dim tdf As DAO.TableDef Set mdb = CurrentDb DoCmd.DeleteObject acTable, "ユーザマスタ" DoCmd.DeleteObject acTable, "貸出マスタ" DoCmd.DeleteObject acTable, "bookマスタ" DoCmd.DeleteObject acTable, "廃棄bookマスタ" deleteLink = True Set mdb = Nothing End Function '外部のデータベースにリンクテーブルを貼る Public Function makelink() On Error GoTo Err_makelink 'リンクテーブルを設定する Dim db As DAO.Database Dim tb As DAO.TableDef Dim basemdbpath As Variant Set db = CurrentDb() '接続パスワード Dim passcode As String passcode = "hogehoge2020" 'データ保存ファイルへリンクテーブルを貼る basemdbpath = CurrentProject.Path & "\database.accdb" Set tb = db.CreateTableDef("ユーザマスタ") tb.Connect = "MS Access;PWD=" & passcode & ";DATABASE=" & basemdbpath tb.SourceTableName = "ユーザマスタ" db.TableDefs.Append tb Set tb = db.CreateTableDef("貸出マスタ") tb.Connect = "MS Access;PWD=" & passcode & ";DATABASE=" & basemdbpath tb.SourceTableName = "貸出マスタ" db.TableDefs.Append tb Set tb = db.CreateTableDef("bookマスタ") tb.Connect = "MS Access;PWD=" & passcode & ";DATABASE=" & basemdbpath tb.SourceTableName = "bookマスタ" db.TableDefs.Append tb Set tb = db.CreateTableDef("廃棄bookマスタ") tb.Connect = "MS Access;PWD=" & passcode & ";DATABASE=" & basemdbpath tb.SourceTableName = "廃棄bookマスタ" db.TableDefs.Append tb Exit_makelink: Exit Function Err_makelink: MsgBox Err.Description Resume Exit_makelink End Function |
database.accdbの接続パスワードを変更するにはコード内のpasscodeを変更するだけでなく、database.accdb側のパスコードを変更する必要があります。以下の手順で行います。
- Accessを起動する
- ファイル⇒開く⇒参照⇒対象のdatabase.accdbを指定する
- ここで開くボタンの横にある▼をクリックし、排他モードで開くにする
- パスワードを入力
- ファイル⇒情報⇒データベースの解読をクリック
- 改めて現在のパスワードを入力
- ファイル⇒情報⇒パスワードを使用して暗号化をクリック
- 新しいパスワードを設定し、コード側のpasscodeも書き換える
- 閉じる
passcodeはコード内に書くのではなく起動時に入力するようなダイアログを用意してメモリ上で保存しておくほうが望ましいでしょう。
図:暗号化は排他モードで開く必要性があります
使い方
データの整備
ユーザの登録
事前にユーザの登録が必要です。
- リボンの各種設定とアプリ⇒ユーザ登録を開く
- ユーザID(例えば社員番号などの重複しないコード)、Felicaコード(NFCのidm番号)の2つは最低限登録しておきます
登録がないと貸出処理が出来ません。
書籍の登録
事前に書籍の登録情報がないと貸出が行なえません。
- リボンの各種設定とアプリ⇒書籍データのスキャン取り込みを開く
- ISBNコードをバーコードリーダなどで読み取る。
- 読み取れたらEnterキーを押すと、国会図書館APIおよび楽天ブックスAPIが実行されて書籍情報がロードされる
- ロードされたら、登録ボタンを押すとマスタに登録されます。重複登録は出来ません。
図:書籍情報は手入力は不要です
書籍の廃棄登録
書籍が破損したり不要になって登録抹消する場合は以下の手順で行います。
- リボンの各種設定とアプリ⇒書籍データのスキャン取り込みを開く
- 書籍廃棄のタブをクリック
- ISBNコードをバーコードリーダなどで読み取る。
- 廃棄ボタンをクリックするとマスタから削除され、廃棄Bookマスタに登録されます。
図:廃棄登録も必要なメンテナンスです
実際の貸出・返却処理
実際の貸出処理は前述のデータ整備がなされていれば簡単です。ユーザは以下の手順で貸し出し・返却を行います。管理者はリボンの各種共通台帳にある「各種設定とアプリ」⇒貸出フォーム表示をクリックすれば、Accessフォームがフルスクリーン表示で開かれますので、この状態で運用を行います(左下のボタンを押せば閉じられます)
- 貸出処理はISBNコードは書籍からバーコードリーダを使って読み込ませましょう
- セキュリティカードNo.はPasoriからNFCをかざして、貸出・返却のボタンを押す事で読み込まれます。
- 貸出・返却のボタンを押すと書籍登録とユーザ登録チェックの後に、データベースへと登録がなされます。
図:フルスクリーン表示で専用端末化
ソースコード
書籍検索用コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
Option Compare Database 'プロキシアドレス Const proxyuri As String = "ここにプロキシのアドレスを入れる" 'XMLデータ保存用 Public xmldata As Variant '楽天Booksパラメータ Const appid As Variant = "ここにAPPIDを入れる" 'Const appsecret As Variant = "ここにAPP Secretを入れる" 'Const afiid As Variant = "ここに楽天アフィリエイトIDを入れる" ' 'Const Callback As String = "ここにコールバックURLを入れる" Const rakutenurl As String = "https://app.rakuten.co.jp/services/api/BooksBook/Search/20170404?applicationId=" 'XMLデータ Private xmlDocument As MSXML2.DOMDocument60 '国会図書館API検索 Public Function kokkaibook(isbncode As Variant) As Boolean On Error Resume Next '変数を宣言 Dim URL As String Dim statusflg As Variant URL = "http://iss.ndl.go.jp/api/opensearch?isbn=" & isbncode 'POST通信でAPIを叩いてデータを取得 With CreateObject("WinHttp.WinHttpRequest.5.1") .Open "GET", URL, False .setProxy 2, proxyuri 'プロキシサーバのURLとポート番号 .send '返ってきた値をもとにデータを処理 Select Case .Status Case 200 'JSONデータを取得する xmldata = .responseText Case Else 'フラグを返す kokkaibook = False End Select End With '取得データの中身を取得 'Microsoft XML v6.0 を参照設定 Set xmlDocument = Nothing Set xmlDocument = New MSXML2.DOMDocument60 xmlDocument.async = False xmlDocument.LoadXML (xmldata) If (xmlDocument.parseError.ErrorCode <> 0) Then 'ロード失敗 Dim strMsg As String strMsg = xmlDocument.parseError.reason 'エラー内容を出力 MsgBox "ロードに失敗しました・・・" & vbCrLf & vbCrLf & strMsg, vbCritical Exit Function End If 'XMLノード取得用 Dim rssNode As IXMLDOMNode Dim chkNode As IXMLDOMNode Dim chanNode As IXMLDOMNode Dim tempdate As Variant Dim tempprice As Variant Dim i As Integer '検索結果をチェックする Set chkNode = xmlDocument.SelectSingleNode("//rss/channel") For Each chanNode In chkNode.ChildNodes DoEvents DoCmd.Hourglass True 'タイトルを取得する If chanNode.nodeName = "openSearch:totalResults" Then If chanNode.Text = "0" Then '終了処理 'オブジェクトの開放 Set chkNode = Nothing Set chanNode = Nothing Set xmlDocument = Nothing DoCmd.Hourglass False kokkaibook = False Exit Function End If End If Next chanNode DoCmd.Hourglass False 'itemノードを取得する Set rssNode = xmlDocument.SelectSingleNode("//rss/channel/item") 'ノードを精査する For Each chanNode In rssNode.ChildNodes DoEvents 'タイトルを取得する If chanNode.nodeName = "title" Then Forms!書籍情報登録!booktitle.Value = chanNode.Text End If 'リンク先を取得する If chanNode.nodeName = "link" Then Forms!書籍情報登録!booklink.Value = chanNode.Text End If '著者名を取得する If chanNode.nodeName = "author" Then Forms!書籍情報登録!authorname.Value = chanNode.Text End If '出版社名を取得する If chanNode.nodeName = "dc:publisher" Then Forms!書籍情報登録!pubname.Value = chanNode.Text End If '発売日 If chanNode.nodeName = "pubDate" Then tempdate = CDate(Mid(chanNode.Text, 5, 12)) Forms!書籍情報登録!pubdate.Value = tempdate End If '価格 If chanNode.nodeName = "dcndl:price" Then tempprice = Replace(chanNode.Text, "円", "") Forms!書籍情報登録!pubprice.Value = tempprice End If Next chanNode 'オブジェクトの開放 Set rssNode = Nothing Set chanNode = Nothing Set xmlDocument = Nothing '値を返す kokkaibook = True End Function '楽天Books API検索モジュール Public Function rakutenbook(isbncode As Variant) As Boolean '変数を宣言 Dim URL As String URL = rakutenurl & appid & "&isbn=" & isbncode Debug.Print URL 'JSONをパースする用の変数 Dim doc, jsn, Json 'JSON受信用 'HTMLDocumentを取得 Set doc = CreateObject("HtmlFile") 'scriptタグを追加 doc.Write "<script>document.JsonParse=function (s) {return eval('(' + s + ')');}</script>" 'POST通信でAPIを叩いてデータを取得 With CreateObject("WinHttp.WinHttpRequest.5.1") .Open "GET", URL, False .setProxy 2, proxyuri 'プロキシサーバのURLとポート番号 .setRequestHeader "Content-Type", "application/json; charset=UTF-8" .send '返ってきた値をもとにデータを処理 Debug.Print .Status Select Case .Status Case 200 'JSONデータを取得する Json = .responseText If Len(Trim(Json)) > 0 Then 'パース関数でJSONオブジェクトを取得 Set jsn = doc.JsonParse(Json) tempArray = Split(jsn.Items, ",") blength = UBound(tempArray) - LBound(tempArray) Set o = CallByName(jsn.Items, 0, VbGet) Set o2 = CallByName(o, "Item", VbGet) '各種返り値をフォームに格納する Forms!書籍情報登録!booktitle.Value = CallByName(o2, "title", VbGet) Forms!書籍情報登録!booklink.Value = CallByName(o2, "itemUrl", VbGet) Forms!書籍情報登録!authorname.Value = CallByName(o2, "author", VbGet) Forms!書籍情報登録!pubname.Value = CallByName(o2, "publisherName", VbGet) tempdate = CDate(CallByName(o2, "salesDate", VbGet)) Forms!書籍情報登録!pubdate.Value = tempdate Forms!書籍情報登録!pubprice.Value = CallByName(o2, "itemPrice", VbGet) '値を返す rakutenbook = True Else rakutenbook = False End If Case Else 'フラグを返す rakutenbook = False End Select End With End Function |
- 今回は社内向けアプリなので、プロキシーを超える為に設定を追加しています。利用しない場合は、WinHTTPの.setProxy 2, proxyuriをコメントアウトすると良いでしょう。
- 今回のケースでは楽天ブックスAPIのAPIIDのみで構築可能です
- コード冒頭のGeneralセクションに、楽天ブックスAPIで使用するAPPIDなどをセットしておく必要があります。
- 国会図書館APIは叩いた結果がXMLで返ってくるので、解析を行いフォームに追加する仕組みです。API実行に特にAPPIDなどは不要ですが、レスポンスが非常に悪いです(とにかく遅い)
- 楽天ブックスAPIは叩いた結果がJSONで返ってくるので、CallByName関数で解析を行います。64bit環境なのでScriptControlを使った手法は利用できません。
書籍の貸出・返却用コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
'貸出ボタンのルーチン Private Sub コマンド11_Click() 'エラーハンドリング開始 On Error GoTo err_handle '入力内容のvalidation If IsNull(Me.isbncode.Value) Or Me.isbncode.Value = "" Then MsgBox "ISBNコードが空っぽですよ" Me.isbncode.SetFocus Exit Sub End If 'ISBNコードが登録済みのデータ内にあるかチェック Dim isbnman As Variant Dim result As Variant isbnman = Me.isbncode.Value result = DCount("*", "bookマスタ", "ISBNコード=" & isbnman) If result = 0 Then MsgBox "入力されたISBNコードはシステムに未登録です。" Me.isbncode.SetFocus Exit Sub End If 'セキュリティカード番号が登録済みのデータ内にあるかチェック Dim secman As Variant 'カードを読み取り secman = readcard() 'retの値が空の場合 If secman = "" Then Exit Sub Else Me.seccard.Value = secman End If 'セキュリティカードナンバーのデータの件数チェック result = DCount("*", "ユーザマスタ", "Felicaコード='" & secman & "'") If result = 0 Then MsgBox "入力されたセキュリティカード番号はシステムに未登録です。" Me.seccard.SetFocus Exit Sub End If '貸出マスタにデータを登録 'データベース接続用変数 Dim db As Database Dim rst As DAO.Recordset Set db = CurrentDb Set rst = db.OpenRecordset("貸出マスタ") With rst .AddNew !Felicaコード = secman !ISBNコード = isbnman !貸出日 = Now() .Update End With '終了処理 Set db = Nothing Set rst = Nothing 'メッセージとフォーカス MsgBox "貸出処理完了!!" Me.isbncode.SetFocus '終了処理 Me.isbncode.Value = "" Me.seccard.Value = "" end_handle: Exit Sub err_handle: MsgBox Err.Number & " : " & Err.Description, vbExclamation Resume end_handle End Sub '返却ボタンのルーチン Private Sub コマンド12_Click() 'エラーハンドリング開始 On Error GoTo err_handle '入力内容のvalidation If IsNull(Me.isbncode.Value) Or Me.isbncode.Value = "" Then MsgBox "ISBNコードが空っぽですよ" Me.isbncode.SetFocus Exit Sub End If 'ISBNコードが登録済みのデータ内にあるかチェック Dim isbnman As Variant Dim result As Variant isbnman = Me.isbncode.Value result = DCount("*", "bookマスタ", "ISBNコード=" & isbnman) If result = 0 Then MsgBox "入力されたISBNコードはシステムに未登録です。" Me.isbncode.SetFocus Exit Sub End If 'セキュリティカード番号が登録済みのデータ内にあるかチェック Dim secman As Variant 'カードを読み取り secman = readcard() 'retの値が空の場合 If secman = "" Then Exit Sub Else Me.seccard.Value = secman End If result = DCount("*", "ユーザマスタ", "Felicaコード='" & secman & "'") If result = 0 Then MsgBox "入力されたセキュリティカード番号はシステムに未登録です。" Me.seccard.SetFocus Exit Sub End If '貸出マスタにデータを登録 'データベース接続用変数 Dim db As Database Dim rst As DAO.Recordset Set db = CurrentDb Dim strSQL As String Dim henday As Variant Dim henflg As Boolean 'フラグ初期化 henflg = False '指定のレコードがあるかどうかチェック result = DCount("*", "貸出マスタ", "Felicaコード='" & secman & "' AND ISBNコード=" & isbnman) If result = 0 Then MsgBox "指定の書籍の貸出履歴が見つかりませんでした。" '終了処理 Set db = Nothing Exit Sub End If '特定のレコードを抽出する strSQL = "SELECT * FROM 貸出マスタ WHERE Felicaコード='" & secman & "' AND ISBNコード=" & isbnman Set rst = db.OpenRecordset(strSQL) Do Until rst.EOF '返却日の値を取得する henday = rst!返却日 '返却日が空かどうかをチェックする If IsNull(henday) Or henday = "" Then '返却日を追加記入 With rst .Edit !返却日 = Now() .Update End With 'フラグを立てる henflg = True Else '既に返却データが入ってるので何もしない End If '次のレコードへ移動 rst.MoveNext Loop '終了処理 Set db = Nothing Set rst = Nothing If henflg = True Then MsgBox "返却処理が完了しました。" Else MsgBox "返却処理すべきデータが見つかりませんでした。" End If '元に位置にフォーカスを移動する Me.isbncode.SetFocus 'データをクリアする Me.isbncode.Value = "" Me.seccard.Value = "" end_handle: Exit Sub err_handle: MsgBox Err.Number & " : " & Err.Description, vbExclamation Resume end_handle End Sub |
- 書籍のISBNコードおよびNFCのidm番号がフォーム上に入ってる状態で貸出および返却を実行した場合の処理です。
- 貸出の場合idmからユーザ登録の有無、isbnから書籍登録の有無をチェックしてから登録になります。
- 今回のアプリでは同じ書籍が複数ある場合のカウントチェックなどは行っていません。
- 返却の場合idmからユーザ登録の有無、isbnから貸出マスタを参照し返却済みかどうかを探しだしチェックしてから登録になります。
- 複数書籍があり貸出を行っていてもidmで区別をしているので、他人の貸出に返却処理が追加される事はありません。
カードリーダ読み取り用コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
Option Compare Database 'SheepSmartCard64.dllを読み込ませる(カレントディレクトリより) Public Declare PtrSafe Function GetSmartCardUID Lib "SheepSmartCard64.dll" (ByVal SCardUID As String) As Integer 'カードリーダー読み取り Public Function readcard() As Variant '変数の宣言 Dim ret As Integer Dim strCardUID As String 'DLL読み込みカレントディレクトリの変更等用 ChDrive Application.CurrentProject.Path ChDir Application.CurrentProject.Path ChDriver = Application.CurrentProject.Path '空文字のままで関数に渡すとエラーとなるので、Null文字で埋めておきます strCardUID = String$(128, vbNullChar) '文字列からNull文字を除去 ret = GetSmartCardUID(strCardUID) strCardUID = Replace(strCardUID, vbNullChar, "") '読み取り結果 Select Case ret Case 0 '読み取り成功 readcard = strCardUID Case 100 'セットされていない? MsgBox "カードがセットされていないか、または読み取りできないカードがセットされています。" readcard = "" Case 200 'カードリーダー接続不良 MsgBox "カードリーダーを認識できません。接続されているかご確認下さい。" readcard = "" Case 300 'SmartCardサービスが起動していない MsgBox "スマートカードサービスが起動していないか、またはインストールされていません。" readcard = "" Case 400 '読み取り失敗 MsgBox "カードIDの取得に失敗しました。" readcard = "" Case Else 'その他のエラー MsgBox "予期しないエラーが発生しました。" readcard = "" End Select End Function |
- SheepSmartCard作者さんのコードそのものですが、通常このDLLはC:\Windows\System32に配置するのですが、社内PCだと制限で入れられない事が多いので、DLL読み込みカレントディレクトリの変更等用のコードを追加して、カレントディレクトリ内にあるDLLを読み込ませています(DLLのフルパス指定は必要ありません)
- Pasoriがきちんと接続されていれば、NFCをかざしてデータ取得処理を行えば、NFCのidm番号がきちんと読み取れます。
図:64bit環境でもNFCが使えるととても利用の幅が広がります