Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

690 lines
21 KiB

/****************************** 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;
}