PaSoRiとDelphiでNFCカード(NXP NTAG215)を読み書き ~Windowsサンプルコード集
PaSoRiとDelphiでNFCカード(NXP NTAG215)を読み書き ~Windowsサンプルコード集
本記事はNXP製NTAG215カード専用の解説です。 PaSoRi(RC-S380)を用いてWindows環境でNFCカード(NTAG215)を読み書きする方法を、Delphiのサンプルソースコードとともに紹介します。 winscard.dllやScardTransmitを利用した具体的な処理例を掲載しており、UIDは他のICカードでも取得可能ですが、本記事で扱う読み書き処理はNTAG215に特化しています。
パソコンでICカードを読み書きするためには非接触ICカードリーダー/ライターが必要になります。 私はアマゾンで中古のPaSoRi(RC-S380)を購入して使用しました。 PaSoRi(RC-S380)をパソコンにUSB接続する前に、 https://www.sony.co.jp/Products/felica/consumer/support/download/nfcportsoftware.html から「NFCポートソフトウェア」をダウンロードしてダブルクリックしてインストールし、Windowsを再起動します。 その後、PaSoRi(RC-S380)をパソコンに接続しましょう。
ICタグ(NFCカード)は、Amazonで購入しました。 「NFCタグ」などで検索して2種類購入しました(タグの種類の説明はないので買わないとわからなかったです)が、2種類とも「NXP NTAG215」のNFCタグのICカードでした。 NFCは13.56MHzで動作するRFIDだそうです。
NXP NTAG215は504バイト(126ページ×4)の書き込みエリアがある ページサイズ: 4バイト メモリ構成
ページ0バイト目1バイト目2バイト目3バイト目 0UIDチェックサム 1UID 2チェックサムInternallock byteslock bytes 3Capability Container(CC)E1(NDEFが存在するマジックナンバー)、バージョン、NDEFメッセージのサイズ(×8)、00 4User Memory . 129 130Dynamic lock byteRFUI 131CFG0 132CFG1 133PWD 134PACKRFUI カードリーダー関連のWindows API 関数 スマートカードリソースマネージャへの接続 Function SCardEstablishContext( dwScope : DWORD; pvReserved1 : Pointer; pvReserved2 : Pointer; phContext : PSCARDCONTEXT ):LONG; stdcall; external 'Winscard.dll'; Function SCardReleaseContext( hContext:SCARDCONTEXT ):LONG; stdcall; external 'Winscard.dll'; カードリーダーのリスト取得 Function SCardListReadersW( hContext : SCARDCONTEXT; mszGroups:PWideChar; szReaders:PWideChar; pcchReaders:PDWORD ):LONG; stdcall; external 'Winscard.dll'; Function SCardConnectW( hContext : SCARDCONTEXT; szReaders:PWideChar; dwShareMode : DWORD; dwPreferredProtocols : DWORD; phCard : PSCARDHANDLE; pdwActiveProtocols:PDWORD ):LONG; stdcall; external 'Winscard.dll'; Function SCardDisconnect( hCard : SCARDHANDLE; dwDisposition :DWORD ):LONG; stdcall; external 'Winscard.dll'; カードのステータス取得 Function SCardStatusW( hCard : SCARDHANDLE; szReaderNames :PWideChar; pcchReaderLen : PDWORD; pdwState :PDWORD; pdwProtocol :PDWORD; pbATR :PBYTE; pcbAtrLen :PDWORD ):LONG; stdcall; external 'Winscard.dll'; サービス要求を送信し、カードからデータを受信する Function SCardTransmit( hCard : SCARDHANDLE; pioSendPci :PSCARD_IO_REQUEST; pbSendBuffer :PByte; cbSendLength :DWORD; pioRecvPci :PSCARD_IO_REQUEST; pbRecvBuffer :PByte; pcbRecvLength:PDWORD ):LONG; stdcall; external 'Winscard.dll'; SCardTransmit()の命令 0バイト目CLA$FF 1バイト目INS$CA 2バイト目P1$00 3バイト目P2$00 4バイト目Le$00 0バイト目CLA$FF 1バイト目INS$B0 2バイト目P1$00 3バイト目P2ページ(ブロック)番号 4バイト目Le読み取るバイト数 データを更新(書き込み) 0バイト目CLA$FF 1バイト目INS$D6 2バイト目P1$00 3バイト目P2ページ(ブロック)番号 4バイト目Lc更新バイト数(NTAG215の場合は$04) 5バイト目DATA0更新データ 6バイト目DATA1更新データ 7バイト目DATA2更新データ 8バイト目DATA3更新データMifare Classic 1Kなどの場合は4セクター毎に認証が必要で、以下のような命令を使う必要がある
0バイト目CLA$FF 1バイト目INS$82 2バイト目P1$00 3バイト目P2キーナンバー($00など) 4バイト目Lcデータ(キー)の長さ($06) 5バイト目DATA0(例)$FF 6バイト目DATA1(例)$FF 7バイト目DATA2(例)$FF 8バイト目DATA3(例)$FF 9バイト目DATA4(例)$FF 10バイト目DATA5(例)$FF 0バイト目CLA$FF 1バイト目INS$86 2バイト目P1$00 3バイト目P2キーナンバー($00など) 4バイト目Lcデータの長さ($05) 5バイト目DATA0$01 6バイト目DATA1$00 7バイト目DATA2ブロック番号 8バイト目DATA3$60(KeyA) 又は $61(KeyB) 9バイト目DATA4$00 フォーマットRFIDカードに書き込む場合はNDEF(NFC Data Exchange Format)というデータフォーマットで書き込む必要があります。
7ビット目6ビット目5ビット目4ビット目3ビット目2ビット目1ビット目0ビット目 MB(始まり)ME(終わり)CF(分割)SR(255Byte以下)IL(IDが設定か) TNF(レコードタイプ $00:Empty $01:Well-Known ・・・) プロジェクトを作成するDelphiを起動し、メニューから「ファイル」⇒「新規作成」⇒「Windows VCLアプリケーション -Delphi(W)」 をクリックする。 TEdit×1個、TButton×5個、TMemo1×1個を配置します。 [ファイル]⇒[すべて保存]からプロジェクトとユニットの保存をします。
ユニットを追加する unit winscard; interface uses Winapi.Windows,System.SysUtils; type SCARDCONTEXT=ULONG; PSCARDCONTEXT=^SCARDCONTEXT; SCARDHANDLE = ULONG; PSCARDHANDLE = ^SCARDHANDLE; SCARD_IO_REQUEST = Record dwProtocol :DWORD; cbPciLength :DWORD; end; PSCARD_IO_REQUEST=^SCARD_IO_REQUEST; TSCard=class(TObject) private fReader:String; fContext:SCARDCONTEXT; fErr:LONG; fErrStr:String; fHCard:SCARDHandle; fProtocol:DWORD; fSendPci:SCARD_IO_REQUEST; fATR:TBytes; fCardName:String; fReadBlockData:TBytes; procedure GetCardName(); //「MIFARE Ultralight」や「NXP NTAG215」は1ブロック(4バイト)ずつしか更新できない function UpdateBuffer4Byte( BlockNum:Byte;Data:TBytes ):Boolean; //指定したDataを指定したBlockNumから更新する function UpdateBuffer(Data:TBytes;BlockNum:Byte=4):Boolean; public constructor Create; destructor Destroy();override; //リーダーに接続 function ConnectReader():Boolean; //ConnectReaderを呼んだ後、カードに接続 function ConnectCard():Boolean; //ATRの取得 function GetATR():String; //UIDの取得 function GetUID():String; //指定したブロックの出データを読む function ReadBlock(BlockNum:Byte):String; //データを初期化する function ClearAll():Boolean; //文字を書き込む(更新する) function UpdateString(WriteStr:String):Boolean; property Err:LONG read fErr; property ErrStr:String read fErrStr; property Reader:String read fReader; property CardName:String read fCardName; property ATR:TBytes read fATR; end; //スマートカードリソースマネージャへの接続 Function SCardEstablishContext( dwScope:DWORD; pvReserved1:Pointer; pvReserved2: Pointer; phContext :PSCARDCONTEXT ):LONG; stdcall; external 'Winscard.dll'; //コンテキストの解放 Function SCardReleaseContext( hContext:SCARDCONTEXT ):LONG; stdcall; external 'Winscard.dll'; ////カードリーダーのリスト取得 Function SCardListReadersA( hContext : SCARDCONTEXT; mszGroups:PAnsiChar; szReaders:PAnsiChar; pcchReaders:PDWORD ):LONG; stdcall; external 'Winscard.dll'; ////カードリーダーのリスト取得 Function SCardListReadersW( hContext : SCARDCONTEXT; mszGroups:PWideChar; szReaders:PWideChar; pcchReaders:PDWORD ):LONG; stdcall; external 'Winscard.dll'; //カードの接続 Function SCardConnectA( hContext : SCARDCONTEXT; szReaders:PAnsiChar; dwShareMode : DWORD; dwPreferredProtocols : DWORD; phCard : PSCARDHANDLE; pdwActiveProtocols:PDWORD ):LONG; stdcall; external 'Winscard.dll'; //カードの接続 Function SCardConnectW( hContext : SCARDCONTEXT; szReaders:PWideChar; dwShareMode : DWORD; dwPreferredProtocols : DWORD; phCard : PSCARDHANDLE; pdwActiveProtocols:PDWORD ):LONG; stdcall; external 'Winscard.dll'; //カードの切断 Function SCardDisconnect( hCard : SCARDHANDLE; dwDisposition :DWORD ):LONG; stdcall; external 'Winscard.dll'; //カードのステータス取得 Function SCardStatusA( hCard : SCARDHANDLE; szReaderNames :PAnsiChar; pcchReaderLen : PDWORD; pdwState :PDWORD; pdwProtocol :PDWORD; pbATR :PBYTE; pcbAtrLen :PDWORD ):LONG; stdcall; external 'Winscard.dll'; Function SCardStatusW( hCard : SCARDHANDLE; szReaderNames :PWideChar; pcchReaderLen : PDWORD; pdwState :PDWORD; pdwProtocol :PDWORD; pbATR :PBYTE; pcbAtrLen :PDWORD ):LONG; stdcall; external 'Winscard.dll'; //サービス要求を送信し、カードからデータを受信する Function SCardTransmit( hCard : SCARDHANDLE; pioSendPci :PSCARD_IO_REQUEST; pbSendBuffer :PByte; cbSendLength :DWORD; pioRecvPci :PSCARD_IO_REQUEST; pbRecvBuffer :PByte; pcbRecvLength:PDWORD ):LONG; stdcall; external 'Winscard.dll'; //指定したブロック(4Byte)のデータを読む function ReadBlock( hCard:SCARDHANDLE; SendPci:SCARD_IO_REQUEST; BlockNum:Byte ):TBytes; //指定したブロック(Blocknum=0が1ブロック目)にdataの先頭4バイトのみ書き込む //1~4ブロック目は書き込めないようです function UpdateBuffer4Byte( hCard:SCARDHANDLE; SendPci:SCARD_IO_REQUEST; BlockNum:Byte;Data:TBytes ):Boolean; //指定したブロックにdata(4の倍数バイト分)を書き込む function UpdateBuffer( hCard:SCARDHANDLE; SendPci:SCARD_IO_REQUEST; Data:TBytes; BlockNum:Byte=4 ):Boolean; //4ブロック目から文字を書く function WreiteString( hCard:SCARDHANDLE; SendPci:SCARD_IO_REQUEST;st:String ):Boolean; const //スマートカードの戻り値 ERROR_BROKEN_PIPE =$00000109; SCARD_E_BAD_SEEK =$80100029; SCARD_E_CANCELLED =$80100002; SCARD_E_CANT_DISPOSE =$8010000E; SCARD_E_CARD_UNSUPPORTED=$8010001C; SCARD_E_CERTIFICATE_UNAVAILABLE=$8010002D; SCARD_E_COMM_DATA_LOST =$8010002; SCARD_E_DIR_NOT_FOUND =$80100023; SCARD_E_DUPLICATE_READER=$8010001B; SCARD_E_FILE_NOT_FOUND =$80100024; SCARD_E_ICC_CREATEORDER =$80100021; SCARD_E_ICC_INSTALLATION=$80100020; SCARD_E_INSUFFICIENT_BUFFER=$80100008; SCARD_E_INVALID_ATR =$80100015; SCARD_E_INVALID_CHV =$8010002A; SCARD_E_INVALID_HANDLE =$80100003; SCARD_E_INVALID_PARAMETER=$80100004; SCARD_E_INVALID_TARGET =$80100005; SCARD_E_INVALID_VALUE =$80100011; SCARD_E_NO_ACCESS =$80100027; SCARD_E_NO_DIR =$80100025; SCARD_E_NO_FILE =$80100026; SCARD_E_NO_KEY_CONTAINER=$80100030; SCARD_E_NO_MEMORY =$80100006; SCARD_E_NO_PIN_CACHE =$80100033; SCARD_E_NO_READERS_AVAILABLE=$8010002E; SCARD_E_NO_SERVICE =$8010001D; SCARD_E_NO_SMARTCARD =$8010000C; SCARD_E_NO_SUCH_CERTIFICATE=$8010002C; SCARD_E_NOT_READY =$80100010; SCARD_E_NOT_TRANSACTED =$80100016; SCARD_E_PCI_TOO_SMALL =$80100019; SCARD_E_PIN_CACHE_EXPIRED=$80100032; SCARD_E_PROTO_MISMATCH =$8010000F; SCARD_E_READ_ONLY_CARD =$80100034; SCARD_E_READER_UNAVAILABLE=$80100017; SCARD_E_READER_UNSUPPORTED=$8010001A; SCARD_E_SERVER_TOO_BUSY =$80100031; SCARD_E_SERVICE_STOPPED =$8010001E; SCARD_E_SHARING_VIOLATION=$8010000B; SCARD_E_SYSTEM_CANCELLED=$80100012; SCARD_E_TIMEOUT =$8010000A; SCARD_E_UNEXPECTED =$8010001F; SCARD_E_UNKNOWN_CARD =$8010000D; SCARD_E_UNKNOWN_READER =$80100009; SCARD_E_UNKNOWN_RES_MNG =$8010002B; SCARD_E_UNSUPPORTED_FEATURE=$80100022; SCARD_E_WRITE_TOO_MANY =$80100028; SCARD_F_COMM_ERROR =$80100013; SCARD_F_INTERNAL_ERROR =$80100001; SCARD_F_UNKNOWN_ERROR =$80100014; SCARD_F_WAITED_TOO_LONG =$80100007; SCARD_P_SHUTDOWN =$80100018; SCARD_S_SUCCESS =$00000000; SCARD_W_CANCELLED_BY_USER=$8010006E; SCARD_W_CACHE_ITEM_NOT_FOUND=$80100070; SCARD_W_CACHE_ITEM_STALE=$80100071; SCARD_W_CACHE_ITEM_TOO_BIG=$80100072; SCARD_W_CARD_NOT_AUTHENTICATED=$8010006F; SCARD_W_CHV_BLOCKED =$8010006C; SCARD_W_EOF =$8010006D; SCARD_W_REMOVED_CARD =$80100069; SCARD_W_RESET_CARD =$80100068; SCARD_W_SECURITY_VIOLATION=$8010006A; SCARD_W_UNPOWERED_CARD =$80100067; SCARD_W_UNRESPONSIVE_CARD=$80100066; SCARD_W_UNSUPPORTED_CARD=$80100065; SCARD_W_WRONG_CHV =$8010006B; //SCardEstablishContext の dwScope //データベース操作は、ユーザーのドメイン内で実行されます SCARD_SCOPE_USER=0; //コンテキストは現在の端末のコンテキストであり、 //データベース操作はすべてその端末のドメイン内で実行されます //(呼び出し側アプリケーションには、データベース アクションに対する // 適切なアクセス許可が必要です。) SCARD_SCOPE_TERMINAL=1; //コンテキストはシステム コンテキストであり、 //データベース操作はすべてシステムのドメイン内で実行されます //(呼び出し側アプリケーションには、データベース アクションに対する //適切なアクセス許可が必要です。) SCARD_SCOPE_SYSTEM=2; //SCardConnectA,SCardConnectW の dwShareMode SCARD_SHARE_EXCLUSIVE =1; SCARD_SHARE_SHARED =2; SCARD_SHARE_DIRECT =3; //SCardConnectA,SCardConnectW の dwPreferredProtocols SCARD_PROTOCOL_UNDEFINED =$00000000; SCARD_PROTOCOL_T0 =$00000001; SCARD_PROTOCOL_T1 =$00000002; SCARD_PROTOCOL_RAW =$00010000; SCARD_PROTOCOL_DEFAULT =$80000000; //SCardDisconnect の dwDisposition SCARD_LEAVE_CARD =$00000000; SCARD_RESET_CARD =$00000001; SCARD_UNPOWER_CARD =$00000002; SCARD_EJECT_CARD =$00000003; //SCardTransmit の pioSendPci SCARD_PCI_T0 : SCARD_IO_REQUEST = (dwProtocol:1; cbPciLength:8); SCARD_PCI_T1 : SCARD_IO_REQUEST = (dwProtocol:2; cbPciLength:8); implementation function ReadBlock( hCard:SCARDHANDLE; SendPci:SCARD_IO_REQUEST; BlockNum:Byte ):TBytes; var res:DWORD; SendLen,RecvLen:DWORD; SendBuf,RecvBuf:TBytes; begin SetLength(Result,4); ZeroMemory(Result,4); SendLen:=255; SetLength(SendBuf,SendLen); ZeroMemory(@SendBuf[0],SendLen); SendBuf[0]:=$FF; //CLA SendBuf[1]:=$B0; //INS FFB0:ReadBinary SendBuf[2]:=$00; //P1 SendBuf[3]:=BlockNum;//P2 SendBuf[4]:=$04; //Le 読み取るバイト数 RecvLen:=255; SetLength(RecvBuf,RecvLen); ZeroMemory(@RecvBuf[0],RecvLen); Res:=SCardTransmit( hCard,@SendPci, @SendBuf[0],SendLen, nil, @RecvBuf[0],@RecvLen ); if Res=SCARD_S_SUCCESS then begin SetLength(RecvBuf,RecvLen); if (RecvBuf[RecvLen-2]=$90) and (RecvBuf[RecvLen-1]=$00) then begin //16バイト(4Block)読み取ってるけど1ブロックのみ返す(4Byte) move(Recvbuf[0],Result[0],4); end; end; end; function UpdateBuffer4Byte( hCard:SCARDHANDLE; SendPci:SCARD_IO_REQUEST; BlockNum:Byte;Data:TBytes ):Boolean; var len:Integer; res:DWORD; SendLen,RecvLen:DWORD; SendBuf,RecvBuf:TBytes; d:TBytes; begin result:=False; len:=Length(Data); if len=0 then exit; if len>4 then len:=4; SetLength(d,4); ZeroMemory(d,4); //CopyMemory(@d[0],@Data[0],len); Move(Data[0],d[0],len); SendLen:=9; SetLength(SendBuf,SendLen); SendBuf[0]:=$FF;//CLA SendBuf[1]:=$D6;//INS FFD6:Update Binary SendBuf[2]:=$00;//P1 SendBuf[3]:=BlockNum;//P2 ブロック番号4以上 SendBuf[4]:=$04;//Lc 書き込みバイト数4固定 SendBuf[5]:=d[0];//書き込みデータ SendBuf[6]:=d[1];//書き込みデータ SendBuf[7]:=d[2];//書き込みデータ SendBuf[8]:=d[3];//書き込みデータ RecvLen:=256; SetLength(RecvBuf,RecvLen); res:=SCardTransmit( hCard,@SendPci, @SendBuf[0],SendLen, nil, @RecvBuf[0],@RecvLen ); if res=SCARD_S_SUCCESS then begin SetLength(RecvBuf,RecvLen); if (RecvBuf[RecvLen-2]=$90) and (RecvBuf[RecvLen-1]=$00) then result:=True; end; end; function UpdateBuffer( hCard:SCARDHANDLE; SendPci:SCARD_IO_REQUEST; Data:TBytes; BlockNum:Byte=4 ):Boolean; var i:Integer; d:TBytes; bn:Byte; len:Integer; begin result:=False; if Length(Data)=0 then exit; SetLength(d,4); for i := 0 to (Length(Data) div 4) do begin ZeroMemory(@d[0],4); len:=Length(Data)-i*4; if len>4 then len:=4; Move(Data[i*4],d[0],len); bn:=BlockNum+i; result:=UpdateBuffer4Byte(hCard,SendPci,bn,d); if result=false then break; end; end; //4ブロック目から文字を書く function WreiteString( hCard:SCARDHANDLE; SendPci:SCARD_IO_REQUEST;st:String ):Boolean; var arr,data:TBytes; paylen:Byte; i:Integer; BlockNum:Byte; begin BlockNum:=4; arr:=TEncoding.UTF8.GetBytes(st); paylen:=Length(arr)+3; SetLength(data,paylen+7); data[0]:=$03;//NDEF識別子 TVL開始 data[1]:=paylen+4; data[2]:=$D1;//MB ME SR TNF data[3]:=$01;//レコードタイプ $00:Empty $01:Well-Known・・・ data[4]:=paylen;//ペイロードの長さ data[5]:=Ord('T');//Text:$54 data[6]:=$02; //言語コードの長さ2 data[7]:=Ord('j');// j data[8]:=Ord('a');// a for i := Low(arr) to High(arr) do data[9+i]:=arr[i]; data[9+Length(arr)]:=$FE;//TVLの終わり result:=UpdateBuffer( hCard,SendPci, data,BlockNum ); end; < TSCard >function TSCard.ConnectCard: Boolean; var ActiveProtocols:DWORD; begin result:=False; if ConnectReader() then begin fErr:=SCardConnectW( fContext,PChar(fReader),SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 or SCARD_PROTOCOL_T1,@fHCard,@ActiveProtocols ); if fErr=SCARD_S_SUCCESS then begin if (ActiveProtocols and SCARD_PROTOCOL_T1)>0 then begin fProtocol:=SCARD_PROTOCOL_T1; fSendPci:=SCARD_PCI_T1; end else begin fProtocol:=SCARD_PROTOCOL_T0; fSendPci:=SCARD_PCI_T0; end; result:=True; end else begin fErrStr:='カードが見つかりません'; //コンテキストの解放 SCardReleaseContext(fContext); end; end else begin //コンテキストの解放 SCardReleaseContext(fContext); end; end; function TSCard.ConnectReader: Boolean; var Readers:String; ReadersLen:DWORD; begin result:=False; fReader:='';//リーダーの名前 fErr:=$00; fErrStr:=''; //スマートカードリソースマネージャへの接続 fErr:=SCardEstablishContext(SCARD_SCOPE_USER,nil,nil,@fContext); if fErr=SCARD_S_SUCCESS then begin ReadersLen:=255; SetLength(Readers,ReadersLen); //リーダーの一覧を取得する fErr:=SCardListReadersW(fContext,nil,PChar(Readers),@ReadersLen); if fErr=SCARD_S_SUCCESS then begin SetLength(Readers,ReadersLen); //一覧の最初の1つのリーダーの名前を取得する fReader:=Readers; result:=True; end; end else begin fErrStr:='カードリーダーが見つかりません'; //コンテキストの解放 SCardReleaseContext(fContext); end; end; constructor TSCard.Create; begin fErr:=0; fErrStr:=''; fReader:='';//リーダーの名前 end; destructor TSCard.Destroy; begin inherited; end; function TSCard.GetATR: String; var i:Integer; AtrLen:DWORD; ReadersLen,State:DWORD; begin Result:=''; if ConnectCard() then begin AtrLen:=32; SetLength(fATR,AtrLen); fErr:=SCardStatusW( fHCard,PChar(fReader),@ReadersLen,@State, @fProtocol, @fATR[0],@AtrLen ); if fErr=SCARD_S_SUCCESS then begin SetLength(fATR,AtrLen); self.GetCardName(); for i := 0 to AtrLen-1 do Result:=Result+IntToHex(fAtr[i],2); Result:=Result+'('+fCardName+')'; end else begin SetLength(fATR,0); fErrStr:='ATR取得失敗'; end; //カードの切断 SCardDisconnect( fHCard, SCARD_LEAVE_CARD ); //コンテキストの解放 SCardReleaseContext(fContext); end; end; procedure TSCard.GetCardName(); begin if Length(fATR)4 then len:=4; Move(Data[i*4],d[0],len); WriteBlockNum:=BlockNum+i; result:=UpdateBuffer4Byte(WriteBlockNum,d); if result=false then break; end; end; function TSCard.UpdateBuffer4Byte(BlockNum: Byte; Data: TBytes): Boolean; var len:Integer; d:TBytes; SendLen,RecvLen:DWORD; SendBuf,RecvBuf:TBytes; begin Result:=false; if ConnectCard() then begin len:=Length(Data); if len=0 then exit; if len>4 then len:=4; SetLength(d,4); ZeroMemory(d,4); Move(Data[0],d[0],len); SendLen:=9; SetLength(SendBuf,SendLen); SendBuf[0]:=$FF;//CLA SendBuf[1]:=$D6;//INS FFD6:Update Binary SendBuf[2]:=$00;//P1 SendBuf[3]:=BlockNum;//P2 ブロック番号4以上 SendBuf[4]:=$04;//Lc 書き込みバイト数4固定 SendBuf[5]:=d[0];//書き込みデータ SendBuf[6]:=d[1];//書き込みデータ SendBuf[7]:=d[2];//書き込みデータ SendBuf[8]:=d[3];//書き込みデータ RecvLen:=256; SetLength(RecvBuf,RecvLen); fErr:=SCardTransmit( fHCard,@fSendPci, @SendBuf[0],SendLen, nil, @RecvBuf[0],@RecvLen ); if fErr=SCARD_S_SUCCESS then begin SetLength(RecvBuf,RecvLen); if (RecvBuf[RecvLen-2]=$90) and (RecvBuf[RecvLen-1]=$00) then result:=True; end else begin fErrStr:=Format( 'SW1:%2.2x SW2:%2.2x', [RecvBuf[RecvLen-2],RecvBuf[RecvLen-1]] ); end; //カードの切断 SCardDisconnect( fHCard, SCARD_LEAVE_CARD ); //コンテキストの解放 SCardReleaseContext(fContext); end; end; function TSCard.UpdateString(WriteStr: String): Boolean; var arr,data:TBytes; paylen:Byte; i:Integer; begin arr:=TEncoding.UTF8.GetBytes(WriteStr); paylen:=Length(arr)+3; SetLength(data,paylen+7); data[0]:=$03;//NDEF識別子 TVL開始 data[1]:=paylen+4; data[2]:=$D1;//MB ME SR TNF data[3]:=$01;//レコードタイプ $00:Empty $01:Well-Known・・・ data[4]:=paylen;//ペイロードの長さ data[5]:=Ord('T');//Text:$54 Uri:$55 data[6]:=$02; //言語コードの長さ2 data[7]:=Ord('j');// j data[8]:=Ord('a');// a for i := Low(arr) to High(arr) do data[9+i]:=arr[i]; data[9+Length(arr)]:=$FE;//TVLの終わり result:=UpdateBuffer(data); end; function TSCard.ClearAll: Boolean; var i:Integer; Data:TBytes; begin //0~3ブロック ロック //4~129ブロック 利用可能 //130~134ブロック ロック SetLength(Data,4); Data[0]:=$03; Data[1]:=$00; Data[2]:=$FE; Data[3]:=$00; Result:=Self.UpdateBuffer4Byte(4,Data); if Result then begin Data[0]:=$00; Data[1]:=$00; Data[2]:=$00; Data[3]:=$00; for i := 5 to 129 do begin Result:=Self.UpdateBuffer4Byte(i,Data); if Result=False then Break; end; end; end; end. フォームのソースコードの入力 unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls ,winscard; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; Button2: TButton; Button3: TButton; Button4: TButton; Edit1: TEdit; Button5: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button5Click(Sender: TObject); private < Private 宣言 >SCard:TSCard; public < Public 宣言 >end; var Form1: TForm1; implementation procedure TForm1.Button1Click(Sender: TObject); begin //ATRを読む Memo1.Lines.Add('ATR:'+SCard.GetATR); end; procedure TForm1.Button2Click(Sender: TObject); begin //UIDを読む Memo1.Lines.Add( 'UID:'+SCard.GetUID() ); end; procedure TForm1.Button3Click(Sender: TObject); var i:Integer; begin //「NXP NTAG215」の容量は540バイト(4Byte × 135ブロック) //全ブロックのメモリを読む for i := 0 to 134 do begin Memo1.Lines.Add( Format('%2.2x:',[i])+SCard.ReadBlock(i) ); end; end; procedure TForm1.Button4Click(Sender: TObject); begin if SCard.ClearAll() then Memo1.Lines.Add('データのクリア成功') else Memo1.Lines.Add('データのクリア失敗'); end; procedure TForm1.Button5Click(Sender: TObject); begin if SCard.UpdateString(Edit1.Text) then Memo1.Lines.Add('文字列の書き込み成功') else Memo1.Lines.Add('文字列の書き込み失敗') end; procedure TForm1.FormCreate(Sender: TObject); begin SCard:=TSCard.Create; end; procedure TForm1.FormDestroy(Sender: TObject); begin SCard.Free; end; end. 実行PaSoRi(RC-S3800)をパソコンに接続し、ICタグを載せます。 Delphi IDEの「実行」ボタンを押して実行します。 Button1をクリックすると、カードのATRとカードの種類が表示されます。