toys/serialport/source/platform/win.cpp

157 lines
4.4 KiB
C++

#if defined(OS_WINDOWS)
// CreateWindowEx GetMessage DispatchMessage RegisterClassEx UnregisterClass
// DefWindowProc SetWindowLong GetWindowLong
#include <dbt.h>
#include <tchar.h> /**< _T */
#include <windows.h>
#include <memory> /**< std::shared_ptr */
#include <thread> /**< std::thread */
#define CLASS_NAME _T("SerialPortHotPlugMonitorWnd")
class SerialPortHotPlugDelegate {
public:
virtual void OnConnected() = 0;
virtual void OnDisconnected() = 0;
};
class SerialPortHotPlugMonitor {
SerialPortHotPlugMonitor(std::shared_ptr<SerialPortHotPlugDelegate> delegate)
: delegate(delegate_) {
Init();
}
~SerialPortHotPlugMonitor() {
if (thread_) {
if (thread_.joinable()) {
thread_.join();
}
}
UnregisterClass(CLASS_NAME, GetModuleHandle(NULL));
}
private:
inline void operator()() {
// create hidden window to receive device change messages
if (nullptr == CreateWindowEx(0, CLASS_NAME, 0, 0, 0, 0, 0, 0,
0 /*HWND_MESSAGE*/, 0,
GetModuleHandle(nullptr), this)) {
return;
}
// message loop
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) {
DispatchMessage(&msg);
}
}
private:
bool Init() {
// https://docs.microsoft.com/zh-cn/windows/win32/devio/registering-for-device-notification
WNDCLASSEX wnd_cls{0};
HINSTANCE instance = GetModuleHandle(nullptr);
if (nullptr == instance) {
return false;
}
wnd_cls.cbSize = sizeof(WNDCLASSEX);
wnd_cls.lpszClassName = CLASS_NAME;
wnd_cls.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProc);
wnd_cls.hInstance = instance;
if (!RegisterClassEx(&wnd_cls)) {
return false;
}
thread_ = std::thread(&SerialPortHotPlugMonitor::operator(), this);
return true;
}
private:
static LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
switch (message) {
case WM_CREATE:
SetWindowLongPtr(hWnd, GWLP_USERDATA,
(LONG_PTR)((CREATESTRUCT *)lParam)->lpCreateParams);
break;
case WM_DEVICECHANGE: {
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
if (lpdb && DBT_DEVTYP_PORT == lpdb->dbch_devicetype) {
SerialPortHotPlugMonitor *monitor =
(SerialPortHotPlugMonitor *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (base && base->delegate) {
PDEV_BROADCAST_PORT devinfo = (PDEV_BROADCAST_PORT)lpdb;
if (DBT_DEVICEARRIVAL == wParam) {
#ifdef UNICODE
char port_name[256];
#ifdef CSERIALPORT_USE_UTF8
monirot->delegate_->OnConnected(
WCharToUTF8(port_name, 256, devinfo->dbcp_name), 1);
#else
monitor->delegate_->OnConnected(
WCharToNativeMB(port_name, 256, devinfo->dbcp_name), 1);
#endif
#else
#ifdef CSERIALPORT_USE_UTF8
char portNameUTF8[256];
wchar_t portNameWChar[256];
// ANSI to WChar
NativeMBToWChar(portNameWChar, 256, devinfo->dbcp_name);
// WChar to UTF8
monitor->delegate_->OnConnected(
WCharToUTF8(portNameUTF8, 256, portNameWChar), 1);
#else
monitor->delegate_->OnConnected(devinfo->dbcp_name, 1);
#endif
#endif
} else if (DBT_DEVICEREMOVECOMPLETE == wParam) {
#ifdef UNICODE
char portName[256];
#ifdef CSERIALPORT_USE_UTF8
monitor->delegate_->OnConnected(
WCharToUTF8(portName, 256, devinfo->dbcp_name), 0);
#else
monitor->delegate_->OnConnected(
WCharToNativeMB(portName, 256, devinfo->dbcp_name), 0);
#endif
#else
#ifdef CSERIALPORT_USE_UTF8
char portNameUTF8[256];
wchar_t portNameWChar[256];
// ANSI to WChar
NativeMBToWChar(portNameWChar, 256, pDevInf->dbcp_name);
// WChar to UTF8
monitor->delegate_->OnConnected(
WCharToUTF8(portNameUTF8, 256, portNameWChar), 0);
#else
monitor->delegate_->OnConnected(devinfo->dbcp_name, 0);
#endif
#endif
} else {
}
}
}
} break;
default:
break;
}
// send all other messages on to the default windows handler
return DefWindowProc(hWnd, message, wParam, lParam);
}
private:
std::thread thread_;
std::shared_ptr<SerialPortHotPlugDelegate> delegate_;
};
#endif /* OS_WINDOWS */