From f49c4df284029ed2be0ca0e71c429ba8bf2aa3f3 Mon Sep 17 00:00:00 2001 From: misirlou-tg <39652424+misirlou-tg@users.noreply.github.com> Date: Sat, 5 Jun 2021 18:27:54 -0500 Subject: [PATCH 1/4] Adding explorer right-click context menu --- src/Winfile.vcxproj | 2 ++ src/Winfile.vcxproj.filters | 2 ++ src/explorermenu.c | 69 +++++++++++++++++++++++++++++++++++++ src/explorermenu.h | 1 + src/wfdlgs.c | 16 ++++++--- 5 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/explorermenu.c create mode 100644 src/explorermenu.h diff --git a/src/Winfile.vcxproj b/src/Winfile.vcxproj index c6ac905c..89b83a84 100644 --- a/src/Winfile.vcxproj +++ b/src/Winfile.vcxproj @@ -297,6 +297,7 @@ + @@ -367,6 +368,7 @@ + diff --git a/src/Winfile.vcxproj.filters b/src/Winfile.vcxproj.filters index aaa8f825..d3bb1061 100644 --- a/src/Winfile.vcxproj.filters +++ b/src/Winfile.vcxproj.filters @@ -37,6 +37,7 @@ + @@ -62,6 +63,7 @@ + diff --git a/src/explorermenu.c b/src/explorermenu.c new file mode 100644 index 00000000..5b137333 --- /dev/null +++ b/src/explorermenu.c @@ -0,0 +1,69 @@ +#include + +// +// How to host an IContextMenu, part 1 – Initial foray +// https://devblogs.microsoft.com/oldnewthing/20040920-00/?p=37823 +// +// How to host an IContextMenu, part 2 – Displaying the context menu +// https://devblogs.microsoft.com/oldnewthing/20040922-00/?p=37793 +// + +static HRESULT GetUIObjectOfFile(HWND hwnd, LPCWSTR pszPath, REFIID riid, void **ppv) +{ + *ppv = NULL; + HRESULT hr; + LPITEMIDLIST pidl; + SFGAOF sfgao; + if (SUCCEEDED(hr = SHParseDisplayName(pszPath, NULL, &pidl, 0, &sfgao))) + { + IShellFolder *psf; + LPCITEMIDLIST pidlChild; + if (SUCCEEDED(hr = SHBindToParent(pidl, &IID_IShellFolder, (void **)&psf, &pidlChild))) + { + hr = psf->lpVtbl->GetUIObjectOf(psf, hwnd, 1, &pidlChild, riid, NULL, ppv); + psf->lpVtbl->Release(psf); + } + CoTaskMemFree(pidl); + } + return hr; +} + +#define SCRATCH_QCM_FIRST 1 +#define SCRATCH_QCM_LAST 0x7FFF + +void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos) +{ + POINT pt = { (long)xPos, (long)yPos }; + if (pt.x == -1 && pt.y == -1) + { + pt.x = pt.y = 0; + ClientToScreen(hwnd, &pt); + } + + IContextMenu *pcm; + if (SUCCEEDED(GetUIObjectOfFile(hwnd, pFileName, &IID_IContextMenu, (void **)&pcm))) + { + HMENU hmenu = CreatePopupMenu(); + if (hmenu) + { + if (SUCCEEDED(pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST, CMF_NORMAL))) + { + int iCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD, pt.x, pt.y, hwnd, NULL); + if (iCmd > 0) + { + CMINVOKECOMMANDINFOEX info = { 0 }; + info.cbSize = sizeof(info); + info.fMask = CMIC_MASK_UNICODE; + info.hwnd = hwnd; + auto i = iCmd - SCRATCH_QCM_FIRST; + info.lpVerb = MAKEINTRESOURCEA(i); + info.lpVerbW = MAKEINTRESOURCEW(i); + info.nShow = SW_SHOWNORMAL; + HRESULT hr = pcm->lpVtbl->InvokeCommand(pcm, (LPCMINVOKECOMMANDINFO)&info); + } + } + DestroyMenu(hmenu); + } + pcm->lpVtbl->Release(pcm); + } +} diff --git a/src/explorermenu.h b/src/explorermenu.h new file mode 100644 index 00000000..45d467c3 --- /dev/null +++ b/src/explorermenu.h @@ -0,0 +1 @@ +void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos); diff --git a/src/wfdlgs.c b/src/wfdlgs.c index b315b12e..b6403ded 100644 --- a/src/wfdlgs.c +++ b/src/wfdlgs.c @@ -14,6 +14,7 @@ #include #include "lfn.h" #include "wfcopy.h" +#include "explorermenu.h" VOID MDIClientSizeChange(HWND hwndActive, INT iFlags); @@ -840,6 +841,7 @@ ActivateCommonContextMenu(HWND hwnd, HWND hwndLB, LPARAM lParam) { DWORD cmd, item; POINT pt; + WCHAR *pSelectedFile = NULL; HMENU hMenu = GetSubMenu(LoadMenu(hAppInstance, TEXT("CTXMENU")), 0); @@ -881,16 +883,22 @@ ActivateCommonContextMenu(HWND hwnd, HWND hwndLB, LPARAM lParam) SendMessage(hwndLB, LB_SETSEL, (WPARAM)TRUE, (LPARAM)item); BOOL bDir = FALSE; - SendMessage(hwnd, FS_GETSELECTION, 5, (LPARAM)&bDir); + pSelectedFile = (WCHAR *)SendMessage(hwnd, FS_GETSELECTION, 5, (LPARAM)&bDir); if (bDir) EnableMenuItem(hMenu, IDM_EDIT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); } } } - cmd = TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hwnd, NULL); - if (cmd != 0) - PostMessage(hwndFrame, WM_COMMAND, GET_WM_COMMAND_MPS(cmd, 0, 0)); + if (pSelectedFile != NULL) { + ShowExplorerContextMenu(hwnd, pSelectedFile, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + else + { + cmd = TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hwnd, NULL); + if (cmd != 0) + PostMessage(hwndFrame, WM_COMMAND, GET_WM_COMMAND_MPS(cmd, 0, 0)); + } DestroyMenu(hMenu); } From 5273e477e6e9c4f9864849e7d514703b00639b44 Mon Sep 17 00:00:00 2001 From: misirlou-tg <39652424+misirlou-tg@users.noreply.github.com> Date: Mon, 7 Jun 2021 10:12:50 -0500 Subject: [PATCH 2/4] Adding invoke point and key context when showing explorer menu --- src/explorermenu.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/explorermenu.c b/src/explorermenu.c index 5b137333..bb3e7bc3 100644 --- a/src/explorermenu.c +++ b/src/explorermenu.c @@ -1,11 +1,17 @@ #include // -// How to host an IContextMenu, part 1 – Initial foray +// How to host an IContextMenu, part 1 - Initial foray // https://devblogs.microsoft.com/oldnewthing/20040920-00/?p=37823 // -// How to host an IContextMenu, part 2 – Displaying the context menu +// How to host an IContextMenu, part 2 - Displaying the context menu // https://devblogs.microsoft.com/oldnewthing/20040922-00/?p=37793 +// +// How to host an IContextMenu, part 3 - Invocation location +// https://devblogs.microsoft.com/oldnewthing/20040923-00/?p=37773 +// +// How to host an IContextMenu, part 4 - Key context +// https://devblogs.microsoft.com/oldnewthing/20040924-00/?p=37753 // static HRESULT GetUIObjectOfFile(HWND hwnd, LPCWSTR pszPath, REFIID riid, void **ppv) @@ -53,12 +59,22 @@ void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos) { CMINVOKECOMMANDINFOEX info = { 0 }; info.cbSize = sizeof(info); - info.fMask = CMIC_MASK_UNICODE; + info.fMask = CMIC_MASK_UNICODE | CMIC_MASK_PTINVOKE; + if (GetKeyState(VK_CONTROL) < 0) + { + info.fMask |= CMIC_MASK_CONTROL_DOWN; + } + if (GetKeyState(VK_SHIFT) < 0) + { + info.fMask |= CMIC_MASK_SHIFT_DOWN; + } info.hwnd = hwnd; auto i = iCmd - SCRATCH_QCM_FIRST; info.lpVerb = MAKEINTRESOURCEA(i); info.lpVerbW = MAKEINTRESOURCEW(i); info.nShow = SW_SHOWNORMAL; + info.ptInvoke.x = xPos; + info.ptInvoke.y = yPos; HRESULT hr = pcm->lpVtbl->InvokeCommand(pcm, (LPCMINVOKECOMMANDINFO)&info); } } From 90482aa207f9dc464cd512c63c67e48ebf737bc3 Mon Sep 17 00:00:00 2001 From: misirlou-tg <39652424+misirlou-tg@users.noreply.github.com> Date: Mon, 7 Jun 2021 10:55:48 -0500 Subject: [PATCH 3/4] Process messages for the context menu when showing explorer menu Without this sub-menus may not work, for example the "Open With" sub-menu. --- src/explorermenu.c | 18 ++++++++++++++++++ src/explorermenu.h | 5 +++++ src/wfdir.c | 14 ++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/explorermenu.c b/src/explorermenu.c index bb3e7bc3..8a516054 100644 --- a/src/explorermenu.c +++ b/src/explorermenu.c @@ -12,8 +12,14 @@ // // How to host an IContextMenu, part 4 - Key context // https://devblogs.microsoft.com/oldnewthing/20040924-00/?p=37753 +// +// How to host an IContextMenu, part 5 - Handling menu messages +// https://devblogs.microsoft.com/oldnewthing/20040927-00/?p=37733 // +IContextMenu2 *pExplorerCm2; +IContextMenu3 *pExplorerCm3; + static HRESULT GetUIObjectOfFile(HWND hwnd, LPCWSTR pszPath, REFIID riid, void **ppv) { *ppv = NULL; @@ -54,7 +60,19 @@ void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos) { if (SUCCEEDED(pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST, CMF_NORMAL))) { + pcm->lpVtbl->QueryInterface(pcm, &IID_IContextMenu2, (void **)&pExplorerCm2); + pcm->lpVtbl->QueryInterface(pcm, &IID_IContextMenu3, (void **)&pExplorerCm3); int iCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD, pt.x, pt.y, hwnd, NULL); + if (pExplorerCm2) + { + pExplorerCm2->lpVtbl->Release(pExplorerCm2); + pExplorerCm2 = NULL; + } + if (pExplorerCm3) + { + pExplorerCm3->lpVtbl->Release(pExplorerCm3); + pExplorerCm3 = NULL; + } if (iCmd > 0) { CMINVOKECOMMANDINFOEX info = { 0 }; diff --git a/src/explorermenu.h b/src/explorermenu.h index 45d467c3..947c1cf6 100644 --- a/src/explorermenu.h +++ b/src/explorermenu.h @@ -1 +1,6 @@ +#include + void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos); + +extern IContextMenu2 *pExplorerCm2; +extern IContextMenu3 *pExplorerCm3; diff --git a/src/wfdir.c b/src/wfdir.c index e1fb64c1..70ce6834 100644 --- a/src/wfdir.c +++ b/src/wfdir.c @@ -16,6 +16,7 @@ #include #include "wfdrop.h" +#include "explorermenu.h" WCHAR szAttr[] = L"RHSACE"; @@ -429,6 +430,19 @@ DirWndProc( static HWND hwndOwnerDraw = NULL; + // Process messages for the explorer context menu + if (pExplorerCm3) { + LRESULT lres; + if (SUCCEEDED(pExplorerCm3->lpVtbl->HandleMenuMsg2(pExplorerCm3, uMsg, wParam, lParam, &lres))) { + return lres; + } + } + else if (pExplorerCm2) { + if (SUCCEEDED(pExplorerCm2->lpVtbl->HandleMenuMsg(pExplorerCm2, uMsg, wParam, lParam))) { + return 0; + } + } + #ifdef PROGMANHSCROLL // // If the window is different and uMsg == WM_DRAWITEM _OR_ From 5da21cbe2d18608efb7e41d8d9dcf27e9a7ff6fd Mon Sep 17 00:00:00 2001 From: misirlou-tg <39652424+misirlou-tg@users.noreply.github.com> Date: Tue, 8 Jun 2021 16:14:29 -0500 Subject: [PATCH 4/4] Changing how the explorer menu is built/displayed It is now a popup added to the WinFile context menu --- src/explorermenu.c | 24 +++++++++++++++++------- src/explorermenu.h | 2 +- src/wfdlgs.c | 6 +++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/explorermenu.c b/src/explorermenu.c index 8a516054..223bfa55 100644 --- a/src/explorermenu.c +++ b/src/explorermenu.c @@ -40,10 +40,12 @@ static HRESULT GetUIObjectOfFile(HWND hwnd, LPCWSTR pszPath, REFIID riid, void * return hr; } -#define SCRATCH_QCM_FIRST 1 -#define SCRATCH_QCM_LAST 0x7FFF +// Since we are combining the WinFile menu with the explorer menu +// all of the WinFile command IDs must be below this range +#define EXPLORER_QCM_FIRST 0x1001 +#define EXPLORER_QCM_LAST 0x7FFF -void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos) +int ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, HMENU hMenuWinFile, UINT xPos, UINT yPos) { POINT pt = { (long)xPos, (long)yPos }; if (pt.x == -1 && pt.y == -1) @@ -52,17 +54,19 @@ void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos) ClientToScreen(hwnd, &pt); } + int ret = 0; IContextMenu *pcm; if (SUCCEEDED(GetUIObjectOfFile(hwnd, pFileName, &IID_IContextMenu, (void **)&pcm))) { HMENU hmenu = CreatePopupMenu(); if (hmenu) { - if (SUCCEEDED(pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST, CMF_NORMAL))) + if (SUCCEEDED(pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, EXPLORER_QCM_FIRST, EXPLORER_QCM_LAST, CMF_NORMAL))) { + InsertMenu(hMenuWinFile, (UINT)-1, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hmenu, L"Explorer"); pcm->lpVtbl->QueryInterface(pcm, &IID_IContextMenu2, (void **)&pExplorerCm2); pcm->lpVtbl->QueryInterface(pcm, &IID_IContextMenu3, (void **)&pExplorerCm3); - int iCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD, pt.x, pt.y, hwnd, NULL); + int iCmd = TrackPopupMenuEx(hMenuWinFile, TPM_RETURNCMD, pt.x, pt.y, hwnd, NULL); if (pExplorerCm2) { pExplorerCm2->lpVtbl->Release(pExplorerCm2); @@ -73,7 +77,12 @@ void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos) pExplorerCm3->lpVtbl->Release(pExplorerCm3); pExplorerCm3 = NULL; } - if (iCmd > 0) + if (iCmd > 0 && iCmd <= EXPLORER_QCM_FIRST) + { + // The command selected was in the caller's range, they will have to execute it + ret = iCmd; + } + else if (iCmd > 0) { CMINVOKECOMMANDINFOEX info = { 0 }; info.cbSize = sizeof(info); @@ -87,7 +96,7 @@ void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos) info.fMask |= CMIC_MASK_SHIFT_DOWN; } info.hwnd = hwnd; - auto i = iCmd - SCRATCH_QCM_FIRST; + int i = iCmd - EXPLORER_QCM_FIRST; info.lpVerb = MAKEINTRESOURCEA(i); info.lpVerbW = MAKEINTRESOURCEW(i); info.nShow = SW_SHOWNORMAL; @@ -100,4 +109,5 @@ void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos) } pcm->lpVtbl->Release(pcm); } + return ret; } diff --git a/src/explorermenu.h b/src/explorermenu.h index 947c1cf6..9bc6dddb 100644 --- a/src/explorermenu.h +++ b/src/explorermenu.h @@ -1,6 +1,6 @@ #include -void ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, UINT xPos, UINT yPos); +int ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, HMENU hMenuWinFile, UINT xPos, UINT yPos); extern IContextMenu2 *pExplorerCm2; extern IContextMenu3 *pExplorerCm3; diff --git a/src/wfdlgs.c b/src/wfdlgs.c index b6403ded..3f726077 100644 --- a/src/wfdlgs.c +++ b/src/wfdlgs.c @@ -891,14 +891,14 @@ ActivateCommonContextMenu(HWND hwnd, HWND hwndLB, LPARAM lParam) } if (pSelectedFile != NULL) { - ShowExplorerContextMenu(hwnd, pSelectedFile, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + cmd = ShowExplorerContextMenu(hwnd, pSelectedFile, hMenu, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); } else { cmd = TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hwnd, NULL); - if (cmd != 0) - PostMessage(hwndFrame, WM_COMMAND, GET_WM_COMMAND_MPS(cmd, 0, 0)); } + if (cmd != 0) + PostMessage(hwndFrame, WM_COMMAND, GET_WM_COMMAND_MPS(cmd, 0, 0)); DestroyMenu(hMenu); }