diff --git a/Installer/Installer.iss b/Installer/Installer.iss index a5fd514..22dee3b 100644 --- a/Installer/Installer.iss +++ b/Installer/Installer.iss @@ -95,7 +95,7 @@ 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 [Code] diff --git a/ShellExtContextMenuHandler/ConvertsterContextMenuHandler.rc b/ShellExtContextMenuHandler/ConvertsterContextMenuHandler.rc index d5b0d48..92cc60d 100644 Binary files a/ShellExtContextMenuHandler/ConvertsterContextMenuHandler.rc and b/ShellExtContextMenuHandler/ConvertsterContextMenuHandler.rc differ diff --git a/ShellExtContextMenuHandler/FileContextMenuExt.cpp b/ShellExtContextMenuHandler/FileContextMenuExt.cpp index 6f12919..e54db6f 100644 --- a/ShellExtContextMenuHandler/FileContextMenuExt.cpp +++ b/ShellExtContextMenuHandler/FileContextMenuExt.cpp @@ -50,11 +50,100 @@ void FileContextMenuExt::OnConvertToPng(HWND hWnd) } } +void FileContextMenuExt::OnConfigure(HWND hWnd) +{ + std::wstring installPath; + HKEY hKey = nullptr; + + LONG result = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + L"Software\\Convertster", + 0, + KEY_READ, + &hKey); + + if (result != ERROR_SUCCESS) + { + MessageBoxW(hWnd, L"Unable to open registry key for Convertster.", L_Friendly_Menu_Name, MB_OK | MB_ICONERROR); + return; + } + + DWORD type = 0; + DWORD size = 0; + + if (RegQueryValueExW(hKey, L"InstallPath", nullptr, &type, nullptr, &size) != ERROR_SUCCESS || type != REG_SZ) + { + RegCloseKey(hKey); + MessageBoxW(hWnd, L"Unable to read Convertster install path.", L_Friendly_Menu_Name, MB_OK | MB_ICONERROR); + return; + } + + installPath.resize(size / sizeof(wchar_t), L'\0'); + + if (RegQueryValueExW(hKey, L"InstallPath", nullptr, nullptr, reinterpret_cast(&installPath[0]), &size) != ERROR_SUCCESS) + { + RegCloseKey(hKey); + MessageBoxW(hWnd, L"Unable to read Convertster install path.", L_Friendly_Menu_Name, MB_OK | MB_ICONERROR); + return; + } + + 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 Configure.exe in the install directory. + std::wstring configurePath = installPath + 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, // then each filename quoted. - std::wstring exePath; + std::wstring installPath; HKEY hKey = nullptr; LONG result = RegOpenKeyExW( @@ -75,7 +164,7 @@ bool FileContextMenuExt::RunConverterCommand(HWND hWnd, PCWSTR targetFormat) if (RegQueryValueExW( hKey, - L"ExecutablePath", + L"InstallPath", nullptr, &type, nullptr, @@ -85,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); @@ -100,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())) { @@ -344,9 +442,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 +455,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 +462,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 +488,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 +514,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