†Sibylのお部屋†

【Sibyl】API関数の基本2 −WinSendMsg関数−

作成開始日 2022.02.02
最終更新日 2022.02.02

API関数の中でも「WinSendMsg」関数は非常に汎用性の高い、ある意味何でもできる、しかし何もかもユーザーが始末しなければならない厄介な関数。はっきり言っても、私も全く使いこなせていない。が、とりあえず、ごく簡単なサンプルで流れだけでも掴んでおきたい。

以下のサンプルコードは、OS/2 APIリファレンスなどに掲載されている「システムメニューの[クローズ]項目を無効化する」というもの。ウィンドウ左上のアイコンをクリックすると出てくるアレである。そのコードに若干手を入れて、理解しやすくした。なお、ここでは、プロジェクトに「Form2」という2番目のフォームを追加していることが前提。まあ、Form1(メインのフォーム)を処理対象にしても良いが、メインウィンドウがクローズできなくなるのも面倒臭いので。

処理の流れ

@まず、Form2ウィンドウを表示して、
Aそのウィンドウのハンドル(=フレームのハンドル)を取得して、
Bさらに、当該ウィンドウのシステムメニューのハンドルを取得して、
Cシステムメニューに「クローズを無効化しろ」というメッセージを送る。
Var
  hwndWindow : HWND;
  hwndSysMenu: HWND;

Begin
  Form2.show;	// ウィンドウを表示

  // ウィンドウのハンドル取得
  hwndWindow:=Form2.Frame.Handle;	// 例によってフレームのハンドル

  // システムメニューのハンドル取得
  hwndSysMenu := WinWindowFromID(hwndWindow, FID_SYSMENU);

  // システムメニューの[クローズ]を無効化
  WinSendMsg(hwndSysMenu, 		// @送り先
	     MM_SETITEMATTR,		// Aメッセージ(大雑把な内容)
             MPFROM2SHORT(SC_CLOSE, 1),	// Bパラメータ1(処理の対象)
             MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
					// Cパラメータ2(処理の内容)

End;
「Form2.Show;」と「hwndWindos:=...」に関しては別項で述べたので、ここでは説明を割愛。

次の「WinWindowFrom」関数は、指定されたウィンドウ内の、指定されたパーツ(item)のハンドルを取得する関数。最初の引数(hwndWindow)がウィンドウを指定するハンドルで、2番目の引数(FID_SYSMENU)が取得するパーツの識別子。「FID_SYSMENU」は「システムメニュー」を意味する。

この他に、「FID_TITLEBAR」(タイトルバー)、「FID_MINMAX」(最小化・最大化ボタン)などが指定できる。「FID_」は恐らく「frame-control identifiers」から名付けられた物と思われる。その他のパーツの指定に関しては「Presentation Manager Programming Guide and Ref vol.4」(pm4.inf)を参照。 

WinSendMsgのパラメータ

次の「WinSendMsg」が今回の処理のキモ。これは@処理対象(ハンドル)にA処理内容(メッセージ)を送り付ける関数。その際、処理内容に関る4バイトのパラメータを2つ指定できる(BC)。パラメータの内容は処理内容によって異なる。また、2つのパラメータを上下2バイトずつに分割して、計4つのパラメータとすることもできる。

@は上記の手順で取得したシステムメニューのハンドル。

Aは処理内容を示すメッセージで、「MM_SETITEMATTR」は「項目の属性の設定」を意味する。また、「MM_QUERYITEMATTR」(項目の属性の取得)、「MM_SETITEM」(項目そのものの設定)、「MM_REMOVEITEM」(項目の削除)などが設定できる。使用可能なメッセージの一覧は「Presentation Manager Programming Guide & Ref Vol.3」(pm3.inf)を参照。

Bはメッセージに付随するパラメータで、これはメッセージによって指定する内容が異なる。ここでは、Aで「項目属性を変更する」と指定したので、このBで「どのパーツ(item)」に対して、次のCで「どんな属性」を設定するのかを指定する。

その前に、パラメータの前に付いている「MPFROM2SHORT」は何ぞや?と言う事になるのだが、これは2つのSHORT型変数を1つのMPARAM型変数にまとめるもの。「2つのSHORT型から(FROM)メッセージパラメータ型(MP)を作成する」ので「MPFROM2SHORT」関数。前述の通り、1つのパラメータを上下2バイトずつに分けて、2つの情報を送るために使用する。なお、MPARAM型=ULONG型(4byte)、SHORT型(正確にはUSHORT型)=WORD型(2byte)。

では、B第1パラメータの内容を見ると;

上位2バイト処理の対象ここでは「SC_CLOSE」(クローズ項目)を指定
下位2バイトサブメニュー
に関する設定
「1」ならば当該項目をサブメニューからも探す、
「0」ならばサブメニューからは探さない

つまり、「MPFROM2SHORT(SC_CLOSE, 1)」はサブメニューも含めた項目の中から[クローズ]を探して出して処理の対象とする、と言う意味。[クローズ]以外の処理対象には、「SC_SIZE」(サイズ変更)、「SC_MOVE」(移動)などがある。詳しくは「Presentation Manager Programming Guide & Ref vol.5」(pm5.inf)参照。なお、ここでは「SC_」をシステムメニューの項目のように扱っているが、そもそもは「System Command」そのもののことらしい。

Cの第2パラメータは少し判り難い。内容自体は「MIA_DISABLED」=「当該項目の無効化」ではっきりししているのだが、なぜ、同じ内容を二つ並べるのか?「MM_SETITEMATTR」の仕様を見ると、このパラメータは「Attribute mask (USHORT)」と「Attribute data (USHORT)」となっている。後者は処理内容を指すとして、前者の「マスク」とは何か?

それについては何の記述もないけど、察するに、前者と後者をAND演算した結果をパラメータとして使用しているのではないかと…「MIA_DISABLED」と「MIA_DISABLED」をAND演算しても、答えは「MIA_DISABLED」そのものなので、要するに「MIA_DISABLED」と言うこと。

なぜこんな回りくどい事をするのかと言うと、これまた推察にすぎないが、おそらく、命令の一部を無効化する必要がある時のためだろう。「MIA_DISABLE」などの定数は、複数の処理を指定する一連のビット列として処理される(と思う)が、その中で「このビットの処理だけはしないでね」みたいに、そもそもの定数の処理内容にバリエーションを持たせるのが目的(だと思う)。ゆえに、特段の理由がなければ、同じ定数を二つ並べる、でいいんじゃないかと思う。

なお、「MIA」は「Menu Item Attributes」のことで、「MIA_DISABLE」の他には、「MIA_HILITED」(項目選択表示)、「MIA_CHECKED」(チェックマーク付き)などがある。詳しくは「Presentation Manager Programming Guide & Ref vol.3」(pm3.inf)参照。

サンプルを少し書き換えてみたところ…

さて、上記のサンプルを実行すると、Form2のシステムメニューの[クローズ]が無効化される(グレー表示になる)。これで、オレもAPIマスターだぁ!と調子に乗って、では、同じように[サイズ変更]や[移動]の項目も無効化してみよう!と、なるのだが…

ソースの変更は簡単。[サイズ変更]を無効化したければ、「SC_CLOSE」を「SC_SIZE」とすれば良いだけだ。が、これを実行すると……あれ?[サイズ変更]が無効化されていない!じゃあ、[移動]はと「SC_MOVE」に変更しても、やっぱりダメ!コンパイルが通るからミスタイプ等があるわけじゃない。しかも、戻り値を確認すると、「SC_CLOSE」のときと同じく「1」が帰って来る。つまり、メッセージの送付は成功しているのである。オイオイ…

これって、つまり「お手紙は承った、しかし乍ら貴殿の御要望に沿いかねる」ってハナシなわけだ。具体的に何でこんなことになるのかは判らないが;

@他の設定と矛盾が生じたため設定そのものが反映されなかった、
A設定は反映されたが直後に元の設定に書き戻された、

のいずれかだろう。ここがAPI関数、就中WinSendMsgの厄介なところ。メッセージの送信に成功しても、期待通りの結果が得られるとは限らないのである。そして、なんでダメなのかの理由が、素人には全くわからない。

では、次はメッセージ自体を変更してみよう。今度はアイテムの属性変更(MM_SETITEMATTR)ではなく、アイテムの削除(MM_REMOVEITEM)を指定してみる。削除対象は[サイズ変更](SC_SIZE)とする。なお、このメッセージでは第2パラメータは不要のため「0」を指定する。

rc:=WinSendMsg(hwndSysMenu, 			// @送り先
	       MM_REMOVEITEM,			// A処理内容は項目削除
               MPFROM2SHORT(SC_SIZE, 1),	// B処理対象は[サイズ変更]
               0);				// C第2パラメータは不要
これはどうなるかと言うと、ちゃんとシステムメニューから[サイズ変更]が削除されるのであった。こんな感じで、我等シロウトとしては、試行錯誤しながら使っていくしかないのであった。


【Sibylのお部屋目次】 【ホーム】