single.php

SDK for NFC for Windowsの使い方(VC++編)

e-Taxでの確定申告用に購入した、ICカードリーダー「PaSoRi RC-S380」ですが、SDKを使うとカード情報を取得することができます。今回は最も簡単なVC++でアクセスする方法を紹介します。

ICカード リーダライタ

PaSoRiは、JRや地下鉄の乗車券として有名な「FeliCa」カードを読み取ることができるデバイスです。別名「ICカード リーダライタ」として呼ばれることもある機器です。

Amazonで2,484円で購入しました。数年に1度モデルチェンジをするので型番(私が購入したモデル RC-S380)は変わっていきますが、仕様は同じで、利用するアプリは継続して使えます。

SDK for NFC for Windows

SONY FeliCa 非接触カード技術のホームページ上にSDK for NFC for Windowsの開発ツールとしてダウンロードすることができます。

評価用として「SDK for NFC Starter Kit」が無償で利用することができます。商用利用時には有償版を購入する必要があります。

ソフトウェアのみの提供なので、当然ですがPaSoRiなどのICカード読み取り装置は自分で用意する必要があります。

SDKは、VisualC++用のヘッダーとスタテックライブラリの形で提供されます。C#などの言語で利用するには、ラッパーのDLLなどを作成して利用することになります。

Visual C++でカード情報にアクセス

今回は、SDK内のサンプルを使わずにVisual Studio 2017のMFCアプリから、Suicaなどの交通系のICカードのカード情報を読み取る方法を紹介します。

といってもICカード内のすべての情報にアクセス出来るわけではありません。今回は、最初なのでカードのシリアル番号的な「IDm」と呼ばれる情報を読み取って、画面に表示するアプリを作成します。

SDKを利用するので、ダウンロードしたファイルを適当な場所に展開して保存しておきます。

1. VisualStudio2017を起動して、新しいプロジェクトで「MFC アプリケーション」を作成します。

2. 「MFC アプリケーション」のオプション選択では、「アプリケーションの種類」を「ダイアログ ベース」に変更して「完了」ボタンをクリックします。

3. 新しいプロジェクトが作成されます。「ソリューション エクスプローラー」の画面で、「NFCApp1Dlg.cpp」(ファイル名は、作成したプロジェクトの名前で変わります)を開きます。

4. ファイルの先頭行に、「#inlude “felica.h”」を追加します。

5. 「プロジェクト|NFCApp1のプロパティ」メニューを選択して表示された「プロパティ ページ」画面の左側のリストから「構成プロパティ|C/C++/全般」を選択します。

6. 右側に表示された一覧から「追加のインクルード ディレクトリ」を選択して、SDK内の「include」フォルダーの場所を入力します。

7. 同様に左側の一覧から「構成プロパティ|リンカー|入力」を選択して「追加の依存ファイル」に、SDK内の「lib」フォルダーを入力します。(x86用とx64用の2種類が用意されているので使い分けます)

8. 「OK」ボタンをクリックして「NFCApp1プロパティ ページ」画面を閉じます。

9. 「NFCApp1Dlg.cpp」の先頭部分に次のコードを追加します。

#include "stdafx.h"
#include "NFCApp1.h"
#include "NFCApp1Dlg.h"
#include "afxdialogex.h"

#include "felica.h"


#define	SIZE_OF_IDM 8
#define	SIZE_OF_PMM 8
CString format_vector(unsigned char* vector, int length);

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

10. OnInitDialogイベントハンドラーに、次のコードを追加します。

// TODO: 初期化をここに追加します。

if (initialize_library() == TRUE)
{
  if (open_reader_writer_auto() == TRUE)
  {
    structure_polling polling;
    unsigned char system_code[2] = {0xff, 0xff};
    polling.system_code = system_code;
    polling.time_slot = 0x0f;

    unsigned char number_of_cards = 0;

    structure_card_information card_information;
    unsigned char card_idm[8];
    unsigned char card_pmm[8];
    card_information.card_idm = card_idm;
    card_information.card_pmm = card_pmm;
    memset(&card_idm, 0x00, sizeof(card_idm));
    memset(&card_pmm, 0x00, sizeof(card_pmm));

    if (polling_and_get_card_information(&polling, &number_of_cards, &card_information) == TRUE)
    {

      CString csIDm = format_vector(card_information.card_idm, SIZE_OF_IDM);
      GetDlgItem(IDC_STATIC)->SetWindowTextW(csIDm);

      if (close_reader_writer() == true)
      {
        dispose_library();
      }
    }

  }
}

11. インライン関数として次のコードを追加します。

CString format_vector(unsigned char* vector, int length)
{
  CString csResult = _T("");
  CString csValue = _T("");
  int nLoop = 0;
  for (nLoop = 0; nLoop < length; nLoop++)
  {
    csValue = "";
    csValue.Format(_T("%02x"), vector[nLoop]);
    csResult += csValue;
  }

  return csResult;
}

すべてのコードを追加したらビルドを行い、PaSoRiにICカード(Suicaなど)を置いて実行をしてみます。

正しく動作すれば、画面にNFCカード内のIDmと呼ばれる値が表示されます。

カードの種類によっては、セキュリティのために読み取れない場合もあるので、注意してください。Suicaなどの交通系のICカードであれば読み取れるはずです。

追加する全てのコード

NFCApp1Dlg.cpp に追加するすべてのコードは次のようになります。

// NFCApp1Dlg.cpp : 実装ファイル
//

#include "stdafx.h"
#include "NFCApp1.h"
#include "NFCApp1Dlg.h"
#include "afxdialogex.h"

#include "felica.h"


#define	SIZE_OF_IDM 8
#define	SIZE_OF_PMM 8
CString format_vector(unsigned char* vector, int length);

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// アプリケーションのバージョン情報に使われる CAboutDlg ダイアログ

class CAboutDlg : public CDialogEx
{
public:
  CAboutDlg();

// ダイアログ データ
#ifdef AFX_DESIGN_TIME
  enum { IDD = IDD_ABOUTBOX };
#endif

  protected:
  virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV サポート

// 実装
protected:
  DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
  CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CNFCApp1Dlg ダイアログ



CNFCApp1Dlg::CNFCApp1Dlg(CWnd* pParent /*=nullptr*/)
  : CDialogEx(IDD_NFCAPP1_DIALOG, pParent)
{
  m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CNFCApp1Dlg::DoDataExchange(CDataExchange* pDX)
{
  CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CNFCApp1Dlg, CDialogEx)
  ON_WM_SYSCOMMAND()
  ON_WM_PAINT()
  ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()


// CNFCApp1Dlg メッセージ ハンドラー

BOOL CNFCApp1Dlg::OnInitDialog()
{
  CDialogEx::OnInitDialog();

  // "バージョン情報..." メニューをシステム メニューに追加します。

  // IDM_ABOUTBOX は、システム コマンドの範囲内になければなりません。
  ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  ASSERT(IDM_ABOUTBOX < 0xF000);

  CMenu* pSysMenu = GetSystemMenu(FALSE);
  if (pSysMenu != nullptr)
  {
    BOOL bNameValid;
    CString strAboutMenu;
    bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
    ASSERT(bNameValid);
    if (!strAboutMenu.IsEmpty())
    {
      pSysMenu->AppendMenu(MF_SEPARATOR);
      pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
    }
  }

  // このダイアログのアイコンを設定します。アプリケーションのメイン ウィンドウがダイアログでない場合、
  //  Framework は、この設定を自動的に行います。
  SetIcon(m_hIcon, TRUE);      // 大きいアイコンの設定
  SetIcon(m_hIcon, FALSE);    // 小さいアイコンの設定

  // TODO: 初期化をここに追加します。

  if (initialize_library() == TRUE)
  {
    if (open_reader_writer_auto() == TRUE)
    {
      structure_polling polling;
      unsigned char system_code[2] = {0xff, 0xff};
      polling.system_code = system_code;
      polling.time_slot = 0x0f;

      unsigned char number_of_cards = 0;

      structure_card_information card_information;
      unsigned char card_idm[8];
      unsigned char card_pmm[8];
      card_information.card_idm = card_idm;
      card_information.card_pmm = card_pmm;
      memset(&card_idm, 0x00, sizeof(card_idm));
      memset(&card_pmm, 0x00, sizeof(card_pmm));

      if (polling_and_get_card_information(&polling, &number_of_cards, &card_information) == TRUE)
      {

        CString csIDm = format_vector(card_information.card_idm, SIZE_OF_IDM);
        GetDlgItem(IDC_STATIC)->SetWindowTextW(csIDm);

        if (close_reader_writer() == true)
        {
          dispose_library();
        }
      }

    }
  }


  return TRUE;  // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}

CString format_vector(unsigned char* vector, int length)
{
  CString csResult = _T("");
  CString csValue = _T("");
  int nLoop = 0;
  for (nLoop = 0; nLoop < length; nLoop++)
  {
    csValue = "";
    csValue.Format(_T("%02x"), vector[nLoop]);
    csResult += csValue;
  }

  return csResult;
}

void CNFCApp1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
  if ((nID & 0xFFF0) == IDM_ABOUTBOX)
  {
    CAboutDlg dlgAbout;
    dlgAbout.DoModal();
  }
  else
  {
    CDialogEx::OnSysCommand(nID, lParam);
  }
}

// ダイアログに最小化ボタンを追加する場合、アイコンを描画するための
//  下のコードが必要です。ドキュメント/ビュー モデルを使う MFC アプリケーションの場合、
//  これは、Framework によって自動的に設定されます。

void CNFCApp1Dlg::OnPaint()
{
  if (IsIconic())
  {
    CPaintDC dc(this); // 描画のデバイス コンテキスト

    SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

    // クライアントの四角形領域内の中央
    int cxIcon = GetSystemMetrics(SM_CXICON);
    int cyIcon = GetSystemMetrics(SM_CYICON);
    CRect rect;
    GetClientRect(&rect);
    int x = (rect.Width() - cxIcon + 1) / 2;
    int y = (rect.Height() - cyIcon + 1) / 2;

    // アイコンの描画
    dc.DrawIcon(x, y, m_hIcon);
  }
  else
  {
    CDialogEx::OnPaint();
  }
}

// ユーザーが最小化したウィンドウをドラッグしているときに表示するカーソルを取得するために、
//  システムがこの関数を呼び出します。
HCURSOR CNFCApp1Dlg::OnQueryDragIcon()
{
  return static_cast<HCURSOR>(m_hIcon);
}

まとめ

SDK for NFC for Windowsを利用して、Suicaや社員証などのICカードの情報を社内システムなどに活用することができます。

今回は、IDmと呼ばれる汎用的な値を読み取るアプリを作ってみましたが、SDKを使って他の情報にもアクセスすることが可能です。

NFCカードを使ったアプリケーションを開発する方の参考になれば幸いです。

スポンサーリンク

最後までご覧いただきありがとうございます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です