SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Windowsアプリケーションで「処理中」を表現する

時間のかかる処理で「処理中」を表現する(前編)

Windowsアプリケーションで処理中を表現する様々な方法

ダウンロード WaitSample.zip (46.5 KB)

反応できないことを明示的に意思表示する

 アプリケーションが処理に反応できない場合、待機カーソル(WaitCursor:XPまでは砂時計、Vista/Windows7では回転する緑のドーナツ状のマウスカーソル)にするという決まりがあります。まずは、先ほどのループにこれを挟み込んで、応答なしが表示される場合でも頑張って作業していることをユーザーに伝えるコードを追加しましょう。

 待機カーソルにするには、LoadCursor APIでシステム定義のIDC_WAITカーソルをロードし、それをSetCursorで一時的にセットします。マウスカーソルは、マウスメッセージが処理されるタイミングでリセットされてしまうため、メッセージループが回るともとのカーソルに戻されてしまうので、注意が必要です。

待機カーソル設定(API)
void BusyLoop( DWORD dwTime )
{
  HCURSOR hWait = LoadCursor( NULL, IDC_WAIT );
  HCURSOR hPrev = SetCursor( hWait );

  DWORD dwStart = GetTickCount();
  while( (GetTickCount()-dwStart) < dwTime );

  SetCursor( hPrev );
}

 これで、ループ中はカーソルが待機カーソルになります。このサンプルは例外が発生したり、途中でリターンしてしまうなどはありませんが、プログラムが大きくなると、例外処理やリターン処理が入ることがあります。そのような場合、マウスカーソルを戻すためだけに例外をキャッチするのはあまり効率がよいとは言えません。C++ではこういう場合RAII(Resource Acquisition Is Initialization)と呼ばれる想定外の事態にも安全に対応できる記述をする手法があります。そちらで記述したものが下記のものです。

待機カーソル設定(RAII)
void BusyLoop( DWORD dwTime )
{
  CSimpleWaitCursor	waitCursor;
  DWORD dwStart = GetTickCount();
  while( (GetTickCount()-dwStart) < dwTime );
}
CSimpleWaitCursorクラス
class CSimpleWaitCursor
{
public:
  CSimpleWaitCursor()
  {
    HCURSOR hWait = ::LoadCursor( NULL, IDC_WAIT );
    if( hWait != NULL ){
      m_hPrev = ::SetCursor( hWait );
    }
    else{
      m_hPrev = NULL;
    }
  }
  ~CSimpleWaitCursor()
  {
    if( m_hPrev != NULL ){
      ::SetCursor( m_hPrev );
    }
    m_hPrev = NULL;
  }
private:
  HCURSOR  m_hPrev;
};

 単純なカーソル設定ですが、たったこれだけで、どのタイミングでBusyLoopから抜けても元のカーソルに戻ります。実際の処理時間がせいぜい2秒程度で終わるようなものであれば、これでも十分と言えます。

作業中にメッセージを処理する

 待機カーソルはマウスカーソルの見た目を変えることで、ユーザーに時間がかかる作業をしているというフィードバックを与えます。しかし、プログラムがしばらくメッセージを処理できないことをシステムに通知するものではありません。そのため、待機カーソルに変えただけでは一定時間ののちに「(応答なし)」状態になってしまうという状況に変化はありません(サンプルプログラムでは実際にマウスカーソルを変えています)。

 冒頭でも書いていたように「(応答なし)」が出る条件は一定時間メッセージを処理しなかった場合です。従って、時間のかかる処理中も何らかの形でメッセージを処理するようにすれば、基本的には問題が解決できるはずです。

 では、そのメッセージを処理するとはいったいどういうことなのでしょうか。答えはいたって簡単で、処理中にメッセージポンプと呼ばれる一時的なメッセージ処理ループを回すことで解決します。具体的には、次のようなごく単純なメッセージループを用意しておき、必要に応じて呼び出すだけです。

メッセージポンプ
void PumpMessage()
{
  MSG msg;
  while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ){
    TranslateMessage( &msg );
    DispatchMessage( &msg );
  }
}

 さっそく最初のビジーループでも、応答なしが出ないように1秒ごとにメッセージを処理するように修正したものを用意、実行してみましょう。

ビジーループV2
void BusyLoop2( DWORD dwTime )
{
  while( dwTime > 0 ){
    PumpMessage();
    // メッセージを処理してから待機カーソルにする
    CSimpleWaitCursor  waitCursor;
    DWORD	dwWait = ( dwTime > 1000 ) ? 1000 : dwTime ;
    DWORD dwStart = GetTickCount();
    while( (GetTickCount()-dwStart) < dwWait );
    dwTime -= dwWait;
  }
}

 サンプルプログラムの[体験メニュー]-[ビジーループV2]で体験できます。これで、応答なしが出なくなりました。しかも、嬉しい副作用として、リサイズなどのウィンドウ操作も行うことができます。まずはいろいろ試してみてください。V2では環境によらず30秒間の処理となっています(実際には1秒×30のループ)。

次のページ
処理してほしくないメッセージまで処理されてしまう

この記事は参考になりましたか?

Windowsアプリケーションで「処理中」を表現する連載記事一覧
この記事の著者

とっちゃん(トッチャン)

Microsoft MVP for Development Tools Visual C++Windows Installer 系を中心にオンライン/オフラインで活動しています。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/5332 2010/07/30 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー