トップへ(mam-mam.net/)

PaSoRiとDelphiでNFCカード(MIFARE Ultralight)を読み書き ~Delphiソースコード集

検索:

PaSoRiとDelphiでNFCカード(MIFARE Ultralight)を読み書き ~Delphiソースコード集

PaSoRi(RC-S3800)をパソコンに接続する前に、
https://www.sony.co.jp/Products/felica/consumer/support/download/nfcportsoftware.html
から「NFCポートソフトウェア」をダウンロードしてダブルクリックしてインストールし、Windowsを再起動します。
その後、PaSoRi(RC-S3800)をパソコンに接続しましょう。

非接触ICカードリーダー/ライターPaSoRi RC-S300

ICタグ(NFCカード)は、Amazonで購入しました。
「NFCタグ」などで検索して2種類購入しました(タグの種類の説明はないので買わないとわからなかったです)が、2種類とも「MIFARE Ultralight C」のNFCタグのICカードでした。
NFCは13.56MHzで動作するRFIDだそうです。

ICタグ MIFARE Ultralight C NFCカード
ICタグ MIFARE Ultralight C NFCタグ

プロジェクトを作成する

Delphiを起動し、メニューから「ファイル」⇒「新規作成」⇒「Windows VCLアプリケーション -Delphi(W)」 をクリックする。
TEdit×1個、TButton×5個、TMemo1×1個を配置します。
[ファイル]⇒[すべて保存]からプロジェクトとユニットの保存をします。

ユニットを追加する

[ファイル]⇒[新規作成]⇒[ユニット]をクリックして新規ユニットを追加します。
以下のソースコードをコピペして、「winscard.pas」として保存します。

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 C」は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)<15 then
    fCardName:='UnKnown'
  else if (fATR[13]=$00) and (fATR[14]=$01) then
    fCardName:='MIFARE Classic 1K'
  else if (fATR[13]=$00) and (fATR[14]=$02) then
    fCardName:='MIFARE Classic 4K'
  else if (fATR[13]=$00) and (fATR[14]=$03) then
    fCardName:='MIFARE Ultralight'
  else if (fATR[13]=$00) and (fATR[14]=$26) then
    fCardName:='MIFARE Mini'
  else if (fATR[13]=$00) and (fATR[14]=$3A) then
    fCardName:='MIFARE Ultralight C'
  else if (fATR[13]=$00) and (fATR[14]=$36) then
    fCardName:='MIFARE Plus SL1 2k'
  else if (fATR[13]=$00) and (fATR[14]=$37) then
    fCardName:='MIFARE Plus SL1 4k'
  else if (fATR[13]=$00) and (fATR[14]=$38) then
    fCardName:='MIFARE Plus SL2 2k'
  else if (fATR[13]=$00) and (fATR[14]=$39) then
    fCardName:='MIFARE Plus SL2 4k'
  else if (fATR[13]=$00) and (fATR[14]=$30) then
    fCardName:='Topaz and Jewel'
  else if (fATR[13]=$00) and (fATR[14]=$3B) then
    fCardName:='FeliCa'
  else if (fATR[13]=$FF) and (fATR[14]=$28) then
    fCardName:='JCOP 30'
  else if (fATR[13]=$00) and (fATR[14]=$07) then
    fCardName:='SRIX'
  else
    fCardName:='UnKnown';
end;

function TSCard.GetUID: String;
var SendLen,RecvLen:DWORD;
    SendBuf,RecvBuf:TBytes;
    i:Integer;
begin
  Result:='';
  if ConnectCard() then
  begin
    SendLen:=5;
    SetLength(SendBuf,SendLen);
    SendBuf[0]:=$FF;//CLA
    SendBuf[1]:=$CA;//INS
    SendBuf[2]:=$00;//P1  $00:UIDを取得 $01:ATSを取得
    SendBuf[3]:=$00;//P2
    SendBuf[4]:=$00;//Le
    RecvLen:=256;
    SetLength(RecvBuf,RecvLen);
    ZeroMemory(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
      begin
        for i := Low(RecvBuf) to High(RecvBuf)-2 do
        begin
          Result:=Result+IntToHex(RecvBuf[i],2);
        end;
      end
      else
      begin
        fErrStr:=Format(
          'SW1:%2.2x SW2:%2.2x',
          [RecvBuf[RecvLen-2],RecvBuf[RecvLen-1]]
        );
      end;
    end
    else
    begin
      fErrStr:='UID取得失敗';
    end;
    //カードの切断
    SCardDisconnect( fHCard, SCARD_LEAVE_CARD );
    //コンテキストの解放
    SCardReleaseContext(fContext);
  end;
end;

function TSCard.ReadBlock(BlockNum: Byte): String;
var SendLen,RecvLen:DWORD;
    SendBuf,RecvBuf:TBytes;
    i:Integer;
begin
  Result:='';
  if ConnectCard() then
  begin
    SendLen:=5;
    SetLength(SendBuf,SendLen);
    ZeroMemory(SendBuf,SendLen);
    SendBuf[0]:=$FF;     //CLA
    SendBuf[1]:=$B0;     //INS FFB0:ReadBinary
    SendBuf[2]:=$00;     //P1
    SendBuf[3]:=BlockNum;//P2
    SendBuf[4]:=$10;      //Le 読み取るバイト数
    RecvLen:=256;
    SetLength(RecvBuf,RecvLen);
    ZeroMemory(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
      begin
        //「MIFARE Ultralight C」は必ず16Byte(4ブロック)読める
        //4Byte(1ブロック分)のみ返す
        SetLength(fReadBlockData,4);
        for i := 0 to 3 do
        begin
          fReadBlockData[i]:=RecvBuf[i];
          Result:=Result+IntToHex(RecvBuf[i],2);
        end;
      end
      else
        fErrStr:=Format(
          'SW1:%2.2x SW2:%2.2x',
          [RecvBuf[RecvLen-2],RecvBuf[RecvLen-1]]
        );
    end
    else
      fErrStr:='読み込み失敗';
    //カードの切断
    SCardDisconnect( fHCard, SCARD_LEAVE_CARD );
    //コンテキストの解放
    SCardReleaseContext(fContext);
  end;
end;

function TSCard.UpdateBuffer(Data: TBytes; BlockNum: Byte): Boolean;
var i:Integer;
    d:TBytes;
    WriteBlockNum: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);
    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.

フォームのソースコードの入力

Form1のOnCreate、OnDestroy、Button1のOnClickなどに以下のソースコードを記述します。

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

{$R *.dfm}

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
  //「MIFARE Ultralight C」の容量は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とカードの種類が表示されます。

ICカードのATRを読む

Button2をクリックすると、カードのUIDが表示されます。

ICカードのUIDを読む

Button3をクリックすると、ICカードの0ブロック目から134ブロック目(全135ブロック、520Byte)を読み込んで4Byteずつ表示します。

ICカードから全ブロックのデータを読む

Button4をクリックすると4ブロック目~129ブロック目のデータをクリアします。(数秒時間がかかります)

ICカードのメモリ領域をクリアする

Edit1に「こんにちは」などICカードに入れたい文字列を設定し、 Button5をクリックするとICカードに文字列を書き込みます。

ICカードに文字列を書き込む

「こんにちは」と書き込んだカードをスマホの背面にかざします。

「こんにちは」と書き込んだカードをスマホの背面にかざします