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.
 
 
 
 
 
 

793 lines
22 KiB

#include "cabinet.h"
#include "rcids.h"
#include <shguidp.h>
#include <lmcons.h>
#include "bandsite.h"
#include "shellp.h"
#include "shdguid.h"
#include <regstr.h>
#include "startmnu.h"
#include "trayp.h" // for WMTRAY_*
#include "tray.h"
#include "util.h"
#include <strsafe.h>
HMENU GetStaticStartMenu(BOOL fEdit);
// *** IUnknown methods ***
STDMETHODIMP CStartMenuHost::QueryInterface (REFIID riid, LPVOID * ppvObj)
{
static const QITAB qit[] =
{
QITABENTMULTI(CStartMenuHost, IOleWindow, IMenuPopup),
QITABENTMULTI(CStartMenuHost, IDeskBarClient, IMenuPopup),
QITABENT(CStartMenuHost, IMenuPopup),
QITABENT(CStartMenuHost, ITrayPriv),
QITABENT(CStartMenuHost, IShellService),
QITABENT(CStartMenuHost, IServiceProvider),
QITABENT(CStartMenuHost, IOleCommandTarget),
QITABENT(CStartMenuHost, IWinEventHandler),
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
STDMETHODIMP_(ULONG) CStartMenuHost::AddRef ()
{
return ++_cRef;
}
STDMETHODIMP_(ULONG) CStartMenuHost::Release()
{
ASSERT(_cRef > 0);
_cRef--;
if( _cRef > 0)
return _cRef;
delete this;
return 0;
}
/*----------------------------------------------------------
Purpose: ITrayPriv::ExecItem method
*/
STDMETHODIMP CStartMenuHost::ExecItem (IShellFolder* psf, LPCITEMIDLIST pidl)
{
// ShellExecute will display errors (if any). No need
// to show errors here.
return SHInvokeDefaultCommand(v_hwndTray, psf, pidl);
}
/*----------------------------------------------------------
Purpose: ITrayPriv::GetFindCM method
*/
STDMETHODIMP CStartMenuHost::GetFindCM(HMENU hmenu, UINT idFirst, UINT idLast, IContextMenu** ppcmFind)
{
*ppcmFind = SHFind_InitMenuPopup(hmenu, v_hwndTray, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST);
if(*ppcmFind)
return NOERROR;
else
return E_FAIL;
}
/*----------------------------------------------------------
Purpose: ITrayPriv::GetStaticStartMenu method
*/
STDMETHODIMP CStartMenuHost::GetStaticStartMenu(HMENU* phmenu)
{
*phmenu = ::GetStaticStartMenu(TRUE);
if(*phmenu)
return NOERROR;
else
return E_FAIL;
}
// *** IServiceProvider ***
STDMETHODIMP CStartMenuHost::QueryService (REFGUID guidService, REFIID riid, void ** ppvObject)
{
if(IsEqualGUID(guidService,SID_SMenuPopup))
return QueryInterface(riid,ppvObject);
else
return E_NOINTERFACE;
}
// *** IShellService ***
STDMETHODIMP CStartMenuHost::SetOwner (struct IUnknown* punkOwner)
{
return E_NOTIMPL;
}
// *** IOleWindow methods ***
STDMETHODIMP CStartMenuHost::GetWindow(HWND * lphwnd)
{
*lphwnd = v_hwndTray;
return NOERROR;
}
/*----------------------------------------------------------
Purpose: IMenuPopup::Popup method
*/
STDMETHODIMP CStartMenuHost::Popup(POINTL *ppt, RECTL *prcExclude, DWORD dwFlags)
{
return E_NOTIMPL;
}
/*----------------------------------------------------------
Purpose: IMenuPopup::OnSelect method
*/
STDMETHODIMP CStartMenuHost::OnSelect(DWORD dwSelectType)
{
return NOERROR;
}
/*----------------------------------------------------------
Purpose: IMenuPopup::SetSubMenu method
*/
STDMETHODIMP CStartMenuHost::SetSubMenu(IMenuPopup* pmp, BOOL fSet)
{
if (!fSet)
{
Tray_OnStartMenuDismissed();
}
return NOERROR;
}
// *** IOleCommandTarget ***
STDMETHODIMP CStartMenuHost::QueryStatus (const GUID * pguidCmdGroup,
ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
return E_NOTIMPL;
}
STDMETHODIMP CStartMenuHost::Exec (const GUID * pguidCmdGroup,
DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
if (IsEqualGUID(CGID_MENUDESKBAR,*pguidCmdGroup))
{
switch (nCmdID)
{
case MBCID_GETSIDE:
pvarargOut->vt = VT_I4;
pvarargOut->lVal = MENUBAR_TOP;
break;
default:
break;
}
}
return NOERROR;
}
// *** IWinEventHandler ***
STDMETHODIMP CStartMenuHost::OnWinEvent(HWND h, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
{
//Forward events to the tray winproc?
return E_NOTIMPL;
}
STDMETHODIMP CStartMenuHost::IsWindowOwner(HWND hwnd)
{
return E_NOTIMPL;
}
CStartMenuHost::CStartMenuHost() : _cRef(1)
{
}
HRESULT StartMenuHost_Create(IMenuPopup** ppmp, IMenuBand** ppmb)
{
HRESULT hres = E_OUTOFMEMORY;
IMenuPopup * pmp = NULL;
IMenuBand * pmb = NULL;
CStartMenuHost *psmh = new CStartMenuHost();
if (psmh)
{
hres = CoCreateInstance(CLSID_StartMenuBar, NULL, CLSCTX_INPROC_SERVER,
IID_IMenuPopup, (LPVOID*)&pmp);
if (SUCCEEDED(hres))
{
IObjectWithSite* pows;
hres = pmp->QueryInterface(IID_IObjectWithSite, (void**)&pows);
if(SUCCEEDED(hres))
{
IInitializeObject* pio;
pows->SetSite(SAFECAST(psmh, ITrayPriv*));
hres = pmp->QueryInterface(IID_IInitializeObject, (void**)&pio);
if(SUCCEEDED(hres))
{
hres = pio->Initialize();
pio->Release();
}
if (SUCCEEDED(hres))
{
IUnknown* punk;
hres = pmp->GetClient(&punk);
if (SUCCEEDED(hres))
{
IBandSite* pbs;
hres = punk->QueryInterface(IID_IBandSite, (void**)&pbs);
if(SUCCEEDED(hres))
{
DWORD dwBandID;
pbs->EnumBands(0, &dwBandID);
hres = pbs->GetBandObject(dwBandID, IID_IMenuBand, (void**)&pmb);
pbs->Release();
// Don't release pmb
}
punk->Release();
}
}
if (FAILED(hres))
pows->SetSite(NULL);
pows->Release();
}
// Don't release pmp
}
psmh->Release();
}
if (FAILED(hres))
{
ATOMICRELEASE(pmp);
ATOMICRELEASE(pmb);
}
*ppmp = pmp;
*ppmb = pmb;
return hres;
}
HRESULT IMenuPopup_SetIconSize(IMenuPopup* pmp,DWORD iIcon)
{
IBanneredBar* pbb;
if (pmp == NULL)
return E_FAIL;
HRESULT hres = pmp->QueryInterface(IID_IBanneredBar,(void**)&pbb);
if (SUCCEEDED(hres))
{
pbb->SetIconSize(iIcon);
pbb->Release();
}
return hres;
}
void CreateInitialMFU(BOOL fReset);
//
// "Delayed per-user install".
//
// StartMenuInit is the value that tells us what version of the shell
// this user has seen most recently.
//
// missing = has never run explorer before, or pre-IE4
// 1 = IE4 or later
// 2 = XP or later
//
void HandleFirstTime()
{
DWORD dwStartMenuInit = 0;
DWORD cb = sizeof(dwStartMenuInit);
SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_ADVANCED, TEXT("StartMenuInit"), NULL, &dwStartMenuInit, &cb);
if (dwStartMenuInit < 2)
{
DWORD dwValue;
switch (dwStartMenuInit)
{
case 0: // Upgrade from 0 to latest
{
// If this is the first boot of the shell for this user, then we need to see if it's an upgrade.
// If it is, then we need set the Logoff option. PM Decision to have a different
// look for upgraded machines...
TCHAR szPath[MAX_PATH];
TCHAR szPathExplorer[MAX_PATH];
DWORD cbSize = ARRAYSIZE(szPath);
DWORD dwType;
// Is this an upgrade (Does WindowsUpdate\UpdateURL Exist?)
PathCombine(szPathExplorer, REGSTR_PATH_EXPLORER, TEXT("WindowsUpdate"));
if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, szPathExplorer, TEXT("UpdateURL"),
&dwType, szPath, &cbSize) &&
szPath[0] != TEXT('\0'))
{
// Yes; Then write the option out to the registry.
dwValue = 1;
SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_ADVANCED, TEXT("StartMenuLogoff"), REG_DWORD, &dwValue, sizeof(DWORD));
}
}
// FALL THROUGH
case 1: // Upgrade from 1 to latest
// User has never seen XP before.
// PMs in certain groups insist on getting free advertising
// even on upgrades, so we do it.
CreateInitialMFU(dwStartMenuInit == 0);
// FALL THROUGH
default:
break;
}
// If AuditInProgress is set; that means that we are in the
// OEM sysprep stage and not running as an end user, in which
// case don't set the flag saying "don't do this again" because
// we do want to do this again when the retail end user logs
// on for the first time.
//
// (We need to do all this work even in Audit mode so the OEM
// gets a warm fuzzy feeling.)
if (!SHRegGetBoolUSValue(TEXT("System\\Setup"), TEXT("AuditinProgress"), TRUE, FALSE))
{
// Mark this so that we know we've been launched once.
dwValue = 2;
SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_ADVANCED, TEXT("StartMenuInit"), REG_DWORD, &dwValue, sizeof(DWORD));
}
}
}
BOOL GetLogonUserName(LPTSTR pszUsername, DWORD* pcchUsername)
{
BOOL fSuccess = FALSE;
HKEY hkeyExplorer = NULL;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, 0, KEY_QUERY_VALUE, &hkeyExplorer))
{
DWORD dwType;
DWORD dwSize = (*pcchUsername) * sizeof(TCHAR);
if (ERROR_SUCCESS == RegQueryValueEx(hkeyExplorer, TEXT("Logon User Name"), 0, &dwType,
(LPBYTE) pszUsername, &dwSize))
{
if ((REG_SZ == dwType) && (*pszUsername))
{
fSuccess = TRUE;
}
}
RegCloseKey(hkeyExplorer);
}
// Fall back on GetUserName if the Logon User Name isn't set.
if (!fSuccess)
{
fSuccess = GetUserName(pszUsername, pcchUsername);
if (fSuccess)
{
CharUpperBuff(pszUsername, 1);
}
}
return fSuccess;
}
BOOL _ShowStartMenuLogoff()
{
// We want the Logoff menu on the start menu if:
// These MUST both be true
// 1) It's not restricted
// 2) We have Logged On.
// Any of these three.
// 3) We've Upgraded from IE4
// 4) The user has specified that it should be present
// 5) It's been "Restricted" On.
// Behavior also depends on whether we are a remote session or not (dsheldon):
// Remote session: Logoff brings up shutdown dialog
// Console session: Logoff directly does logoff
DWORD dwRest = SHRestricted(REST_STARTMENULOGOFF);
SHELLSTATE ss = {0};
SHGetSetSettings(&ss, SSF_STARTPANELON, FALSE); // if the new start menu is on, always show logoff
BOOL fUserWantsLogoff = ss.fStartPanelOn || GetExplorerUserSetting(HKEY_CURRENT_USER, TEXT("Advanced"), TEXT("StartMenuLogoff")) > 0;
BOOL fAdminWantsLogoff = (BOOL)(dwRest == 2) || SHRestricted(REST_FORCESTARTMENULOGOFF);
BOOL fIsFriendlyUIActive = IsOS(OS_FRIENDLYLOGONUI);
BOOL fIsTS = GetSystemMetrics(SM_REMOTESESSION);
if ((dwRest != 1 && (GetSystemMetrics(SM_NETWORK) & RNC_LOGON) != 0) &&
( fUserWantsLogoff || fAdminWantsLogoff || fIsFriendlyUIActive || fIsTS))
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL _ShowStartMenuEject()
{
if(SHRestricted(REST_NOSMEJECTPC)) //Is there a policy restriction?
return FALSE;
// CanShowEject Queries the user's permission to eject,
// IsEjectAllowed queries the hardware.
return SHTestTokenPrivilege(NULL, SE_UNDOCK_NAME) &&
IsEjectAllowed(FALSE) &&
!GetSystemMetrics(SM_REMOTESESSION);
}
BOOL _ShowStartMenuRun()
{
return !IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NORUN, TEXT("Advanced"), TEXT("StartMenuRun"), ROUS_KEYALLOWS | ROUS_DEFAULTALLOW);
}
BOOL _ShowStartMenuHelp()
{
return !IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOSMHELP, TEXT("Advanced"), TEXT("NoStartMenuHelp"), ROUS_KEYRESTRICTS | ROUS_DEFAULTALLOW);
}
BOOL _ShowStartMenuShutdown()
{
return !SHRestricted(REST_NOCLOSE) &&
(IsOS(OS_ANYSERVER) ||
(!GetSystemMetrics(SM_REMOTESESSION) &&
(!IsOS(OS_FRIENDLYLOGONUI) || SHTestTokenPrivilege(NULL, SE_SHUTDOWN_NAME))));
// if friendly logon is active, then don't show shutdown unless they have privileges, since shutdown "only" shuts you down.
// if they're not using friendly logon ui, then shutdown also contains options to log you off/hibernate, so show it...
}
// If remote and not disabled by administrator then show "Disconnect".
// Don't show on Server SKUs because they already have Turn Off Computer.
BOOL _ShowStartMenuDisconnect()
{
return GetSystemMetrics(SM_REMOTESESSION) &&
!SHRestricted(REST_NODISCONNECT) &&
!IsOS(OS_ANYSERVER);
}
BOOL _ShowStartMenuSearch()
{
return !SHRestricted(REST_NOFIND);
}
HMENU GetStaticStartMenu(BOOL fEdit)
{
#define CITEMSMISSING 4
HMENU hStartMenu = LoadMenuPopup(MAKEINTRESOURCE(MENU_START));
// If no editing requested, then we're done, lickity-split
if (!fEdit)
return hStartMenu;
HMENU hmenu;
UINT iSep2ItemsMissing = 0;
//
// Default to the Win95/NT4 version of the Settings menu.
//
// Restictions
if (!_ShowStartMenuRun())
{
DeleteMenu(hStartMenu, IDM_FILERUN, MF_BYCOMMAND);
}
if (!_ShowStartMenuHelp())
{
DeleteMenu(hStartMenu, IDM_HELPSEARCH, MF_BYCOMMAND);
}
if (IsRestrictedOrUserSetting(HKEY_LOCAL_MACHINE, REST_NOCSC, TEXT("Advanced"), TEXT("StartMenuSyncAll"), ROUS_KEYALLOWS | ROUS_DEFAULTRESTRICT))
{
DeleteMenu(hStartMenu, IDM_CSC, MF_BYCOMMAND);
iSep2ItemsMissing++;
}
BOOL fIsFriendlyUIActive = IsOS(OS_FRIENDLYLOGONUI);
if (_ShowStartMenuLogoff())
{
UINT idMenuRenameToLogoff = IDM_LOGOFF;
TCHAR szUserName[200];
TCHAR szTemp[256];
TCHAR szMenuText[256];
DWORD dwSize = ARRAYSIZE(szUserName);
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.dwTypeData = szTemp;
mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_SUBMENU | MIIM_STATE | MIIM_DATA;
mii.cch = ARRAYSIZE(szTemp);
mii.hSubMenu = NULL;
mii.fType = MFT_SEPARATOR; // to avoid ramdom result.
mii.dwItemData = 0;
GetMenuItemInfo(hStartMenu,idMenuRenameToLogoff,MF_BYCOMMAND,&mii);
if (GetLogonUserName(szUserName, &dwSize))
{
if (fIsFriendlyUIActive)
{
dwSize = ARRAYSIZE(szUserName);
if (FAILED(SHGetUserDisplayName(szUserName, &dwSize)))
{
dwSize = ARRAYSIZE(szUserName);
GetLogonUserName(szUserName, &dwSize);
}
}
StringCchPrintf(szMenuText,ARRAYSIZE(szMenuText), szTemp, szUserName);
}
else if (!LoadString(hinstCabinet, IDS_LOGOFFNOUSER,
szMenuText, ARRAYSIZE(szMenuText)))
{
// mem error, use the current string.
szUserName[0] = 0;
StringCchPrintf(szMenuText, ARRAYSIZE(szMenuText), szTemp, szUserName);
}
mii.dwTypeData = szMenuText;
mii.cch = ARRAYSIZE(szMenuText);
SetMenuItemInfo(hStartMenu,idMenuRenameToLogoff,MF_BYCOMMAND,&mii);
}
else
{
DeleteMenu(hStartMenu, IDM_LOGOFF, MF_BYCOMMAND);
iSep2ItemsMissing++;
}
// If restricted, then user cannot shut down at all.
// If friendly UI is active change "Shut Down..." to "Turn Off Computer..."
if (!_ShowStartMenuShutdown())
{
DeleteMenu(hStartMenu, IDM_EXITWIN, MF_BYCOMMAND);
iSep2ItemsMissing++;
}
else if (fIsFriendlyUIActive)
{
// If the user has the SE_SHUTDOWN_NAME privilege
// then rename the menu item.
if (SHTestTokenPrivilege(NULL, SE_SHUTDOWN_NAME) && !GetSystemMetrics(SM_REMOTESESSION))
{
MENUITEMINFO mii;
TCHAR szMenuText[256];
(int)LoadString(hinstCabinet, IDS_TURNOFFCOMPUTER, szMenuText, ARRAYSIZE(szMenuText));
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_TYPE;
mii.fType = MFT_STRING;
mii.dwTypeData = szMenuText;
mii.cch = ARRAYSIZE(szMenuText);
TBOOL(SetMenuItemInfo(hStartMenu, IDM_EXITWIN, FALSE, &mii));
}
// Otherwise delete the menu item.
else
{
DeleteMenu(hStartMenu, IDM_EXITWIN, MF_BYCOMMAND);
iSep2ItemsMissing++;
}
}
if (!_ShowStartMenuDisconnect())
{
DeleteMenu(hStartMenu, IDM_MU_DISCONNECT, MF_BYCOMMAND);
iSep2ItemsMissing++;
}
if (iSep2ItemsMissing == CITEMSMISSING)
{
DeleteMenu(hStartMenu, IDM_SEP2, MF_BYCOMMAND);
}
if (!_ShowStartMenuEject())
{
DeleteMenu(hStartMenu, IDM_EJECTPC, MF_BYCOMMAND);
}
// Setting stuff.
hmenu = SHGetMenuFromID(hStartMenu, IDM_SETTINGS);
if (hmenu)
{
int iMissingSettings = 0;
#define CITEMS_SETTINGS 5 // Number of items in settings menu
if (SHRestricted(REST_NOSETTASKBAR))
{
DeleteMenu(hStartMenu, IDM_TRAYPROPERTIES, MF_BYCOMMAND);
iMissingSettings++;
}
if (SHRestricted(REST_NOSETFOLDERS) || SHRestricted(REST_NOCONTROLPANEL))
{
DeleteMenu(hStartMenu, IDM_CONTROLS, MF_BYCOMMAND);
// For the separator that now on top
DeleteMenu(hmenu, 0, MF_BYPOSITION);
iMissingSettings++;
}
if (SHRestricted(REST_NOSETFOLDERS))
{
DeleteMenu(hStartMenu, IDM_PRINTERS, MF_BYCOMMAND);
iMissingSettings++;
}
if (SHRestricted(REST_NOSETFOLDERS) || SHRestricted(REST_NONETWORKCONNECTIONS) )
{
DeleteMenu(hStartMenu, IDM_NETCONNECT, MF_BYCOMMAND);
iMissingSettings++;
}
if (!SHGetMachineInfo(GMI_TSCLIENT) || SHRestricted(REST_NOSECURITY))
{
DeleteMenu(hStartMenu, IDM_MU_SECURITY, MF_BYCOMMAND);
iMissingSettings++;
}
// Are all the items missing?
if (iMissingSettings == CITEMS_SETTINGS)
{
// Yes; don't bother showing the menu at all
DeleteMenu(hStartMenu, IDM_SETTINGS, MF_BYCOMMAND);
}
}
else
{
DebugMsg(DM_ERROR, TEXT("c.fm_rui: Settings menu couldn't be found. Restricted items may not have been removed."));
}
// Find menu.
if (!_ShowStartMenuSearch())
{
DeleteMenu(hStartMenu, IDM_MENU_FIND, MF_BYCOMMAND);
}
// Documents menu.
if (SHRestricted(REST_NORECENTDOCSMENU))
{
DeleteMenu(hStartMenu, IDM_RECENT, MF_BYCOMMAND);
}
// Favorites menu.
if (IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOFAVORITESMENU, TEXT("Advanced"), TEXT("StartMenuFavorites"), ROUS_KEYALLOWS | ROUS_DEFAULTRESTRICT))
{
DeleteMenu(hStartMenu, IDM_FAVORITES, MF_BYCOMMAND);
}
return hStartMenu;
}
//
// CHotKey class
//
// constructor
CHotKey::CHotKey() : _cRef(1)
{
}
STDMETHODIMP CHotKey::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IShellHotKey))
{
*ppvObj = SAFECAST(this, IShellHotKey *);
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
STDMETHODIMP_(ULONG) CHotKey::AddRef()
{
return ++_cRef;
}
STDMETHODIMP_(ULONG) CHotKey::Release()
{
ASSERT(_cRef > 0);
_cRef--;
if( _cRef > 0)
return _cRef;
delete this;
return 0;
}
HRESULT Tray_RegisterHotKey(WORD wHotkey, LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl)
{
if (wHotkey)
{
int i = c_tray.HotkeyAdd(wHotkey, (LPITEMIDLIST)pidlParent, (LPITEMIDLIST)pidl, TRUE);
if (i != -1)
{
// Register in the context of the tray's thread.
PostMessage(v_hwndTray, WMTRAY_REGISTERHOTKEY, i, 0);
}
}
return S_OK;
}
/*----------------------------------------------------------
Purpose: IShellHotKey::RegisterHotKey method
*/
STDMETHODIMP CHotKey::RegisterHotKey(IShellFolder * psf, LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl)
{
WORD wHotkey;
HRESULT hr = S_OK;
wHotkey = _GetHotkeyFromFolderItem(psf, pidl);
if (wHotkey)
{
hr = ::Tray_RegisterHotKey(wHotkey, pidlParent, pidl);
}
return hr;
}
STDAPI CHotKey_Create(IShellHotKey ** ppshk)
{
HRESULT hres = E_OUTOFMEMORY;
CHotKey * photkey = new CHotKey;
if (photkey)
{
hres = S_OK;
}
*ppshk = SAFECAST(photkey, IShellHotKey *);
return hres;
}