2009-08-24 7 views
2

私はプロトタイプHMENUハンドルを考える起動時に特定のHMENUを開くメニュー項目を見つける方法(もしあれば)?

/* Locates the menu item of the application which caused the given menu 'mnu' to 
* show up. 
* @return true if the given menu 'mnu' was opened by another menu item, false 
* if not. 
*/ 
bool getParentMenuItem(HMENU mnu, HMENU *parentMenu, int *parentMenuIdx); 

で機能を実装したいのですが、私はそれを開いたアプリケーションでどのメニュー項目(もしあれば)を見つけることができるようにしたいと思います。これは基本的にGetSubMenu関数の逆です。

私の現在のアプローチは、アプリケーションのトップレベルウィンドウの各HMENUを調べ、起動時に特定のサブメニューを開くメニュー項目が見つかるかどうかを確認することです。私はGetMenuItemCount/GetSubMenuを使ってこれを再帰的に行います。

これはかなり効率が悪く、コンテキストメニュー項目で開くメニューでは失敗します。したがって、私は疑問に思っています:

誰かがどのように特定のHMENUを起動するメニュー項目(もしあれば)を見つける良いアイデアを持っていますか?

更新日:ちょうど私の心に来たアイデア。 (SetWindowsHookEx関数を使用して)メニュー内で起こったすべての入力イベントを通知されるフックをインストールすることが可能でなければなりません。メニュー項目の起動が検出されるたびに、メニュー項目(HMENU、int)によって識別されるメニュー項目と、グローバル項目のメニュー項目によって開かれるHMENUを記憶する。上記の関数getParentMenuItemは、単にマップを検索するだけです。

アップデートへの更新:上記のアップデートで説明されているフックのアイデアは、当然動作しません。何らかの時点でアクティブ化されたアイテムのメニュー項目 - >メニューの関連付けのみを認識するからです。

これは、私が多くの状態(地図)を維持するために必要なので、少し醜いと感じます。簡単な可能性はありますか?

答えて

1

あなたは、あなたのアプリケーションで作成するすべてのメニューの親メニューハンドルにMENUINFO.dwMenuDataを設定してみてください:

MENUINFO mi; 
mi.cbSize = sizeof(MENUINFO); 
mi.dwMenuData = (ULONG_PTR)<parent HMENU if this is a sub menu> 
mi.fMask = MIM_MENUDATA; 

SetMenuInfo(hCreatedMenu, &mi); 

その後、あなただけの機能でこのdwMenuDataフィールドを照会する必要があります。

bool getParentMenuItem(HMENU mnu, HMENU *parentMenu, int *parentMenuIdx) 
{ 
    MENUINFO mi; 
    mi.cbSize = sizeof(MENUINFO); 
    mi.fMask = MIM_MENUDATA; 

    if (!GetMenuInfo(mnu,&mi) || mi.dwMenuData == 0) 
     return false; 

    *parentMenu = (HMENU)mi.dwMenuData; 

    // not sure how or why you need the parentMenuIdx, but you should be able 
    // to derive that from the parent HMENU 

    return true; 
} 

編集:すべてのメニューの作成方法を制御できない場合は、WH_CALLWNDPROCメニューが最初に作成されたときにトラップします。 A good article(ソースコード付き)は、これを行う方法を記述しています。上で説明した方法を使用して親HMENUを作成メニューに挿入しようとします。

+0

良いアイデア!残念なことに、私はしばしばメニューを作成するコードにアクセスできないので、多くの場合これを行うことはできません。私は、既存のソースコードにそのような変更を必要としない関数を探しています。 あなたは私が*ソースコードを変更することができるので、+ 1あなたのために良いオプションを指摘します! –

+0

この回答を受け入れることで、賞金総額を得ることができます。残念ながら、この質問に対する賞金を設定しても、新たな洞察は得られませんでした。 : - / –

1

私は同じ必要性を見つけました。 .rcファイルにメニューを定義してあり、そのサブアイテムがすべてグレー表示になっている場合は、ポップアップメニューへのアクセスをグレーアウトしたい。 (これは発見を阻害すると主張することができますが、この特定のケースでは、私たちが必要とするものです)。

前述のレスポンダのように、メニューをプログラムで作成する場合は、アイテムの親メニューハンドルを付随情報として保存することができます。

POPUPキーワードを使用してリソースファイルで定義されたメニューの場合、IDをPOPUPに関連付けることはできず、メニューツリーをプログラム上で簡単に登ることはできません。メニュー項目を再帰的に検索し、両親を追跡する必要があります。

私はこれを行うために次のコードを書いています。 EnableSubmenuItemはEnableMenuItemのように動作し、メニュー項目をIDで有効または無効にします。次に、アイテムの親メニューをチェックします。親メニューのすべての項目が無効になっている場合、親は無効になります。逆にいずれかのサブアイテムが有効になっている場合、親は有効になります。

(元の質問、メニュー項目の親hMenuの検索方法は、次のコードのルーチンFindParentMenuで対応しています)。

//////////////////////////////////////////////////////////////////////////////// 
// MenuContainsID - return TRUE if menu hMenu contains an item with specified ID 
//////////////////////////////////////////////////////////////////////////////// 

static BOOL MenuContainsID (HMENU hMenu, UINT id) 
{ 
    int pos;           // use signed int so we can count down and detect passing 0 
    MENUITEMINFO mf; 

    ZeroMemory(&mf, sizeof(mf));      // request just item ID 
    mf.cbSize = sizeof(mf); 
    mf.fMask = MIIM_ID; 

    for (pos = GetMenuItemCount(hMenu); --pos >= 0;)   // enumerate menu items 
     if (GetMenuItemInfo(hMenu, (UINT) pos, TRUE, &mf)) // if we find the ID we are looking for return TRUE 
      if (mf.wID == id) 
       return TRUE; 

    return FALSE; 
} 

//////////////////////////////////////////////////////////////////////////////// 
// MenuItemIsSubmenu - returns TRUE if item # pos (position) of menu hMenu is a 
// submenu. Sets phSubMenu to menu handle if so 
//////////////////////////////////////////////////////////////////////////////// 

static BOOL MenuItemIsSubmenu (HMENU hMenu, UINT pos, HMENU *phSubMenu) 
{ 
    MENUITEMINFO mf; 

    ZeroMemory(&mf, sizeof(mf));      // request just submenu handle 
    mf.cbSize = sizeof(mf); 
    mf.fMask = MIIM_SUBMENU; 

    if (! GetMenuItemInfo(hMenu, pos, TRUE, &mf))  // failed to get item? 
     return FALSE; 

    *phSubMenu = mf.hSubMenu;       // pass back by side effect 
    return (mf.hSubMenu != NULL);      // it's a submenu if handle is not NULL 
} 

//////////////////////////////////////////////////////////////////////////////// 
// MenuItemIsEnabled - returns true if item # pos (position) of menu hMenu is 
// enabled (that is, is not disabled or grayed) 
//////////////////////////////////////////////////////////////////////////////// 

static BOOL MenuItemIsEnabled (HMENU hMenu, UINT pos) 
{ 
    return ! (GetMenuState(hMenu, pos, MF_BYPOSITION) & (MF_GRAYED | MF_DISABLED)); 
} 

//////////////////////////////////////////////////////////////////////////////// 
// FindParentMenu - returns handle of the submenu of menu bar hMenu that contains 
// an item with id "id". Position of this submenu is passed by side effect to 
// pParentPos, and /its/ parent menu is passed by side effect through phGrandparentMenu. 
//////////////////////////////////////////////////////////////////////////////// 

static HMENU FindParentMenu (HMENU hMenu, UINT id, HMENU *phGrandparentMenu, UINT *pparentPos) 
{ 
    int pos;           // use signed int so we can count down and detect passing 0 
    HMENU hSubMenu, hx; 

    for (pos = GetMenuItemCount(hMenu); --pos >= 0;) { 
     if (MenuItemIsSubmenu(hMenu, (UINT) pos, &hSubMenu)) { 
      if (MenuContainsID(hSubMenu, id)) { 
       *phGrandparentMenu = hMenu;   // set grandparent info by side effect 
       *pparentPos = (UINT) pos; 
       return hSubMenu;      // we found the item directly 
      } 

      if ((hx = FindParentMenu(hSubMenu, id, phGrandparentMenu, pparentPos)) != NULL) 
       return hx;       // we found the item recursively (in a sub-sub menu). It set grandparent info 
     } 
    } 

    return NULL; 
} 

//////////////////////////////////////////////////////////////////////////////// 
// AllSubitemsAreDisabled - returns TRUE if all items in a submenu are disabled 
//////////////////////////////////////////////////////////////////////////////// 

static BOOL AllSubitemsAreDisabled (HMENU hMenu) 
{ 
    int pos;           // use signed int so we can count down and detect passing 0 

    for (pos = GetMenuItemCount(hMenu); --pos >= 0;) 
     if (MenuItemIsEnabled(hMenu, (UINT) pos)) 
      return FALSE;        // finding one enabled item is enough to stop 

    return TRUE; 
} 

//////////////////////////////////////////////////////////////////////////////// 
// EnableSubMenuItem - like EnableMenuItem, enables or disables a menu item 
// by ID (only; not position!) where hMenu is top level menu. 
// Added bonus: If the item is in a pop-up menu, and all items in the popup are 
// now disabled, we disable the popup menu itself. 
// 
// Example: 
//  EnableSubMenuItem(hMainMenu, IDM_CONFIGURE, MF_GRAYED); 
// 
//////////////////////////////////////////////////////////////////////////////// 

void EnableSubMenuItem (HMENU hMenu, UINT id, UINT enable) 
{ 
    HMENU hParentMenu, hGrandparentMenu; 
    UINT parentPos; 

    // EnableMenuItem does its job recursively and takes care of the item 
    EnableMenuItem(hMenu, id, enable | MF_BYPOSITION); 

    // But popup menus don't have IDs so we have find the parent popup menu, and its parent (the 
    // grandparent menu), so we can enable or disable the popup entry by position 

    if ((hParentMenu = FindParentMenu(hMenu, id, &hGrandparentMenu, &parentPos)) != NULL) 
     EnableMenuItem(hGrandparentMenu, parentPos, 
      MF_BYPOSITION | (AllSubitemsAreDisabled(hParentMenu) ? MF_GRAYED : MF_ENABLED)); 
} 
関連する問題