OSDN Git Service

WinMergeContextMenu: Fix the problem that the WinMerge icon is not correctly displaye...
authorTakashi Sawanaka <sdottaka@users.sourceforge.net>
Mon, 23 May 2022 12:52:31 +0000 (21:52 +0900)
committerTakashi Sawanaka <sdottaka@users.sourceforge.net>
Mon, 23 May 2022 12:52:31 +0000 (21:52 +0900)
I referred to the following URL

- https://gitlab.com/tortoisegit/tortoisegit/-/merge_requests/187
- https://devblogs.microsoft.com/oldnewthing/20131118-00/?p=2643
- https://devblogs.microsoft.com/oldnewthing/20130318-00/?p=4933

ShellExtension/Common/WinMergeContextMenu.cpp
ShellExtension/Common/WinMergeContextMenu.h
ShellExtension/WinMergeContextMenu/dllmain.cpp

index a0603fd..6286f14 100644 (file)
@@ -3,6 +3,11 @@
 #include "LanguageSelect.h"
 #include "../ShellExtension/Resource.h"
 #include <Shlwapi.h>
+#include <atlbase.h>
+#include <shobjidl_core.h>
+#include <ShlObj_core.h>
+#include <shldisp.h>
+#include <ShlGuid.h>
 
 /// Max. filecount to select
 static const int MaxFileCount = 3;
@@ -81,9 +86,110 @@ static String FormatCmdLine(const String &winmergePath,
        return strCommandline;
 }
 
+// https://devblogs.microsoft.com/oldnewthing/20130318-00/?p=4933
+static HRESULT FindDesktopFolderView(REFIID riid, void** ppv)
+{
+       HRESULT hr;
+       CComPtr<IShellWindows> spShellWindows;
+       if (FAILED(hr = spShellWindows.CoCreateInstance(CLSID_ShellWindows)))
+               return hr;
+
+       CComVariant vtLoc(CSIDL_DESKTOP);
+       CComVariant vtEmpty;
+       long lhwnd;
+       CComPtr<IDispatch> spdisp;
+       if (FAILED(hr = spShellWindows->FindWindowSW(
+               &vtLoc, &vtEmpty,
+               SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp)))
+               return hr;
+
+       CComPtr<IShellBrowser> spBrowser;
+       if (FAILED(hr = CComQIPtr<IServiceProvider>(spdisp)->
+               QueryService(SID_STopLevelBrowser,
+                       IID_PPV_ARGS(&spBrowser))))
+               return hr;
+
+       CComPtr<IShellView> spView;
+       if (FAILED(hr = spBrowser->QueryActiveShellView(&spView)))
+               return hr;
+
+       return spView->QueryInterface(riid, ppv);
+}
+
+// https://gitlab.com/tortoisegit/tortoisegit/-/merge_requests/187
+static HRESULT GetFolderView(IUnknown* pSite, IShellView** psv)
+{
+       CComPtr<IUnknown> site(pSite);
+       CComPtr<IServiceProvider> serviceProvider;
+       HRESULT hr;
+       if (FAILED(hr = site.QueryInterface(&serviceProvider)))
+               return hr;
+
+       CComPtr<IShellBrowser> shellBrowser;
+       if (FAILED(hr = serviceProvider->QueryService(SID_SShellBrowser, IID_PPV_ARGS(&shellBrowser))))
+               return hr;
+
+       return shellBrowser->QueryActiveShellView(psv);
+}
+
+// https://devblogs.microsoft.com/oldnewthing/20131118-00/?p=2643
+// https://gitlab.com/tortoisegit/tortoisegit/-/merge_requests/187
+static HRESULT GetFolderAutomationObject(IUnknown* pSite, REFIID riid, void** ppv)
+{
+       HRESULT hr;
+       CComPtr<IShellView> spsv;
+       if (FAILED(hr = GetFolderView(pSite, &spsv)))
+       {
+               if (FAILED(hr = FindDesktopFolderView(IID_PPV_ARGS(&spsv))))
+                       return hr;
+       }
+
+       CComPtr<IDispatch> spdispView;
+       if (FAILED(hr = spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView))))
+               return hr;
+       return spdispView->QueryInterface(riid, ppv);
+}
+
+// https://devblogs.microsoft.com/oldnewthing/20131118-00/?p=2643
+// https://gitlab.com/tortoisegit/tortoisegit/-/merge_requests/187
+static HRESULT ShellExecuteFromExplorer(
+       IUnknown* pSite,
+       PCWSTR pszFile,
+       PCWSTR pszParameters = nullptr,
+       PCWSTR pszDirectory = nullptr,
+       PCWSTR pszOperation = nullptr,
+       int nShowCmd = SW_SHOWNORMAL)
+{
+       HRESULT hr;
+       CComPtr<IShellFolderViewDual> spFolderView;
+       if (FAILED(hr = GetFolderAutomationObject(pSite, IID_PPV_ARGS(&spFolderView))))
+               return hr;
+
+       CComPtr<IDispatch> spdispShell;
+       if (FAILED(hr = spFolderView->get_Application(&spdispShell)))
+               return hr;
+
+       // without this, the launched app is not moved to the foreground
+       AllowSetForegroundWindow(ASFW_ANY);
+
+       return CComQIPtr<IShellDispatch2>(spdispShell)
+               ->ShellExecute(CComBSTR(pszFile),
+                       CComVariant(pszParameters ? pszParameters : L""),
+                       CComVariant(pszDirectory ? pszDirectory : L""),
+                       CComVariant(pszOperation ? pszOperation : L""),
+                       CComVariant(nShowCmd));
+}
+
 static BOOL LaunchWinMerge(const String &winmergePath,
-               const std::vector<String>& paths, BOOL bAlterSubFolders)
+               const std::vector<String>& paths, BOOL bAlterSubFolders, IUnknown* pSite)
 {
+       if (pSite)
+       {
+               String strCommandLine = FormatCmdLine(_T(""), paths, bAlterSubFolders);
+               if (SUCCEEDED(ShellExecuteFromExplorer(pSite, winmergePath.c_str(), strCommandLine.c_str())))
+                       return TRUE;
+       }
+
        String strCommandLine = FormatCmdLine(winmergePath, paths, bAlterSubFolders);
 
        // Finally start a new WinMerge process
@@ -121,6 +227,7 @@ WinMergeContextMenu::WinMergeContextMenu(HINSTANCE hInstance)
        , m_dwMenuState(MENU_HIDDEN)
        , m_dwContextMenuEnabled(0)
        , m_langID(0)
+       , m_pSite(nullptr)
 {
        CRegKeyEx reg;
        if (reg.Open(HKEY_CURRENT_USER, f_RegDir) == ERROR_SUCCESS)
@@ -317,7 +424,7 @@ HRESULT WinMergeContextMenu::InvokeCommand(DWORD verb)
        if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0)
                bAlterSubFolders = TRUE;
 
-       return LaunchWinMerge(strWinMergePath, m_strPaths, bAlterSubFolders) ? S_OK : S_FALSE;
+       return LaunchWinMerge(strWinMergePath, m_strPaths, bAlterSubFolders, m_pSite) ? S_OK : S_FALSE;
 }
 
 /**
index 3f267a0..8b4b4a3 100644 (file)
@@ -61,6 +61,8 @@ public:
        std::wstring GetResourceString(UINT id) const;
        DWORD GetMenuState() const { return m_dwMenuState; }
        DWORD GetContextMenuEnabled() const { return m_dwContextMenuEnabled; }
+       void SetSite(IUnknown* pUnknown) { m_pSite = pUnknown; };
+       const std::vector<std::wstring>& GetPaths() const { return m_strPaths; }
 
 private:
        DWORD m_dwMenuState; /**< Shown menuitems */
@@ -70,5 +72,6 @@ private:
        inline static CLanguageSelect* s_pLang;
        mutable LANGID m_langID; /**< Current Language Id */
        DWORD m_dwContextMenuEnabled; /**< 0, 2: context menu disabled 1: context menu enabled 3: Advanced menu enabled */
+    IUnknown *m_pSite;
 };
 
index cd4a835..33915cb 100644 (file)
@@ -38,7 +38,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
     return TRUE;
 }
 
-class WinMergeExplorerCommandBase : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand>
+class WinMergeExplorerCommandBase : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand, IObjectWithSite>
 {
 public:
     WinMergeExplorerCommandBase(WinMergeContextMenu* pContextMenu) : m_pContextMenu(pContextMenu) {}
@@ -101,6 +101,10 @@ public:
         return E_NOTIMPL;
     }
 
+    // IObjectWithSite
+    IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept { m_site = site; m_pContextMenu->SetSite(site); return S_OK; }
+    IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept { return m_site.CopyTo(riid, site); }
+
 protected:
 
     // code from https://github.com/microsoft/terminal/blob/main/src/cascadia/ShellExtension/OpenTerminalHere.cpp
@@ -220,6 +224,7 @@ protected:
         return paths;
     }
     WinMergeContextMenu* m_pContextMenu;
+    ComPtr<IUnknown> m_site;
 };
 
 class SubExplorerCommandHandler final : public WinMergeExplorerCommandBase