|
|
/****************************** Module Header ******************************\
* * Module Name: clmenu.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Menu Loading Routines * * History: * 24-Sep-1990 mikeke From win30 * 29-Nov-1994 JimA Moved from server. \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/***************************************************************************\
* MenuLoadWinTemplates * * Recursive routine that loads in the new style menu template and * builds the menu. Assumes that the menu template header has already been * read in and processed elsewhere... * * History: * 28-Sep-1990 mikeke from win30 \***************************************************************************/
LPBYTE MenuLoadWinTemplates( LPBYTE lpMenuTemplate, HMENU *phMenu) { HMENU hMenu; UINT menuFlags = 0; ULONG_PTR menuId = 0; LPWSTR lpmenuText; MENUITEMINFO mii; UNICODE_STRING str;
if (!(hMenu = NtUserCreateMenu())) goto memoryerror;
do {
/*
* Get the menu flags. */ menuFlags = (UINT)(*(WORD *)lpMenuTemplate); lpMenuTemplate += 2;
if (menuFlags & ~MF_VALID) { RIPERR1(ERROR_INVALID_DATA, RIP_WARNING, "Menu Flags %lX are invalid", menuFlags); goto memoryerror; }
if (!(menuFlags & MF_POPUP)) { menuId = *(WORD *)lpMenuTemplate; lpMenuTemplate += 2; }
lpmenuText = (LPWSTR)lpMenuTemplate;
if (*lpmenuText) { /*
* Some Win3.1 and Win95 16 bit apps (chessmaster, mavis typing) know that * dwItemData for MFT_OWNERDRAW items is a pointer to a string in the resource data. * So WOW has given us the proper pointer from the 16 bit resource. * * Sundown Note: * __unaligned unsigned long value pointed by lpMenuTemplate is zero-extended to * update lpmenuText. WOW restrictions. */ if ((menuFlags & MFT_OWNERDRAW) && (GetClientInfo()->dwTIFlags & TIF_16BIT)) { lpmenuText = (LPWSTR)ULongToPtr( (*(DWORD UNALIGNED *)lpMenuTemplate) ); /*
* We'll skip one WCHAR later; so skip only the difference now. */ lpMenuTemplate += sizeof(DWORD) - sizeof(WCHAR); } else { /*
* If a string exists, then skip to the end of it. */ RtlInitUnicodeString(&str, lpmenuText); lpMenuTemplate = lpMenuTemplate + str.Length; }
} else { lpmenuText = NULL; }
/*
* Skip over terminating NULL of the string (or the single NULL * if empty string). */ lpMenuTemplate += sizeof(WCHAR); lpMenuTemplate = NextWordBoundary(lpMenuTemplate);
RtlZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_ID | MIIM_STATE | MIIM_FTYPE; if (lpmenuText) { mii.fMask |= MIIM_STRING; }
if (menuFlags & MF_POPUP) { mii.fMask |= MIIM_SUBMENU; lpMenuTemplate = MenuLoadWinTemplates(lpMenuTemplate, (HMENU *)&menuId); if (!lpMenuTemplate) goto memoryerror;
mii.hSubMenu = (HMENU)menuId; }
/*
* We have to take out MF_HILITE since that bit marks the end of a * menu in a resource file. Since we shouldn't have any pre hilited * items in the menu anyway, this is no big deal. */ if (menuFlags & MF_BITMAP) {
/*
* Don't allow bitmaps from the resource file. */ menuFlags = (UINT)((menuFlags | MFT_RIGHTJUSTIFY) & ~MF_BITMAP); }
// We have to take out MFS_HILITE since that bit marks the end of a menu in
// a resource file. Since we shouldn't have any pre hilited items in the
// menu anyway, this is no big deal.
mii.fState = (menuFlags & MFS_OLDAPI_MASK) & ~MFS_HILITE; mii.fType = (menuFlags & MFT_OLDAPI_MASK); if (menuFlags & MFT_OWNERDRAW) { mii.fMask |= MIIM_DATA; mii.dwItemData = (ULONG_PTR) lpmenuText; lpmenuText = 0; } mii.dwTypeData = (LPWSTR) lpmenuText; mii.cch = (UINT)-1; mii.wID = (UINT)menuId;
if (!NtUserThunkedMenuItemInfo(hMenu, MFMWFP_NOITEM, TRUE, TRUE, &mii, lpmenuText ? &str : NULL)) { if (menuFlags & MF_POPUP) NtUserDestroyMenu(mii.hSubMenu); goto memoryerror; }
} while (!(menuFlags & MF_END));
*phMenu = hMenu; return lpMenuTemplate;
memoryerror: if (hMenu != NULL) NtUserDestroyMenu(hMenu); *phMenu = NULL; return NULL; }
/***************************************************************************\
* MenuLoadChicagoTemplates * * Recursive routine that loads in the new new style menu template and * builds the menu. Assumes that the menu template header has already been * read in and processed elsewhere... * * History: * 15-Dec-93 SanfordS Created \***************************************************************************/
PMENUITEMTEMPLATE2 MenuLoadChicagoTemplates( PMENUITEMTEMPLATE2 lpMenuTemplate, HMENU *phMenu, WORD wResInfo, UINT mftRtl) { HMENU hMenu; HMENU hSubMenu; long menuId = 0; LPWSTR lpmenuText; MENUITEMINFO mii; UNICODE_STRING str; DWORD dwHelpID;
if (!(hMenu = NtUserCreateMenu())) goto memoryerror;
do { if (!(wResInfo & MFR_POPUP)) { /*
* If the PREVIOUS wResInfo field was not a POPUP, the * dwHelpID field is not there. Back up so things fit. */ lpMenuTemplate = (PMENUITEMTEMPLATE2)(((LPBYTE)lpMenuTemplate) - sizeof(lpMenuTemplate->dwHelpID)); dwHelpID = 0; } else dwHelpID = lpMenuTemplate->dwHelpID;
menuId = lpMenuTemplate->menuId;
RtlZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_ID | MIIM_STATE | MIIM_FTYPE ;
mii.fType = lpMenuTemplate->fType | mftRtl; if (mii.fType & ~MFT_MASK) { RIPERR1(ERROR_INVALID_DATA, RIP_WARNING, "Menu Type flags %lX are invalid", mii.fType); goto memoryerror; }
mii.fState = lpMenuTemplate->fState; if (mii.fState & ~MFS_MASK) { RIPERR1(ERROR_INVALID_DATA, RIP_WARNING, "Menu State flags %lX are invalid", mii.fState); goto memoryerror; }
wResInfo = lpMenuTemplate->wResInfo; if (wResInfo & ~(MF_END | MFR_POPUP)) { RIPERR1(ERROR_INVALID_DATA, RIP_WARNING, "Menu ResInfo flags %lX are invalid", wResInfo); goto memoryerror; }
if (dwHelpID) { NtUserSetMenuContextHelpId(hMenu,dwHelpID); } if (lpMenuTemplate->mtString[0]) { lpmenuText = lpMenuTemplate->mtString; mii.fMask |= MIIM_STRING; } else { lpmenuText = NULL; } RtlInitUnicodeString(&str, lpmenuText);
mii.dwTypeData = (LPWSTR) lpmenuText;
/*
* skip to next menu item template (DWORD boundary) */ lpMenuTemplate = (PMENUITEMTEMPLATE2) (((LPBYTE)lpMenuTemplate) + sizeof(MENUITEMTEMPLATE2) + ((str.Length + 3) & ~3));
if (mii.fType & MFT_OWNERDRAW) { mii.fMask |= MIIM_DATA; mii.dwItemData = (ULONG_PTR) mii.dwTypeData; mii.dwTypeData = 0; }
/*
* If MFT_RIGHTORDER is specified then all subsequent * menus are right-to-left as well. */ if (mii.fType & MFT_RIGHTORDER) { mftRtl = MFT_RIGHTORDER; NtUserSetMenuFlagRtoL(hMenu); }
if (wResInfo & MFR_POPUP) { mii.fMask |= MIIM_SUBMENU; lpMenuTemplate = MenuLoadChicagoTemplates(lpMenuTemplate, &hSubMenu, MFR_POPUP, mftRtl); if (lpMenuTemplate == NULL) goto memoryerror; mii.hSubMenu = hSubMenu; }
if (mii.fType & MFT_BITMAP) {
/*
* Don't allow bitmaps from the resource file. */ mii.fType = (mii.fType | MFT_RIGHTJUSTIFY) & ~MFT_BITMAP; }
mii.cch = (UINT)-1; mii.wID = menuId; if (!NtUserThunkedMenuItemInfo(hMenu, MFMWFP_NOITEM, TRUE, TRUE, &mii, &str)) { if (wResInfo & MFR_POPUP) NtUserDestroyMenu(mii.hSubMenu); goto memoryerror; } wResInfo &= ~MFR_POPUP; } while (!(wResInfo & MFR_END));
*phMenu = hMenu; return lpMenuTemplate;
memoryerror: if (hMenu != NULL) NtUserDestroyMenu(hMenu); *phMenu = NULL; return NULL; }
/***************************************************************************\
* CreateMenuFromResource * * Loads the menu resource named by the lpMenuTemplate parameter. The * template specified by lpMenuTemplate is a collection of one or more * MENUITEMTEMPLATE structures, each of which may contain one or more items * and popup menus. If successful, returns a handle to the menu otherwise * returns NULL. * * History: * 28-Sep-1990 mikeke from win30 \***************************************************************************/
HMENU CreateMenuFromResource( LPBYTE lpMenuTemplate) { HMENU hMenu = NULL; UINT menuTemplateVersion; UINT menuTemplateHeaderSize;
/*
* Win3 menu resource: First, strip version number word out of the menu * template. This value should be 0 for Win3, 1 for win4. */ menuTemplateVersion = *((WORD *)lpMenuTemplate)++; if (menuTemplateVersion > 1) { RIPMSG0(RIP_WARNING, "Menu Version number > 1"); return NULL; } menuTemplateHeaderSize = *((WORD *)lpMenuTemplate)++; lpMenuTemplate += menuTemplateHeaderSize; switch (menuTemplateVersion) { case 0: MenuLoadWinTemplates(lpMenuTemplate, &hMenu); break;
case 1: MenuLoadChicagoTemplates((PMENUITEMTEMPLATE2)lpMenuTemplate, &hMenu, 0, 0); break; } return hMenu; }
/***************************************************************************\
* SetMenu (API) * * Sets the menu for the hwnd. * * History: * 10-Mar-1996 ChrisWil Created. \***************************************************************************/
FUNCLOG2(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, SetMenu, HWND, hwnd, HMENU, hmenu) BOOL SetMenu( HWND hwnd, HMENU hmenu) { return NtUserSetMenu(hwnd, hmenu, TRUE); }
/***************************************************************************\
* LoadMenu (API) * * Loads the menu resource named by lpMenuName from the executable * file associated by the module specified by the hInstance parameter. The * menu is loaded only if it hasn't been previously loaded. Otherwise it * retrieves a handle to the loaded resource. Returns NULL if unsuccessful. * * History: * 04-05-91 ScottLu Fixed to work with client/server. * 28-Sep-1990 mikeke from win30 \***************************************************************************/
HMENU CommonLoadMenu( HINSTANCE hmod, HANDLE hResInfo ) { HANDLE h; PVOID p; HMENU hMenu = NULL;
if (h = LOADRESOURCE(hmod, hResInfo)) {
if (p = LOCKRESOURCE(h, hmod)) {
hMenu = CreateMenuFromResource(p);
UNLOCKRESOURCE(h, hmod); } /*
* Win95 and Win3.1 do not free this resource; some 16 bit apps (chessmaster * and mavis typing) require this for their ownerdraw menu stuff. * For 32 bit apps, FreeResource is a nop anyway. For 16 bit apps, * Wow frees the 32 bit resource (returned by LockResource16) * in UnlockResource16; the actual 16 bit resource is freed when the task * goes away. * * FREERESOURCE(h, hmod); */ }
return (hMenu); }
FUNCLOG2(LOG_GENERAL, HMENU, WINAPI, LoadMenuA, HINSTANCE, hmod, LPCSTR, lpName) HMENU WINAPI LoadMenuA( HINSTANCE hmod, LPCSTR lpName) { HANDLE hRes;
if (hRes = FINDRESOURCEA(hmod, (LPSTR)lpName, (LPSTR)RT_MENU)) return CommonLoadMenu(hmod, hRes); else return NULL; }
FUNCLOG2(LOG_GENERAL, HMENU, WINAPI, LoadMenuW, HINSTANCE, hmod, LPCWSTR, lpName) HMENU WINAPI LoadMenuW( HINSTANCE hmod, LPCWSTR lpName) { HANDLE hRes;
if (hRes = FINDRESOURCEW(hmod, (LPWSTR)lpName, RT_MENU)) return CommonLoadMenu(hmod, hRes); else return NULL; } /***************************************************************************\
* InternalInsertMenuItem * * History: * 09/20/96 GerardoB - Created \***************************************************************************/ BOOL InternalInsertMenuItem (HMENU hMenu, UINT uID, BOOL fByPosition, LPCMENUITEMINFO lpmii) { return ThunkedMenuItemInfo(hMenu, uID, fByPosition, TRUE, (LPMENUITEMINFOW)lpmii, FALSE); }
/***************************************************************************\
* ValidateMENUITEMINFO() - * it converts and validates a MENUITEMINFO95 or a new-MENUITEMINFO-with-old-flags * to a new MENUITEMINFO -- this way all internal code can assume one look for the * structure * * History: * 12-08-95 Ported from Nashville - jjk * 07-19-96 GerardoB - Fixed up for 5.0 \***************************************************************************/ BOOL ValidateMENUITEMINFO(LPMENUITEMINFO lpmiiIn, LPMENUITEMINFO lpmii, DWORD dwAPICode) { BOOL fOldApp;
if (lpmiiIn == NULL) { VALIDATIONFAIL(lpmiiIn); }
/*
* In order to map the old flags to the new ones, we might have to modify * the lpmiiIn structure. So we make a copy to avoid breaking anyone. */ fOldApp = (lpmiiIn->cbSize == SIZEOFMENUITEMINFO95); UserAssert(SIZEOFMENUITEMINFO95 < sizeof(MENUITEMINFO)); RtlCopyMemory(lpmii, lpmiiIn, SIZEOFMENUITEMINFO95); if (fOldApp) { lpmii->cbSize = sizeof(MENUITEMINFO); lpmii->hbmpItem = NULL; } else if (lpmiiIn->cbSize == sizeof(MENUITEMINFO)) { lpmii->hbmpItem = lpmiiIn->hbmpItem; } else { VALIDATIONFAIL(lpmiiIn->cbSize); }
if (lpmii->fMask & ~MIIM_MASK) { VALIDATIONFAIL(lpmii->fMask); } else if ((lpmii->fMask & MIIM_TYPE) && (lpmii->fMask & (MIIM_FTYPE | MIIM_STRING | MIIM_BITMAP))) { /*
* Don't let them mix new and old flags */ VALIDATIONFAIL(lpmii->fMask); }
/*
* No more validation needed for Get calls */ if (dwAPICode == MENUAPI_GET) { /*
* Map MIIM_TYPE for old apps doing a Get. * Keep the MIIM_TYPE flag so we'll know this guy passed the old flags. * GetMenuItemInfo uses lpmii->hbmpItem to determine if a bitmap * was returned. So we NULL it out here. The caller is using the * old flags so he shouldn't care about it. */ if (lpmii->fMask & MIIM_TYPE) { lpmii->fMask |= MIIM_FTYPE | MIIM_BITMAP | MIIM_STRING; lpmii->hbmpItem = NULL; } return TRUE; }
/*
* Map MIIM_TYPE to MIIM_FTYPE */ if (lpmii->fMask & MIIM_TYPE) { lpmii->fMask |= MIIM_FTYPE; }
if (lpmii->fMask & MIIM_FTYPE) { if (lpmii->fType & ~MFT_MASK) { VALIDATIONFAIL(lpmii->fType); } /*
* If using MIIM_TYPE, Map MFT_BITMAP to MIIM_BITMAP * and MFT_NONSTRING to MIIM_STRING. * Old applications couldn't use string and bitmap simultaneously * so setting one implies clearing the other. */ if (lpmii->fMask & MIIM_TYPE) { if (lpmii->fType & MFT_BITMAP) { /*
* Don't display a warning. A lot of shell menus hit this * if (!fOldApp) { * VALIDATIONOBSOLETE(MFT_BITMAP, MIIM_BITMAP); * } */ lpmii->fMask |= MIIM_BITMAP | MIIM_STRING; lpmii->hbmpItem = (HBITMAP)lpmii->dwTypeData; lpmii->dwTypeData = 0; } else if (!(lpmii->fType & MFT_NONSTRING)) { /*
* Don't display a warning. A lot of shell menus hit this * if (!fOldApp) { * VALIDATIONOBSOLETE(MFT_STRING, MIIM_STRING); * } */ lpmii->fMask |= MIIM_BITMAP | MIIM_STRING; lpmii->hbmpItem = NULL; } } else if (lpmii->fType & MFT_BITMAP) { /*
* Don't let them mix new and old flags */ VALIDATIONFAIL(lpmii->fType); } }
if ((lpmii->fMask & MIIM_STATE) && (lpmii->fState & ~MFS_MASK)){ VALIDATIONFAIL(lpmii->fState); }
if (lpmii->fMask & MIIM_CHECKMARKS) { if ((lpmii->hbmpChecked != NULL) && !GdiValidateHandle((HBITMAP)lpmii->hbmpChecked)) { VALIDATIONFAIL(lpmii->hbmpChecked); } if ((lpmii->hbmpUnchecked != NULL) && !GdiValidateHandle((HBITMAP)lpmii->hbmpUnchecked)) { VALIDATIONFAIL(lpmii->hbmpUnchecked); } }
if (lpmii->fMask & MIIM_SUBMENU) { if ((lpmii->hSubMenu != NULL) && !VALIDATEHMENU(lpmii->hSubMenu)) { VALIDATIONFAIL(lpmii->hSubMenu); } }
/*
* Warning: NULL lpmii->hbmpItem accepted as valid (or the explorer breaks) */ if (lpmii->fMask & MIIM_BITMAP) { if ((lpmii->hbmpItem != HBMMENU_CALLBACK) && (lpmii->hbmpItem >= HBMMENU_MAX) && !GdiValidateHandle(lpmii->hbmpItem)) {
/*
* Compatibility hack */ if (((HBITMAP)LOWORD(HandleToUlong(lpmii->hbmpItem)) >= HBMMENU_MAX) || !IS_PTR(lpmii->hbmpItem)) { VALIDATIONFAIL(lpmii->hbmpItem); } } }
/*
* Warning: No dwTypeData / cch validation */
return TRUE;
VALIDATIONERROR(FALSE); }
/***************************************************************************\
* ValidateMENUINFO() - * * History: * 07-22-96 GerardoB - Added header and Fixed up for 5.0 \***************************************************************************/
BOOL ValidateMENUINFO(LPCMENUINFO lpmi, DWORD dwAPICode) { if (lpmi == NULL) { VALIDATIONFAIL(lpmi); }
if (lpmi->cbSize != sizeof(MENUINFO)) { VALIDATIONFAIL(lpmi->cbSize); }
if (lpmi->fMask & ~MIM_MASK) { VALIDATIONFAIL(lpmi->fMask); }
/*
* No more validation needed for Get calls */ if (dwAPICode == MENUAPI_GET){ return TRUE; }
if ((lpmi->fMask & MIM_STYLE) && (lpmi->dwStyle & ~MNS_VALID)) { VALIDATIONFAIL(lpmi->dwStyle); }
if (lpmi->fMask & MIM_BACKGROUND) { if ((lpmi->hbrBack != NULL) && !GdiValidateHandle((HBRUSH)lpmi->hbrBack)) {
VALIDATIONFAIL(lpmi->hbrBack); } }
return TRUE;
VALIDATIONERROR(FALSE); } /***************************************************************************\
* GetMenuInfo * * History: * 07-22-96 GerardoB - Added header and Fixed up for 5.0 \***************************************************************************/
FUNCLOG2(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, GetMenuInfo, HMENU, hMenu, LPMENUINFO, lpmi) BOOL GetMenuInfo(HMENU hMenu, LPMENUINFO lpmi) { PMENU pMenu;
if (!ValidateMENUINFO(lpmi, MENUAPI_GET)) { return FALSE; }
pMenu = VALIDATEHMENU(hMenu); if (pMenu == NULL) { return FALSE; }
if (lpmi->fMask & MIM_STYLE) { lpmi->dwStyle = pMenu->fFlags & MNS_VALID; }
if (lpmi->fMask & MIM_MAXHEIGHT) { lpmi->cyMax = pMenu->cyMax; }
if (lpmi->fMask & MIM_BACKGROUND) { lpmi->hbrBack = KHBRUSH_TO_HBRUSH(pMenu->hbrBack); }
if (lpmi->fMask & MIM_HELPID) { lpmi->dwContextHelpID = pMenu->dwContextHelpId; }
if (lpmi->fMask & MIM_MENUDATA) { lpmi->dwMenuData = KERNEL_ULONG_PTR_TO_ULONG_PTR(pMenu->dwMenuData); }
return TRUE; }
|