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);
}