VB.NET で felicalib.dll を使ってみる
- ■ このページの目標
-
(1) まずパソコンに PaSoRi をつないで…
(2) 携帯の FeLiCa (おサイフケータイ)を認識させた状態にして…
(3) ソフトを起動し、FeliCa 読み取りボタンをクリックすると…
(4) IDm, PMm が表示されることを目指します!
- ■ PaSoRi を使って Felica にアクセスするには?
-
PaSoRi を使って Felica の IDM, PMm を読み取るために、VB.NET で自作ソフトを作ってみようと思いました。
しかし、PaSoRi を直接操作して Felica の内容を読み取る手段がありません…
そこで、発見したのが felicalib.dll です。
felicalib.dll を仲介して PaSoRi を操作します。
イメージ的にはこんな感じでアクセスします。
- ■ felicalib.dll とは?
-
PaSoRi を使って FeliCa のデータを読み書きするためのライブラリです。 有志の方が作られました。
今でこそ Sony の公式サイト から SDK for NFC Starter Kit を無償でダウンロードできますが
以前は、お値段が10万円以上で法人しか入手できなかったようです。
そのため、無償で使える felicalib.dll が開発されたみたいです。
また、SDK for NFC Starter Kit を使えば PaSoRi RC-S370 以前の型番だと操作できましたが
最新の PaSoRi RC-S380 は操作できなくなっています。
felica_nfc_library.dll が PaSoRi RC-S380 に未対応で、ライブラリの初期化に失敗するのが原因みたいです。
しかし、felicalib.dll では PaSoRi RC-S380 も含めて IDm、PMm の取得、フリー領域の読み書きはできます。
以上の理由から、今回は felicalib.dll を使わせて頂きます。
- ■ プログラム作成に必要な物
-
(1) Visual Basic 2005, 2008, 2010, 2012 のいずれかで Express Edition がインストールされていれば十分です。
プロジェクトを新規作成する時に、ターゲットとなる .NET Framework のバージョンは 2.0 でかまいません。
(2) FeliCaポートソフトウェア がインストールされていること。
PaSoRi があれば、付属のCDにドライバが入ってるはずなので、そこからインストールして下さい。
手元にCDがない場合は こちら からダウンロードしてインストールして下さい。
(3) PaSoRi がパソコンに接続されていること。
対応機種: RC-S380 / S370 / S330 / S320
(4) felicalib.dll がダウンロードされていること。
こちら から felicalib-0.4.2.zip をダウンロードして下さい。
解凍して felicalib.dll を実行ファイル(exe)と同じフォルダに入れれば完了です。
- ■ VB.NET で felicalib.dll を使うためのサンプル
-
felicalib.dll を使うためのサンプルは C/C++, C# 対応のものはありますが、VB.NET 対応のものがなかったので作ってみました。
modFelicaLib.vb がプログラムの中心になります。
使いたい人がいるかはわかりませんが、自由に使って頂いて結構です (^^;
※VB 2005 (.NET Framework 2.0) 以降を対象としています。
[ Visual Basic .NET ]
[ Form1.vb ]
Public Class Form1 '====================== ' felicalib.dll クラス '====================== Private felicalib As New CFelicaLib '================ ' フォームロード '================ Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load '------------------ ' DLL 存在チェック '------------------ If Not isDLLExists() Then MessageBox.Show("felicalib.dll がありません。", Me.Text) Application.Exit() End If '------------------ ' ボタンの名前変更 '------------------ btnFelica.Text = "FeliCa 読み取り" End Sub '====================== ' ボタン・クリック処理 '====================== Private Sub btnFelica_Click(sender As System.Object, e As System.EventArgs) Handles btnFelica.Click Dim sIDm As String Dim sPMm As String Dim sMsg As String '------------------- ' PaSoRi に接続する '------------------- If Not felicalib.Pasori_Connect() Then MessageBox.Show("PaSoRi に接続できませんでした。", Me.Text) Return End If '---------------------------- ' ポーリング(FeliCa読み取り) '---------------------------- If felicalib.Polling() Then '----------------- ' IDm, PMm を取得 '----------------- sIDm = felicalib.getIDm() sPMm = felicalib.getPMm() '------------------ ' メッセージを表示 '------------------ sMsg = "IDm=[" & sIDm & "]" & vbNewLine & _ "PMm=[" & sPMm & "]" MessageBox.Show(sMsg, Me.Text) Else '-------------------------- ' ポーリングに失敗した場合 '-------------------------- MessageBox.Show("FeliCa がセットされていません。", Me.Text) End If '------------------- ' PaSoRi を解放する '------------------- felicalib.Pasori_Free() End Sub End Class
[ modFelicaLib.vb ]
Imports System.Runtime.InteropServices Imports UInt8 = System.Byte Module modFelicaLib Private Const MAX_SYSTEM_CODE = 8 Private Const MAX_AREA_CODE = 16 Private Const MAX_SERVICE_CODE = 256 '------------------------------- ' FeliCa の情報を格納する構造体 '------------------------------- <StructLayout(LayoutKind.Sequential)> _ Private Structure felica Public p As IntPtr 'PaSoRi ハンドル Public systemcode As UInt16 'システムコード <MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> _ Public IDm() As UInt8 'IDm <MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> _ Public PMm() As UInt8 'PMm 'systemcode Public num_system_code As UInt8 '列挙システムコード数 <MarshalAs(UnmanagedType.ByValArray, SizeConst:=MAX_SYSTEM_CODE)> _ Public system_code() As UInt16 '列挙システムコード 'area/service codes Public num_area_code As UInt8 'エリアコード数 <MarshalAs(UnmanagedType.ByValArray, SizeConst:=MAX_AREA_CODE)> _ Public area_code() As UInt16 'エリアコード <MarshalAs(UnmanagedType.ByValArray, SizeConst:=MAX_AREA_CODE)> _ Public end_service_code() As UInt16 'エンドサービスコード Public num_service_code As UInt8 'サービスコード数 <MarshalAs(UnmanagedType.ByValArray, SizeConst:=MAX_SERVICE_CODE)> _ Public service_code() As UInt16 'サービスコード End Structure '----------- ' constants '----------- 'システムコード (ネットワークバイトオーダ/ビックエンディアンで表記) Private Const POLLING_ANY = &HFFFF Private Const POLLING_EDY = &HFE00 'システムコード: 共通領域 (Edyなどが使用) Private Const POLLING_SUICA = &H3 'システムコード: サイバネ領域 '------ ' APIs '------ '【関 数 名】pasori_open '【第1引数】[in] dummy '【戻 り 値】pasori ハンドル <DllImport("felicalib.dll")> _ Private Function pasori_open(ByVal dummy As IntPtr) As IntPtr End Function '【関 数 名】pasori_close '【第1引数】[in] p pasoriハンドル (pasori_open で得たポインタを指定する) <DllImport("felicalib.dll")> _ Private Sub pasori_close(ByVal p As IntPtr) End Sub '【関 数 名】InteropServices '【第1引数】[in] p pasoriハンドル (pasori_open で得たポインタを指定する) '【戻 り 値】エラーコード <DllImport("felicalib.dll")> _ Private Function pasori_init(ByVal p As IntPtr) As Integer End Function '【関 数 名】felica_polling '【第1引数】[in] p pasoriハンドル (pasori_open で得たポインタを指定する) '【第2引数】[in] systemcode システムコード '【第3引数】[in] RFU RFU (使用しない) '【第4引数】[in] timeslot タイムスロット '【戻 り 値】felicaハンドル (MarshalクラスのPtrToStructureメソッドを使用) <DllImport("felicalib.dll")> _ Private Function felica_polling( _ ByVal p As IntPtr, _ ByVal systemcode As UInt16, _ ByVal RFU As UInt8, _ ByVal timeslot As UInt8 _ ) As IntPtr End Function '【関 数 名】felica_free '【第1引数】[in] f felicaハンドル (felica構造体のポインタを指定する) <DllImport("felicalib.dll")> _ Private Sub felica_free(ByVal f As IntPtr) End Sub '【関 数 名】felica_getidm '【第1引数】[in] f felicaハンドル (felica構造体のポインタを指定する) '【第2引数】[out] buf IDm を格納するバッファ(8バイト) <DllImport("felicalib.dll")> _ Private Sub felica_getidm(ByVal f As IntPtr, ByVal buf As IntPtr) End Sub '【関 数 名】felica_getpmm '【第1引数】[in] f felicaハンドル (felica構造体のポインタを指定する) '【第2引数】[out] buf PMm を格納するバッファ(8バイト) <DllImport("felicalib.dll")> _ Private Sub felica_getpmm(ByVal f As IntPtr, ByVal buf As IntPtr) End Sub '【関 数 名】felica_read_without_encryption02 '【第1引数】[in] f felicaハンドル (felica構造体のポインタを指定する) '【第2引数】[in] servicecode サービスコード '【第3引数】[in] mode モード(使用しない) '【第4引数】[in] addr ブロック番号 '【第5引数】[out] data データ(16バイト) '【戻 り 値】エラーコード <DllImport("felicalib.dll")> _ Private Function felica_read_without_encryption02( _ ByVal f As IntPtr, _ ByVal servicecode As Integer, _ ByVal mode As Integer, _ ByVal addr As UInt8, _ ByVal buf As IntPtr _ ) As Integer End Function '【関 数 名】felica_write_without_encryption '【第1引数】[in] f felicaハンドル (felica構造体のポインタを指定する) '【第2引数】[in] servicecode サービスコード '【第3引数】[in] mode モード(使用しない) '【第4引数】[in] addr ブロック番号 '【第5引数】[out] data データ(16バイト) '【戻 り 値】エラーコード <DllImport("felicalib.dll")> _ Private Function felica_write_without_encryption( _ ByVal f As IntPtr, _ ByVal servicecode As Integer, _ ByVal addr As UInt8, _ ByVal buf As IntPtr _ ) As Integer End Function '【関 数 名】felica_enum_systemcode '【第1引数】[in] p pasoriハンドル (pasori_open で得たポインタを指定する) '【戻 り 値】felicaハンドル (felica構造体のポインタを指定する) <DllImport("felicalib.dll")> _ Private Function felica_enum_systemcode(ByVal p As IntPtr) As IntPtr End Function '【関 数 名】felica_enum_service '【第1引数】[in] p pasoriハンドル (pasori_open で得たポインタを指定する) '【第2引数】[in] systemcode システムコード '【戻 り 値】felicaハンドル (felica構造体のポインタを指定する) <DllImport("felicalib.dll")> _ Private Function felica_enum_service(ByVal p As IntPtr, ByVal systemcode As UInt16) As IntPtr End Function '=================================== ' Win32 API (DLL存在チェックに必要) '=================================== <DllImport("kernel32")> _ Private Function SetDllDirectory(ByVal lpPathName As String) As Boolean End Function <DllImport("kernel32")> _ Private Function LoadLibrary(ByVal lpLibFileName As String) As Integer End Function <DllImport("kernel32")> _ Private Function GetProcAddress(ByVal hModule As Integer, ByVal lpProcName As String) As Integer End Function <DllImport("kernel32")> _ Private Function FreeLibrary(ByVal hLibModule As Integer) As Boolean End Function '==================================================== '【関数名】isDLLExists '【引 数】なし '【戻り値】[out] Boolean TRUE DLLの読み込みに成功 ' FALSE DLLの読み込みに失敗 '---------------------------------------------------- ' felicalib.dll が存在するかチェックする。 '==================================================== Public Function isDLLExists() As Boolean 'DLLのハンドル Dim hModule As IntPtr = IntPtr.Zero 'DLL内の関数を受け取るポインタ Dim pPasoriOpen As IntPtr = IntPtr.Zero Dim pPasoriClose As IntPtr = IntPtr.Zero Dim pPasoriInit As IntPtr = IntPtr.Zero Dim pFelicaPolling As IntPtr = IntPtr.Zero Dim pFelicaFree As IntPtr = IntPtr.Zero Dim pFelicaGetidm As IntPtr = IntPtr.Zero Dim pFelicaGetpmm As IntPtr = IntPtr.Zero Dim pFelicaReadWithoutEncryption02 As IntPtr = IntPtr.Zero Dim pFelicaWriteWithoutEncryption As IntPtr = IntPtr.Zero Dim pFelicaEnumSystemcode As IntPtr = IntPtr.Zero Dim pFelicaEnumService As IntPtr = IntPtr.Zero Try 'DLLプリロード対策 ※DLLの読み込み先から現在の作業ディレクトリ(CWD)を除外する SetDllDirectory("") 'DLLを読み込む hModule = LoadLibrary("felicalib.dll") If hModule = IntPtr.Zero Then Return False End If 'DLLの関数を読み込む pPasoriOpen = GetProcAddress(hModule, "pasori_open") pPasoriClose = GetProcAddress(hModule, "pasori_close") pPasoriInit = GetProcAddress(hModule, "pasori_init") pFelicaPolling = GetProcAddress(hModule, "felica_polling") pFelicaFree = GetProcAddress(hModule, "felica_free") pFelicaGetidm = GetProcAddress(hModule, "felica_getidm") pFelicaGetpmm = GetProcAddress(hModule, "felica_getpmm") pFelicaReadWithoutEncryption02 = GetProcAddress(hModule, "felica_read_without_encryption02") pFelicaWriteWithoutEncryption = GetProcAddress(hModule, "felica_write_without_encryption") pFelicaEnumSystemcode = GetProcAddress(hModule, "felica_enum_systemcode") pFelicaEnumService = GetProcAddress(hModule, "felica_enum_service") If pPasoriOpen = IntPtr.Zero OrElse _ pPasoriClose = IntPtr.Zero OrElse _ pPasoriInit = IntPtr.Zero OrElse _ pFelicaPolling = IntPtr.Zero OrElse _ pFelicaFree = IntPtr.Zero OrElse _ pFelicaGetidm = IntPtr.Zero OrElse _ pFelicaGetpmm = IntPtr.Zero OrElse _ pFelicaReadWithoutEncryption02 = IntPtr.Zero OrElse _ pFelicaWriteWithoutEncryption = IntPtr.Zero OrElse _ pFelicaEnumSystemcode = IntPtr.Zero OrElse _ pFelicaEnumService = IntPtr.Zero _ Then FreeLibrary(hModule) Return False End If '読み込み成功 FreeLibrary(hModule) Return True Catch ex As Exception MessageBox.Show(ex.Message, "isDLLExists()") If hModule <> IntPtr.Zero Then FreeLibrary(hModule) Return False End Try End Function '============================================= '【関 数 名】hexdump '【第1引数】[in] UInt8() データ配列 '【第2引数】[in] Integer 配列のサイズ '【戻 り 値】[out] String 16進数文字列 '--------------------------------------------- ' 受け取ったデータを16進数の文字列に変換する。 '============================================= Private Function hexdump(ByVal arg() As UInt8, ByVal size As Integer) As String Dim sResult As String = "" For I As Integer = 0 To size - 1 sResult &= arg(I).ToString("X2") Next Return sResult End Function '================================ ' felicalib.dll アクセス用クラス '================================ Class CFelicaLib Implements IDisposable 'デストラクタ '========== ' 変数定義 '========== Private p_ptr As IntPtr 'Pasoriポインタ Private f_ptr As IntPtr 'felica構造体ポインタ '=========================================== '【関数名】New '【引 数】なし '【戻り値】なし '------------------------------------------- ' コンストラクタ。ポインタの初期化を行なう。 '=========================================== Public Sub New() '初期化 p_ptr = IntPtr.Zero f_ptr = IntPtr.Zero End Sub '================================= '【関数名】Dispose '【引 数】なし '【戻り値】なし '--------------------------------- ' デストラクタ。PaSoRiを解放する。 '================================= Public Sub Dispose() Implements IDisposable.Dispose 'PaSoRi の接続を解放 Pasori_Free() End Sub '=============================================== '【関数名】Pasori_Connect '【引 数】なし '【戻り値】[out] Boolean TRUE PaSoRi接続成功 ' FALSE PaSoRi接続失敗 '----------------------------------------------- ' PaSoRiに接続して使用可能な状態にする。 '=============================================== Public Function Pasori_Connect() As Boolean Try '---------------------- ' PaSoRi ハンドル取得 '---------------------- p_ptr = pasori_open(Nothing) If p_ptr = IntPtr.Zero Then Return False End If '--------------------- ' PaSoRi 初期化(接続) '--------------------- If pasori_init(p_ptr) <> 0 Then Return False End If Return True Catch ex As Exception MessageBox.Show(ex.Message, "Pasori_Connect()") Return False End Try End Function '====================== '【関数名】Pasori_Free '【引 数】なし '【戻り値】なし '---------------------- ' PaSoRiの接続を解放 '====================== Public Sub Pasori_Free() Try If f_ptr <> IntPtr.Zero Then felica_free(f_ptr) If p_ptr <> IntPtr.Zero Then pasori_close(p_ptr) Catch ex As Exception MessageBox.Show(ex.Message, "Pasori_Free()") End Try End Sub '=========================================================== '【関数名】Polling '【引 数】なし '【戻り値】[out] Boolean TRUE felicaハンドルの取得に成功 ' FALSE felicaハンドルの取得に失敗 '----------------------------------------------------------- ' ポーリング。FeliCaの読み取り準備。 '=========================================================== Public Function Polling() As Boolean Try '-------------------------------- ' felicaハンドルを一度クリアする '-------------------------------- If f_ptr <> IntPtr.Zero Then felica_free(f_ptr) End If '------------ ' ポーリング '------------ f_ptr = felica_polling(p_ptr, POLLING_ANY, 0, 0) '------------ ' 結果を返す '------------ If f_ptr = IntPtr.Zero Then Return False Else Return True End If Catch ex As Exception MessageBox.Show(ex.Message, "Polling()") Return False End Try End Function '==================================== '【関数名】getIDm '【引 数】なし '【戻り値】[out] String FeliCaのIDm '------------------------------------ ' FeliCaのIDmを取得する。 '==================================== Public Function getIDm() As String Try '------------ ' エラー処理 '------------ If f_ptr = IntPtr.Zero Then Return "" End If '------------- ' IDm読み取り '------------- Dim IDm As String Dim buf(8) As UInt8 '--------------------- ' bufのアドレスを取得 '--------------------- Dim gch As GCHandle = GCHandle.Alloc(buf, GCHandleType.Pinned) Dim b As IntPtr = gch.AddrOfPinnedObject().ToInt32 felica_getidm(f_ptr, b) IDm = hexdump(buf, 8) gch.Free() Return IDm Catch ex As Exception MessageBox.Show(ex.Message, "getIDm()") Return "" End Try End Function '==================================== '【関数名】getPMm '【引 数】なし '【戻り値】[out] String FeliCaのPMm '------------------------------------ ' FeliCaのPMmを取得する。 '==================================== Public Function getPMm() As String Try '------------ ' エラー処理 '------------ If f_ptr = IntPtr.Zero Then Return "" End If '------------- ' PMm読み取り '------------- Dim PMm As String Dim buf(8) As UInt8 '--------------------- ' bufのアドレスを取得 '--------------------- Dim gch As GCHandle = GCHandle.Alloc(buf, GCHandleType.Pinned) Dim b As IntPtr = gch.AddrOfPinnedObject().ToInt32 felica_getpmm(f_ptr, b) PMm = hexdump(buf, 8) gch.Free() Return PMm Catch ex As Exception MessageBox.Show(ex.Message, "getIDm()") Return "" End Try End Function '====================================== '【関 数 名】getIDmPMm '【第1引数】[out] String FeliCaのIDm '【第2引数】[out] String FeliCaのPMm '【戻 り 値】なし '-------------------------------------- ' FeliCaのIDmとPMmを同時に取得する。 '====================================== Public Sub getIDmPMm(ByRef IDm As String, ByRef PMm As String) Try '------------ ' エラー処理 '------------ If f_ptr = IntPtr.Zero Then IDm = "" PMm = "" Return End If '-------------------------- ' felica構造体の実体を取得 '-------------------------- Dim f As felica = Marshal.PtrToStructure(f_ptr, GetType(felica)) '------------------ ' IDm・PMm読み取り '------------------ IDm = hexdump(f.IDm, 8) PMm = hexdump(f.PMm, 8) Catch ex As Exception MessageBox.Show(ex.Message, "getIDmPMm()") IDm = "" PMm = "" End Try End Sub End Class End Module
- ■ DllImport について
-
DLL がない場合、DllImport しただけではエラーは発生しません。
実際にその関数を使おうとしたタイミングで例外が発生し、プログラムが異常終了します。
DllImport した関数を使う場合は Try Catch 文で例外を処理するようにして下さい。
-
■構造体 で 固定長の配列 を宣言する方法
-
構造体の中で配列を宣言する場合、VB.NETでは要素数を指定できません。
通常は次のように宣言せざるをえません。
Structure Sample Dim buf() As UInt16 Public Sub Initialize() ReDim buf(8) End Sub End Structure
しかし、C , C++ で作られた DLL を VB.NET から使う場合、
データをやりとりするために配列の要素数を固定しなければいけない時があります。
そのため、VB.NET の構造体で 固定長の配列 を強引に宣言するサンプルを示します。
Structure Sample <MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> _ Public buf() As UInt16 Public Sub Initialize() ReDim buf(8) End Sub End Structure
これで、構造体の中はOKです。 しかし、まだ問題があります。
実はこの構造体、.NET Framework (マネージ環境) に対応した構造体なのです。
これは アンマネージ環境 の構造体と互換性がありません。
では、アンマネージ互換 の構造体を宣言してみましょう。
<StructLayout(LayoutKind.Sequential)> _ Structure Sample <MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> _ Public buf() As UInt16 Public Sub Initialize() ReDim buf(8) End Sub End Structure
先頭に <StructLayout(LayoutKind.Sequential)> が付いただけですね (^^;
これで .NET Framework 以外の環境とデータをやりとりできます。
- ■ .NET Framework 4.0 でDLLを呼び出すとエラーが出る?? (2014/10/26 追記)
-
.NET Framework 2.0~3.5 の実行環境は CLR2.0 でした。
しかし、.NET Framework 4.0 で CLR のバージョンが一新されました。
その結果、DLLImport による DLL の呼び出し規約がより厳しくなったようです。
具体的には以下のように書き直さないと実行時にエラーが出ます。
変更前 <DllImport("DLL名")>
変更後 <DllImport("DLL名", CallingConvention:=CallingConvention.Cdecl)>
Win32API を呼び出す場合は下記のように呼び出します。
変更前 <DllImport("DLL名")>
変更後 <DllImport("DLL名", CallingConvention:=CallingConvention.StdCall)>
※ () と , の位置がおかしかったので修正しました
- ■ コンパイル時の .NET Framework のバージョンについて (2014/10/26 追記)
-
.NET Framework 2.0~3.5 をターゲットにコンパイルされた場合、実行環境は CLR2.0 になります。
Windows Vista / 7 は最初から OS に .NET Framework 2.0 がインストールされています。
Windows 8 / 8.1 の場合は コントロールパネル>プログラムと機能>Windowsの機能の有効化または無効化 で
.NET Framework 3.5(.NET 2.0 および 3.0 を含む) を選択してインストールして下さい。
.NET Framework 4.0~4.5 をターゲットにコンパイルされた場合、実行環境は CLR4 になります。
Windows Vista / 7 の場合は .NET Framework 4.0 または 4.5 をインストールして下さい。
Windows 8 / 8.1 は最初から OS に .NET Framework 4.5 がインストールされています。
それぞれ対応したバージョンの実行環境(CLR)がないと実行時にエラーが出るはずです。
- ■ サンプル公開 (2014/10/26 追記)
-
Visual Studio 2010 でサンプルを作ってみました。
こちら のページの felicalib_sample.zip からダウンロードできます。
.NET Framework 2.0 でコンパイルするように設定していますが、後で .NET Framework 4.0 に変更することもできます。
ただし、32ビット(x86)でコンパイルするようにして下さい。
詳しい解説は こちら にございますので、参考にして下さい。
- ■ FelicaDump に関して (2015/2/23 追記)
-
felicalib.dll を配布しているサイトで、Felica の情報をダンプするプログラム FelicaDump.exe が公開されています。
これを元に VB.NET でプログラムを作ってみました。
こちら のページの felicadump_sample.zip からダウンロードできます。
何かの役に立てば幸いです。
ただし、コメント欄でご意見を頂いたように (Protected) となっている部分は情報を取得できません。
おそらく暗号化部分はアクセス不可になっているためと思われますが、詳しい原因はわかりません。
試しに、C言語で書かれた FelicaDump.exe のソースコードを Debug モードでコンパイルしてみると
今回、私が作った VB.NET のプログラムと同じ結果になるんですけどね… (^^;
FelicaDump.exe のソースコードを Release モードでコンパイルすると結果が違うのも気になります。
おそらく、VC++の最適化処理で予期しないコードが生成されているのでしょう。
結局、詳しい原因がわからないままで申し訳ございません…
- ■ リンク先を変更しました (2018/1/11 追記)
-
サンプルのリンク先が切れていたようなので修正しました。
また、おまけとして felicalib.dll を使わず、Windowsの標準機能だけを使って Felica の IDm を読むサンプルも置いておきました。
こちら のページの nfc_pcsc_sample.zip からダウンロードできますので参考になれば幸いです。
ただし、このサンプルは Windows7 以降の winscard.dll に実装されている PC/SC という機能を使っています。
これは、NFC という国際規格を使ってICカードと通信するための仕組みです。
そのため、nfc_pcsc_sample.zip は Windows7 以降にのみ対応しています。
スポンサーサイト
windows8でVB2010でサンプルプログラムを作成走らせたところ、p_ptr = pasori_open(Nothing)のところでPInvokeStackImbalanceが検出されました。とのerrorが発生しました。
どのように、したら回避できるかご教示ください。
宜しくお願いします。
どのように、したら回避できるかご教示ください。
宜しくお願いします。
コメントチェックをしていなくて申し訳ございませんでした。
felicalib.dll は NFCポートソフトウェア(旧FeliCaポートソフトウェア)に含まれているfelica.dll を直接操作しております。
その為、NFCポートソフトウェアがインストールされていない環境では動作しません。
NFCポートソフトウェアをインストールすれば動作すると思いますのでお試し下さい。
felicalib.dll は NFCポートソフトウェア(旧FeliCaポートソフトウェア)に含まれているfelica.dll を直接操作しております。
その為、NFCポートソフトウェアがインストールされていない環境では動作しません。
NFCポートソフトウェアをインストールすれば動作すると思いますのでお試し下さい。
すいません、1つ前のコメントは正確ではありませんでした。
.NET Framework 2.0~3.5 の実行環境は全て CLR 2.0 でした。
しかし、.NET Framework 4.0 では実行環境である CLR のバージョンが一新されています。
そのため、DLLの呼び出し規約も多少変更されています。(より厳しくなったようです。)
DLLImport を少し書き直す必要があるため、後ほどブログ本文で解説を追記いたします。
.NET Framework 2.0~3.5 の実行環境は全て CLR 2.0 でした。
しかし、.NET Framework 4.0 では実行環境である CLR のバージョンが一新されています。
そのため、DLLの呼び出し規約も多少変更されています。(より厳しくなったようです。)
DLLImport を少し書き直す必要があるため、後ほどブログ本文で解説を追記いたします。
サンプルソースコード、素晴らしいです。
とても参考になりました。
VBでのIDmとPMmだけじゃなく、システムコード、エリアコード、サービスコードの一覧まで取得できました。
しかし、さすがにVBでは、
felica_read_without_encryption02
を使用する事はできませんでしょうか。
構造体のポインタ渡しとなると、無理ですよね(^^;
とても参考になりました。
VBでのIDmとPMmだけじゃなく、システムコード、エリアコード、サービスコードの一覧まで取得できました。
しかし、さすがにVBでは、
felica_read_without_encryption02
を使用する事はできませんでしょうか。
構造体のポインタ渡しとなると、無理ですよね(^^;
よく吟味せずにコメントしてしまいました。
サンプルソースの、getIDmメソッドを見れば一目瞭然でした。
ちゃんと、felica_read_without_encryption02が使えました。
ただ、felicalib.dll のサンプルである、FelicaDump.exe(C言語)ではfelica_read_without_encryption02で値が取得できている感じなのに、FelicaDump.exのソースと同じようなコーディングをしたのに、サービスコードがProtectになっていたら、Dumpする値は取得できないようですね。不思議です・・・
サンプルソースの、getIDmメソッドを見れば一目瞭然でした。
ちゃんと、felica_read_without_encryption02が使えました。
ただ、felicalib.dll のサンプルである、FelicaDump.exe(C言語)ではfelica_read_without_encryption02で値が取得できている感じなのに、FelicaDump.exのソースと同じようなコーディングをしたのに、サービスコードがProtectになっていたら、Dumpする値は取得できないようですね。不思議です・・・
サンプルソース、ありがとうございました。大変参考になりました。Visual Basic 2013 Expressでも特に問題なく動かせました。
felica_read_without_encryption02も、次のような感じで利用できました。サービスコードとアドレスの指定方法で少し戸惑いましたが、felicalib付属のFelicaDump.exeでデータをダンプして確認したところ上手くいきました。
'====================================
'【関数名】readWithoutEncryption02
'【引 数】なし
'【戻り値】[out] String 読み取ったデータ
'------------------------------------
' FeliCaのデータ読み取り
'====================================
Public Function readWithoutEncryption02() As String
Try
'------------
' エラー処理
'------------
If f_ptr = IntPtr.Zero Then
Return ""
End If
'----------
' 変数定義
'----------
Dim buf(16) As UInt8 ' 16桁分取得する。
'-------------------------
' 変数bufのアドレスを取得
'-------------------------
Dim gch As GCHandle = GCHandle.Alloc(buf, GCHandleType.Pinned)
Dim b As IntPtr = gch.AddrOfPinnedObject().ToInt32
Dim iRet As Integer
Dim iServiceCode As Integer
Dim iMode As Integer
Dim bAddr As Byte
Dim sReadData As String
iServiceCode = &H100B ' # 任意の位置のサービスコード Serivce code = 100B : Random Access Read only
bAddr = 0 ' # 任意の位置のアドレス
'-------------
' 読み取り
'-------------
iRet = felica_read_without_encryption02(f_ptr, iServiceCode, 0, bAddr, b)
sReadData = hexdump(buf, 16)
'------------
' 結果を返す
'------------
Return sReadData
Catch ex As Exception
MessageBox.Show(ex.Message, "readWithoutEncryption02()")
Return ""
End Try
End Function
felica_read_without_encryption02も、次のような感じで利用できました。サービスコードとアドレスの指定方法で少し戸惑いましたが、felicalib付属のFelicaDump.exeでデータをダンプして確認したところ上手くいきました。
'====================================
'【関数名】readWithoutEncryption02
'【引 数】なし
'【戻り値】[out] String 読み取ったデータ
'------------------------------------
' FeliCaのデータ読み取り
'====================================
Public Function readWithoutEncryption02() As String
Try
'------------
' エラー処理
'------------
If f_ptr = IntPtr.Zero Then
Return ""
End If
'----------
' 変数定義
'----------
Dim buf(16) As UInt8 ' 16桁分取得する。
'-------------------------
' 変数bufのアドレスを取得
'-------------------------
Dim gch As GCHandle = GCHandle.Alloc(buf, GCHandleType.Pinned)
Dim b As IntPtr = gch.AddrOfPinnedObject().ToInt32
Dim iRet As Integer
Dim iServiceCode As Integer
Dim iMode As Integer
Dim bAddr As Byte
Dim sReadData As String
iServiceCode = &H100B ' # 任意の位置のサービスコード Serivce code = 100B : Random Access Read only
bAddr = 0 ' # 任意の位置のアドレス
'-------------
' 読み取り
'-------------
iRet = felica_read_without_encryption02(f_ptr, iServiceCode, 0, bAddr, b)
sReadData = hexdump(buf, 16)
'------------
' 結果を返す
'------------
Return sReadData
Catch ex As Exception
MessageBox.Show(ex.Message, "readWithoutEncryption02()")
Return ""
End Try
End Function
このコメントは管理者の承認待ちです
コメントの投稿
トラックバックURL:http://siroshitsuji.blog.fc2.com/tb.php/18-78035767
Felicaでちょっと遊んでいるのですが、とあるカードには、とあるサービスコードのとあるブロック番号に とある文字列が書き込まれていて、それを読みだすといろいろ便利なことが出来そうです。 ということで、ここのページで公開されている「CFelicaLib.vb」に機能を追加…