2013-06-04 27 views
6

私はsmartcardioを使ってスマートカードを扱うJavaアプリケーションを開発しています。 アプレットを再起動せずにUSBカードリーダーを取り外してからもう一度挿入する必要があります。スマートカード端末の取り外し:SCARD_E_NO_SERVICE CardException

端末の変更を検出するためにterminals()waitForChange()の方法を使用していますが、Linux、MacOS、Win7では正常に動作しています。

しかし、Windows 8(およびWindows 8のみ)では、最後の端末を削除した後、これらのメソッドはSCARD_E_NO_SERVICECardExceptionを投げて、これ以上の変更は検出されません。

"サービス"が何を話しているのかよく分かりません。しかし、TerminalFactory.getDefault()TerminalFactoryシングルトンがあると呼ぶと、これは私のスレッドで開始されたと思います。そして、私はこのシングルトンが下敷きサービスを管理する方法を持っている可能性があり、これが壊れていると思います。

Windows 8でsmartcardioを使用して端末の切断を管理する方法について、他にも誰かがいますか?

答えて

9

この投稿はかなり古いですが、私はJR UtilyからソリューションのWindows 8上

を説明し、問題が完全には動作しませんでした修正することが有用であった:アンプラグドリーダーの場合には、そこ、再び差し込みますCardTerminalインスタンスのエラーです。

以下のコードでわかるように、端末リストをクリアするコードをいくつか追加しました。

 Class pcscterminal = Class.forName("sun.security.smartcardio.PCSCTerminals"); 
     Field contextId = pcscterminal.getDeclaredField("contextId"); 
     contextId.setAccessible(true); 

     if(contextId.getLong(pcscterminal) != 0L) 
     { 
      // First get a new context value 
      Class pcsc = Class.forName("sun.security.smartcardio.PCSC"); 
      Method SCardEstablishContext = pcsc.getDeclaredMethod(
               "SCardEstablishContext", 
               new Class[] {Integer.TYPE } 
              ); 
      SCardEstablishContext.setAccessible(true); 

      Field SCARD_SCOPE_USER = pcsc.getDeclaredField("SCARD_SCOPE_USER"); 
      SCARD_SCOPE_USER.setAccessible(true); 

      long newId = ((Long)SCardEstablishContext.invoke(pcsc, 
        new Object[] { SCARD_SCOPE_USER.getInt(pcsc) } 
      )); 
      contextId.setLong(pcscterminal, newId); 


      // Then clear the terminals in cache 
      TerminalFactory factory = TerminalFactory.getDefault(); 
      CardTerminals terminals = factory.terminals(); 
      Field fieldTerminals = pcscterminal.getDeclaredField("terminals"); 
      fieldTerminals.setAccessible(true); 
      Class classMap = Class.forName("java.util.Map"); 
      Method clearMap = classMap.getDeclaredMethod("clear"); 

      clearMap.invoke(fieldTerminals.get(terminals)); 
     } 
+1

ありがとうございます。私は、PCSCTerminal Mapフィールド "stateMap"もクリアする必要があることを指摘しなければなりません。 – vellotis

+0

私はもはやこれを必要とするプロジェクトでは動作しませんが、私はもっと良い解決策について教えてください:) –

+0

ありがとう@vellotis、私のために働いた。 –

4

私は方法を見つけましたが、反射的なコードを使用しています。むしろ、より洗練された方法を見つけたいと思いますが、Smart Card Contextを管理する公式のAPIはないようです。すべてのクラスはプライベートです。メソッドが呼び出されますが、コンテキストはシングルトンとして見られていると、再初期化されていません。

sun.security.smartcardio.PCSCTerminalshttp://www.docjar.com/html/api/sun/security/smartcardio/PCSCTerminals.java.html)のinitContext()方法は、最初の1が初期化された後、新しいコンテキストを取得してから、新しいスレッドを防ぎます。

java.lang.reflectでこれを囲むものすべてを渡すと、新しいコンテキストの作成を強制し、新しいIDを「公式」contextIdとして保存することができます。これは新しいTerminalFactoryをインスタンス化する前に行う必要があります。

// ... 
    Class pcscterminal = Class.forName("sun.security.smartcardio.PCSCTerminals"); 
    Field contextId = pcscterminal.getDeclaredField("contextId"); 
    contextId.setAccessible(true); 

    if(contextId.getLong(pcscterminal) != 0L) 
    { 
     Class pcsc = Class.forName("sun.security.smartcardio.PCSC"); 
     Method SCardEstablishContext = pcsc.getDeclaredMethod(
              "SCardEstablishContext", 
              new Class[] {Integer.TYPE } 
             ); 
     SCardEstablishContext.setAccessible(true); 

     Field SCARD_SCOPE_USER = pcsc.getDeclaredField("SCARD_SCOPE_USER"); 
     SCARD_SCOPE_USER.setAccessible(true); 

     long newId = ((Long)SCardEstablishContext.invoke(pcsc, 
       new Object[] { Integer.valueOf(SCARD_SCOPE_USER.getInt(pcsc)) } 
      )).longValue(); 
     contextId.setLong(pcscterminal, newId); 
    } 
    // ... 
2

(これは単なるコメントであるが、私はコメントを投稿するのに十分な担当者を持っていません。)

、それはまた、スマートカードリソースマネージャとして知られているWindowsのスマートカードサービスです指しサービス。 サービス MMCコンソールを開くと、スタートアップタイプがに設定され、マニュアル(トリガースタート)に設定されています。 Windows 8では、このサービスはスマートカードリーダーがシステムに接続されている間のみ実行されるように変更され(リソースを節約するために)、最後のリーダーが削除されるとサービスは自動的に停止します。サービスを停止すると、未処理のハンドルが無効になります。

ネイティブのWindowsソリューションでは、SCardAccessStartedEventを呼び出し、返されるハンドルを使用してサービスが開始するのを待ってから、SCardEstablishContextを使用してリソースマネージャに再度接続します。

関連する問題