白執事の徒然なる日々


主にコンピュータ関係の記事を書いています。

プロフィール

白執事☆

こんにちは、白執事です。
PC関係の記事を書いていきますので
よろしくお願いします。

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.dllPaSoRi 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++ で作られた DLLVB.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.zipWindows7 以降にのみ対応しています。


スポンサーサイト




テーマ:プログラミング | ジャンル:コンピュータ | カテゴリー:VB.NET | タグ:
コメント(7)トラックバック (1) | 2013年09月27日 (金)10時01分
windows8でVB2010でサンプルプログラムを作成走らせたところ、p_ptr = pasori_open(Nothing)のところでPInvokeStackImbalanceが検出されました。とのerrorが発生しました。
どのように、したら回避できるかご教示ください。
宜しくお願いします。
2014/10/04(土) 15:58:25|URL|相澤憲次 #zdvXpt9s [編集]
コメントチェックをしていなくて申し訳ございませんでした。

felicalib.dll は NFCポートソフトウェア(旧FeliCaポートソフトウェア)に含まれているfelica.dll を直接操作しております。
その為、NFCポートソフトウェアがインストールされていない環境では動作しません。
NFCポートソフトウェアをインストールすれば動作すると思いますのでお試し下さい。
2014/10/25(土) 22:05:10|URL|管理人 #- [編集]
すいません、1つ前のコメントは正確ではありませんでした。

.NET Framework 2.0~3.5 の実行環境は全て CLR 2.0 でした。
しかし、.NET Framework 4.0 では実行環境である CLR のバージョンが一新されています。
そのため、DLLの呼び出し規約も多少変更されています。(より厳しくなったようです。)

DLLImport を少し書き直す必要があるため、後ほどブログ本文で解説を追記いたします。
2014/10/25(土) 23:27:21|URL|管理人 #- [編集]
サンプルソースコード、素晴らしいです。
とても参考になりました。
VBでのIDmとPMmだけじゃなく、システムコード、エリアコード、サービスコードの一覧まで取得できました。

しかし、さすがにVBでは、
felica_read_without_encryption02
を使用する事はできませんでしょうか。
構造体のポインタ渡しとなると、無理ですよね(^^;
2014/11/24(月) 23:11:45|URL|じょばんに #.6NMvfV. [編集]
よく吟味せずにコメントしてしまいました。
サンプルソースの、getIDmメソッドを見れば一目瞭然でした。
ちゃんと、felica_read_without_encryption02が使えました。
ただ、felicalib.dll のサンプルである、FelicaDump.exe(C言語)ではfelica_read_without_encryption02で値が取得できている感じなのに、FelicaDump.exのソースと同じようなコーディングをしたのに、サービスコードがProtectになっていたら、Dumpする値は取得できないようですね。不思議です・・・
2014/11/26(水) 02:00:16|URL|じょばんに #.6NMvfV. [編集]
サンプルソース、ありがとうございました。大変参考になりました。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
2015/02/17(火) 12:03:26|URL|munmun #GWUCrvXI [編集]
このコメントは管理者の承認待ちです
2021/01/04(月) 01:22:53|| # [編集]
コメントの投稿
  • URL
  • コメント
  • パスワード
  • 秘密
  • 管理者にだけ表示を許可する
トラックバックURL:http://siroshitsuji.blog.fc2.com/tb.php/18-78035767
Felicaでちょっと遊んでいるのですが、とあるカードには、とあるサービスコードのとあるブロック番号に とある文字列が書き込まれていて、それを読みだすといろいろ便利なことが出来そうです。 ということで、ここのページで公開されている「CFelicaLib.vb」に機能を追加…
2016/03/17(木)21:26:24 | 今是昨非