6 タイマー
スレッドと同様に平行運転など時間を意識したプログラミングを行う時に便利な機能にタイマがあります。
タイマは設定された時間間隔をおいてタイマメッセージをウィンドウに送る働き、つまり一定間隔でウィンドウプロシージャを呼び出す動作と設定された任意のタイマプロシージャを同じく一定間隔で呼び出す働きがあります。後者を利用すればウィンドウを用意しなくとも一定間隔で動作するアプリケーションが実現できます。
後者の簡単な例をリスト3.6に示します。リスト3.6では設定された間隔毎にタイマプロシージャであるTimProc()が呼び出されます。リスト3.6の実行画面を図3.20に示します。
ただしタイマプロシージャは別スレッドで呼び出されるわけではありませんので、タイマプロシージャが呼び出されている間はmain側の処理は中断します。平行した動作が必要な場合は、新しくタイマ用のスレッドを作りそのスレッドでタイマを設定します。
<リスト3.6>タイマのテスト:list306.cpp
//
// list306.cpp
// タイマプロシージャのテスト
//
#include // kbhit(),getch()
#include
#include
#define ID_ti 100
VOID CALLBACK TimProc(HWND ,UINT ,UINT ,DWORD ); // タイマプロシージャ
int main( )
{
MSG msg;
SetTimer(NULL,ID_ti,1000,(TIMERPROC)TimProc);
// 1秒毎にTimProcを呼び出す
while( ! kbhit() ){
GetMessage( &msg, NULL, 0, 0 );
DispatchMessage( &msg );
}
printf("メッセージポンプを終了しました\n");
}
VOID CALLBACK TimProc(HWND ,UINT ,UINT ,DWORD ti) // タイマプロシージャ
{
printf("何かキーを押すと終了します 経過時間 %d msec \n",ti);
Beep( 400,100 );
return;
}
<図3.20>リスト3.6の実行画面
<表3.18>タイマに関するAPI SetTimer関数とKilltimer関数
UINT SetTimer(HWND hwnd, UINT idTimer, UINT uTimeout, TIMERPROC tmprc)
BOOL KillTimer(HWND hwnd, UNIT idTimer)
パラメータ 説明
hwnd タイマに関連付けられるウィンドウを識別します。
tmprcパラメータにNULLを指定すると、このウィンドウに関連するウィンドウ
プロシージャは、タイマにより生成されたWM_TIMERメッセージを受け取ります。
このパラメータがNULLのときには、どのウィンドウもタイマに関連付けられません。
idTimer 0以外のタイマ識別子を指定します。hwndパラメータがNULLのとき、このパラメータ
は無視されます。
uTimeout タイムアウト値をミリ秒単位で指定します。
tmprc WM_TIMERメッセージを処理するコールバック関数のプロシージャ アドレスを指定します。
このパラメータがNULLのときは、アプリケーションのメッセージ キューにWM_TIMERメッ
セージが置かれ、MSG構造体のhwnd要素に、hwndに指定されたウィンドウ ハンドルが格
納されます。
戻り値(SetTimer関数のみ)
タイマを作成できなかった場合は、0を返します。タイマを正常に作成できた場合は、0以外の値を返します。hwndがNULLで、正常にタイマを作成できた場合は、戻り値をKillTimerに渡すタイマ識別子として使用します。hwndが有効なウィンドウ ハンドルで、正常にタイマを作成できた場合は、idTimerをKillTimer関数に渡すタイマ識別子として使用します。
解説
SetTimer関数は、システム タイマを起動します。タイムアウト値を指定すると、タイムアウトが起こるたびに、システムはタイマを起動したウィンドウにWM_TIMERメッセージを送ります。あるいは、TIMERPROCコールバック関数を呼び出します。
タイマは、限られたグローバル リソースです。このため、アプリケーションはSetTimer関数が返す値をチェックし、タイマが実際に利用できるかどうかを確認しなければなりません。
KillTimer関数は、指定されたタイマを削除します。タイマに関連付けられている未処理のWM_TIMERメッセージは、メッセージ キューから削除されます。
7 COMを使う
○COM
Windowsプログラミングに関連して知っておくべき技術にCOMがあります。COMはコンポーネント オブジェクト モデル (Component Object Model : COM)の略で、ソフトウェアを部品として利用するための技術です。COMの背景には、例えばワード上でエクセルの表を貼り付けるなどのWindowsアプリケーションで何気なく使っている異なるデータを混在させて利用するための技術であるOLEとオートメーションがあります。OLEとオートメーションの基礎となる技術の1つがCOMです。COMをインターネットの利用に拡張したものがActiveXです。
COM自体はその名のとうり1つの型、ソフトウエア仕様と言うことになります。そのCOM仕様で作られたプログラムをCOMオブジェクトと呼び、C言語のライブラリー関数を使うように他のプログラムから呼び出して利用することができます。またCOM仕様に従ってプログラムすればCOMオブジェクトを作成することもできますし、COMオブジェクトからWinodwsAPIを呼び出している場合もあります(図3.21)。
ではCOMとは何でしょうか。一言でいえば「いくつかの約束に従ったクラス定義とそのインスタンス」ということになります。COMにはクラスに存在するメソッド(関数)を機能別にまとめたインターフェースという機構があります。COMを利用する場合、C++のクラス異なりクラスのメソッドを直接呼び出すのではなく、このインターフェースを介してCOMのメソッドを呼び出します。また各インターフェースにはCOM内の他のインターフェースを呼び出すためのQueryInterface()メソッドが必ず存在します(図3.22)。
従来のライブラリ関数などと違うところは1つ1つのCOMオブジェクトや各COMオブジェクト毎に備わっているインターフェスはGUIDとよばれる128ビットのユニークな数値がつけられている点です。COMオブジェクトにつけられるGUIDをCLSID(クラスID)、インターフェースにつけられるIID(インターフェースID)と呼びます。そのマシンにインストールされているCOMオブジェクトとそのインターフェースは各GUIDを使ってレジストリにすべて記録されています。この記録はVisualC++に付属するOLEViewerで見ることができます。
<図3.21>COMとAPI
<図3.22>COMとインターフェース
○COM時計
簡単なCOMオブジェクトの利用を紹介します。一般にCOMオブジェクトは図3.23に示す手順で利用します。またインターフェースは図3.24に示すように各メソッドの場所を示すテーブルを指すポインタを指す構造をとります。リスト3.7はWindowsのシェル機能の提供するCOMを利用して時計を表示します。CoCreateInstance()で、COMオブジェクトの生成を行い、COMオブジェクトのインタフェースに対する間接的なポインタが返されます。第1引数は、生成するオブジェクトのCLSID、第4引数は取得したいインタフェースのIIDの指定です。第2引数は、COMオブジェクトの結合に関する指定で、通常はNULLを指定します。第3引数はCOMオブジェクトの形態を指定します。CLSCTX_INPROC_SERVER は、オブジェクトが DLL として実装され、アプリケーションの処理の一部として実行されることを指定します。第5引数はインターフェース・ポインタの代入先を指定します。CoCreateInstance()で取得されたシェルディスパッチ・インターフェースのSetTime()を呼び出すことで時計が表示されます(図3.25)。
<図3.23>
<リスト3.7:list307.cpp>
//
// list307.cpp COMの呼び出し
//
#include
void main(void)
{
IShellDispatch *pDispatch;
CoInitialize(NULL); // COM初期化
// ShellDispatchインターフェースを取り出す
if( CoCreateInstance( CLSID_Shell, NULL, CLSCTX_INPROC,
IID_IShellDispatch, (void **)&pDispatch)
== S_OK ) {
pDispatch -> SetTime(); // 時計の表示
pDispatch -> Release(); // インターフェースを開放
}
CoUninitialize(); // COM終了処理
return ;
}
<表3.19 COMの初期化と生成関数>
┌─────────────────────────────────────────┐
│HRESULT CoInitialize( LPVOID pvReserved ); // 引数は必ずNULLにします。 |
|void CoUninitialize( ); |
|STDAPI CoCreateInstance( REFCLSID rclsid, LPUNKNOWN pUnkOuter, |
| DWORD dwClsContext, REFIID riid, PVOID *ppv ); |
├─────────┬───────────────────────────────┤
│パラメータ │ 説明 │
├─────────┼───────────────────────────────┤
|rclsid |オブジェクトのCLSID 。 |
├─────────┼───────────────────────────────┤
|pUnkOuter |オブジェクトがアグリゲートオブジェクトの一部として作成する場合|
| |はアグリゲートオブジェクトの IUnknown インターフェイスへのポイ|
| |ンタを指定します。 |
├─────────┼───────────────────────────────┤
|dwClsContext |新規に作成されたオブジェクトを管理するコンテキスト。 |
├─────────┼───────────────────────────────┤
|riid |オブジェクトとの通信に使用するインターフェースの IID 。 |
├─────────┼───────────────────────────────┤
|ppv |インターフェースポインタを格納するための変数のアドレス。 |
├─────────┼───────────────────────────────┤
|戻り値 |正常に動作した場合は 0 (S_OK) が返ります。 |
├─────────┴───────────────────────────────┤
|CoCreateInstance()は指定された CLSID に関連付けられたクラスの未初期化オブジェクト |
|を作成します。 |
|CoCreateInstance() を使ってオブジェクトを作成する場合は、明示的に CoInitialize () |
|を呼び出さなければならなりません。オブジェクトの作成を終了したら、CoUninitialize()|
|を呼び出して COM を未初期化する必要があります。 |
└─────────────────────────────────────────┘