UnityでFeliCaからIDmを取得する
UnityでPaSoRiから取得したIDmをもとにログインなどの処理を行う。
winscard.dll(PC/SC)を用いるため、Windowsのみ対応。
環境
- Windows 10 Pro
- Unity 2019.2.0f1
- Sony PaSoRi RC-S380
ソースコード
ソースコードは 「WindowsでNFCタグを読み取る」を参考にした(API部分はほとんどそのまま)。
NfcApi.cs
usingSystem;usingSystem.Runtime.InteropServices;namespaceSmardCard{classNfcApi{[DllImport("winscard.dll")]publicstaticexternuintSCardEstablishContext(uintdwScope,IntPtrpvReserved1,IntPtrpvReserved2,outIntPtrphContext);[DllImport("winscard.dll",EntryPoint="SCardListReadersW",CharSet=CharSet.Unicode)]publicstaticexternuintSCardListReaders(IntPtrhContext,byte[]mszGroups,byte[]mszReaders,refUInt32pcchReaders);[DllImport("winscard.dll")]publicstaticexternuintSCardReleaseContext(IntPtrphContext);[DllImport("winscard.dll",EntryPoint="SCardConnectW",CharSet=CharSet.Unicode)]publicstaticexternuintSCardConnect(IntPtrhContext,stringszReader,uintdwShareMode,uintdwPreferredProtocols,refIntPtrphCard,refIntPtrpdwActiveProtocol);[DllImport("winscard.dll")]publicstaticexternuintSCardDisconnect(IntPtrhCard,intDisposition);[StructLayout(LayoutKind.Sequential)]internalclassSCARD_IO_REQUEST{internaluintdwProtocol;internalintcbPciLength;publicSCARD_IO_REQUEST(){dwProtocol=0;}}[DllImport("winscard.dll")]publicstaticexternuintSCardTransmit(IntPtrhCard,IntPtrpioSendRequest,byte[]SendBuff,intSendBuffLen,SCARD_IO_REQUESTpioRecvRequest,byte[]RecvBuff,refintRecvBuffLen);[DllImport("winscard.dll")]publicstaticexternuintSCardControl(IntPtrhCard,intcontrolCode,byte[]inBuffer,intinBufferLen,byte[]outBuffer,intoutBufferLen,refintbytesReturned);[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]publicstructSCARD_READERSTATE{internalstringszReader;internalIntPtrpvUserData;internalUInt32dwCurrentState;internalUInt32dwEventState;internalUInt32cbAtr;[MarshalAs(UnmanagedType.ByValArray,SizeConst=36)]internalbyte[]rgbAtr;}[DllImport("winscard.dll",EntryPoint="SCardGetStatusChangeW",CharSet=CharSet.Unicode)]publicstaticexternuintSCardGetStatusChange(IntPtrhContext,intdwTimeout,[In,Out]SCARD_READERSTATE[]rgReaderStates,intcReaders);[DllImport("winscard.dll")]publicstaticexternintSCardStatus(IntPtrhCard,stringszReader,refintcch,refintstate,refIntPtrprotocol,byte[]bAttr,refintcByte);[DllImport("kernel32.dll",SetLastError=true)]publicstaticexternIntPtrLoadLibrary(stringlpFileName);[DllImport("kernel32.dll")]publicstaticexternvoidFreeLibrary(IntPtrhandle);[DllImport("kernel32.dll")]publicstaticexternIntPtrGetProcAddress(IntPtrhandle,stringprocName);}}
NfcConstant.cs
usingSystem;namespaceSmardCard{classNfcConstant{publicconstuintSCARD_S_SUCCESS=0;publicconstuintSCARD_E_NO_SERVICE=0x8010001D;publicconstuintSCARD_E_TIMEOUT=0x8010000A;publicconstuintSCARD_SCOPE_USER=0;publicconstuintSCARD_SCOPE_TERMINAL=1;publicconstuintSCARD_SCOPE_SYSTEM=2;publicconstintSCARD_STATE_UNAWARE=0x0000;publicconstintSCARD_STATE_CHANGED=0x00000002;publicconstintSCARD_STATE_PRESENT=0x00000020;publicconstUInt32SCARD_STATE_EMPTY=0x00000010;publicconstintSCARD_SHARE_SHARED=0x00000002;publicconstintSCARD_SHARE_EXCLUSIVE=0x00000001;publicconstintSCARD_SHARE_DIRECT=0x00000003;publicconstintSCARD_PROTOCOL_T0=1;publicconstintSCARD_PROTOCOL_T1=2;publicconstintSCARD_PROTOCOL_RAW=4;publicconstintSCARD_LEAVE_CARD=0;publicconstintSCARD_RESET_CARD=1;publicconstintSCARD_UNPOWER_CARD=2;publicconstintSCARD_EJECT_CARD=3;// SCardStatus status valuespublicconstintSCARD_UNKNOWN=0x00000000;publicconstintSCARD_ABSENT=0x00000001;publicconstintSCARD_PRESENT=0x00000002;publicconstintSCARD_SWALLOWED=0x00000003;publicconstintSCARD_POWERED=0x00000004;publicconstintSCARD_NEGOTIABLE=0x00000005;publicconstintSCARD_SPECIFICMODE=0x00000006;}}
NfcReader.cs
usingSystem;usingUnityEngine;usingSmardCard;usingSystem.Text;publicclassNfcReader:MonoBehaviour{privateIntPtrhContext=IntPtr.Zero;privateIntPtrhCard;privateIntPtractiveProtocol;publicstringreaderName;publicstringcardId;publicboolDetectOnlyFeliCa;publicboolThrowExceptionLog;publicclassCardData{publicstringReaderName="";publicstringCardID="";}publicCardDataReadCardData(){try{SCardEstablishContext();SCardListReaders();SCardConnect();SCardStatus();SCardTransmit();SCardDisconnect();}catch(Exceptione){if(ThrowExceptionLog){Debug.LogWarning(e);}returnnewCardData();}returnnewCardData{ReaderName=readerName,CardID=cardId};}publicstringGetCardReaderName(){try{SCardEstablishContext();SCardListReaders();}catch(Exceptione){if(ThrowExceptionLog){Debug.LogWarning(e);}return"";}returnreaderName;}privateuintSCardEstablishContext(){uintret=NfcApi.SCardEstablishContext(NfcConstant.SCARD_SCOPE_USER,IntPtr.Zero,IntPtr.Zero,outhContext);if(ret!=NfcConstant.SCARD_S_SUCCESS){stringmessage;switch(ret){caseNfcConstant.SCARD_E_NO_SERVICE:message="サービスが起動されていません。";break;default:message="サービスに接続できません。code = "+ret;break;}thrownewApplicationException(message);}if(hContext==IntPtr.Zero){thrownewApplicationException("コンテキストの取得に失敗しました。");}returnret;}privatevoidSCardListReaders(){uintpcchReaders=0;// NFCリーダの文字列バッファのサイズを取得uintret=NfcApi.SCardListReaders(hContext,null,null,refpcchReaders);if(ret!=NfcConstant.SCARD_S_SUCCESS){// 検出失敗thrownewApplicationException("NFCリーダを確認できません。");}// NFCリーダの文字列を取得byte[]mszReaders=newbyte[pcchReaders*2];// 1文字2byteret=NfcApi.SCardListReaders(hContext,null,mszReaders,refpcchReaders);if(ret!=NfcConstant.SCARD_S_SUCCESS){// 検出失敗thrownewApplicationException("NFCリーダの取得に失敗しました。");}UnicodeEncodingunicodeEncoding=newUnicodeEncoding();stringreaderNameMultiString=unicodeEncoding.GetString(mszReaders);// 認識したNDCリーダの最初の1台を使用intnullindex=readerNameMultiString.IndexOf((char)0);readerName=readerNameMultiString.Substring(0,nullindex);}privatevoidSCardConnect(){activeProtocol=IntPtr.Zero;hCard=IntPtr.Zero;uintret=NfcApi.SCardConnect(hContext,readerName,NfcConstant.SCARD_SHARE_SHARED,NfcConstant.SCARD_PROTOCOL_T1,refhCard,refactiveProtocol);if(ret!=NfcConstant.SCARD_S_SUCCESS){thrownewApplicationException("カードに接続できません。code = "+ret);}}privatevoidSCardStatus(){intdwReaderLen=readerName.Length;intdwState=0;byte[]atr=newbyte[64];//ATRintdwAtrLen=atr.Length;longlResult=NfcApi.SCardStatus(hCard,null,refdwReaderLen,refdwState,refactiveProtocol,atr,refdwAtrLen);if(lResult!=NfcConstant.SCARD_S_SUCCESS){thrownewApplicationException("ATR取得に失敗しました。");}lResult=NfcApi.SCardStatus(hCard,readerName,refdwReaderLen,refdwState,refactiveProtocol,atr,refdwAtrLen);if(lResult!=NfcConstant.SCARD_S_SUCCESS){thrownewApplicationException("ATR取得に失敗しました。");}// FeliCaかどうか判別if((atr[13]!=0x00||atr[14]!=0x3b)&&DetectOnlyFeliCa){thrownewApplicationException("FeliCaではありません。");}}privatevoidSCardTransmit(){uintmaxRecvDataLen=256;varrecvBuffer=newbyte[maxRecvDataLen+2];varsendBuffer=newbyte[]{0xff,0xca,0x00,0x00,0x00};// IDmを取得するコマンドNfcApi.SCARD_IO_REQUESTioRecv=newNfcApi.SCARD_IO_REQUEST();ioRecv.cbPciLength=255;intpcbRecvLength=recvBuffer.Length;intcbSendLength=sendBuffer.Length;IntPtrhandle=NfcApi.LoadLibrary("Winscard.dll");IntPtrpci=NfcApi.GetProcAddress(handle,"g_rgSCardT1Pci");NfcApi.FreeLibrary(handle);uintret=NfcApi.SCardTransmit(hCard,pci,sendBuffer,cbSendLength,ioRecv,recvBuffer,refpcbRecvLength);if(ret!=NfcConstant.SCARD_S_SUCCESS){thrownewApplicationException("NFCカードへの送信に失敗しました。code = "+ret);}// 受信データからIDmを抽出cardId=BitConverter.ToString(recvBuffer,0,pcbRecvLength-2);}privatevoidSCardDisconnect(){uintret=NfcApi.SCardDisconnect(hCard,NfcConstant.SCARD_LEAVE_CARD);if(ret!=NfcConstant.SCARD_S_SUCCESS){thrownewApplicationException("NFCカードとの切断に失敗しました。code = "+ret);}}}
ゲーム上での使用
任意のスクリプト(ここではLoginController
とする)からNfcReader
を用いて、IDmを一定間隔で常時取得する例を示す。
インスペクターからNfcReader
を含むゲームオブジェクトを、LoginController
のNfcReader
にドラッグアンドドロップしておく。
LoginController.cs
usingUnityEngine;publicclassLoginController:MonoBehaviour{NFCReaderR;publicGameObjectNfcReader;publicfloatTimeOutLength=1f;privatefloatLastCardReadTime;publicstringIDm;voidAwake(){R=NfcReader.GetComponent<NfcReader>();DontDestroyOnLoad(this.gameObject);}voidUpdate(){if(Time.time-LastCardReadTime>=TimeOutLength&&CardReadingEnabled){LastCardReadTime=Time.time;IDm=R.ReadCardData().CardID;}if(IDm!=""){//ここで処理を行う}}}