なお、Win32 API SetConsoleTitle() は、ウィンドウ名が完全に変更される前
に関数が終了してしまうようなので、完全に変更されるまで待つ処理が必要
となります(例9-1, 例9-2)。
例9-1:コンソールのウィンドウハンドルを取得する
GetConsoleTitle(szOldText, sizeof(szOldText));
sprintf(szId, "bHide%08X", timeGetTime());
SetConsoleTitle(szId);
/* ウィンドウ名が変更されるまで待つ */
do{
Sleep(100);
GetConsoleTitle(szTemp, sizeof(szTemp));
}while(strcmp(szId, szTemp) != 0);
hWnd = FindWindow(NULL, szId);
SetConsoleTitle(szOldText);
例9-2:コンソールを5秒間非表示状態にする
@echo off
bhide on
bsleep 5
bhide off
9-5 指定されたウィンドウが存在するかを得る
○ウィンドウの状態チェック
バッチファイルからウィンドウを持つアプリケーションをバッチファイルから起動
し、起動したアプリケーションと同期をとって動作するには、そのアプリケーション
が表示するウィンドウがどのような状態になっているかを知る必要があります。
たとえば、あるアプリケーションで処理を行い、その結果を元に次の処理を行う必要
があるときや、起動したウィンドウにメッセージを送信してバッチファイルから
操作を行う必要があるとき、ウィンドウがメッセージを受信できる状態になるまで
待つ必要があります。ウィンドウのチェックを行った結果は、main 関数の
戻り値に設定することにより、バッチファイルの if errorlevel で取得する
ことができます(例9-3)。
例9-3:指定ウィンドウが生成されるまで待つ
@echo off
set WM_COMMAND=273
start winfile.exe ← ファイルマネージャを起動する
:loop
bsleep 1 ← 1秒待つ
bfindwin -cls WFS_Frame ← ファイルマネージャ
if errorlevel 1 goto loop ← エラーなら繰り返す
○Windows95の場合:起動時に最小化
さて、Windows95では、ファイルマネージャ自身をアイコン化して起動すると、
フォーマットダイアログボックスだけを表示させることができます。
ファイルマネージャの起動をチェックするときと同様に、ダイアログボックスの存在を
チェックすることで、フォーマットが終了したかを検出することも可能に
なります。
○Windows95の場合:PIFファイル設定
Windows95 では、16ビット、32ビットアプリケーション共にPIFファイルの設定に
よってコンソール(DOSプロンプト)の動作が決定されます。
ここで注意しなければならないのは、PIFファイルの
[その他]タグ - 「バックグラウンド時の設定」
が、
「つねに実行を中断」
になっている場合、コンソールウィンドウのフォーカスを奪われないように
他のウィンドウにメッセージを送信しなければ、送信元、送信先ともにデッドロックを
起こすことがあります。
○Windows95のコンソール表示プログラムCONAGENT.EXE
コンソールアプリケーションを実行するとき、システムは
C:\WINDOWS\SYSTEM\CONAGENT.EXE
を起動してコンソールの表示を行います。CONAGENT.EXE は、16ビット
アプリケーションで、32ビットアプリケーションが起動されるとCONAGENT.EXE
が起動されます。
起動したアプリケーションからコンソールへのアクセスが行われると、
CONAGENT.EXE によって、VCOND(仮想コンソールデバイス)の呼び出しが
行われます。これは、コンソールの内部制御が16ビットコードで行われているため
です。
CONAGENT.EXE の起動時に、
C:\WINDOW\S\SYSTEM\CONAGENT.PIF
が存在すると、その設定に従い、環境設定が行われます。このPIFファイルの設定が、
バックグラウンド時の設定が「常に実行を中断」になっていると、
コンソールからフォーカスが奪われると処理が停止されます。
○Windows95のCONAGENT.PIFの設定
コンソールを Win32 API AllocConsole() でコンソールを生成し、
そのウィンドウにフォーカスが移動すると、マウスカーソルが消えてしまうことが
あります。これは、CONAGENT.PIF の設定のうち、
[その他]タグ -「マウスポインタを表示しない(排他モード)」
がチェックされているために起こります。この設定は、マウスの制御権を占有しなけれ
ば正しく動作しないMS-DOSアプリケーションのために利用します。
この設定はコンソールアプリケーションには無意味なうえトラブルの素になります
ので、CONAGENT.PIF ではこの設定を行わないほうがよいでしょう。
9-6 指定されたウィンドウにメッセージを送信する
ウィンドウを持つアプリケーションに、キーやマウスで操作しているときと同じ
メッセージを送信することで、簡単に制御することができます(例9-4)。
ウィンドウにメッセージを送信するには、送信先ウィンドウのウィンドウハンドルが
必要になります。ウィンドウハンドルは、ウィンドウの存在を調べるときと同様に、
ウィンドウ名やクラス名が特定できれば、Win32 API FindWindow() で取得
することができます。取得したウィンドウハンドルを利用して、
Win32 API PostMessage() でウィンドウにメッセージを送信します。
例9-4:指定ウィンドウを閉じる
@echo off
set WM_CLOSE=16
bmessage -win %1 -cls %2 %WM_CLOSE% 0 0
set WM_CLOSE=
9-7 補助用外部コマンド(処理を一時停止する)
ウィンドウの存在をチェックするときなど、処理を一時的に停止してある一定時間
処理を停止して、ある一定時間後に再度処理を行うといった処理を行う必要が
あります。
もし、処理を単純にループすると、無限ループ状態になり、CPUがその処理に集中
して、他の処理に影響を与えるためです。
また、失敗した処理が次の瞬間に成功することは少ないため、一定時間待って再試行
するようにします。
処理を中断して、一定時間CPUをシステムに譲るには、Win32 API Sleep() で停止
する時間を指定します。
9-8 補助用外部コマンド(他のマシンにメッセージを送信する)
コンソールウィンドウを非表示にして実行させて状態がわからなくなったり、
サーバ上でバッチファイルを実行して、終了するまで、他のマシンで作業をしたい
とき、バッチファイルから処理が終了したことを知らせる必要があります。
他のマシンにメッセージを送信したいとき、NET.EXE を利用することが
できます。NET コマンドはMS-DOSのLANマネージャから利用できるため、複数のOSで
稼動するマシンで構成されるLAN環境では効果的に利用することができます。
NETコマンドで送信したメッセージは、OS/2, Windows3.1, Windows95, WindowsNT
で受信することができますが、Windows3.1 や Windows95 では、ポップアップ
サービス (WINPOPUP.EXE) をあらかじめ起動しておく必要があります。
NETコマンドでのメッセージ送信は、
NET SEND /?
で利用方法を知ることができます。
ただし、Windows95 では NET SEND コマンドはサポートしていません。
○メールスロット
NETコマンドや ポップアップサービスはメールスロットAPIを利用しています
(表9-2, 表9-3)。
Windows95 や WindowsNTのメールスロットAPIは、OS/2のLANマネージャで提供される
機能のサブセット版で、MS-DOSのLANマネージャとも互換性があります。
メールスロットAPIは、データグラム型の通信を行い、1対1または、1対多の通信を
行うことができます。
クライアントは、任意のマシンのサーバにメッセージを送信することができます。
ただし、メッセージサイズは最大400バイトに制限されます。
LANマネージャのメールスロットには、クラス1とクラス2のメッセージ機能が
ありますが、Windows95やWindowsNT ではクラス2のメッセージ機能をサポートして
います。
TCP/IPのTCPや名前付きパイプは1対1のセッションが開設されると、
他のクライアントは割り込むことはありません(図9-2)。
このため、サーバで受け取るデータは必ず特定クライアントのデータであることが
保証されます。しかし、ソケットのUDPやメールスロットは、複数のクライアントから
同時にメッセージを受け取ってしまいます。このため、メールスロットサーバは、
受け取ったデータが誰のものかを識別して処理を行う必要があります。データグラム型
の通信は、テレビやラジオのようにサーバがデータを正しく受信したかをチェック
しませんので、確実にデータを送信したいときには、名前付きパイプを利用したほうが
よいでしょう。
○メールスロットのパス名
Microsoft Win32 SDK KnowledgeBase
BUG: Windows95 Limits Mailslot Names to 8.3 Naming Convertion(PSS ID:Q139716)
で、Windows95 で利用するのメールスロットのパス名は、8.3形式でなければ
ならないとありますが、筆者のマシンでは、8.3を超えるファイル名でも正常に動作
します。日本語版は、US版のバグが修正されていることもありますし、
また Service Pack を入手できますので、どのバージョンなら問題がないかを確認して
おく必要があるかもしれません。
また、WindowsNTがメールスロットサーバで、Windows95がクライアントとして送信する
ときも、この制限を受けるようです。このため、WindowsNTで動作するときでも、
クライアントにWindows95を利用するときは、8.3形式である必要があります。
表9-2:LAN Manager2.x と Win32 API のメールスロット
| LAN Manager 2.xのメールスロット関数 |
Win32 API のメールスロット関数 |
| DosDeleteMailslot | CloseHandle |
| DosMailslotInfo | GetMailslotInfo |
| DosMakeMailslot | CreateMailslot |
| DosReadMailslot | ReadFile |
| DosWriteMailslot | WriteFile |
表9-3:メールスロットで利用できる関数
| メールスロットサーバ用関数 |
| CreateMailslot |
| GetMailslotInfo |
| SetMailslotInfo |
| DuplicateHandle |
| ReadFile |
| GetFileTime |
| SetFileTime |
| メールスロットクライアント用関数 |
| CloseHandle |
| CreateFile |
| DuplicateHandle |
| WriteFile |
図9-2:パイプとメールスロットの違い
+--------------+ +--------------+
| | ------------------------------ | |
| サーバ | ○○○ ← | クライアント |
| | ------------------------------ | |
+--------------+ |↑| +--------------+
| |
割り込めない
(a)TCP/IPのTCPや名前付きパイプ
+--------------+
+-------------------------- | |
| ← ○ | クライアント |
| +--------------------- | |
| | +--------------+
| |
+--------------+ | | +--------------+
| | ---+ +--------------------- | |
| サーバ | ○△□ ← △ | クライアント |
| | ---+ +--------------------- | |
+--------------+ | | +--------------+
| |
| | +--------------+
| +--------------------- | |
| ← □ | クライアント |
+-------------------------- | |
+--------------+
(b)TCP/IPのUDP,ICMPやメールスロット
9-9 プログラムについて
○バッチファイル用ウィンドウ制御コマンド \BATFILES\CMD
バッチファイルで利用できる、ウィンドウ制御コマンドと、メッセージ送受信
ツールです。
(1)バッチファイルを実行しているコンソールを非表示にするBHIDE.EXE
(BHIDE.C)
使い方
>BHIDE ON | OFF
ON ... コンソールを非表示状態にする
OFF ... コンソールを表示状態にする
(2)指定されたウィンドウが存在するかを得るBFINDWIN.EXE(BFINDWIN.C)
使い方
>BFINDWIN [ -cls クラス名 ] [ -win ウィンドウ名 ]
クラス名またはウィンドウ名または両方を指定します。
ERRORLEVEL = 0 ... 指定されたウィンドウは存在します
ERRORLEVEL = 1 ... 指定されたウィンドウは存在しません
ウィンドウの検索は、Win32 API FindWindow() で可能ですが、本コマンド
では、Win32 API EnumWindows() と EnumChildWindows() で行って
います。
ソース内の #if 0 を #if 1 にすることにより、全てのウィンドウを
列挙することができます。
(3)指定されたウィンドウにメッセージを送信するBMESSAGE.EXE
(BMESSAGE.C)
使い方
>BMESSAGE [ -cls クラス名 ] [ -win ウィンドウ名 ] uMsg wParam lParam
クラス名またはウィンドウ名または両方を指定します。
uMsg ... メッセージIDを10進文字列で指定する
wParam ... パラメータを10進文字列で指定する
lParam ... パラメータを10進文字列で指定する
ERRORLEVEL = 0 ... 正常終了
ERRORLEVEL = 1 ... 異常終了
(4)指定時間処理を停止するBSLEEP.EXE(BSLEEP.C)
使い方
>BSLEEP 時間
時間を秒で10進文字列で指定する
(5)他のマシンにメッセージを送信するBSEND.EXE(BSEND.C)
使い方
>BSEND マシン名 メッセージ
RCVMSG.EXEで受信できるメッセージを送信します。
○サンプルバッチファイル \BATFILES\CMD
(1)FMT.BAT
ファイルマネージャを起動してフォーマットダイアログボックスを表示させます。
(2)BHIDE.BAT
5秒間コンソールウィンドウを非表示(HIDE)状態にします。
○メッセージ送受信ツール \BATFILES\RCVMSG
インジケータ領域にアイコンを表示して常駐するメッセージ送受信ツールです。
メッセージを受信すると、メッセージボックスにメッセージを表示させます。
送信は、インジケータ領域のアイコンをダブルクリックするか、ポップアップメニュー
から送信を選択すると、メッセージ送信ダイアログボックスを表示します。
RCVMSG.C(本体)
SENDDATA.C(メッセージ送信ダイアログボックス)
RCVMSG.H(ヘッダファイル)
RCVMSG.RC(リソースファイル)
RCVMSG.ICO(アイコンファイル)
MAKEFILE(メイクファイル)
NETコマンドや、ポップアップサービスは標準で提供されるため、これらのツールを
利用してもよいのですが、用途に合わせてカスタマイズすることができません。
そこで、これらの機能に似たものを作成して、環境や用途に合わせてカスタマイズで
きれば便利ではないかと思います。
このサンプルは、カスタマイズして利用することを前提としていますので、必要最小限
の機能しか持たせていません。
ポップアップサービスには、メッセージのログ機能がありますが、WindowsNT の
メッセンジャーサービスは、受信したメッセージをいきなりデスクトップに表示して
しまいます。WindowsNTで ポップアップサービスのような機能を利用したいときには、
自家製の「ポップアップサービス」 を作成する必要があります。