Skip to content

Instantly share code, notes, and snippets.

@Helios-vmg
Created October 17, 2021 08:05
Show Gist options
  • Save Helios-vmg/940a977ceb687b0babf9b9811f11cd3d to your computer and use it in GitHub Desktop.
Save Helios-vmg/940a977ceb687b0babf9b9811f11cd3d to your computer and use it in GitHub Desktop.
#include <iostream>
#include <string>
#include <vector>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <functional>
#include <Windows.h>
typedef std::function<bool(HWND)> enumerate_windows_cb;
static BOOL CALLBACK EnumWindows_cb(HWND handle, LPARAM param){
auto &cb = *(const enumerate_windows_cb *)param;
return cb(handle);
}
void enumerate_windows(const enumerate_windows_cb &cb){
if (!EnumWindows(EnumWindows_cb, (LPARAM)&cb)){
auto error = GetLastError();
if (!error)
return;
std::stringstream stream;
stream << "EnumWindows() failed with error " << error;
throw std::runtime_error(stream.str());
}
}
std::string read_string(){
std::string ret;
std::getline(std::cin, ret);
return ret;
}
std::optional<int> read_number(){
auto input = read_string();
std::stringstream stream(input);
int ret;
if (!(stream >> ret))
return {};
return ret;
}
std::wstring to_wstring(const std::string &s){
std::wstring ret;
ret.reserve(s.size());
for (auto c : s)
ret += (wchar_t)c;
return ret;
}
std::string to_string(const std::wstring &s){
std::string ret;
ret.reserve(s.size());
for (auto c : s)
ret += c < 0x80 ? (char)c : '?';
return ret;
}
std::wstring get_window_text(HWND handle){
std::wstring ret(8192, 0);
auto length = GetWindowTextW(handle, &ret[0], ret.size());
if (!length){
auto error = GetLastError();
if (!error)
return {};
std::stringstream stream;
stream << "GetWindowTextW() failed with error " << error;
throw std::runtime_error(stream.str());
}
ret.resize(length);
ret.shrink_to_fit();
return ret;
}
bool matches(const std::wstring &text, const std::wstring &pattern){
if (text.size() < pattern.size())
return false;
auto n = text.size() - pattern.size() + 1;
for (size_t i = 0; i < n; i++){
bool match = true;
for (size_t j = 0; j < pattern.size(); j++){
if (tolower(text[i + j]) != tolower(pattern[j])){
match = false;
break;
}
}
if (match)
return true;
}
return false;
}
class Window{
public:
HWND handle;
std::wstring title;
DWORD pid;
Window() = default;
Window(const Window &) = default;
Window &operator=(const Window &) = default;
Window(Window &&other){
*this = std::move(other);
}
Window &operator=(Window &&other){
this->handle = other.handle;
this->title = std::move(other.title);
this->pid = other.pid;
return *this;
}
Window(HWND handle){
this->handle = handle;
this->title = get_window_text(handle);
GetWindowThreadProcessId(this->handle, &this->pid);
}
};
void bring_to_front(HWND handle){
WINDOWPLACEMENT placement;
if (!GetWindowPlacement(handle, &placement)){
auto error = GetLastError();
std::stringstream stream;
stream << "GetWindowPlacement() failed with error " << error;
throw std::runtime_error(stream.str());
}
int state;
switch (placement.showCmd){
case SW_SHOWMINIMIZED:
state = SW_RESTORE;
break;
case SW_SHOWMAXIMIZED:
state = SW_SHOWMAXIMIZED;
break;
default:
state = SW_NORMAL;
break;
}
if (!ShowWindow(handle, state)){
auto error = GetLastError();
std::stringstream stream;
std::cerr << "ShowWindow() failed with error " << error << ". Will continue anyway.\n";
}
if (!SetForegroundWindow(handle)){
auto error = GetLastError();
std::stringstream stream;
stream << "SetForegroundWindow() failed with error " << error;
throw std::runtime_error(stream.str());
}
}
std::optional<Window> find_window(const std::wstring &query){
std::vector<Window> non_matching_windows;
std::optional<Window> ret;
enumerate_windows([&query, &ret, &non_matching_windows](HWND handle){
Window window(handle);
if (matches(window.title, query)){
ret = std::move(window);
return false;
}
non_matching_windows.emplace_back(std::move(window));
return true;
});
if (!ret){
std::cout << "Window not found. The following titles did not match the query:\n";
for (auto &w : non_matching_windows)
std::cout << to_string(w.title) << std::endl;
return {};
}
return ret;
}
void operate_one_window(){
std::cout << "Please enter a string to match against: ";
auto query = to_wstring(read_string());
auto o = find_window(query);
if (!o)
return;
auto window = std::move(*o);
std::cout <<
"Window found: " << to_string(window.title) << " PID " << window.pid << "\n"
"Press ENTER to bring it to front.\n";
read_string();
bring_to_front(window.handle);
}
void operate_two_windows(){
std::cout << "Please enter a string to match against: ";
auto o = find_window(to_wstring(read_string()));
if (!o)
return;
auto window1 = std::move(*o);
std::cout <<
"Window found: " << to_string(window1.title) << " PID " << window1.pid << "\n"
"Please enter another string to match against: ";
o = find_window(to_wstring(read_string()));
if (!o)
return;
auto window2 = std::move(*o);
std::cout <<
"Window found: " << to_string(window2.title) << " PID " << window2.pid << "\n"
"Press ENTER to start.\n";
read_string();
for (int i = 0; i < 5; i++){
bring_to_front(window1.handle);
Sleep(1000);
bring_to_front(window2.handle);
Sleep(1000);
}
}
int main(){
try{
int mode;
while (true){
std::cout <<
"Select operation mode:\n"
"1. Bring one window to front.\n"
"2. Repeatedly switch between two windows.\n";
auto input = read_number();
if (!input){
std::cerr << "You must enter a number.\n";
continue;
}
mode = *input;
if (mode < 1 || mode > 2){
std::cerr << "You must enter either 1 or 2.\n";
continue;
}
break;
}
if (mode == 1)
operate_one_window();
else
operate_two_windows();
}catch (std::exception &e){
std::cerr << "Exception caught: " << e.what() << std::endl;
return -1;
}catch (...){
std::cerr << "Unknown exception caught.\n";
return -1;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment