#include "configuration.h" #include namespace ks { bool configurationPeer(HWND window, const wchar_t *filename) { DWORD pid{}; GetWindowThreadProcessId(window, &pid); HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); if (!process) return false; wchar_t path[32768]; DWORD count = std::size(path); bool ok = QueryFullProcessImageNameW(process, 0, path, &count) != 0; CloseHandle(process); auto expected = std::filesystem::path(executable()).parent_path() / filename; return ok && _wcsicmp(path, expected.c_str()) == 0; } void launchConfiguration(App &app, bool snippets, bool logs) { auto path = std::filesystem::path(executable()).parent_path() / L"KSwitcher.Config.exe"; if (!std::filesystem::exists(path)) throw std::runtime_error("KSwitcher.Config.exe is missing. Extract the complete application package."); auto command = L"\"" + path.wstring() + L"\"" + (logs ? L" --logs" : snippets ? L" --snippets" : L""); STARTUPINFOW startup{sizeof(startup)}; PROCESS_INFORMATION process{}; if (!CreateProcessW(path.c_str(), command.data(), nullptr, nullptr, FALSE, 0, nullptr, path.parent_path().c_str(), &startup, &process)) throw std::runtime_error(utf8(lastError(L"Cannot start configurator"))); AllowSetForegroundWindow(process.dwProcessId); CloseHandle(process.hThread); CloseHandle(process.hProcess); (void)app; } LRESULT configurationRequest(App &app, HWND sender, const COPYDATASTRUCT &message) { if (!configurationPeer(sender, L"KSwitcher.Config.exe") || message.cbData > ConfigLimit || (message.cbData && !message.lpData)) return FALSE; Bytes reply; bool ok = false; try { switch (message.dwData) { case ConfigSave: { if (app.busy) throw std::runtime_error("Wait for the current text operation to finish."); if (message.cbData < sizeof(DWORD)) throw std::runtime_error("Invalid settings request."); DWORD length{}; memcpy(&length, message.lpData, sizeof(length)); if (length > message.cbData - sizeof(length)) throw std::runtime_error("Invalid settings snapshot."); auto begin = static_cast(message.lpData) + sizeof(length); if (Bytes(begin, begin + length) != encodeSettings(app.settings)) throw std::runtime_error("Settings changed in KSwitcher. Close and reopen the configurator before saving."); Bytes next(begin + length, static_cast(message.lpData) + message.cbData); if (next.empty()) throw std::runtime_error("Empty settings request."); app.applySettings(decodeSettings(next, app.layouts)); reply = encodeSettings(app.settings); break; } case ConfigLayouts: if (app.busy) throw std::runtime_error("Wait for the current text operation to finish."); app.reloadLayouts(); [[fallthrough]]; case ConfigRead: reply = encodeSettings(app.settings); break; case ConfigClearHistory: app.clearHistory(); break; case ConfigCapture: { if (message.cbData != sizeof(HWND)) throw std::runtime_error("Invalid shortcut field."); HWND field{}; memcpy(&field, message.lpData, sizeof(field)); DWORD fieldPid{}, senderPid{}; GetWindowThreadProcessId(field, &fieldPid); GetWindowThreadProcessId(sender, &senderPid); if (field && fieldPid != senderPid) throw std::runtime_error("Invalid shortcut field owner."); app.setHotkeyCapture(field); break; } case ConfigLogs: PostMessageW(app.window, WM_HOTKEY, 22, 0); break; case ConfigClearLog: if (message.cbData != 1 || *static_cast(message.lpData) > 1) throw std::runtime_error("Invalid log selection."); app.clearLog(*static_cast(message.lpData) != 0); break; default: return FALSE; } ok = true; } catch (const std::exception &error) { std::string text = error.what(); reply.assign(text.begin(), text.end()); } COPYDATASTRUCT response{ConfigReply, (DWORD)reply.size(), reply.data()}; DWORD_PTR received{}; if (!SendMessageTimeoutW(sender, WM_COPYDATA, (WPARAM)app.window, (LPARAM)&response, SMTO_ABORTIFHUNG, 2000, &received)) return FALSE; return ok ? 1 : 2; } }