.NETアプリからPaSoRiを使ってFeliCaカードを読み取ってみる(felica_for_vb.dllを使わない版)

ちょっと思いつきでやってみたのでメモ。

経緯

  • 自分のPC、なにかのアプリがポート10250でListenしている(#゚Д゚)
  • FeliCa Proxy Service…って、PaSoRiのドライバを入れたときに入ったものか*1
  • 昔はfelica_for_vb.dllを使って遊んだけど、今ってドライバに付属していないの(・ω・)?
  • その代わりに、FeliCa Proxy Serviceに接続してコマンド送れば制御できるってことかしら
  • SDK for FeliCa & Adobe AIR / Adobe Flash Basicが無償で使えるのね(・∀・)
  • 通信内容を覗いてみたら、最初はXMLで通信してるけど、認証後のデータはバイナリでした


  • 解析するのは面倒なので終了(´・ω・`)
  • …っというのも何か悔しいので、無理矢理使い方を考えてみる(`・ω・´)
  • Flashから通信できるのであれば、Flash.ocxをホストしたアプリケーションからのPaSoRiの使用も可能ジャネ( ゜Д゜)?
  • このやりかたと同じでいけるし

概念図。

っというわけで、やってみた(・∀・)

Flash(swf)の作成

Flash(swf)のファイルはFlexを使って作成してみました。
ソースはこんな感じ(・∀・)

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init(event)">
        
    <mx:Script>
        <![CDATA[
            import com.sony.jp.felica.FeliCaControl;
            import com.sony.jp.felica.event.OpenStatusEvent;
            import com.sony.jp.felica.FeliCaPollingAndGetCardInformationRequest;
            import com.sony.jp.felica.FeliCaPollingAndGetCardInformationResponse;
            import com.sony.jp.felica.event.FeliCaStatusEvent;
            import com.sony.jp.felica.error.FeliCaAccessError;
            import com.sony.jp.felica.FeliCaOpenReaderWriterAutoRequest;
            import com.sony.jp.felica.FeliCaOpenReaderWriterAutoResponse;
            import com.sony.jp.felica.event.FeliCaStatusEvent;
            import com.sony.jp.felica.error.FeliCaAccessError;

            private var fc:FeliCaControl = new FeliCaControl();

            // 初期化
            private function init(event:Event):void
            {
                ExternalInterface.addCallback( "open", open );
                ExternalInterface.addCallback( "openReaderWriterAuto", openReaderWriterAuto );
                ExternalInterface.addCallback( "pollingAndGetCardInformation", pollingAndGetCardInformation );

                fc.addEventListener( OpenStatusEvent.OPEN_COMPLETE, onOpenComplete );
                fc.addEventListener( OpenStatusEvent.OPEN_FAILURE, onOpenFailure );

                fc.addEventListener( FeliCaStatusEvent.FELICA_ACCESS_COMPLETE, onFeliCaAccessComplete );
                fc.addEventListener( FeliCaStatusEvent.FELICA_ACCESS_FAILURE, onFeliCaAccessFailure );
                fc.addEventListener( FeliCaStatusEvent.FELICA_ACCESS_PARAMETER_ERROR, onFeliCaAccessParameterError );
            }

            // 接続
            private function open():void
            {
                fc.open(10250);
            }

            // SDK for FelICa SubSet コマンド open_reader_writer_auto()
            private function openReaderWriterAuto():void
            {
                var request:FeliCaOpenReaderWriterAutoRequest = new FeliCaOpenReaderWriterAutoRequest();
                fc.access(request);
            }

            // SDK for FelICa : コマンド polling_and_get_card_information()
            private function pollingAndGetCardInformation():void
            {
                var request:FeliCaPollingAndGetCardInformationRequest = new FeliCaPollingAndGetCardInformationRequest();
                request.systemCode = "FFFF";
                fc.access(request);
            }

            private function onOpenComplete(event:OpenStatusEvent):void
            {
                ExternalInterface.call( "OpenComplete" );
            }

            private function onOpenFailure(event:OpenStatusEvent):void
            {
                var error:Error = event.object as Error;
                ExternalInterface.call( "OpenFailure", error.errorID, error.message );
            }

            private function onFeliCaAccessComplete(event:FeliCaStatusEvent):void
            {
                if (event.object is FeliCaOpenReaderWriterAutoResponse)
                {
                    var openResponse:FeliCaOpenReaderWriterAutoResponse = event.object as FeliCaOpenReaderWriterAutoResponse;
                    ExternalInterface.call( "OpenReaderWriterAuto", openResponse.felicaError, openResponse.rwError, openResponse.felicaProxyError );
                }
                else if (event.object is FeliCaPollingAndGetCardInformationResponse)
                {
                    var pollingResponse:FeliCaPollingAndGetCardInformationResponse = event.object as FeliCaPollingAndGetCardInformationResponse;
                    ExternalInterface.call( "PollingAndGetCardInformation", pollingResponse.numberOfCards, pollingResponse.idm, pollingResponse.pmm, pollingResponse.felicaError, pollingResponse.rwError, pollingResponse.felicaProxyError );
                    }
            }

            private function onFeliCaAccessFailure(event:FeliCaStatusEvent):void
            {
                if (event.object is FeliCaAccessError)
                {
                    var accessErr:FeliCaAccessError = event.object as FeliCaAccessError;
                    ExternalInterface.call( "FeliCaAccessError", accessErr.errorID, accessErr.message, accessErr.felicaError, accessErr.rwError, accessErr.felicaProxyError );
                }
                else if (event.object is Error)
                {
                    var error:Error = event.object as Error;
                    ExternalInterface.call( "Error", error.errorID, error.message );
                }
            }

            private function onFeliCaAccessParameterError(event:FeliCaStatusEvent):void
            {
                var error:Error = event.object as Error;
                ExternalInterface.call( "Error", error.errorID, error.message );
            }
        ]]>
    </mx:Script>

</mx:Application>

やっていることは、ExternalInterfaceを使って、右から来たものを左へ、左から来たものを右へ受け渡しているだけ(´Д`)
FeliCaのライブラリの使い方については、SDK for FeliCa & Adobe AIR / Adobe Flashの中のドキュメントを参照してくださいな。

ホストアプリケーションの作成

そしてFlash.ocxを使用し、swfファイルをホストするアプリケーションの作成手順について。
とりあえずWindowsフォームアプリケーションを作って、ツールボックスアイテムの選択から、COMコンポーネントでFlash10.ocxを追加。
っで、ツールボックスに追加されたShockwave Flash Objectをフォームに貼り付け、VisibleをFalseに設定。


後は、Formのコードでこんな処理を書けば終了。

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();

        // Flashロード
        this.flashPlayer.LoadMovie( 0, Path.Combine( Application.StartupPath, "FelicaProxyClient.swf" ) );
        this.flashPlayer.FlashCall += OnFlashPlayerFlashCall;
    }

    // open()呼び出し
    private void OnOpenButtonClick(object sender, EventArgs e)
    {
        this.flashPlayer.CallFunction( "<invoke name=\"open\" returntype=\"xml\"></invoke>" );
    }

    // openReaderWriterAuto()呼び出し
    private void OnOpenReaderWriterAutoButtonClick(object sender, EventArgs e)
    {
        this.flashPlayer.CallFunction( "<invoke name=\"openReaderWriterAuto\" returntype=\"xml\"></invoke>" );
    }

    // pollingAndGetCardInformation()呼び出し
    private void OnPollingAndGetCardInformationButtonClick(object sender, EventArgs e)
    {
        this.flashPlayer.CallFunction( "<invoke name=\"pollingAndGetCardInformation\" returntype=\"xml\"></invoke>" );
    }

    // Flashからの通知
    private void OnFlashPlayerFlashCall(object sender, _IShockwaveFlashEvents_FlashCallEvent e)
    {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml( e.request );

        XmlAttribute attr = doc.DocumentElement.Attributes["name"];
        if( attr.Value == "PollingAndGetCardInformation" )
        {
            XmlNodeList list = doc.GetElementsByTagName( "arguments" );

            string message =
                String.Format( "numberOfCards={0}\nidm={1}\npmm={2}\n",
                               list[ 0 ].ChildNodes[ 0 ].InnerText,
                               list[ 0 ].ChildNodes[ 1 ].InnerText,
                               list[ 0 ].ChildNodes[ 2 ].InnerText );
            MessageBox.Show( message );
        }
...
    }
}

作成したswfファイルをロードして、それ経由でFeliCa Proxy Serviceと通信してるわけですね(・ω・)


っで、めでたくカードの情報を読み取ることができました。

っというわけで、多少無理矢理ですが、.NETアプリケーションからの(Flash経由での)PaSoRiの使い方でした(・∀・)
ちょっとしたおもちゃなら、このやり方もありかな(・ω・)?

*1:ついでに、このサービス.NET製なのね。