https://git.reactos.org/?p=reactos.git;a=commitdiff;h=7fb91d98f92d0600b0173b6fe1d6be8dda4066f8

commit 7fb91d98f92d0600b0173b6fe1d6be8dda4066f8
Author:     Whindmar Saksit <[email protected]>
AuthorDate: Mon Nov 13 16:21:13 2023 +0100
Commit:     GitHub <[email protected]>
CommitDate: Mon Nov 13 16:21:13 2023 +0100

    [SHELL32] Add support for more registry verb flags and CMF flags (#5785)
    
    - Adds support for registry controlled menu separators and the documented
      values to turn off verbs.
    - Adds support for CMF_OPTIMIZEFORINVOKE, CMF_NODEFAULT, 
CMF_DONOTPICKDEFAULT,
      CMF_EXPLORE and CMF_DISABLEDVERBS.
    
    Bugs fixed:
    
    - A verb with "Extended" set in the registry could cause the menu to invoke
      the incorrect command! This happened because skipping InsertMenuItemW
      caused InvokeCommand to use the wrong index with m_StaticEntries.
    - Uses IS_INTRESOURCE instead of HIWORD to check if something is a string
      (only matters on 64-bit).
    - TryToBrowse leaking a PIDL when calling ILCombine.
    
    Notes:
    
    - This PR introduces the RosGetProcessEffectiveVersion() helper function
      discussed in chat.
    - Relaxed FAILED_UNEXPECTEDLY to FAILED in two places because IContextMenu
      cannot assume that it has a site that leads to IShellBrowser.
---
 dll/win32/shell32/CDefaultContextMenu.cpp | 345 ++++++++++++++++++++----------
 sdk/include/psdk/shobjidl.idl             |  25 ++-
 sdk/include/reactos/compat_undoc.h        |  11 +
 sdk/include/reactos/shellutils.h          |  12 ++
 4 files changed, 270 insertions(+), 123 deletions(-)

diff --git a/dll/win32/shell32/CDefaultContextMenu.cpp 
b/dll/win32/shell32/CDefaultContextMenu.cpp
index 577e93d332e..af57d92d3bb 100644
--- a/dll/win32/shell32/CDefaultContextMenu.cpp
+++ b/dll/win32/shell32/CDefaultContextMenu.cpp
@@ -7,9 +7,24 @@
  */
 
 #include "precomp.h"
+#include <compat_undoc.h>
 
 WINE_DEFAULT_DEBUG_CHANNEL(dmenu);
 
+static inline bool RegValueExists(HKEY hKey, LPCWSTR Name)
+{
+    return RegQueryValueExW(hKey, Name, NULL, NULL, NULL, NULL) == 
ERROR_SUCCESS;
+}
+
+static BOOL InsertMenuItemAt(HMENU hMenu, UINT Pos, UINT Flags)
+{
+    MENUITEMINFOW mii;
+    mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem); // USER32 version 
agnostic
+    mii.fMask = MIIM_TYPE;
+    mii.fType = Flags;
+    return InsertMenuItemW(hMenu, Pos, TRUE, &mii);
+}
+
 typedef struct _DynamicShellEntry_
 {
     UINT iIdCmdFirst;
@@ -24,30 +39,32 @@ typedef struct _StaticShellEntry_
     HKEY hkClass;
 } StaticShellEntry, *PStaticShellEntry;
 
+#define DCM_FCIDM_SHVIEW_OFFSET 0x7000 // Offset from the menu ids in the menu 
resource to FCIDM_SHVIEW_*
 
 //
 // verbs for InvokeCommandInfo
 //
-struct _StaticInvokeCommandMap_
+static const struct _StaticInvokeCommandMap_
 {
     LPCSTR szStringVerb;
-    UINT IntVerb;
+    WORD IntVerb;
+    SHORT DfmCmd;
 } g_StaticInvokeCmdMap[] =
 {
     { "RunAs", 0 },  // Unimplemented
     { "Print", 0 },  // Unimplemented
     { "Preview", 0 }, // Unimplemented
-    { "Open", FCIDM_SHVIEW_OPEN },
-    { CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER },
-    { "cut", FCIDM_SHVIEW_CUT},
-    { "copy", FCIDM_SHVIEW_COPY},
-    { "paste", FCIDM_SHVIEW_INSERT},
-    { "link", FCIDM_SHVIEW_CREATELINK},
-    { "delete", FCIDM_SHVIEW_DELETE},
-    { "properties", FCIDM_SHVIEW_PROPERTIES},
-    { "rename", FCIDM_SHVIEW_RENAME},
-    { "copyto", FCIDM_SHVIEW_COPYTO },
-    { "moveto", FCIDM_SHVIEW_MOVETO },
+    { "Open",            FCIDM_SHVIEW_OPEN },
+    { CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER,  (SHORT)DFM_CMD_NEWFOLDER },
+    { "cut",             FCIDM_SHVIEW_CUT,        /* ? */ },
+    { "copy",            FCIDM_SHVIEW_COPY,       (SHORT)DFM_CMD_COPY },
+    { "paste",           FCIDM_SHVIEW_INSERT,     (SHORT)DFM_CMD_PASTE },
+    { "link",            FCIDM_SHVIEW_CREATELINK, (SHORT)DFM_CMD_LINK },
+    { "delete",          FCIDM_SHVIEW_DELETE,     (SHORT)DFM_CMD_DELETE },
+    { "properties",      FCIDM_SHVIEW_PROPERTIES, (SHORT)DFM_CMD_PROPERTIES },
+    { "rename",          FCIDM_SHVIEW_RENAME,     (SHORT)DFM_CMD_RENAME },
+    { "copyto",          FCIDM_SHVIEW_COPYTO },
+    { "moveto",          FCIDM_SHVIEW_MOVETO },
 };
 
 class CDefaultContextMenu :
@@ -79,13 +96,14 @@ class CDefaultContextMenu :
         UINT m_iIdDfltLast; /* last default part id */
 
         HRESULT _DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam);
-        void AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb);
-        void AddStaticEntriesForKey(HKEY hKey);
+        void AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb, UINT 
uFlags);
+        void AddStaticEntriesForKey(HKEY hKey, UINT uFlags);
+        void TryPickDefault(HMENU hMenu, UINT idCmdFirst, UINT DfltOffset, 
UINT uFlags);
         BOOL IsShellExtensionAlreadyLoaded(REFCLSID clsid);
         HRESULT LoadDynamicContextMenuHandler(HKEY hKey, REFCLSID clsid);
         BOOL EnumerateDynamicContextHandlerForKey(HKEY hRootKey);
         UINT AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT 
idCmdFirst, UINT idCmdLast, UINT uFlags);
-        UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT* IndexMenu, UINT 
iIdCmdFirst, UINT iIdCmdLast);
+        UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT* IndexMenu, UINT 
iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
         HRESULT DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink);
         HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi);
         HRESULT DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi);
@@ -229,7 +247,7 @@ HRESULT CDefaultContextMenu::_DoCallback(UINT uMsg, WPARAM 
wParam, LPVOID lParam
     return E_FAIL;
 }
 
-void CDefaultContextMenu::AddStaticEntry(const HKEY hkeyClass, const WCHAR 
*szVerb)
+void CDefaultContextMenu::AddStaticEntry(const HKEY hkeyClass, const WCHAR 
*szVerb, UINT uFlags)
 {
     POSITION it = m_StaticEntries.GetHeadPosition();
     while (it != NULL)
@@ -244,7 +262,7 @@ void CDefaultContextMenu::AddStaticEntry(const HKEY 
hkeyClass, const WCHAR *szVe
 
     TRACE("adding verb %s\n", debugstr_w(szVerb));
 
-    if (!wcsicmp(szVerb, L"open"))
+    if (!wcsicmp(szVerb, L"open") && !(uFlags & CMF_NODEFAULT))
     {
         /* open verb is always inserted in front */
         m_StaticEntries.AddHead({ szVerb, hkeyClass });
@@ -255,7 +273,7 @@ void CDefaultContextMenu::AddStaticEntry(const HKEY 
hkeyClass, const WCHAR *szVe
     }
 }
 
-void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey)
+void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey, UINT uFlags)
 {
     WCHAR wszName[40];
     DWORD cchName, dwIndex = 0;
@@ -271,7 +289,7 @@ void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey)
         if (RegEnumKeyExW(hShellKey, dwIndex++, wszName, &cchName, NULL, NULL, 
NULL, NULL) != ERROR_SUCCESS)
             break;
 
-        AddStaticEntry(hKey, wszName);
+        AddStaticEntry(hKey, wszName, uFlags);
     }
 
     RegCloseKey(hShellKey);
@@ -450,13 +468,15 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
     HMENU hMenu,
     UINT* pIndexMenu,
     UINT iIdCmdFirst,
-    UINT iIdCmdLast)
+    UINT iIdCmdLast,
+    UINT uFlags)
 {
+    UINT ntver = RosGetProcessEffectiveVersion();
     MENUITEMINFOW mii;
     UINT idResource;
     WCHAR wszVerb[40];
     UINT fState;
-    UINT cIds = 0;
+    UINT cIds = 0, indexFirst = *pIndexMenu;
 
     mii.cbSize = sizeof(mii);
     mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
@@ -468,6 +488,7 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
     while (it != NULL)
     {
         StaticShellEntry& info = m_StaticEntries.GetNext(it);
+        BOOL forceFirstPos = FALSE;
 
         fState = MFS_ENABLED;
         mii.dwTypeData = NULL;
@@ -481,12 +502,19 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
 
         if (info.Verb.CompareNoCase(L"open") == 0)
         {
-            /* override default when open verb is found */
-            fState |= MFS_DEFAULT;
             idResource = IDS_OPEN_VERB;
+            fState |= MFS_DEFAULT; /* override default when open verb is found 
*/
+            forceFirstPos++;
         }
         else if (info.Verb.CompareNoCase(L"explore") == 0)
+        {
             idResource = IDS_EXPLORE_VERB;
+            if (uFlags & CMF_EXPLORE)
+            {
+                fState |= MFS_DEFAULT;
+                forceFirstPos++;
+            }
+        }
         else if (info.Verb.CompareNoCase(L"runas") == 0)
             idResource = IDS_RUNAS_VERB;
         else if (info.Verb.CompareNoCase(L"edit") == 0)
@@ -496,9 +524,7 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
         else if (info.Verb.CompareNoCase(L"print") == 0)
             idResource = IDS_PRINT_VERB;
         else if (info.Verb.CompareNoCase(L"printto") == 0)
-        {
             continue;
-        }
         else
             idResource = 0;
 
@@ -513,53 +539,96 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
             continue;
         }
 
-        BOOL Extended = FALSE;
+        UINT cmdFlags = 0;
+        BOOL hide = FALSE;
         HKEY hkVerb;
         if (idResource > 0)
         {
-            if (LoadStringW(shell32_hInstance, idResource, wszVerb, 
_countof(wszVerb)))
-                mii.dwTypeData = wszVerb; /* use translated verb */
-            else
-                ERR("Failed to load string\n");
-
-            LONG res = RegOpenKeyW(info.hkClass, wszKey, &hkVerb);
-            if (res == ERROR_SUCCESS)
+            if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
             {
-                res = RegQueryValueExW(hkVerb, L"Extended", NULL, NULL, NULL, 
NULL);
-                Extended = (res == ERROR_SUCCESS);
-
-                RegCloseKey(hkVerb);
+                if (LoadStringW(shell32_hInstance, idResource, wszVerb, 
_countof(wszVerb)))
+                    mii.dwTypeData = wszVerb; /* use translated verb */
+                else
+                    ERR("Failed to load string\n");
             }
+
+            if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) != ERROR_SUCCESS)
+                hkVerb = NULL;
         }
         else
         {
-            LONG res = RegOpenKeyW(info.hkClass, wszKey, &hkVerb);
-            if (res == ERROR_SUCCESS)
+            if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) == ERROR_SUCCESS)
             {
-                DWORD cbVerb = sizeof(wszVerb);
-                res = RegLoadMUIStringW(hkVerb, NULL, wszVerb, cbVerb, NULL, 
0, NULL);
-                if (res == ERROR_SUCCESS)
+                if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
                 {
-                    /* use description for the menu entry */
-                    mii.dwTypeData = wszVerb;
-                }
+                    DWORD cbVerb = sizeof(wszVerb);
 
-                res = RegQueryValueExW(hkVerb, L"Extended", NULL, NULL, NULL, 
NULL);
-                Extended = (res == ERROR_SUCCESS);
+                    LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszVerb, 
cbVerb, NULL, 0, NULL);
+                    if (res || !*wszVerb)
+                        res = RegLoadMUIStringW(hkVerb, NULL, wszVerb, cbVerb, 
NULL, 0, NULL);
 
-                RegCloseKey(hkVerb);
+                    if (res == ERROR_SUCCESS && *wszVerb)
+                    {
+                        /* use description for the menu entry */
+                        mii.dwTypeData = wszVerb;
+                    }
+                }
+            }
+            else
+            {
+                hkVerb = NULL;
             }
         }
 
-        if (!Extended || GetAsyncKeyState(VK_SHIFT) < 0)
+        if (hkVerb)
         {
-            mii.cch = wcslen(mii.dwTypeData);
+            // FIXME: GetAsyncKeyState should not be called here, clients
+            // need to be updated to set the CMF_EXTENDEDVERBS flag.
+            if (!(uFlags & CMF_EXTENDEDVERBS) && GetAsyncKeyState(VK_SHIFT) >= 
0)
+                hide |= RegValueExists(hkVerb, L"Extended");
+
+            hide |= RegValueExists(hkVerb, L"ProgrammaticAccessOnly");
+
+            if (!(uFlags & CMF_DISABLEDVERBS))
+                hide |= RegValueExists(hkVerb, L"LegacyDisable");
+
+            if (RegValueExists(hkVerb, L"NeverDefault"))
+                fState &= ~MFS_DEFAULT;
+
+            if (RegValueExists(hkVerb, L"SeparatorBefore"))
+                cmdFlags |= ECF_SEPARATORBEFORE;
+            if (RegValueExists(hkVerb, L"SeparatorAfter"))
+                cmdFlags |= ECF_SEPARATORAFTER;
+
+            RegCloseKey(hkVerb);
+        }
+
+        if (((uFlags & CMF_NODEFAULT) && ntver >= _WIN32_WINNT_VISTA) ||
+            ((uFlags & CMF_DONOTPICKDEFAULT) && ntver >= _WIN32_WINNT_WIN7))
+        {
+            fState &= ~MFS_DEFAULT;
+        }
+
+        if (!hide)
+        {
+            if (cmdFlags & ECF_SEPARATORBEFORE)
+            {
+                if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR))
+                    (*pIndexMenu)++;
+            }
+
             mii.fState = fState;
             mii.wID = iIdCmdFirst + cIds;
-            InsertMenuItemW(hMenu, *pIndexMenu, TRUE, &mii);
-            (*pIndexMenu)++;
-            cIds++;
+            if (InsertMenuItemW(hMenu, forceFirstPos ? indexFirst : 
*pIndexMenu, TRUE, &mii))
+                (*pIndexMenu)++;
+
+            if (cmdFlags & ECF_SEPARATORAFTER)
+            {
+                if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR))
+                    (*pIndexMenu)++;
+            }
         }
+        cIds++; // Always increment the id because it acts as the index into 
m_StaticEntries
 
         if (mii.wID >= iIdCmdLast)
             break;
@@ -587,7 +656,7 @@ void WINAPI _InsertMenuItemW(
     else if (fType == MFT_STRING)
     {
         mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
-        if ((ULONG_PTR)HIWORD((ULONG_PTR)dwTypeData) == 0)
+        if (IS_INTRESOURCE(dwTypeData))
         {
             if (LoadStringW(shell32_hInstance, LOWORD((ULONG_PTR)dwTypeData), 
wszText, _countof(wszText)))
                 mii.dwTypeData = wszText;
@@ -607,6 +676,42 @@ void WINAPI _InsertMenuItemW(
     InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);
 }
 
+void
+CDefaultContextMenu::TryPickDefault(HMENU hMenu, UINT idCmdFirst, UINT 
DfltOffset, UINT uFlags)
+{
+    // Are we allowed to pick a default?
+    UINT ntver = RosGetProcessEffectiveVersion();
+    if (((uFlags & CMF_NODEFAULT) && ntver >= _WIN32_WINNT_VISTA) ||
+        ((uFlags & CMF_DONOTPICKDEFAULT) && ntver >= _WIN32_WINNT_WIN7))
+    {
+        return;
+    }
+
+    // Do we already have a default?
+    if ((int)GetMenuDefaultItem(hMenu, MF_BYPOSITION, 0) != -1)
+        return;
+
+    // Does the view want to pick one?
+    INT_PTR forceDfm = 0;
+    if (_DoCallback(DFM_GETDEFSTATICID, 0, &forceDfm) == S_OK && forceDfm)
+    {
+        for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i)
+        {
+            UINT menuItemId = g_StaticInvokeCmdMap[i].IntVerb + DfltOffset - 
DCM_FCIDM_SHVIEW_OFFSET;
+            if (g_StaticInvokeCmdMap[i].DfmCmd == forceDfm &&
+                SetMenuDefaultItem(hMenu, menuItemId, MF_BYCOMMAND))
+            {
+                return;
+            }
+        }
+    }
+
+    // Don't want to pick something like cut or delete as the default but
+    // a static or dynamic verb is a good default.
+    if (m_iIdSCMLast > m_iIdSCMFirst || m_iIdSHELast > m_iIdSHEFirst)
+        SetMenuDefaultItem(hMenu, idCmdFirst, MF_BYCOMMAND);
+}
+
 HRESULT
 WINAPI
 CDefaultContextMenu::QueryContextMenu(
@@ -623,15 +728,15 @@ CDefaultContextMenu::QueryContextMenu(
     TRACE("BuildShellItemContextMenu entered\n");
 
     /* Load static verbs and shell extensions from registry */
-    for (UINT i = 0; i < m_cKeys; i++)
+    for (UINT i = 0; i < m_cKeys && !(uFlags & CMF_NOVERBS); i++)
     {
-        AddStaticEntriesForKey(m_aKeys[i]);
+        AddStaticEntriesForKey(m_aKeys[i], uFlags);
         EnumerateDynamicContextHandlerForKey(m_aKeys[i]);
     }
 
     /* Add static context menu handlers */
-    cIds = AddStaticContextMenusToMenu(hMenu, &IndexMenu, idCmdFirst, 
idCmdLast);
-    m_iIdSCMFirst = 0;
+    cIds = AddStaticContextMenusToMenu(hMenu, &IndexMenu, idCmdFirst, 
idCmdLast, uFlags);
+    m_iIdSCMFirst = 0; // FIXME: This should be = idCmdFirst?
     m_iIdSCMLast = cIds;
     idCmdNext = idCmdFirst + cIds;
 
@@ -654,44 +759,49 @@ CDefaultContextMenu::QueryContextMenu(
         idCmdNext = idCmdFirst + cIds;
     }
 
-    if (uFlags & CMF_VERBSONLY)
-        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
+    //TODO: DFM_MERGECONTEXTMENU_BOTTOM
+
+    UINT idDefaultOffset = 0;
+    BOOL isBackgroundMenu = !m_cidl;
+    if (!(uFlags & CMF_VERBSONLY) && !isBackgroundMenu)
+    {
+        /* Get the attributes of the items */
+        SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | 
SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | 
SFGAO_FILESYSTEM | SFGAO_FOLDER;
+        hr = m_psf->GetAttributesOf(m_cidl, m_apidl, &rfg);
+        if (FAILED_UNEXPECTEDLY(hr))
+            return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
+
+        /* Add the default part of the menu */
+        HMENU hmenuDefault = LoadMenu(_AtlBaseModule.GetResourceInstance(), 
L"MENU_SHV_FILE");
+
+        /* Remove uneeded entries */
+        if (!(rfg & SFGAO_CANMOVE))
+            DeleteMenu(hmenuDefault, IDM_CUT, MF_BYCOMMAND);
+        if (!(rfg & SFGAO_CANCOPY))
+            DeleteMenu(hmenuDefault, IDM_COPY, MF_BYCOMMAND);
+        if (!((rfg & SFGAO_FILESYSTEM) && HasClipboardData()))
+            DeleteMenu(hmenuDefault, IDM_INSERT, MF_BYCOMMAND);
+        if (!(rfg & SFGAO_CANLINK))
+            DeleteMenu(hmenuDefault, IDM_CREATELINK, MF_BYCOMMAND);
+        if (!(rfg & SFGAO_CANDELETE))
+            DeleteMenu(hmenuDefault, IDM_DELETE, MF_BYCOMMAND);
+        if (!(rfg & SFGAO_CANRENAME))
+            DeleteMenu(hmenuDefault, IDM_RENAME, MF_BYCOMMAND);
+        if (!(rfg & SFGAO_HASPROPSHEET))
+            DeleteMenu(hmenuDefault, IDM_PROPERTIES, MF_BYCOMMAND);
+
+        idDefaultOffset = idCmdNext;
+        UINT idMax = Shell_MergeMenus(hMenu, GetSubMenu(hmenuDefault, 0), 
IndexMenu, idCmdNext, idCmdLast, 0);
+        m_iIdDfltFirst = cIds;
+        cIds += idMax - idCmdNext;
+        m_iIdDfltLast = cIds;
+
+        DestroyMenu(hmenuDefault);
+    }
 
-    /* If this is a background context menu we are done */
-    if (!m_cidl)
-        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
+    TryPickDefault(hMenu, idCmdFirst, idDefaultOffset, uFlags);
 
-    /* Get the attributes of the items */
-    SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | 
SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | 
SFGAO_FILESYSTEM | SFGAO_FOLDER;
-    hr = m_psf->GetAttributesOf(m_cidl, m_apidl, &rfg);
-    if (FAILED_UNEXPECTEDLY(hr))
-        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
-
-    /* Add the default part of the menu */
-    HMENU hmenuDefault = LoadMenu(_AtlBaseModule.GetResourceInstance(), 
L"MENU_SHV_FILE");
-
-    /* Remove uneeded entries */
-    if (!(rfg & SFGAO_CANMOVE))
-        DeleteMenu(hmenuDefault, IDM_CUT, MF_BYCOMMAND);
-    if (!(rfg & SFGAO_CANCOPY))
-        DeleteMenu(hmenuDefault, IDM_COPY, MF_BYCOMMAND);
-    if (!((rfg & SFGAO_FILESYSTEM) && HasClipboardData()))
-        DeleteMenu(hmenuDefault, IDM_INSERT, MF_BYCOMMAND);
-    if (!(rfg & SFGAO_CANLINK))
-        DeleteMenu(hmenuDefault, IDM_CREATELINK, MF_BYCOMMAND);
-    if (!(rfg & SFGAO_CANDELETE))
-        DeleteMenu(hmenuDefault, IDM_DELETE, MF_BYCOMMAND);
-    if (!(rfg & SFGAO_CANRENAME))
-        DeleteMenu(hmenuDefault, IDM_RENAME, MF_BYCOMMAND);
-    if (!(rfg & SFGAO_HASPROPSHEET))
-        DeleteMenu(hmenuDefault, IDM_PROPERTIES, MF_BYCOMMAND);
-
-    UINT idMax = Shell_MergeMenus(hMenu, GetSubMenu(hmenuDefault, 0), 
IndexMenu, idCmdNext, idCmdLast, 0);
-    m_iIdDfltFirst = cIds;
-    cIds += idMax - idCmdNext;
-    m_iIdDfltLast = cIds;
-
-    DestroyMenu(hmenuDefault);
+    // TODO: DFM_MERGECONTEXTMENU_TOP
 
     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
 }
@@ -1040,7 +1150,7 @@ 
CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStatic
 
     /* Get a pointer to the shell browser */
     hr = IUnknown_QueryService(m_site, SID_IShellBrowser, 
IID_PPV_ARG(IShellBrowser, &psb));
-    if (FAILED_UNEXPECTEDLY(hr))
+    if (FAILED(hr))
         return 0;
 
     /* See if we are in Explore or Browse mode. If the browser's tree is 
present, we are in Explore mode.*/
@@ -1065,7 +1175,7 @@ 
CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStatic
 
 HRESULT
 CDefaultContextMenu::TryToBrowse(
-    LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, DWORD wFlags)
+    LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidlChild, DWORD wFlags)
 {
     CComPtr<IShellBrowser> psb;
     HRESULT hr;
@@ -1075,10 +1185,17 @@ CDefaultContextMenu::TryToBrowse(
 
     /* Get a pointer to the shell browser */
     hr = IUnknown_QueryService(m_site, SID_IShellBrowser, 
IID_PPV_ARG(IShellBrowser, &psb));
-    if (FAILED_UNEXPECTEDLY(hr))
-        return 0;
+    if (FAILED(hr))
+        return hr;
 
-    return psb->BrowseObject(ILCombine(m_pidlFolder, pidl), wFlags);
+    PIDLIST_ABSOLUTE pidl;
+    hr = SHILCombine(m_pidlFolder, pidlChild, &pidl);
+    if (FAILED_UNEXPECTEDLY(hr))
+        return hr;
+    
+    hr = psb->BrowseObject(pidl, wFlags & ~SBSP_RELATIVE);
+    ILFree(pidl);
+    return hr;
 }
 
 HRESULT
@@ -1101,7 +1218,8 @@ CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFOEX 
lpcmi, LPCITEMIDLIST pid
     }
     else
     {
-        SHGetPathFromIDListW(m_pidlFolder, wszDir);
+        if (!SHGetPathFromIDListW(m_pidlFolder, wszDir))
+            *wszDir = UNICODE_NULL;
     }
 
     SHELLEXECUTEINFOW sei;
@@ -1143,23 +1261,24 @@ CDefaultContextMenu::InvokeRegVerb(
 
     /* Get the browse flags to see if we need to browse */
     DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
-    BOOL bBrowsed = FALSE;
 
     for (i=0; i < m_cidl; i++)
     {
         /* Check if we need to browse */
-        if (wFlags > 0)
+        if (wFlags)
         {
-            /* In xp if we have browsed, we don't open any more folders.
-             * In win7 we browse to the first folder we find and
-             * open new windows for each of the rest of the folders */
-            if (bBrowsed)
-                continue;
-
             hr = TryToBrowse(lpcmi, m_apidl[i], wFlags);
             if (SUCCEEDED(hr))
             {
-                bBrowsed = TRUE;
+                /* In WinXP if we have browsed, we don't open any more folders.
+                 * In Win7 we browse to the first folder we find and
+                 * open new windows for each of the rest of the folders */
+                UINT ntver = RosGetProcessEffectiveVersion();
+                if (ntver >= _WIN32_WINNT_VISTA)
+                    wFlags = 0; // FIXME: = SBSP_NEWBROWSER | (wFlags & 
~SBSP_SAMEBROWSER);
+                else
+                    i = m_cidl;
+
                 continue;
             }
         }
@@ -1184,7 +1303,7 @@ CDefaultContextMenu::InvokeCommand(
     memcpy(&LocalInvokeInfo, lpcmi, min(sizeof(LocalInvokeInfo), 
lpcmi->cbSize));
 
     /* Check if this is a string verb */
-    if (HIWORD(LocalInvokeInfo.lpVerb))
+    if (!IS_INTRESOURCE(LocalInvokeInfo.lpVerb))
     {
         /* Get the ID which corresponds to this verb, and update our local 
copy */
         if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE))
@@ -1217,7 +1336,7 @@ CDefaultContextMenu::InvokeCommand(
     {
         CmdId -= m_iIdDfltFirst;
         /* See the definitions of IDM_CUT and co to see how this works */
-        CmdId += 0x7000;
+        CmdId += DCM_FCIDM_SHVIEW_OFFSET;
     }
 
     if (LocalInvokeInfo.cbSize >= sizeof(CMINVOKECOMMANDINFOEX) && 
(LocalInvokeInfo.fMask & CMIC_MASK_PTINVOKE))
@@ -1344,7 +1463,7 @@ CDefaultContextMenu::GetCommandString(
     {
         CmdId -= m_iIdDfltFirst;
         /* See the definitions of IDM_CUT and co to see how this works */
-        CmdId += 0x7000;
+        CmdId += DCM_FCIDM_SHVIEW_OFFSET;
     }
 
     /* Loop looking for a matching Id */
diff --git a/sdk/include/psdk/shobjidl.idl b/sdk/include/psdk/shobjidl.idl
index 54bf45a34c2..ef9e51d16e1 100644
--- a/sdk/include/psdk/shobjidl.idl
+++ b/sdk/include/psdk/shobjidl.idl
@@ -1596,16 +1596,21 @@ interface IDropTargetHelper : IUnknown
 ]
 interface IContextMenu : IUnknown
 {
-cpp_quote("#define CMF_NORMAL        0x00000000")
-cpp_quote("#define CMF_DEFAULTONLY   0x00000001")
-cpp_quote("#define CMF_VERBSONLY     0x00000002")
-cpp_quote("#define CMF_EXPLORE       0x00000004")
-cpp_quote("#define CMF_NOVERBS       0x00000008")
-cpp_quote("#define CMF_CANRENAME     0x00000010")
-cpp_quote("#define CMF_NODEFAULT     0x00000020")
-cpp_quote("#define CMF_INCLUDESTATIC 0x00000040")
-cpp_quote("#define CMF_EXTENDEDVERBS 0x00000100")
-cpp_quote("#define CMF_RESERVED      0xffff0000")
+cpp_quote("#define CMF_NORMAL            0x00000000")
+cpp_quote("#define CMF_DEFAULTONLY       0x00000001")
+cpp_quote("#define CMF_VERBSONLY         0x00000002")
+cpp_quote("#define CMF_EXPLORE           0x00000004")
+cpp_quote("#define CMF_NOVERBS           0x00000008")
+cpp_quote("#define CMF_CANRENAME         0x00000010")
+cpp_quote("#define CMF_NODEFAULT         0x00000020")
+cpp_quote("#define CMF_INCLUDESTATIC     0x00000040")
+cpp_quote("#define CMF_EXTENDEDVERBS     0x00000100")
+cpp_quote("#define CMF_DISABLEDVERBS     0x00000200")
+cpp_quote("#define CMF_ASYNCVERBSTATE    0x00000400")
+cpp_quote("#define CMF_OPTIMIZEFORINVOKE 0x00000800")
+cpp_quote("#define CMF_SYNCCASCADEMENU   0x00001000")
+cpp_quote("#define CMF_DONOTPICKDEFAULT  0x00002000")
+cpp_quote("#define CMF_RESERVED          0xffff0000")
 
 cpp_quote("#define GCS_VERBA         0x00000000")
 cpp_quote("#define GCS_HELPTEXTA     0x00000001")
diff --git a/sdk/include/reactos/compat_undoc.h 
b/sdk/include/reactos/compat_undoc.h
index 44212efb2d2..b0516322a37 100644
--- a/sdk/include/reactos/compat_undoc.h
+++ b/sdk/include/reactos/compat_undoc.h
@@ -34,5 +34,16 @@ DWORD RosGetProcessCompatVersion(VOID)
     return g_CompatVersion < REACTOS_COMPATVERSION_UNINITIALIZED ? 
g_CompatVersion : 0;
 }
 
+static
+inline
+UINT RosGetProcessEffectiveVersion(VOID)
+{
+    PPEB peb = NtCurrentPeb();
+    UINT shimVer = RosGetProcessCompatVersion();
+    if (shimVer)
+        return shimVer;
+    else
+        return (peb->OSMajorVersion << 8) | (peb->OSMinorVersion);
+}
 
 #endif // COMPAT_UNDOC_H
diff --git a/sdk/include/reactos/shellutils.h b/sdk/include/reactos/shellutils.h
index 9ccce4d3373..226de2ee325 100644
--- a/sdk/include/reactos/shellutils.h
+++ b/sdk/include/reactos/shellutils.h
@@ -410,6 +410,18 @@ HRESULT inline ShellObjectCreatorInit(T1 initArg1, T2 
initArg2, T3 initArg3, T4
     return hResult;
 }
 
+template<class P, class R> static HRESULT SHILClone(P pidl, R *ppOut)
+{
+    R r = *ppOut = (R)ILClone((PIDLIST_RELATIVE)pidl);
+    return r ? S_OK : E_OUTOFMEMORY;
+}
+
+template<class B, class R> static HRESULT SHILCombine(B base, 
PCUIDLIST_RELATIVE sub, R *ppOut)
+{
+    R r = *ppOut = (R)ILCombine((PCIDLIST_ABSOLUTE)base, sub);
+    return r ? S_OK : E_OUTOFMEMORY;
+}
+
 HRESULT inline SHSetStrRet(LPSTRRET pStrRet, LPCSTR pstrValue)
 {
     pStrRet->uType = STRRET_CSTR;

Reply via email to