Skip to content

Instantly share code, notes, and snippets.

@caiorss
Forked from yak1ex/AboutDlg.cpp
Created August 20, 2018 15:24
Show Gist options
  • Save caiorss/df9117214949a06a39633326ada3b268 to your computer and use it in GitHub Desktop.
Save caiorss/df9117214949a06a39633326ada3b268 to your computer and use it in GitHub Desktop.
MX FLOW
#include <windows.h>
#include <Strsafe.h>
#include <vector>
#include <boost/align/aligned_allocator.hpp>
typedef boost::alignment::aligned_allocator<void, 4> aligned_allocator;
class buffer
{
public:
buffer() : buf(aligned_allocator()) { buf.reserve(100); }
void align(std::size_t size)
{
std::size_t pad = size - buf.size() % size;
if(pad != size) buf.insert(buf.end(), pad , 0);
}
template<typename T>
void add(const T& t)
{
auto st = static_cast<const char*>(static_cast<const void*>(&t));
buf.insert(buf.end(), st, st + sizeof(T));
}
void add(LPCWSTR lp)
{
std::size_t len = lstrlenW(lp);
auto st = static_cast<const char*>(static_cast<const void*>(lp));
buf.insert(buf.end(), st, st+(len+1)*sizeof(WCHAR));
}
void add(const std::wstring& w)
{
auto st = static_cast<const char*>(static_cast<const void*>(w.data()));
buf.insert(buf.end(), st, st+(w.size()+1)*sizeof(WCHAR));
}
const char* data() const { return buf.data(); }
std::size_t size() const { return buf.size(); }
private:
std::vector<char, aligned_allocator> buf;
};
static POINT GetPos(POINT pt, const RECT &rcDlg, const RECT &rcWork)
{
if(pt.x + rcDlg.right > rcWork.right) {
pt.x = rcWork.right - rcDlg.right;
}
if(pt.x < rcWork.left) {
pt.x = rcWork.left;
}
if(pt.y + rcDlg.bottom > rcWork.bottom) {
pt.y = rcWork.bottom - rcDlg.bottom;
}
if(pt.y < rcWork.top) {
pt.y = rcWork.top;
}
return pt;
}
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_INITDIALOG: {
POINT pt;
RECT rc;
if(GetWindowRect(hwndDlg, &rc) && GetCursorPos(&pt)) {
if(HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
MONITORINFO mi = { sizeof(MONITORINFO) };
if(GetMonitorInfo(hm, &mi)) {
POINT ptPlace = GetPos(pt, rc, mi.rcWork);
SetWindowPos(hwndDlg, HWND_TOP, ptPlace.x, ptPlace.y, rc.right - rc.left, rc.bottom - rc.top, 0);
}
}
}
return TRUE;
}
case WM_COMMAND:
switch(LOWORD(wParam)) {
case IDOK:
EndDialog(hwndDlg, 0);
break;
}
break;
case WM_CLOSE:
EndDialog(hwndDlg, 0);
return TRUE;
}
return FALSE;
}
struct VerStr
{
std::wstring wsProductName;
std::wstring wsProductVersion;
std::wstring wsFileDescription;
std::wstring wsLegalCopyright;
};
static VerStr GetVerStr(HINSTANCE hInst)
{
char buf[2048];
GetModuleFileName(hInst, buf, sizeof(buf));
DWORD dwSize = GetFileVersionInfoSize(buf, 0);
std::vector<char> v(dwSize);
GetFileVersionInfo(buf, 0, dwSize, v.data());
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
UINT uiLen;
VerQueryValue(v.data(), "\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &uiLen);
UINT uiIdx = 0, uiPrior = 0;
for(UINT i = 1; i < uiLen / sizeof(*lpTranslate); ++i) {
UINT uiTemp = 0;
if(lpTranslate[i].wLanguage == 0x0411 && lpTranslate[i].wCodePage == 0x03A4) { // JP, SJIS
uiTemp = 4;
} else if(lpTranslate[i].wLanguage == 0x0411) { // JP
uiTemp = 3;
} else if(lpTranslate[i].wLanguage == 0x0409 && lpTranslate[i].wCodePage == 0x04E4) { // ENG, ANSI
uiTemp = 2;
} else if(lpTranslate[i].wLanguage == 0x0409) { // ENG
uiTemp = 1;
}
if(uiTemp > uiPrior) {
uiTemp = uiPrior;
uiIdx = i;
}
}
WCHAR wbuf[128];
LPWSTR lp;
UINT uiSize;
VerStr vsRet;
StringCbPrintfW(wbuf, sizeof(wbuf), L"\\StringFileInfo\\%04x%04x\\ProductName", lpTranslate[uiIdx].wLanguage, lpTranslate[uiIdx].wCodePage);
VerQueryValueW(v.data(), wbuf, (LPVOID*)&lp, &uiSize);
vsRet.wsProductName.assign(lp);
std::wstring wsProductName(lp);
StringCbPrintfW(wbuf, sizeof(wbuf), L"\\StringFileInfo\\%04x%04x\\ProductVersion", lpTranslate[uiIdx].wLanguage, lpTranslate[uiIdx].wCodePage);
VerQueryValueW(v.data(), wbuf, (LPVOID*)&lp, &uiSize);
vsRet.wsProductVersion.assign(lp);
StringCbPrintfW(wbuf, sizeof(wbuf), L"\\StringFileInfo\\%04x%04x\\FileDescription", lpTranslate[uiIdx].wLanguage, lpTranslate[uiIdx].wCodePage);
VerQueryValueW(v.data(), wbuf, (LPVOID*)&lp, &uiSize);
vsRet.wsFileDescription.assign(lp);
StringCbPrintfW(wbuf, sizeof(wbuf), L"\\StringFileInfo\\%04x%04x\\LegalCopyright", lpTranslate[uiIdx].wLanguage, lpTranslate[uiIdx].wCodePage);
VerQueryValueW(v.data(), wbuf, (LPVOID*)&lp, &uiSize);
vsRet.wsLegalCopyright.assign(lp);
return std::move(vsRet);
}
// TODO: size adjustment
static buffer MakeBuffer(const VerStr& vs)
{
buffer buffer;
// DLGTEMPLATEEX
buffer.add<WORD>(1); // dlgVer
buffer.add<WORD>(0xFFFF); // signature
buffer.add<DWORD>(0); // helpID
buffer.add<DWORD>(0); // exStyle
buffer.add<DWORD>(DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU);
// style
buffer.add<WORD>(4); // cDlgItems
buffer.add<short>(0); // x
buffer.add<short>(0); // y
buffer.add<short>(140); // cx
buffer.add<short>(80); // cy
buffer.add<WORD>(0); // menu
buffer.add<WORD>(0); // windowClass
buffer.add(L"About"); // title
buffer.add<WORD>(8); // pointsize
buffer.add<WORD>(400); // weight
buffer.add<BYTE>(0); // italic
buffer.add<BYTE>(1); // charset
buffer.add(L"MS Shell Dlg");
// typeface
// DLGITEMTEMPLATEEX
buffer.align(4);
buffer.add<DWORD>(0); // helpID
buffer.add<DWORD>(0); // exStyle
buffer.add<DWORD>(SS_LEFT | WS_VISIBLE);
// style
buffer.add<short>(10); // x
buffer.add<short>(10); // y
buffer.add<short>(120); // cx
buffer.add<short>(7); // cy
buffer.add<DWORD>(-1); // id
buffer.add<WORD>(0xFFFF); // windowClass
buffer.add<WORD>(0x0082);
buffer.add(vs.wsProductName + L" " + vs.wsProductVersion);
// title
buffer.add<WORD>(0); // extraCount
buffer.align(4);
buffer.add<DWORD>(0); // helpID
buffer.add<DWORD>(0); // exStyle
buffer.add<DWORD>(SS_LEFT | WS_VISIBLE);
// style
buffer.add<short>(10); // x
buffer.add<short>(30); // y
buffer.add<short>(120); // cx
buffer.add<short>(7); // cy
buffer.add<DWORD>(-1); // id
buffer.add<WORD>(0xFFFF); // windowClass
buffer.add<WORD>(0x0082);
buffer.add(vs.wsFileDescription);
// title
buffer.add<WORD>(0); // extraCount
buffer.align(4);
buffer.add<DWORD>(0); // helpID
buffer.add<DWORD>(0); // exStyle
buffer.add<DWORD>(SS_LEFT | WS_VISIBLE);
// style
buffer.add<short>(10); // x
buffer.add<short>(40); // y
buffer.add<short>(120); // cx
buffer.add<short>(7); // cy
buffer.add<DWORD>(-1); // id
buffer.add<WORD>(0xFFFF); // windowClass
buffer.add<WORD>(0x0082);
buffer.add(vs.wsLegalCopyright);
// title
buffer.add<WORD>(0); // extraCount
buffer.align(4);
buffer.add<DWORD>(0); // helpID
buffer.add<DWORD>(0); // exStyle
buffer.add<DWORD>(BS_DEFPUSHBUTTON | WS_TABSTOP | WS_VISIBLE);
// style
buffer.add<short>(45); // x
buffer.add<short>(60); // y
buffer.add<short>(50); // cx
buffer.add<short>(14); // cy
buffer.add<DWORD>(IDOK); // id
buffer.add<WORD>(0xFFFF); // windowClass
buffer.add<WORD>(0x0080);
buffer.add(L"OK"); // title
buffer.add<WORD>(0); // extraCount
return std::move(buffer);
}
// TODO: icon control
INT_PTR AboutDialogBox(HINSTANCE hInst, HWND hwndParent)
{
auto vs = GetVerStr(hInst);
auto buf = MakeBuffer(vs);
DialogBoxIndirect(hInst, reinterpret_cast<LPCDLGTEMPLATE>(buf.data()), 0, DialogProc);
return 0;
}
#ifndef ABOUTDLG_H
#define ABOUTDLG_H
INT_PTR AboutDialogBox(HINSTANCE hInst, HWND hwndParent);
#endif
//
// client.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::udp;
int main(int argc, char* argv[])
{
if(argc < 1) return 1;
try
{
boost::asio::io_service io_service;
udp::resolver resolver(io_service);
udp::resolver::query query(udp::v4(), argv[1], "59865");
udp::endpoint receiver_endpoint = *resolver.resolve(query);
udp::socket socket(io_service);
socket.open(udp::v4());
std::string send_buf(argc > 2 ? "6b01d_2de54084:1" : "6b01d_2de54084:0");
socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);
boost::array<char, 128> recv_buf;
udp::endpoint sender_endpoint;
size_t len = socket.receive_from(
boost::asio::buffer(recv_buf), sender_endpoint);
std::cout.write(recv_buf.data(), len);
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
#include <fstream>
#include <string>
#include <array>
#include <regex>
#include <filesystem>
namespace fs = std::experimental::filesystem;
#include <boost/asio.hpp>
using boost::asio::ip::udp;
#include <windows.h>
#include <windowsx.h>
#include <shlobj.h>
#include <wtsapi32.h>
#include <Shellapi.h>
#include <Strsafe.h>
#include <cstdarg>
#include "notify.h"
#include "AboutDlg.h"
#include "FlowCompanion.rh"
// FIXME: tip state
// TODO: log window
enum class mode {
ENABLE, DISABLE, CANCEL
};
char* to_string(mode m)
{
switch(m) {
case mode::ENABLE: return "ENABLE";
case mode::DISABLE: return "DISABLE";
case mode::CANCEL: return "CANCEL";
default: return "";
}
}
mode last_enable_mode;
bool session_locked;
boost::asio::io_service io_service;
#define WM_NOTIFYFROMICON WM_APP
///////////////////////////////////////////////////////////////////////
#ifndef NUM_OUTPUT_DEBUG_PRINTF_BUFFER
#define NUM_OUTPUT_DEBUG_PRINTF_BUFFER 2048
#endif
inline void OutputDebugPrintf(LPCTSTR format, ...)
{
std::va_list ap;
va_start(ap, format);
TCHAR buf[NUM_OUTPUT_DEBUG_PRINTF_BUFFER];
StringCbVPrintf(buf, sizeof(buf), format, ap);
OutputDebugString(buf);
va_end(ap);
}
inline void OutputDebugVPrintf(LPCTSTR format, std::va_list ap)
{
TCHAR buf[NUM_OUTPUT_DEBUG_PRINTF_BUFFER];
StringCbVPrintf(buf, sizeof(buf), format, ap);
OutputDebugString(buf);
}
///////////////////////////////////////////////////////////////////////
inline void Debug(const char *format, ...)
{
std::va_list ap;
va_start(ap, format);
char buf[NUM_OUTPUT_DEBUG_PRINTF_BUFFER];
StringCbPrintf(buf, sizeof(buf), "FLOW companion: %s", format);
OutputDebugVPrintf(buf, ap);
va_end(ap);
}
const int RESERVE_WIDTH = 200;
const int RESERVE_HEIGHT = 100;
const int MENU_OFFSET = 30;
void ToggleFlow()
{
UINT mid = RegisterWindowMessage("Tray.{47BCDAC1-2E6F-4f9a-9A3F-68A3B97CE33E}");
HWND hwnd = FindWindow("LOGI_LOGIOPTIONSMGR", "LogiOptionsMgr");
if(hwnd) {
POINT pt;
if(GetCursorPos(&pt)) {
HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
if(hm) {
MONITORINFO mi = { sizeof(MONITORINFO) };
GetMonitorInfo(hm, &mi);
Debug("(%d,%d) in (%d,%d)-(%d,%d) (%d,%d)-(%d,%d)", pt.x, pt.y, mi.rcWork.left, mi.rcWork.top, mi.rcWork.right, mi.rcWork.bottom, mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right, mi.rcMonitor.bottom);
if(pt.x - RESERVE_WIDTH < mi.rcMonitor.left) { pt.x = mi.rcMonitor.left + RESERVE_WIDTH; }
if(pt.x + RESERVE_WIDTH > mi.rcMonitor.right) { pt.x = mi.rcMonitor.right - RESERVE_WIDTH; }
if(pt.y + RESERVE_HEIGHT > mi.rcMonitor.bottom) { pt.y = mi.rcMonitor.bottom - RESERVE_HEIGHT; }
SetCursorPos(pt.x, pt.y);
GetCursorPos(&pt);
Debug("(%d,%d) in (%d,%d)-(%d,%d) (%d,%d)-(%d,%d)", pt.x, pt.y, mi.rcWork.left, mi.rcWork.top, mi.rcWork.right, mi.rcWork.bottom, mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right, mi.rcMonitor.bottom);
} else {
Debug("monitor null at (%d,%d)", pt.x, pt.y);
}
PostMessage(hwnd, mid, 1, WM_RBUTTONDOWN);
PostMessage(hwnd, mid, 1, WM_RBUTTONUP);
auto timer = std::make_shared<boost::asio::deadline_timer>(io_service, boost::posix_time::milliseconds(500));
timer->async_wait([timer, hwnd, pt](const boost::system::error_code&){
const unsigned int ptval = ((pt.y+MENU_OFFSET)<<16)+pt.x+MENU_OFFSET;
PostMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, ptval);
PostMessage(hwnd, WM_LBUTTONUP, 0, ptval);
});
} else { // NOTE: failed in lock screen
Debug("GetCurPos() failed: %d", GetLastError());
}
}
}
enum class STATE { UNKNOWN, ENABLED, DISABLED };
fs::path GetConfPathInit()
{
char buf[MAX_PATH];
if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, buf))) {
std::string s(buf);
s += "\\LogiShrd\\LogiOptions\\devices";
decltype(fs::last_write_time(std::declval<fs::path>())) ftCur{};
fs::path pCur;
int nCount = 0;
for(auto p : fs::recursive_directory_iterator(s)) {
if(p.path().extension() == ".json") {
auto ft = fs::last_write_time(p);
if(ft > ftCur) {
ftCur = ft;
pCur = p.path();
++nCount;
Debug("ConfPath %s", pCur.string().c_str());
}
}
}
if(nCount == 0) {
NotifyError("FLOW companion", "No config found");
} else if(nCount != 1) {
std::string s("Multiple config found, ");
s += pCur.filename().string();
s += " is anyway used.";
NotifyWarn("FLOW companion", s.c_str());
}
return pCur;
} else return {};
}
fs::path& GetConfPath()
{
static fs::path path = GetConfPathInit();
return path;
}
STATE GetFlow()
{
STATE st = STATE::UNKNOWN;
std::string s(GetConfPath().string());
if(s.size()) {
std::ifstream ifs(s);
std::string line;
while(getline(ifs, line)) {
if(line.find("\"enabled\" : 0,") != std::string::npos) {
st = STATE::DISABLED;
} else if(line.find("\"enabled\" : 1,") != std::string::npos) {
st = STATE::ENABLED;
}
}
}
return st;
}
bool IsFlowMismatched(bool enable)
{
auto st = GetFlow();
return (st != STATE::UNKNOWN) && ((st == STATE::ENABLED) ^ enable);
}
bool SetFlow(bool enable)
{
if(IsFlowMismatched(enable)) {
ToggleFlow();
return true;
} else {
return false;
}
}
///////////////////////////////////////////////////////////////////////
namespace detail {
template<typename T, typename FromThis>
struct shared_only_base
{
using ReturnType = decltype(std::declval<FromThis>().shared_from_this());
using ElementType = typename ReturnType::element_type;
using type = FromThis;
};
template<typename T>
struct shared_only_base<T, void>
{
using ReturnType = void;
using ElementType = void;
using type = struct {};
};
};
template<typename T, typename FromThis = std::enable_shared_from_this<T>>
struct shared_only : detail::shared_only_base<T, FromThis>::type
{
static_assert(std::is_same<FromThis, void>::value || std::is_convertible<typename detail::shared_only_base<T, FromThis>::ReturnType, std::shared_ptr<T>>::value, "mismatch types between smart pointer and enable_shared_from_this");
static_assert(std::is_same<FromThis, void>::value || std::is_same<typename detail::shared_only_base<T, FromThis>::ElementType, T>::value, "mismatch types between element type and enable_shared_from_this");
protected:
shared_only() = default; // prohibit construction without inheritance
template<typename ... U>
static std::shared_ptr<T> make(U&& ... u)
{
return std::make_shared<impl>(std::forward<U>(u)...);
}
private:
struct impl : T
{
// inheriting constructor keeps accessibility so that explicit definition is required
template<typename ... U>
impl(U&& ... u) : T(std::forward<U>(u)...) {}
};
};
///////////////////////////////////////////////////////////////////////
struct enabler : shared_only<enabler>
{
public:
void request()
{
auto self(shared_from_this());
if(IsFlowMismatched(enable)) { // necessary to switch
LASTINPUTINFO lii = { sizeof(LASTINPUTINFO) };
GetLastInputInfo(&lii);
DWORD dwCur = GetTickCount();
if(dwCur + (lii.dwTime > dwCur ? 0xFFFFFFFF : 0) - lii.dwTime > 3000) { // idle passed
if(SetFlow(enable)) {
timer.expires_from_now(boost::posix_time::milliseconds(1000));
timer.async_wait([self,this](const boost::system::error_code &error) {
if(error != boost::asio::error::operation_aborted && alive) {
auto st = GetFlow();
if((st != STATE::UNKNOWN) && ((st == STATE::ENABLED) ^ enable)) {
if(error != boost::asio::error::operation_aborted && alive) {
Debug("enabler: retry");
request();
}
} else {
if(enable) {
NotifyInfoWithStatus("FLOW companion", "MX flow has been enabled.", notify_icon::ICON::ON, "FLOW companion\n[enable]");
Debug("enabler: true");
} else {
NotifyInfoWithStatus("FLOW companion", "MX flow has been disabled.", notify_icon::ICON::OFF, "FLOW companion\n[disable]");
Debug("enabler: false");
}
}
}
});
}
} else { // idle waiting
if(!shown) {
if(enable) {
NotifyInfoWithStatus("FLOW companion", "Counterpart has been unlocked.\nAfter input idle for 3 secs, MX flow will be enabled.", notify_icon::ICON::OFF2ON, "FLOW companion\n[waiting idle...]");
} else {
NotifyInfoWithStatus("FLOW companion", "Counterpart has been locked.\nAfter input idle for 3 secs, MX flow will be disabled.", notify_icon::ICON::ON2OFF, "FLOW companion\n[waiting idle...]");
}
shown = true;
}
Debug("enabler: wait");
timer.expires_from_now(boost::posix_time::seconds(1));
timer.async_wait([self,this](const boost::system::error_code& error){
if(error != boost::asio::error::operation_aborted && alive) {
request();
}
});
}
}
}
mode rest()
{
if(alive) {
alive = false;
return enable ? mode::ENABLE : mode::DISABLE;
} else {
return mode::CANCEL;
}
}
static std::shared_ptr<enabler> make(boost::asio::io_service& io_service, mode enable)
{
static std::weak_ptr<enabler> last;
if(enable == mode::CANCEL) {
return last.lock();
} else {
auto ret = shared_only<enabler>::make(io_service, enable == mode::ENABLE);
if(auto p = last.lock()) { p->alive = false; }
last = ret;
Debug("enabler: make %p", ret.get());
return std::move(ret);
}
}
~enabler() { Debug("enabler: destruct %p", this); }
private:
friend class shared_only<enabler>;
enabler(boost::asio::io_service& io_service, bool enable) : alive(true), shown(false), enable(enable), timer(io_service)
{
}
bool alive;
bool shown;
bool enable;
boost::asio::deadline_timer timer;
};
class udp_server
{
public:
// FIXME: Hardcoded port number
udp_server(boost::asio::io_service& io_service)
: socket_(io_service, udp::endpoint(udp::v4(), 59865))
{
do_receive();
}
private:
void do_receive()
{
socket_.async_receive_from(
boost::asio::buffer(recv_buffer_), remote_endpoint_,
[this](const boost::system::error_code &error, std::size_t bytes_transferred) {
if (!error || error == boost::asio::error::message_size)
{
std::string command(recv_buffer_.data(), bytes_transferred);
Debug(command.c_str());
bool valid;
mode req;
if(command == GetConfPath().stem().string() + ":1") {
valid = true;
req = mode::ENABLE;
} else if(command == GetConfPath().stem().string() + ":0") {
valid = true;
req = mode::DISABLE;
} else {
valid = false;
}
message = valid ? "ACK" : "NAK";
if(valid) {
if(session_locked) {
if(auto p = enabler::make(io_service, mode::CANCEL)) {
p->rest();
}
last_enable_mode = req;
Debug("suspend %s", to_string(last_enable_mode));
} else {
enabler::make(io_service, req)->request();
}
}
do_send();
} else {
do_receive();
}
});
}
void do_send()
{
socket_.async_send_to(boost::asio::buffer(message), remote_endpoint_,
[this](const boost::system::error_code &error, std::size_t bytes_transferred) {
do_receive();
});
}
udp::socket socket_;
udp::endpoint remote_endpoint_;
std::array<char, 20> recv_buffer_;
std::string message;
};
// Currently, only 2 hosts are considered
std::string GetHost()
{
std::string s(GetConfPath().string());
if(s.size()) {
std::ifstream ifs(s);
std::string line;
std::regex regex_current("\"current_host\" : (\\d),");
std::regex regex_hostidx("\"host_index\" : (\\d+),");
std::regex regex_hostname("\"host_name\" : \"([^\"]+)\",");
std::smatch m;
int current_idx = -1;
bool is_target = false;
while(getline(ifs, line)) {
if(std::regex_search(line, m, regex_current)) {
current_idx = std::stoi(m[1]);
} else if(std::regex_search(line, m, regex_hostidx)) {
if(std::stoi(m[1]) != current_idx) is_target = true;
} else if(is_target && std::regex_search(line, m, regex_hostname)) {
return m[1];
}
}
}
return {};
}
struct notifier : shared_only<notifier>
{
public:
void request()
{
auto self(shared_from_this());
send_buf = (GetConfPath().stem().string() + (enable ? ":1" : ":0"));
timer.expires_from_now(boost::posix_time::seconds(10));
timer.async_wait([self,this](const boost::system::error_code& error){
if(!error) {
socket.cancel();
}
});
socket.async_send_to(boost::asio::buffer(send_buf), endpoint,
[self,this](const boost::system::error_code& error, std::size_t bytes_transferred) {
if(error == boost::asio::error::operation_aborted) {
if(alive) request();
} else {
timer.expires_from_now(boost::posix_time::seconds(10));
timer.async_wait([self,this](const boost::system::error_code& error){
if(!error) {
socket.cancel();
}
});
udp::endpoint recv_endpoint;
socket.async_receive_from(boost::asio::buffer(recv_buf), recv_endpoint,
[self,this](const boost::system::error_code& error, std::size_t bytes_transferred) {
if(error == boost::asio::error::operation_aborted) {
if(alive) request();
} else {
timer.cancel();
recv_buf.data()[bytes_transferred]=0;
Debug(recv_buf.data());
}
}
);
}
});
}
static std::shared_ptr<notifier> make(boost::asio::io_service& io_service, udp::endpoint endpoint, bool enable) {
static std::weak_ptr<notifier> last;
auto ret = shared_only<notifier>::make(io_service, endpoint, enable);
if(auto p = last.lock()) { p->alive = false; }
last = ret;
return std::move(ret);
}
private:
friend class shared_only<notifier>;
notifier(boost::asio::io_service& io_service, udp::endpoint endpoint, bool enable) : alive(true), enable(enable), socket(io_service, udp::v4()), endpoint(endpoint), timer(io_service)
{
}
bool alive;
bool enable;
std::string send_buf;
std::array<char, 128> recv_buf;
udp::endpoint endpoint;
udp::socket socket;
boost::asio::deadline_timer timer;
};
void SendEnable(bool enable)
{
auto resolver = std::make_shared<udp::resolver>(io_service);
// FIXME: Hardcoded port number
udp::resolver::query query(udp::v4(), GetHost(), "59865");
resolver->async_resolve(query, [resolver, enable](const boost::system::error_code& error, udp::resolver::iterator iterator) {
if(!error) {
notifier::make(io_service, *iterator, enable)->request();
} else {
Debug("Can't resolve %s by %s", GetHost().c_str(), error.message().c_str());
}
});
}
struct window_data
{
HINSTANCE hInst;
HICON hIcon[4];
HMENU hMenu;
notify_icon ni;
};
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
try { // It seems that this try block is crucial for stability even though no exception is thrown
auto pwd = reinterpret_cast<window_data*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
switch(uMsg) {
case WM_CREATE: {
auto pcs = reinterpret_cast<CREATESTRUCT*>(lParam);
pwd = reinterpret_cast<window_data*>(pcs->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pwd));
WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_THIS_SESSION);
bool enable = GetFlow() == STATE::ENABLED;
pwd->ni.add(hwnd, WM_NOTIFYFROMICON, enable ? notify_icon::ICON::ON : notify_icon::ICON::OFF, enable ? "FLOW companion\n[enable]" : "FLOW companion\n[disable]");
SetTimer(hwnd, 0, 5000, 0);
break;
}
case WM_WTSSESSION_CHANGE:
switch(wParam) {
case WTS_SESSION_LOCK:
session_locked = true;
if(auto p = enabler::make(io_service, mode::CANCEL)) {
last_enable_mode = p->rest();
} else {
last_enable_mode = mode::CANCEL;
}
Debug("WTS_SESSION_LOCK enable_mode %s", to_string(last_enable_mode));
SendEnable(false);
break;
case WTS_SESSION_UNLOCK:
session_locked = false;
Debug("WTS_SESSION_UNLOCK");
if(last_enable_mode != mode::CANCEL) {
enabler::make(io_service, last_enable_mode)->request();
Debug("resume %s", to_string(last_enable_mode));
last_enable_mode = mode::CANCEL;
}
SendEnable(true);
break;
}
break;
case WM_COMMAND:
switch(LOWORD(wParam)) {
case IDM_ABOUT:
AboutDialogBox(pwd->hInst, 0);
break;
case IDM_EXIT:
DestroyWindow(hwnd);
break;
}
break;
case WM_NOTIFYFROMICON:
switch(LOWORD(lParam)) {
case WM_RBUTTONUP:
HMENU hmenu = GetSubMenu(pwd->hMenu,0);
SetForegroundWindow(hwnd);
TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_TOPALIGN, GET_X_LPARAM(wParam), GET_Y_LPARAM(wParam), 0, hwnd, NULL);
PostMessage(hwnd, WM_NULL, 0, 0);
break;
}
break;
case WM_NOTIFYREQ:
NotifyHandle(&pwd->ni, lParam);
break;
case WM_TIMER:
// FIXME: This overwrites intermediate state
pwd->ni.icon((GetFlow() == STATE::ENABLED) ? notify_icon::ICON::ON : notify_icon::ICON::OFF);
return 0;
case WM_DESTROY:
pwd->ni.remove();
KillTimer(hwnd, 0);
PostQuitMessage(0);
io_service.stop();
break;
}
}
catch (std::exception& e)
{
Debug("exception in wndproc: %s", e.what());
}
catch (...)
{
Debug("unknown exception in wndproc");
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
///////////////////////////////////////////////////////////////////////
template<int PAUSE_NUM = 3, int PROCESS_NUM = 10>
struct win32_message_loop
{
win32_message_loop(boost::asio::io_service &ios) : ios(ios), process_count(0), pause_count(0) {
ios.post([this]{ process(); });
}
~win32_message_loop() { Debug("win32_message_loop: destructor"); }
void process() {
MSG msg;
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
if(msg.message == WM_QUIT) return;
TranslateMessage(&msg);
DispatchMessage(&msg);
process_count = (process_count + 1) % PROCESS_NUM;
pause_count = 0;
} else {
process_count = 0;
}
if(process_count == 0) {
if(pause_count++ < PAUSE_NUM) {
YieldProcessor();
} else {
pause_count = 0;
Sleep(1);
}
}
ios.post([this]{ process(); });
}
int process_count, pause_count;
boost::asio::io_service &ios;
};
///////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)
{
try
{
Debug("Counterpart host %s", GetHost().c_str());
udp_server server(io_service);
WNDCLASSEX wce = {};
wce.cbSize = sizeof(WNDCLASSEX);
wce.style = CS_HREDRAW | CS_VREDRAW;
wce.lpfnWndProc = WindowProc;
wce.hInstance = hInst;
wce.lpszClassName = "DummyWindowClass";
RegisterClassEx(&wce);
window_data wd;
wd.hInst = hInst;
wd.ni.init(hInst, { IDI_ON, IDI_OFF, IDI_ON2OFF, IDI_OFF2ON });
wd.hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDM_POPUP));
last_enable_mode = mode::CANCEL;
session_locked = false;
HWND hwnd = CreateWindowEx(0, wce.lpszClassName, "Test", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, HWND_MESSAGE, 0, hInst, static_cast<void*>(&wd));
NotifySetTarget(hwnd);
win32_message_loop<> loop(io_service);
io_service.run();
}
catch (std::exception& e)
{
Debug("exception: %s", e.what());
}
catch (...)
{
Debug("unknown exception");
}
return 0;
}
#include <windows.h>
#include "FlowCompanion.rh"
IDI_ON ICON DISCARDABLE "on.ico"
IDI_OFF ICON DISCARDABLE "off.ico"
IDI_ON2OFF ICON DISCARDABLE "on2off.ico"
IDI_OFF2ON ICON DISCARDABLE "off2on.ico"
IDM_POPUP MENU DISCARDABLE
BEGIN
POPUP "Popup"
BEGIN
MENUITEM "&About", IDM_ABOUT
MENUITEM SEPARATOR
MENUITEM "Exit", IDM_EXIT
END
END
IDV_VERSIONINFO VERSIONINFO
FILEVERSION 0,1,2018,119
PRODUCTVERSION 0,1,2018,119
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0x00000000
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "041103A4"
BEGIN
VALUE "CompanyName", "Yak!"
VALUE "FileDescription", "Lock detection utility for MX FLOW"
VALUE "FileVersion", "Ver 0.01 (2018/01/19)"
VALUE "InternalName", "FlowCompanion.exe"
VALUE "LegalCopyright", "Written by Yak!"
VALUE "OriginalFilename", "FlowCompanion.exe"
VALUE "ProductName", "MX FLOW companion"
VALUE "ProductVersion", "Ver 0.01 (2018/01/19)"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0411, 0x03A4
END
END
#define IDI_ON 1
#define IDI_OFF 2
#define IDI_ON2OFF 3
#define IDI_OFF2ON 4
#define IDM_POPUP 1
#define IDM_EXIT 2
#define IDM_ABOUT 3
#define IDV_VERSIONINFO 1
BOOST_PATH=C:\usr\local\boost\1_67_0
BOOST_LIB=$(BOOST_PATH)\lib32-msvc-14.0
OPT=/EHsc /Zi /MDd /D_WIN32_WINNT=0x0601
all: FlowCompanion.exe FCtest.exe
FlowCompanion.exe: FlowCompanion.obj notify.obj AboutDlg.obj FlowCompanion.res
cl $(OPT) FlowCompanion.obj notify.obj AboutDlg.obj FlowCompanion.res user32.lib shell32.lib wtsapi32.lib version.lib /link /libpath:$(BOOST_LIB)
FlowCompanion.map: FlowCompanion.obj notify.obj AboutDlg.obj FlowCompanion.res
cl $(OPT) FlowCompanion.obj notify.obj AboutDlg.obj FlowCompanion.res user32.lib shell32.lib wtsapi32.lib version.lib /link /libpath:$(BOOST_LIB) /map:FlowComparison.map
FlowCompanion.obj: FlowCompanion.cpp notify.h FlowCompanion.rh
cl $(OPT) /I $(BOOST_PATH) /c FlowCompanion.cpp
notify.obj: notify.cpp notify.h
cl $(OPT) /c notify.cpp
AboutDlg.obj: AboutDlg.cpp AboutDlg.h
cl $(OPT) /I $(BOOST_PATH) /c AboutDlg.cpp
FlowCompanion.res: FlowCompanion.rc FlowCompanion.rh on.ico off.ico on2off.ico off2on.ico
rc FlowCompanion.rc
FCtest.exe:
cl $(OPT) /I c:\usr\local\boost /EHsc FCtest.cpp /link /libpath:c:\usr\local\boost\lib32-msvc-14.0
clean:
del *.obj *.exe *.res
#include <windows.h>
#include "notify.h"
static HWND hwndMain;
struct notify_req
{
enum class info_type
{
ERR, WARN, INFO, NONE
} info;
const char* szTitle;
const char* szInfo;
bool status;
notify_icon::ICON icon_id;
const char* szStatus;
};
void NotifySetTarget(HWND hwnd)
{
hwndMain = hwnd;
}
void NotifyError(const char* szTitle, const char* szInfo)
{
notify_req req = {
notify_req::info_type::ERR,
szTitle,
szInfo,
false
};
SendMessage(hwndMain, WM_NOTIFYREQ, 0, reinterpret_cast<LPARAM>(&req));
}
void NotifyWarn(const char* szTitle, const char* szInfo)
{
notify_req req = {
notify_req::info_type::WARN,
szTitle,
szInfo,
false
};
SendMessage(hwndMain, WM_NOTIFYREQ, 0, reinterpret_cast<LPARAM>(&req));
}
void NotifyInfo(const char* szTitle, const char* szInfo)
{
notify_req req = {
notify_req::info_type::INFO,
szTitle,
szInfo,
false
};
SendMessage(hwndMain, WM_NOTIFYREQ, 0, reinterpret_cast<LPARAM>(&req));
}
void NotifyInfoWithStatus(const char* szTitle, const char* szInfo, notify_icon::ICON icon_id, const char* szStatus)
{
notify_req req = {
notify_req::info_type::INFO,
szTitle,
szInfo,
true,
icon_id,
szStatus
};
SendMessage(hwndMain, WM_NOTIFYREQ, 0, reinterpret_cast<LPARAM>(&req));
}
void NotifyStatus(notify_icon::ICON icon_id, const char* szStatus)
{
notify_req req = {
notify_req::info_type::NONE,
nullptr,
nullptr,
true,
icon_id,
szStatus
};
SendMessage(hwndMain, WM_NOTIFYREQ, 0, reinterpret_cast<LPARAM>(&req));
}
void NotifyHandle(notify_icon* pni, LPARAM req_)
{
auto req = reinterpret_cast<notify_req*>(req_);
switch(req->info) {
case notify_req::info_type::ERR:
pni->error(req->szTitle, req->szInfo);
break;
case notify_req::info_type::WARN:
pni->warn(req->szTitle, req->szInfo);
break;
case notify_req::info_type::INFO:
if(req->status) {
pni->info_with_status(req->szTitle, req->szInfo, req->icon_id, req->szStatus);
} else {
pni->info(req->szTitle, req->szInfo);
}
break;
case notify_req::info_type::NONE:
pni->status(req->icon_id, req->szStatus);
break;
}
}
#ifndef NOTIFY_H
#define NOTIFY_H
#include <utility>
#include <algorithm>
#define WM_NOTIFYREQ (WM_APP+1)
class notify_icon
{
public:
enum class ICON {
ON, OFF, ON2OFF, OFF2ON, NUM
};
notify_icon()
{
ZeroMemory(&nid, sizeof(nid));
nid.cbSize = sizeof(nid);
}
void init(HINSTANCE hInst, std::initializer_list<int> l)
{
std::transform(l.begin(), l.end(), std::begin(hIcon), [hInst](int n){ return LoadIcon(hInst, MAKEINTRESOURCE(n)); });
}
void add(HWND hwnd, UINT uMsg, ICON icon, const char* szTip)
{
nid.hWnd = hwnd;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_SHOWTIP;
nid.uCallbackMessage = uMsg;
nid.hIcon = update_icon(icon);
lstrcpy(nid.szTip, szTip);
nid.uVersion = NOTIFYICON_VERSION_4;
Shell_NotifyIcon(NIM_ADD, &nid);
Shell_NotifyIcon(NIM_SETVERSION, &nid);
}
void icon(ICON icon)
{
nid.uFlags = NIF_ICON | NIF_TIP | NIF_SHOWTIP;
nid.hIcon = update_icon(icon);
Shell_NotifyIcon(NIM_MODIFY, &nid);
iCur = icon;
}
void status(ICON icon, const char* szTip)
{
nid.uFlags = NIF_ICON | NIF_TIP | NIF_SHOWTIP;
nid.hIcon = update_icon(icon);
lstrcpy(nid.szTip, szTip);
Shell_NotifyIcon(NIM_MODIFY, &nid);
iCur = icon;
}
void error(const char* szTitle, const char* szInfo)
{
show_info(info_type::ERR, szTitle, szInfo);
}
void warn(const char* szTitle, const char* szInfo)
{
show_info(info_type::WARN, szTitle, szInfo);
}
void info(const char* szTitle, const char* szInfo)
{
show_info(info_type::INFO, szTitle, szInfo);
}
void info_with_status(const char* szTitle, const char* szInfo, ICON icon, const char* szTip)
{
nid.uFlags = NIF_INFO | NIF_ICON | NIF_TIP | NIF_SHOWTIP;
lstrcpy(nid.szInfoTitle, szTitle);
lstrcpy(nid.szInfo, szInfo);
nid.dwInfoFlags = NIIF_INFO;
nid.uTimeout = 5000;
nid.hIcon = update_icon(icon);
lstrcpy(nid.szTip, szTip);
Shell_NotifyIcon(NIM_MODIFY, &nid);
iCur = icon;
}
void remove()
{
Shell_NotifyIcon(NIM_DELETE, &nid);
}
private:
enum class info_type
{
ERR, WARN, INFO
};
void show_info(info_type type, const char* szTitle, const char *szInfo)
{
nid.uFlags = NIF_INFO | NIF_TIP | NIF_SHOWTIP;
lstrcpy(nid.szInfoTitle, szTitle);
lstrcpy(nid.szInfo, szInfo);
nid.dwInfoFlags = type == info_type::ERR ? NIIF_ERROR : type == info_type::WARN ? NIIF_WARNING : NIIF_INFO;
nid.uTimeout = 5000;
Shell_NotifyIcon(NIM_MODIFY, &nid);
}
HICON update_icon(ICON iReq)
{
if((iCur == ICON::ON2OFF && iReq == ICON::ON) ||
(iCur == ICON::OFF2ON && iReq == ICON::OFF) ||
(iCur == ICON::ON && iReq == ICON::OFF2ON) ||
(iCur == ICON::OFF && iReq == ICON::ON2OFF)) {
} else {
iCur = iReq;
}
return hIcon[static_cast<int>(iCur)];
}
NOTIFYICONDATA nid;
HICON hIcon[ICON::NUM];
ICON iCur;
};
void NotifySetTarget(HWND hwnd);
void NotifyError(const char* szTitle, const char* szInfo);
void NotifyWarn(const char* szTitle, const char* szInfo);
void NotifyInfo(const char* szTitle, const char* szInfo);
void NotifyInfoWithStatus(const char* szTitle, const char* szInfo, notify_icon::ICON icon_id, const char* szStatus);
void NotifyStatus(int icon_id, const char* szStatus);
void NotifyHandle(notify_icon* pni, LPARAM req_);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment