From 9bc15f6e5558f22caa9e1cd6a042557127227793 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 01:56:45 +0000 Subject: [PATCH 1/4] Initial plan From e0db0170b07e0b3b70e4ae8b601bead517718871 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 02:01:09 +0000 Subject: [PATCH 2/4] Add Configure option to context menu with separator and localization Co-authored-by: pg94au <5140674+pg94au@users.noreply.github.com> --- .../ConvertsterContextMenuHandler.rc | Bin 3150 -> 3502 bytes .../FileContextMenuExt.cpp | 113 ++++++++++++++++-- .../FileContextMenuExt.h | 3 + ShellExtContextMenuHandler/common.h | 1 + ShellExtContextMenuHandler/resource.h | 1 + 5 files changed, 111 insertions(+), 7 deletions(-) diff --git a/ShellExtContextMenuHandler/ConvertsterContextMenuHandler.rc b/ShellExtContextMenuHandler/ConvertsterContextMenuHandler.rc index d5b0d48bd6520c15511c24dab780782a17cc8e55..92cc60d4848c8b2e832ab8aaee9539d0638317af 100644 GIT binary patch delta 289 zcmX>nu}*pe8_Q%9W&v$y27d-W1~&#z26u)~h9Cx41_dZoVsK{2XUJnnW5{GkXDDST z+WeRKC*$OIECTu@XkthOYJAW7nQ`(1HqtaEG89ez&uqAvhog&e@&OL~PW47}YAfeW TMv(t>NpWf-&_2=0Ioyu`z%Dv5 delta 43 vcmZ1{eNJKn8_Q-dmJf`Ry;!s+pJC(Myn}5E(&exePath[0]), &size) != ERROR_SUCCESS) + { + RegCloseKey(hKey); + MessageBoxW(hWnd, L"Unable to read Convertster executable path.", L_Friendly_Menu_Name, MB_OK | MB_ICONERROR); + return; + } + + exePath.resize(wcslen(exePath.c_str())); + RegCloseKey(hKey); + + // Replace the ImageConverter.exe filename with Configure.exe in the same directory. + size_t lastSlash = exePath.rfind(L'\\'); + std::wstring configurePath = (lastSlash != std::wstring::npos) + ? exePath.substr(0, lastSlash + 1) + L"Configure.exe" + : L"Configure.exe"; + + if (!PathFileExistsW(configurePath.c_str())) + { + MessageBoxW(hWnd, L"Configure.exe not found.", L_Friendly_Menu_Name, MB_OK | MB_ICONERROR); + return; + } + + std::wstring cmdLine = L"\"" + configurePath + L"\""; + std::vector cmdLineBuf(cmdLine.begin(), cmdLine.end()); + cmdLineBuf.push_back(L'\0'); + + STARTUPINFOW si = {}; + si.cb = sizeof(si); + PROCESS_INFORMATION pi = {}; + + BOOL created = CreateProcessW( + nullptr, + cmdLineBuf.data(), + nullptr, + nullptr, + FALSE, + 0, + nullptr, + nullptr, + &si, + &pi + ); + + if (!created) + { + DWORD err = GetLastError(); + wchar_t errMsg[256]; + StringCchPrintfW(errMsg, ARRAYSIZE(errMsg), L"CreateProcess failed (0x%08X).", static_cast(err)); + MessageBoxW(hWnd, errMsg, L_Friendly_Menu_Name, MB_OK | MB_ICONERROR); + return; + } + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); +} + bool FileContextMenuExt::RunConverterCommand(HWND hWnd, PCWSTR targetFormat) { // Build command-line arguments: program path first, then the format, @@ -344,9 +430,11 @@ IFACEMETHODIMP FileContextMenuExt::QueryContextMenu(HMENU hMenu, UINT indexMenu, // Load localized submenu strings into member variables int toJpgLen = LoadStringW(m_hResourceInstance, IDS_TO_JPG, m_toJpgTextBuf, ARRAYSIZE(m_toJpgTextBuf)); int toPngLen = LoadStringW(m_hResourceInstance, IDS_TO_PNG, m_toPngTextBuf, ARRAYSIZE(m_toPngTextBuf)); + int configureLen = LoadStringW(m_hResourceInstance, IDS_CONFIGURE, m_configureTextBuf, ARRAYSIZE(m_configureTextBuf)); const wchar_t* toJpgText = (toJpgLen > 0) ? m_toJpgTextBuf : L_To_JPG; const wchar_t* toPngText = (toPngLen > 0) ? m_toPngTextBuf : L_To_PNG; + const wchar_t* configureText = (configureLen > 0) ? m_configureTextBuf : L_Configure; if (!AppendMenuW(hSubMenu, MF_STRING, idCmdFirst + IDM_CONVERT_JPG, toJpgText)) { @@ -355,7 +443,6 @@ IFACEMETHODIMP FileContextMenuExt::QueryContextMenu(HMENU hMenu, UINT indexMenu, } // Add "To PNG" only if none of the selected files are already PNGs - bool pngAdded = false; if (!anyHasPng) { if (!AppendMenuW(hSubMenu, MF_STRING, idCmdFirst + IDM_CONVERT_PNG, toPngText)) @@ -363,7 +450,19 @@ IFACEMETHODIMP FileContextMenuExt::QueryContextMenu(HMENU hMenu, UINT indexMenu, DestroyMenu(hSubMenu); return HRESULT_FROM_WIN32(GetLastError()); } - pngAdded = true; + } + + // Add separator then Configure option + if (!AppendMenuW(hSubMenu, MF_SEPARATOR, 0, nullptr)) + { + DestroyMenu(hSubMenu); + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (!AppendMenuW(hSubMenu, MF_STRING, idCmdFirst + IDM_CONFIGURE, configureText)) + { + DestroyMenu(hSubMenu); + return HRESULT_FROM_WIN32(GetLastError()); } mii.hSubMenu = hSubMenu; @@ -377,11 +476,7 @@ IFACEMETHODIMP FileContextMenuExt::QueryContextMenu(HMENU hMenu, UINT indexMenu, // Return an HRESULT value with the severity set to SEVERITY_SUCCESS. // Set the code value to the offset of the largest command identifier // that was assigned, plus one (1). - USHORT largestId = static_cast(IDM_CONVERT_JPG); - if (pngAdded) - { - largestId = static_cast(IDM_CONVERT_PNG); - } + USHORT largestId = static_cast(IDM_CONFIGURE); return MAKE_HRESULT(SEVERITY_SUCCESS, 0, static_cast(largestId + 1)); } @@ -407,6 +502,10 @@ IFACEMETHODIMP FileContextMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO pCommandI { OnConvertToPng(pCommandInfo->hwnd); } + else if (LOWORD(pCommandInfo->lpVerb) == IDM_CONFIGURE) + { + OnConfigure(pCommandInfo->hwnd); + } else { return E_FAIL; diff --git a/ShellExtContextMenuHandler/FileContextMenuExt.h b/ShellExtContextMenuHandler/FileContextMenuExt.h index 3af4663..225aa58 100644 --- a/ShellExtContextMenuHandler/FileContextMenuExt.h +++ b/ShellExtContextMenuHandler/FileContextMenuExt.h @@ -10,6 +10,7 @@ enum { IDM_CONVERT_JPG = 0, IDM_CONVERT_PNG = 1, + IDM_CONFIGURE = 2, }; class FileContextMenuExt : public IShellExtInit, public IContextMenu @@ -47,10 +48,12 @@ class FileContextMenuExt : public IShellExtInit, public IContextMenu wchar_t m_menuTextBuf[256]; wchar_t m_toJpgTextBuf[128]; wchar_t m_toPngTextBuf[128]; + wchar_t m_configureTextBuf[128]; // Handlers for conversion submenu void OnConvertToJpg(HWND hWnd); void OnConvertToPng(HWND hWnd); + void OnConfigure(HWND hWnd); bool FileContextMenuExt::RunConverterCommand(HWND hWnd, PCWSTR targetFormat); diff --git a/ShellExtContextMenuHandler/common.h b/ShellExtContextMenuHandler/common.h index 405eeae..275f1c6 100644 --- a/ShellExtContextMenuHandler/common.h +++ b/ShellExtContextMenuHandler/common.h @@ -6,6 +6,7 @@ // English (default) #define L_To_JPG L"To JPG" #define L_To_PNG L"To PNG" +#define L_Configure L"Configure" #define Verb_Name "imageconverter" #define L_Verb_Name L"imageconverter" diff --git a/ShellExtContextMenuHandler/resource.h b/ShellExtContextMenuHandler/resource.h index 484290c..b874b6e 100644 --- a/ShellExtContextMenuHandler/resource.h +++ b/ShellExtContextMenuHandler/resource.h @@ -9,3 +9,4 @@ #define IDS_TO_JPG 201 #define IDS_TO_PNG 202 #define IDS_HELP_TEXT 203 +#define IDS_CONFIGURE 204 From 697680cd8c8cd7d33d3e7dbdf6a376d09c520c0c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 02:50:53 +0000 Subject: [PATCH 3/4] Use InstallPath registry value instead of string-manipulating ExecutablePath Co-authored-by: pg94au <5140674+pg94au@users.noreply.github.com> --- Installer/Installer.iss | 1 + .../FileContextMenuExt.cpp | 27 ++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Installer/Installer.iss b/Installer/Installer.iss index a5fd514..3385f5b 100644 --- a/Installer/Installer.iss +++ b/Installer/Installer.iss @@ -96,6 +96,7 @@ Name: "{commonstartmenu}\Programs\{#MyAppName}\{cm:ConfigureShortcutName}"; File [Registry] Root: HKLM; Subkey: "Software\Convertster"; ValueType: string; ValueName: "ExecutablePath"; ValueData: "{app}\ImageConverter.exe"; Flags: uninsdeletekey +Root: HKLM; Subkey: "Software\Convertster"; ValueType: string; ValueName: "InstallPath"; ValueData: "{app}"; Flags: uninsdeletekey Root: HKCU; Subkey: "Software\Convertster"; Flags: uninsdeletekey [Code] diff --git a/ShellExtContextMenuHandler/FileContextMenuExt.cpp b/ShellExtContextMenuHandler/FileContextMenuExt.cpp index e835eae..323f819 100644 --- a/ShellExtContextMenuHandler/FileContextMenuExt.cpp +++ b/ShellExtContextMenuHandler/FileContextMenuExt.cpp @@ -52,7 +52,7 @@ void FileContextMenuExt::OnConvertToPng(HWND hWnd) void FileContextMenuExt::OnConfigure(HWND hWnd) { - std::wstring exePath; + std::wstring installPath; HKEY hKey = nullptr; LONG result = RegOpenKeyExW( @@ -71,30 +71,33 @@ void FileContextMenuExt::OnConfigure(HWND hWnd) DWORD type = 0; DWORD size = 0; - if (RegQueryValueExW(hKey, L"ExecutablePath", nullptr, &type, nullptr, &size) != ERROR_SUCCESS || type != REG_SZ) + if (RegQueryValueExW(hKey, L"InstallPath", nullptr, &type, nullptr, &size) != ERROR_SUCCESS || type != REG_SZ) { RegCloseKey(hKey); - MessageBoxW(hWnd, L"Unable to read Convertster executable path.", L_Friendly_Menu_Name, MB_OK | MB_ICONERROR); + MessageBoxW(hWnd, L"Unable to read Convertster install path.", L_Friendly_Menu_Name, MB_OK | MB_ICONERROR); return; } - exePath.resize(size / sizeof(wchar_t), L'\0'); + installPath.resize(size / sizeof(wchar_t), L'\0'); - if (RegQueryValueExW(hKey, L"ExecutablePath", nullptr, nullptr, reinterpret_cast(&exePath[0]), &size) != ERROR_SUCCESS) + if (RegQueryValueExW(hKey, L"InstallPath", nullptr, nullptr, reinterpret_cast(&installPath[0]), &size) != ERROR_SUCCESS) { RegCloseKey(hKey); - MessageBoxW(hWnd, L"Unable to read Convertster executable path.", L_Friendly_Menu_Name, MB_OK | MB_ICONERROR); + MessageBoxW(hWnd, L"Unable to read Convertster install path.", L_Friendly_Menu_Name, MB_OK | MB_ICONERROR); return; } - exePath.resize(wcslen(exePath.c_str())); + installPath.resize(wcslen(installPath.c_str())); RegCloseKey(hKey); - // Replace the ImageConverter.exe filename with Configure.exe in the same directory. - size_t lastSlash = exePath.rfind(L'\\'); - std::wstring configurePath = (lastSlash != std::wstring::npos) - ? exePath.substr(0, lastSlash + 1) + L"Configure.exe" - : L"Configure.exe"; + // Strip any trailing backslash before appending the filename. + if (!installPath.empty() && installPath.back() == L'\\') + { + installPath.pop_back(); + } + + // Build path to Configure.exe in the install directory. + std::wstring configurePath = installPath + L"\\Configure.exe"; if (!PathFileExistsW(configurePath.c_str())) { From 3c7213494ac15115c3e640b0deb9897cd2c77e6d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 23:40:52 +0000 Subject: [PATCH 4/4] Remove ExecutablePath registry entry; use InstallPath for both exe paths Co-authored-by: pg94au <5140674+pg94au@users.noreply.github.com> --- Installer/Installer.iss | 1 - .../FileContextMenuExt.cpp | 21 +++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Installer/Installer.iss b/Installer/Installer.iss index 3385f5b..22dee3b 100644 --- a/Installer/Installer.iss +++ b/Installer/Installer.iss @@ -95,7 +95,6 @@ Source: "Prerequisites\VC_redist.x64.exe"; DestDir: "{tmp}"; Flags: dontcopy Name: "{commonstartmenu}\Programs\{#MyAppName}\{cm:ConfigureShortcutName}"; Filename: "{app}\Configure.exe"; Comment: "{cm:ConfigureShortcutName}" [Registry] -Root: HKLM; Subkey: "Software\Convertster"; ValueType: string; ValueName: "ExecutablePath"; ValueData: "{app}\ImageConverter.exe"; Flags: uninsdeletekey Root: HKLM; Subkey: "Software\Convertster"; ValueType: string; ValueName: "InstallPath"; ValueData: "{app}"; Flags: uninsdeletekey Root: HKCU; Subkey: "Software\Convertster"; Flags: uninsdeletekey diff --git a/ShellExtContextMenuHandler/FileContextMenuExt.cpp b/ShellExtContextMenuHandler/FileContextMenuExt.cpp index 323f819..e54db6f 100644 --- a/ShellExtContextMenuHandler/FileContextMenuExt.cpp +++ b/ShellExtContextMenuHandler/FileContextMenuExt.cpp @@ -143,7 +143,7 @@ bool FileContextMenuExt::RunConverterCommand(HWND hWnd, PCWSTR targetFormat) { // Build command-line arguments: program path first, then the format, // then each filename quoted. - std::wstring exePath; + std::wstring installPath; HKEY hKey = nullptr; LONG result = RegOpenKeyExW( @@ -164,7 +164,7 @@ bool FileContextMenuExt::RunConverterCommand(HWND hWnd, PCWSTR targetFormat) if (RegQueryValueExW( hKey, - L"ExecutablePath", + L"InstallPath", nullptr, &type, nullptr, @@ -174,14 +174,14 @@ bool FileContextMenuExt::RunConverterCommand(HWND hWnd, PCWSTR targetFormat) return false; } - exePath.resize(size / sizeof(wchar_t), L'\0'); + installPath.resize(size / sizeof(wchar_t), L'\0'); if (RegQueryValueExW( hKey, - L"ExecutablePath", + L"InstallPath", nullptr, nullptr, - reinterpret_cast(&exePath[0]), + reinterpret_cast(&installPath[0]), &size) != ERROR_SUCCESS) { RegCloseKey(hKey); @@ -189,10 +189,19 @@ bool FileContextMenuExt::RunConverterCommand(HWND hWnd, PCWSTR targetFormat) } // Remove trailing null added by registry - exePath.resize(wcslen(exePath.c_str())); + installPath.resize(wcslen(installPath.c_str())); RegCloseKey(hKey); + // Strip any trailing backslash before appending the filename. + if (!installPath.empty() && installPath.back() == L'\\') + { + installPath.pop_back(); + } + + // Build path to ImageConverter.exe in the install directory. + std::wstring exePath = installPath + L"\\ImageConverter.exe"; + // Verify executable exists. if (!PathFileExistsW(exePath.c_str())) {