Files
PowerToys/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp
Niels Laute fc214a80c5 [ContextMenus]Update context menu labels and improve localization calls (#31000)
* Update file locksmith

* ImageResizer

* Update imageresizer context menu

* Changes

* [PowerRename]Don't use app name for context menu caption

* [ImageResizer]Apply string to old context menu too

* Add localization to the PowerRenameContextMenu project

* Show actual context menu string in PowerRename

* New Ids for the resource strings

* Add do not loc comments

* Add fallback and cache resource values

* Update src/modules/imageresizer/dll/ContextMenuHandler.cpp

Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>

* Remove do not loc comments from Image Resizer

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>
2024-01-25 12:27:25 +00:00

293 lines
9.3 KiB
C++

// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <atlfile.h>
#include <atlstr.h>
#include <Shlwapi.h>
#include <shobjidl_core.h>
#include <string>
#include <common/utils/elevation.h>
#include <common/utils/process_path.h>
#include <common/utils/resources.h>
#include <Settings.h>
#include <trace.h>
#include <wil/win32_helpers.h>
#include <wrl/module.h>
#include "Generated Files/resource.h"
using namespace Microsoft::WRL;
HINSTANCE g_hInst = 0;
#define BUFSIZE 4096 * 4
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInst = hModule;
Trace::RegisterProvider();
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
class __declspec(uuid("8F491918-259F-451A-950F-8C3EBF4864AF")) ImageResizerContextMenuCommand final : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand, IObjectWithSite>
{
public:
virtual const wchar_t* Title() { return L"Image Resizer"; }
virtual const EXPCMDFLAGS Flags() { return ECF_DEFAULT; }
virtual const EXPCMDSTATE State(_In_opt_ IShellItemArray* selection) { return ECS_ENABLED; }
// IExplorerCommand
IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name)
{
return SHStrDup(context_menu_caption.c_str(), name);
}
IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon)
{
std::wstring iconResourcePath = get_module_folderpath(g_hInst);
iconResourcePath += L"\\Assets\\ImageResizer\\";
iconResourcePath += L"ImageResizer.ico";
return SHStrDup(iconResourcePath.c_str(), icon);
}
IFACEMETHODIMP GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* infoTip)
{
*infoTip = nullptr;
return E_NOTIMPL;
}
IFACEMETHODIMP GetCanonicalName(_Out_ GUID* guidCommandName)
{
*guidCommandName = __uuidof(this);
return S_OK;
}
IFACEMETHODIMP GetState(_In_opt_ IShellItemArray* selection, _In_ BOOL okToBeSlow, _Out_ EXPCMDSTATE* cmdState)
{
if (nullptr == selection) {
// We've observed that it's possible that a null gets passed instead of an empty array. Just don't show the context menu in this case.
*cmdState = ECS_HIDDEN;
return S_OK;
}
if (!CSettingsInstance().GetEnabled())
{
*cmdState = ECS_HIDDEN;
return S_OK;
}
// Hide if the file is not an image
*cmdState = ECS_HIDDEN;
// Suppressing C26812 warning as the issue is in the shtypes.h library
#pragma warning(suppress : 26812)
PERCEIVED type;
PERCEIVEDFLAG flag;
IShellItem* shellItem=nullptr;
//Check extension of first item in the list (the item which is right-clicked on)
HRESULT getItemResult = selection->GetItemAt(0, &shellItem);
if (S_OK != getItemResult || nullptr == shellItem) {
// Some safeguards to avoid runtime errors.
*cmdState = ECS_HIDDEN;
return S_OK;
}
LPTSTR pszPath;
// Retrieves the entire file system path of the file from its shell item
HRESULT getDisplayResult = shellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
if (S_OK != getDisplayResult || nullptr == pszPath)
{
// Avoid crashes in the following code.
return E_FAIL;
}
LPTSTR pszExt = PathFindExtension(pszPath);
if (nullptr == pszExt)
{
CoTaskMemFree(pszPath);
// Avoid crashes in the following code.
return E_FAIL;
}
// TODO: Instead, detect whether there's a WIC codec installed that can handle this file
AssocGetPerceivedType(pszExt, &type, &flag, NULL);
CoTaskMemFree(pszPath);
// If selected file is an image...
if (type == PERCEIVED_TYPE_IMAGE)
{
*cmdState = ECS_ENABLED;
}
return S_OK;
}
IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept
try
{
Trace::Invoked();
HRESULT hr = S_OK;
if (selection)
{
hr = ResizePictures(selection);
}
Trace::InvokedRet(hr);
return hr;
}
CATCH_RETURN();
IFACEMETHODIMP GetFlags(_Out_ EXPCMDFLAGS* flags)
{
*flags = Flags();
return S_OK;
}
IFACEMETHODIMP EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** enumCommands)
{
*enumCommands = nullptr;
return E_NOTIMPL;
}
// IObjectWithSite
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept
{
m_site = site;
return S_OK;
}
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept { return m_site.CopyTo(riid, site); }
protected:
ComPtr<IUnknown> m_site;
private:
HRESULT StartNamedPipeServerAndSendData(std::wstring pipe_name)
{
hPipe = CreateNamedPipe(
pipe_name.c_str(),
PIPE_ACCESS_DUPLEX |
WRITE_DAC,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
BUFSIZE,
BUFSIZE,
0,
NULL);
if (hPipe == NULL || hPipe == INVALID_HANDLE_VALUE)
{
return E_FAIL;
}
// This call blocks until a client process connects to the pipe
BOOL connected = ConnectNamedPipe(hPipe, NULL);
if (!connected)
{
if (GetLastError() == ERROR_PIPE_CONNECTED)
{
return S_OK;
}
else
{
CloseHandle(hPipe);
}
return E_FAIL;
}
return S_OK;
}
HRESULT ResizePictures(IShellItemArray* psiItemArray)
{
// Set the application path based on the location of the dll
std::wstring path = get_module_folderpath(g_hInst);
path = path + L"\\PowerToys.ImageResizer.exe";
std::wstring pipe_name(L"\\\\.\\pipe\\powertoys_imageresizerinput_");
UUID temp_uuid;
wchar_t* uuid_chars = nullptr;
if (UuidCreate(&temp_uuid) == RPC_S_UUID_NO_ADDRESS)
{
auto val = get_last_error_message(GetLastError());
Logger::warn(L"UuidCreate can not create guid. {}", val.has_value() ? val.value() : L"");
}
else if (UuidToString(&temp_uuid, reinterpret_cast<RPC_WSTR*>(& uuid_chars)) != RPC_S_OK)
{
auto val = get_last_error_message(GetLastError());
Logger::warn(L"UuidToString can not convert to string. {}", val.has_value() ? val.value() : L"");
}
if (uuid_chars != nullptr)
{
pipe_name += std::wstring(uuid_chars);
RpcStringFree(reinterpret_cast<RPC_WSTR*>(&uuid_chars));
uuid_chars = nullptr;
}
create_pipe_thread = std::thread(&ImageResizerContextMenuCommand::StartNamedPipeServerAndSendData, this, pipe_name);
RunNonElevatedEx(path.c_str(), pipe_name, get_module_folderpath(g_hInst));
create_pipe_thread.join();
if (hPipe != INVALID_HANDLE_VALUE)
{
CAtlFile writePipe(hPipe);
//m_pdtobj will be NULL when invoked from the MSIX build as Initialize is never called (IShellExtInit functions aren't called in case of MSIX).
DWORD fileCount = 0;
// Gets the list of files currently selected using the IShellItemArray
psiItemArray->GetCount(&fileCount);
// Iterate over the list of files
for (DWORD i = 0; i < fileCount; i++)
{
IShellItem* shellItem;
psiItemArray->GetItemAt(i, &shellItem);
LPWSTR itemName;
// Retrieves the entire file system path of the file from its shell item
shellItem->GetDisplayName(SIGDN_FILESYSPATH, &itemName);
CString fileName(itemName);
fileName.Append(_T("\r\n"));
// Write the file path into the input stream for image resizer
writePipe.Write(fileName, fileName.GetLength() * sizeof(TCHAR));
}
writePipe.Close();
}
return S_OK;
}
std::thread create_pipe_thread;
HANDLE hPipe = INVALID_HANDLE_VALUE;
std::wstring context_menu_caption = GET_RESOURCE_STRING_FALLBACK(IDS_IMAGERESIZER_CONTEXT_MENU_ENTRY, L"Resize with Image Resizer");
};
CoCreatableClass(ImageResizerContextMenuCommand)
CoCreatableClassWrlCreatorMapInclude(ImageResizerContextMenuCommand)
STDAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory** factory)
{
return Module<ModuleType::InProc>::GetModule().GetActivationFactory(activatableClassId, factory);
}
STDAPI DllCanUnloadNow()
{
return Module<InProc>::GetModule().GetObjectCount() == 0 ? S_OK : S_FALSE;
}
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _COM_Outptr_ void** instance)
{
return Module<InProc>::GetModule().GetClassObject(rclsid, riid, instance);
}