2596890212
This is me trying to merge5ff9a24
and75e2b5f
and LAWD it's impossible. But we don't really care, because we're gonna just toss out the locking from #11083 and instead move ApplicationState to the Monarch (in the future)
247 lines
7.4 KiB
C++
247 lines
7.4 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
#include "OpenTerminalHere.h"
|
|
#include "../WinRTUtils/inc/WtExeUtils.h"
|
|
#include "../WinRTUtils/inc/LibraryResources.h"
|
|
|
|
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
|
|
#include <ShlObj.h>
|
|
|
|
static constexpr std::wstring_view VerbName{ L"WindowsTerminalOpenHere" };
|
|
|
|
// This code is aggressively copied from
|
|
// https://github.com/microsoft/Windows-classic-samples/blob/master/Samples/
|
|
// Win7Samples/winui/shell/appshellintegration/ExplorerCommandVerb/ExplorerCommandVerb.cpp
|
|
|
|
// Method Description:
|
|
// - This method is called when the user activates the context menu item. We'll
|
|
// launch the Terminal using the current working directory.
|
|
// Arguments:
|
|
// - psiItemArray: a IShellItemArray which contains the item that's selected.
|
|
// Return Value:
|
|
// - S_OK if we successfully attempted to launch the Terminal, otherwise a
|
|
// failure from an earlier HRESULT.
|
|
HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
|
|
IBindCtx* /*pBindContext*/)
|
|
{
|
|
wil::unique_cotaskmem_string pszName;
|
|
|
|
if (psiItemArray == nullptr)
|
|
{
|
|
// get the current path from explorer.exe
|
|
const auto path = this->_GetPathFromExplorer();
|
|
|
|
// no go, unable to get a reasonable path
|
|
if (path.empty())
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
pszName = wil::make_cotaskmem_string(path.c_str(), path.length());
|
|
}
|
|
else
|
|
{
|
|
DWORD count;
|
|
psiItemArray->GetCount(&count);
|
|
|
|
winrt::com_ptr<IShellItem> psi;
|
|
RETURN_IF_FAILED(psiItemArray->GetItemAt(0, psi.put()));
|
|
RETURN_IF_FAILED(psi->GetDisplayName(SIGDN_FILESYSPATH, &pszName));
|
|
}
|
|
|
|
{
|
|
wil::unique_process_information _piClient;
|
|
STARTUPINFOEX siEx{ 0 };
|
|
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
|
|
|
|
auto cmdline{ wil::str_printf<std::wstring>(LR"-("%s" -d %s)-", GetWtExePath().c_str(), QuoteAndEscapeCommandlineArg(pszName.get()).c_str()) };
|
|
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
|
|
nullptr, // lpApplicationName
|
|
cmdline.data(),
|
|
nullptr, // lpProcessAttributes
|
|
nullptr, // lpThreadAttributes
|
|
false, // bInheritHandles
|
|
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
|
|
nullptr, // lpEnvironment
|
|
pszName.get(),
|
|
&siEx.StartupInfo, // lpStartupInfo
|
|
&_piClient // lpProcessInformation
|
|
));
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OpenTerminalHere::GetToolTip(IShellItemArray* /*psiItemArray*/,
|
|
LPWSTR* ppszInfoTip)
|
|
{
|
|
// tooltip provided here, in this case none is provided
|
|
*ppszInfoTip = nullptr;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT OpenTerminalHere::GetTitle(IShellItemArray* /*psiItemArray*/,
|
|
LPWSTR* ppszName)
|
|
{
|
|
// Change the string we return depending on if we're running from the dev
|
|
// build package or not.
|
|
const auto resource =
|
|
#if defined(WT_BRANDING_RELEASE)
|
|
RS_(L"ShellExtension_OpenInTerminalMenuItem");
|
|
#elif defined(WT_BRANDING_PREVIEW)
|
|
RS_(L"ShellExtension_OpenInTerminalMenuItem_Preview");
|
|
#else
|
|
RS_(L"ShellExtension_OpenInTerminalMenuItem_Dev");
|
|
#endif
|
|
return SHStrDup(resource.data(), ppszName);
|
|
}
|
|
|
|
HRESULT OpenTerminalHere::GetState(IShellItemArray* /*psiItemArray*/,
|
|
BOOL /*fOkToBeSlow*/,
|
|
EXPCMDSTATE* pCmdState)
|
|
{
|
|
// compute the visibility of the verb here, respect "fOkToBeSlow" if this is
|
|
// slow (does IO for example) when called with fOkToBeSlow == FALSE return
|
|
// E_PENDING and this object will be called back on a background thread with
|
|
// fOkToBeSlow == TRUE
|
|
|
|
// We however don't need to bother with any of that, so we'll just return
|
|
// ECS_ENABLED.
|
|
|
|
*pCmdState = ECS_ENABLED;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OpenTerminalHere::GetIcon(IShellItemArray* /*psiItemArray*/,
|
|
LPWSTR* ppszIcon)
|
|
try
|
|
{
|
|
std::filesystem::path modulePath{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
|
|
modulePath.replace_filename(WindowsTerminalExe);
|
|
// WindowsTerminal.exe,-101 will be the first icon group in WT
|
|
// We're using WindowsTerminal here explicitly, and not wt (from GetWtExePath), because
|
|
// WindowsTerminal is the only one built with the right icons.
|
|
const auto resource{ modulePath.wstring() + L",-101" };
|
|
return SHStrDupW(resource.c_str(), ppszIcon);
|
|
}
|
|
CATCH_RETURN();
|
|
|
|
HRESULT OpenTerminalHere::GetFlags(EXPCMDFLAGS* pFlags)
|
|
{
|
|
*pFlags = ECF_DEFAULT;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OpenTerminalHere::GetCanonicalName(GUID* pguidCommandName)
|
|
{
|
|
*pguidCommandName = __uuidof(this);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OpenTerminalHere::EnumSubCommands(IEnumExplorerCommand** ppEnum)
|
|
{
|
|
*ppEnum = nullptr;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
std::wstring OpenTerminalHere::_GetPathFromExplorer() const
|
|
{
|
|
using namespace std;
|
|
using namespace winrt;
|
|
|
|
wstring path;
|
|
HRESULT hr = NOERROR;
|
|
|
|
auto hwnd = ::GetForegroundWindow();
|
|
if (hwnd == nullptr)
|
|
{
|
|
return path;
|
|
}
|
|
|
|
TCHAR szName[MAX_PATH] = { 0 };
|
|
::GetClassName(hwnd, szName, MAX_PATH);
|
|
if (0 == StrCmp(szName, L"WorkerW") ||
|
|
0 == StrCmp(szName, L"Progman"))
|
|
{
|
|
//special folder: desktop
|
|
hr = ::SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, szName);
|
|
if (FAILED(hr))
|
|
{
|
|
return path;
|
|
}
|
|
|
|
path = szName;
|
|
return path;
|
|
}
|
|
|
|
if (0 != StrCmp(szName, L"CabinetWClass"))
|
|
{
|
|
return path;
|
|
}
|
|
|
|
com_ptr<IShellWindows> shell;
|
|
try
|
|
{
|
|
shell = create_instance<IShellWindows>(CLSID_ShellWindows, CLSCTX_ALL);
|
|
}
|
|
catch (...)
|
|
{
|
|
//look like try_create_instance is not available no more
|
|
}
|
|
|
|
if (shell == nullptr)
|
|
{
|
|
return path;
|
|
}
|
|
|
|
com_ptr<IDispatch> disp;
|
|
wil::unique_variant variant;
|
|
variant.vt = VT_I4;
|
|
|
|
com_ptr<IWebBrowserApp> browser;
|
|
// look for correct explorer window
|
|
for (variant.intVal = 0;
|
|
shell->Item(variant, disp.put()) == S_OK;
|
|
variant.intVal++)
|
|
{
|
|
com_ptr<IWebBrowserApp> tmp;
|
|
if (FAILED(disp->QueryInterface(tmp.put())))
|
|
{
|
|
disp = nullptr; // get rid of DEBUG non-nullptr warning
|
|
continue;
|
|
}
|
|
|
|
HWND tmpHWND = NULL;
|
|
hr = tmp->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&tmpHWND));
|
|
if (hwnd == tmpHWND)
|
|
{
|
|
browser = tmp;
|
|
disp = nullptr; // get rid of DEBUG non-nullptr warning
|
|
break; //found
|
|
}
|
|
|
|
disp = nullptr; // get rid of DEBUG non-nullptr warning
|
|
}
|
|
|
|
if (browser != nullptr)
|
|
{
|
|
wil::unique_bstr url;
|
|
hr = browser->get_LocationURL(&url);
|
|
if (FAILED(hr))
|
|
{
|
|
return path;
|
|
}
|
|
|
|
wstring sUrl(url.get(), SysStringLen(url.get()));
|
|
DWORD size = MAX_PATH;
|
|
hr = ::PathCreateFromUrl(sUrl.c_str(), szName, &size, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
path = szName;
|
|
}
|
|
}
|
|
|
|
return path;
|
|
}
|