Skip to content

Instantly share code, notes, and snippets.

@ekussa
Created September 29, 2023 04:34
Show Gist options
  • Save ekussa/f94c1dc570c6770c98a9d7ec50ebe218 to your computer and use it in GitHub Desktop.
Save ekussa/f94c1dc570c6770c98a9d7ec50ebe218 to your computer and use it in GitHub Desktop.
ShortcutLauncher
#include <Windows.h>
#include <time.h>
#include <iostream>
#include <set>
#include <algorithm>
#include <vector>
#include <functional>
#include <thread>
static LRESULT __stdcall HookCallback(int nCode, WPARAM wParam, LPARAM lParam);
void printer(const std::string& data)
{
std::cout << data;
}
//C++ string to WINDOWS UNICODE string
std::wstring s2ws(const std::string& s)
{
auto slength = s.length() + 1;
auto len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
void runner(const std::string& data)
{
std::thread([data]
{
//https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-startupinfoexa
STARTUPINFO info;
//https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
PROCESS_INFORMATION processInfo;
ZeroMemory(&info, sizeof(info));
info.cb = sizeof(info);
ZeroMemory(&processInfo, sizeof(processInfo));
wchar_t text[512];
std::size_t x = 0;
mbstowcs_s(&x, text, 512, data.c_str(), data.size());
if (CreateProcessW(L"cmd.exe", (LPWSTR)text, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &info, &processInfo))
{
WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
}
);
}
void printerAndRunner(const std::string& data)
{
printer(data);
runner(data);
}
class CombinationEvent
{
//https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
std::set<int32_t> _combination;
std::string _data;
const std::function<void(const std::string&)> _onMatch;
template <typename Map>
bool map_compare(Map const& lhs, Map const& rhs)
{
return lhs.size() == rhs.size() &&
std::equal(lhs.begin(), lhs.end(), rhs.begin());
}
public:
CombinationEvent(
const std::set<int32_t>& combination,
const std::string& data,
const std::function<void(const std::string&)> onMatch = printer) :
_combination(combination),
_data(data),
_onMatch(onMatch)
{
}
bool matches(const std::set<int32_t>& combination)
{
return map_compare(_combination, combination);
}
void run()
{
return _onMatch(_data);
}
};
static HHOOK _hook;
class ShortcutLauncher
{
bool _debugMode;
std::vector<CombinationEvent> _combinationEvents;
std::set<int32_t> _keysPressed;
public:
ShortcutLauncher(bool debugMode) :
_debugMode(debugMode)
{
}
void add(CombinationEvent& combinationEvent)
{
_combinationEvents.push_back(combinationEvent);
}
void activate()
{
_hook = SetWindowsHookEx(WH_KEYBOARD_LL, HookCallback, nullptr, 0);
if (_hook == nullptr)
MessageBoxA(nullptr, "Failed to install hook!", "Error", MB_ICONERROR);
}
void blockPumpingMessages()
{
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) {}
}
void deactivate()
{
UnhookWindowsHookEx(_hook);
}
bool hasNewEvent(WPARAM _event, DWORD vkCode)
{
if (_event == WM_KEYDOWN || _event == WM_SYSKEYDOWN)
_keysPressed.insert(vkCode);
if (_event == WM_KEYUP || _event == WM_SYSKEYUP)
_keysPressed.erase(vkCode);
if (_debugMode)
{
for (const auto& k : _keysPressed)
std::cout << k << " ";
std::cout << std::endl;
}
for (auto& combinationEvent : _combinationEvents)
{
if (!combinationEvent.matches(_keysPressed))
continue;
combinationEvent.run();
return true;
}
return false;
}
};
static ShortcutLauncher* _shortcutLauncher;
static LRESULT __stdcall HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0)
{
if (wParam == WM_KEYDOWN || wParam == WM_KEYUP || wParam == WM_SYSKEYDOWN || wParam == WM_SYSKEYUP)
{
auto vkCode = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)->vkCode;
bool captured = _shortcutLauncher->hasNewEvent( wParam, vkCode);
if (captured == true)
return -1;
}
}
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
int main(int argc, char* argv[])
{
bool debugMode = false;
if (argc == 2)
debugMode = std::string(argv[1]) == "Debug";
_shortcutLauncher = new ShortcutLauncher(debugMode);
std::set<int32_t> m1{ VK_LMENU, VK_SPACE };
std::string p1("C:\\Program Files\\Notepad++\\notepad++.exe");
CombinationEvent c1(m1, p1, printerAndRunner);
_shortcutLauncher->add(c1);
std::set<int32_t> m2{ VK_LCONTROL, VK_SPACE };
std::string p2("C:\\Program Files\\Notepad++\\notepad++.exe");
CombinationEvent c2(m2, p2, printerAndRunner);
_shortcutLauncher->add(c2);
_shortcutLauncher->activate();
_shortcutLauncher->blockPumpingMessages();
_shortcutLauncher->deactivate();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment