157 lines
4.4 KiB
C++
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 */
|