はじめに
PaSoRi RC-S300を確定申告用に購入しました。
ドライバインストールでコケる
PaSoriのインストール失敗問題は結構あるようで、試行錯誤してもうまくいきませんでした。
このままだと「機器が壊れているのか」「環境(ドライバ/設定)が悪いのか」の切り分けができません。
そこでまずは、RC-S300 本体が PC/SC リーダーとして認識され、カードと通信できるか を最小構成で確認することにしました。
今回は手元にあった NTAG 215(NFCタグ) を使い、C#(WinForms)で UID が取得できるか を確かめるサンプルプログラムを作成します。
UID が読めれば「少なくともリーダーとカード間の基本的な通信はできている」ことが分かり、以降のトラブルシュートの土台になります。
環境
- Windows 11 Pro
- Visual Studio 2026
- C#(.NET 10)
- NTAG 215 NFCカード
- PaSoRi RC-S300
実装
まずは Visual Studio で Windows Forms アプリ(.NET) の新規プロジェクトを作成します(.NET 10 を選択)。
PaSoRi RC-S300 は Windows 上では PC/SC(Smart Card) の仕組みで扱えるため、.NET から PC/SC API を呼び出すためのライブラリを NuGet で追加します。
NuGet では以下の 2 つをインストールします。
【補足】NuGet でインストールする PCSC / PCSC.Iso7816 とは
PCSC(pcsc-sharp)
PCSC は、Windows が標準で持っている PC/SC API(winscard.dll) を .NET から扱いやすくするためのラッパーライブラリです。
PaSoRi のような IC カードリーダーは、基本的に OS 側では スマートカードリーダー として扱われるので、PC/SC 経由で「リーダー一覧の取得」「リーダーへの接続」「カード状態/ATR の取得」「APDU の送受信」といった操作ができます。
今回のコードで PCSC 側が担当しているのはこのあたりです。
-
ContextFactory.Instance.Establish(...)
→ PC/SC リソースマネージャへ接続するための コンテキスト確立 -
context.GetReaders()
→ OS が認識している リーダー名一覧の取得 -
new SCardReader(context)/reader.Connect(...)
→ リーダーへ接続 -
reader.Status(...)
→ カード状態・プロトコル・ATR の取得 -
reader.Transmit(...)
→ APDU の送受信(低レベル)
つまり PCSC は「PaSoRi を Windows のスマートカード基盤(PC/SC)越しに叩くための土台」です。
PCSC.Iso7816
PCSC.Iso7816 は、PCSC の上で ISO/IEC 7816 の APDU(コマンド/レスポンス) を組み立てたり解釈したりするための補助ライブラリです。
APDU は「カードに投げる命令(Command APDU)と、カードから返る応答(Response APDU)」の枠組みで、典型的には応答の末尾に SW1/SW2(ステータスワード) が付きます(例:90 00)。
ソースコード
Formにボタンを設置し、ボタンクリックイベントでNFCカードを読み込むようにしました。
private void button1_Click(object sender, EventArgs e)
{
try
{
// 1) PC/SC のコンテキストを確立
using var context = ContextFactory.Instance.Establish(SCardScope.System);
// 2) OS が認識している PC/SC リーダー名の一覧を取得
var readers = context.GetReaders();
if (readers == null || readers.Length == 0)
{
Debug.WriteLine("PC/SC リーダーが見つかりません。");
return;
}
// 3) RC-S300 を優先して選択(見つからなければ先頭のリーダーを使用)
var readerName = readers.FirstOrDefault(r => r.Contains("RC-S300")) ?? readers[0];
Debug.WriteLine($"Reader: {readerName}");
// 4) リーダー操作用のオブジェクトを生成(コンテキストに紐づく)
using var reader = new SCardReader(context);
// 5) リーダーへ接続
// - Shared: 他アプリと共有してアクセス(Exclusive にすると占有)
// - Any : プロトコルは自動選択(T=0 / T=1 など)
var rc = reader.Connect(readerName, SCardShareMode.Shared, SCardProtocol.Any);
if (rc != SCardError.Success)
{
Debug.WriteLine($"Connect failed: {SCardHelper.StringifyError(rc)}");
return;
}
// 6) カードの状態・プロトコル・ATR を取得
// ATR(Answer To Reset) はカードが返す識別情報(カード種別の推定などに利用)
rc = reader.Status(out var _, out var state, out var protocol, out var atr);
if (rc != SCardError.Success)
{
Debug.WriteLine($"Status failed: {SCardHelper.StringifyError(rc)}");
reader.Disconnect(SCardReaderDisposition.Leave);
return;
}
Debug.WriteLine($"State : {state}");
Debug.WriteLine($"Protocol: {protocol}");
Debug.WriteLine($"ATR : {BitConverter.ToString(atr ?? Array.Empty<byte>())}");
// 7) APDU を構築して送信(PC/SC 経由でカードとコマンド交換する)
#
var apdu = new CommandApdu(IsoCase.Case2Short, protocol)
{
CLA = 0xFF,
INS = 0xCA,
P1 = 0x00,
P2 = 0x00,
Le = 0x00
};
// 8) プロトコルに対応する PCI(プロトコル制御情報)を取得して Transmit に渡す
var sendPci = SCardPCI.GetPci(protocol);
var receivePci = new SCardPCI();
// 9) レスポンス格納バッファ(実装によっては固定長で返るため大きめに確保)
var receiveBuffer = new byte[256];
// 10) APDU を送信し、レスポンスを受信
rc = reader.Transmit(sendPci, apdu.ToArray(), receivePci, ref receiveBuffer);
if (rc != SCardError.Success)
{
Debug.WriteLine($"Transmit failed: {SCardHelper.StringifyError(rc)}");
reader.Disconnect(SCardReaderDisposition.Leave);
return;
}
// 11) ISO7816 ではレスポンス末尾に SW1/SW2(ステータスワード)が付く
// 90 00 は一般的な成功コードの一つ
const byte SW1_OK = 0x90;
const byte SW2_OK = 0x00;
// 12) 受信バッファから "90 00" の位置を探し、そこまでをデータ部として取り出す
int swPos = -1;
for (int i = 0; i <= receiveBuffer.Length - 2; i++)
{
if (receiveBuffer[i] == SW1_OK && receiveBuffer[i + 1] == SW2_OK)
{
swPos = i;
break;
}
}
if (swPos > 0)
{
// 13) データ部(SW1/SW2 の直前まで)を UID 相当として表示
var data = receiveBuffer.Take(swPos).ToArray();
Debug.WriteLine($"UID: {BitConverter.ToString(data)}");
Debug.WriteLine("SW: 90-00");
}
else
{
// 14) 成功コードが見つからない場合は生データを出して調査に回す
Debug.WriteLine($"Response(raw): {BitConverter.ToString(receiveBuffer)}");
}
// 15) 切断(Leave: カード状態を維持したまま離脱)
reader.Disconnect(SCardReaderDisposition.Leave);
}
catch (Exception ex)
{
// 例外は環境差やドライバ状態でも起きるため、詳細をそのまま出力
Debug.WriteLine(ex.ToString());
}
}
結果
ボタンを押してカードをかざすと、Debug 出力に以下のようなログが出ました。
-
Reader:に RC-S300 が表示される(PC/SC からリーダーとして認識されている) -
ATRが表示される(カードが反応していることの確認になる) -
UID:が表示される(NTAG215 の UID を取得できた)
つまり 「Windows → PC/SC → RC-S300 → カード」 の経路で通信できていることが確認でき、少なくとも RC-S300 本体は正常に動作していると判断できました。
ドライバインストールでつまずいていても、このように PC/SC 経由で読めるなら、機器側の故障切り分けにも使えます。
なお、UID 取得は FF CA 00 00 00(Get Data - UID)を Transmit で送っています。カード種別やリーダー実装によってはこのコマンドが通らない場合もありますが、今回は NTAG 215 では問題なく UID を取得できました。
おわりに
今回は PaSoRi RC-S300 のドライバインストールでつまずいたため、まずは PC/SC 経由でリーダーとカードが通信できるか を確認する目的で、WinForms の最小サンプルを作成しました。
結果として NTAG 215 の UID を取得できたので、少なくとも RC-S300 本体と Windows 側の PC/SC 経路は動作している ことが確認でき、故障か環境問題かの切り分けが前に進みました。
あとは用途に応じて、読み取り対象を マイナンバーカード(ISO7816 APDU) に切り替えたり、交通系 IC のような FeliCa 系 を扱う場合は別の手順が必要になったりしますが、今回のコードは「まずリーダーが生きているか」を確かめる土台として使えるはずです。
同じようにセットアップで詰まっている方の、切り分けの一助になれば幸いです。

