iooiau.net

GetMenuState() の罠

GetMenuState はメニュー項目の種類や状態を取得する関数ですが、ある項目がセパレータ(区切り)であるかを判定しようとすると陥る罠があります。

ある位置のメニュー項目がセパレータであるか判定する関数を作ろうと素直に書くと、以下のようになるでしょう。


BOOL IsSeparator(HMENU hMenu, int Pos)
{
    return (GetMenuState(hMenu, Pos, MF_BYPOSITION) & MF_SEPARATOR) != 0;
}

しかし、このコードでは正しく判定できない場合があります。

メニュー項目がサブメニューの場合、下位2バイト目にサブメニューの項目数が返ります。
しかし MF_SEPARATOR は値が 0x00000800 であるため、このメニュー項目数と領域が被ってしまっているので、区切りでない項目が誤って区切りであると判定されてしまうことがあり得ます。

正しくは以下のようになります。


BOOL IsSeparator(HMENU hMenu, int Pos)
{
    return (GetMenuState(hMenu, Pos, MF_BYPOSITION) & (MF_SEPARATOR | MF_POPUP)) == MF_SEPARATOR;
}

MF_OWNERDRAW も 0x00000100 ですから、ある項目がオーナー描画項目かを判定したい場合も同じ問題があります。
しかも MF_POPUP と MF_OWNERDRAW は併用できるわけですから、GetMenuState では判定が不可能ということになります。
代わりに GetMenuItemInfo を使いましょう。

サブメニューの項目数を返すという誰得機能がただ邪魔なだけですね。
なぜこのようなアホな仕様になってしまったのでしょうか…。