2009-08-24 1 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을 사용한다.

이것은 다소 비효율적이며 상황에 맞는 메뉴 항목에 의해 열리는 메뉴에서는 실패합니다. 따라서 궁금 하군요 :

아무도 좋은 HMOG를 활성화 할 때 열리는 메뉴 항목 (있는 경우)을 찾는 방법이 있습니까?

업데이트 : 그냥 내 마음에 온 아이디어. (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; 
} 

편집 : 모든 메뉴를 만드는 방법을 제어 할 수없는 경우을 사용할 수 있습니다메뉴가 처음 생성 될 때 트랩합니다. 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)); 
} 
관련 문제