IConnectableCredentialProviderCredential の利用

PlatformSDK( Windows SDK) サポートの mitsuruw です。

いよいよ春の気配が近付いてまいりました。春と言えば、東大寺のお水取りですね。
この季節、奈良では 「お水取りも終わったし、いよいよ春ですね」 というやりとりをよく耳にしますが、この 「お水取り」 というのは通称で、正しくは東大寺二月堂の修二会 (しゅにえ) のことを指します。

全国の神様が東大寺二月堂に集まった際、若狭の国の遠敷明神 (おにゅうみょうじん) という神様だけが川で魚をとっていたため、集会に遅れてしまいました。そのお詫びに遠敷明神は遠敷川から二月堂にお水を送ることにしたそうで、お水取りはこのエピソードに由来していると言われています。私のまわりには 「お水取り」 という単語は知っている方が多いのですが、このあたりの由来をご存じの方はあまりいないようです。
これはつまり 「遅刻してしまったおわびにお水をあげます」 というお話なのですが、いつの時代も時間はとても貴重なものです。プログラムが処理に時間がかかっている間も、あまり長く放置されると、待っているユーザーはだんだんイライラしてきてしまいます。こういう時は 「いまがんばっているのでもう少しまってください」 というメッセージを出すだけでも随分印象が違うことでしょう。

そこで今回は、もし Credential Provider で時間のかかる処理を行わなければならない場合に、ユーザーにその旨を伝えるような便利なインターフェスをご紹介したいと思います。

IConnectableCredentialProviderCredential インターフェース

Credential Provider を開発したことのある方でも、このインターフェースを使用したことがある方は案外少ないようです。このインターフェースは名前が長いので、ここでは ICCPC と省略させてください。
Credential Provider でサブミット ボタン (→ のボタンです) を押下した後で時間がかかるような処理を行う場合には、ICCPC を実装し、Connect() メソッド内でその処理を行う方法が用意されています。このインターフェイスは、本来 VPN などのネットワーク認証を必要とする PLAP (Pre Logon Access Provider) 向けに用意されたものですが、GetSerialization() での認証処理に時間がかかるような場合にも利用することが可能です。

Credential Provider で ICCPC を実装した場合、サブミット ボタン押下時のシーケンスは以下のようになります。

  < ICCPC 実装時の処理シーケンス >
  1. サブミット ボタンを押下します。
  2. ICCPC::Connect() の呼び出しが行われます。
  3. 画面が切り替わり、以下の UI が表示されます。

     a) 既定では "接続中..." というメッセージ表示 (この文字列は変更可能です)
     b) [キャンセル] ボタン表示

  4. Connect() から復帰後、GetSerialization() の呼び出しが行われます。

また、上述のシーケンスの 3. において、Connect() で時間のかかる処理を行っている間は Credential Provider の入力画面は表示されない状態になりますので、ユーザーが画面操作を行うことができない状態を作り出すことができます。
では、次に ICCPC を実装したい場合どのように実装すればよいか、具体的な実装例をもとにご説明したいと思います。

ICCPC を実装する場合の具体的な手順

ICCPC を実装する場合、CSampleCredential クラスが継承するインターフェイスを ICCPC にする必要があります。以前ご紹介した SampleCredentialProider サンプルをもとに、早速 ICCPC を実装してみましょう。

1. CSampleCredentialクラスが継承するインターフェイスを ICCPC に変更します。

    < CSampleCredential.h >
  // class CSampleCredential : public ICredentialProviderCredential
    class CSampleCredential : public IConnectableCredentialProviderCredential

2. CSampleCredential クラスの QueryInterface() の実装において、IID_IUnknown、IID_ICredentialProviderCredential に加え、 IID_IConnectableCredentialProviderCredential  の場合にもポインタを返すようにします。

    < CSampleCredential.h >
    if (IID_IUnknown == riid ||
        IID_ICredentialProviderCredential == riid || 
        IID_IConnectableCredentialProviderCredential == riid) // 追加

3. Connect() と Disconnect() メソッドを実装します。

    < CSampleCredential.h >
    IFACEMETHODIMP Connect (IQueryContinueWithStatus *pqcws);
    IFACEMETHODIMP Disconnect (VOID);

    < CSampleCredential.cpp >
    HRESULT CSampleCredential::Connect(IQueryContinueWithStatus *pqcws)
    {
       HRESULT hr;
       int i;

       // 文字列を設定します
       pqcws->SetStatusMessage(L"処理中です。しばらくおまちください");

       // キャンセルを確認します
       for (i = 0; i < 10; i++)
       {
         hr = pqcws->QueryContinue();
         if (hr != S_OK){
           
           // キャンセルが押されました
           return S_OK;
         }

         Sleep(1000);
       }

       return S_OK;
    }

    HRESULT CSampleCredential::Disconnect(VOID)
    {
      return S_OK;
    }

また、Connect() 処理中には [キャンセル] ボタンが必ず表示され、ユーザーが任意で時間のかかる処理を中断できるインターフェースを提供しています。この [キャンセル] ボタンが押されたことを判定するためは、IQueryContinue インターフェースの QueryContinue() メソッドを使用します。

サブミット ボタン押下後、Connect() メソッド処理中の画面

IConnectableCredentialProviderCredential や IQueryContinue インターフェースの詳細については以下のドキュメントも参考にしてください。

  Title : IConnectableCredentialProviderCredential Interface (Windows)
  URL   : <https://msdn.microsoft.com/en-us/library/bb776110.aspx>

  Title : IConnectableCredentialProviderCredential::Connect Method (Windows)
  URL   : <https://msdn.microsoft.com/en-us/library/bb776100.aspx>

  Title : IQueryContinue Interface (Windows)
  URL   : <https://msdn.microsoft.com/en-us/library/bb761367.aspx>

  Title : IQueryContinue::QueryContinue Method (Windows)
  URL   : <https://msdn.microsoft.com/en-us/library/bb761369.aspx>

これで Credential Provider で少しくらい時間がかかる処理を実装してもモニターの前にいる方もイライラすることはないと思います。もしイライラした場合でも、すぐに処理をキャンセルできるので安心です。Credential Provider で時間のかかる処理を行う実装をされる場合には、ぜひ IConnectableCredentialProviderCredential インターフェースをご利用ください。

 

これからいよいよ花粉症の季節ですね。私は今のところ花粉症ではないのですが、くしゃみをするたびに 「いよいよきたか」 と思いビクビクしています。奈良県は吉野杉が有名だからスギ花粉もすごいんですよね、と聞かれたことがあるのですが、実際はどうなんでしょう。また機会があれば調べてみたいと思います。mitsuruw でした。

 

前回のおまけの答え:

b) 特定の Credential Provider だけがフィルタしても表示される。

が正解でした。実は標準の PasswordCredentialProvider だけが表示されます。

Credential Provider Filter (CP Filter) によって、システムに登録されているすべての Credential Provider がフィルタされた場合、ログオン不可能な状態を回避するために、Windows 標準で用意されている PasswordCredentialProvider (GUID: {6f45dc1e-5384-457a-bc13-2cd81b0d28ed} ) が表示される動作となっています。

ちなみに、この動作 (Fallback と呼ばれます) は Credential Provider のロード元である LogonUI.exe で CP Filter を処理する過程において行われるのですが、Fallback 対象となっている Password CP の GUID は内部で固定値として保持されているため、その他の自分の Credential Provider を Fallback の対象とすることはできません。