Win32 specific: Using SHChangeNotifyRegister to get notifications
on removable media insert / eject events. From now on we no more poll for removable media on Windows. Thanks @mjgtp from prusaprinters.org See the following discussion: https://forum.prusaprinters.org/forum/prusaslicer/prusaslicer-trying-to-access-my-floppy-disk-a The final working code sample was taken from Chromium source code, volume_mount_watcher_win.cc
This commit is contained in:
parent
3fdd643f49
commit
58192ba6c2
4 changed files with 73 additions and 11 deletions
|
@ -50,8 +50,8 @@
|
|||
#include "RemovableDriveManager.hpp"
|
||||
|
||||
#ifdef __WXMSW__
|
||||
#include <Shlobj.h>
|
||||
#include <dbt.h>
|
||||
#include <shlobj.h>
|
||||
#endif // __WXMSW__
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
|
||||
|
@ -158,6 +158,41 @@ static void register_win32_device_notification_event()
|
|||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
wxWindow::MSWRegisterMessageHandler(MainFrame::WM_USER_MEDIACHANGED, [](wxWindow *win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
|
||||
// Some messages are sent to top level windows by default, some messages are sent to only registered windows, and we explictely register on MainFrame only.
|
||||
auto main_frame = dynamic_cast<MainFrame*>(win);
|
||||
auto plater = (main_frame == nullptr) ? nullptr : main_frame->plater();
|
||||
if (plater == nullptr)
|
||||
// Maybe some other top level window like a dialog or maybe a pop-up menu?
|
||||
return true;
|
||||
wchar_t sPath[MAX_PATH];
|
||||
if (lParam == SHCNE_MEDIAINSERTED || lParam == SHCNE_MEDIAREMOVED) {
|
||||
struct _ITEMIDLIST* pidl = *reinterpret_cast<struct _ITEMIDLIST**>(wParam);
|
||||
if (! SHGetPathFromIDList(pidl, sPath)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "MediaInserted: SHGetPathFromIDList failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
switch (lParam) {
|
||||
case SHCNE_MEDIAINSERTED:
|
||||
{
|
||||
//printf("SHCNE_MEDIAINSERTED %S\n", sPath);
|
||||
plater->GetEventHandler()->AddPendingEvent(VolumeAttachedEvent(EVT_VOLUME_ATTACHED));
|
||||
break;
|
||||
}
|
||||
case SHCNE_MEDIAREMOVED:
|
||||
{
|
||||
//printf("SHCNE_MEDIAREMOVED %S\n", sPath);
|
||||
plater->GetEventHandler()->AddPendingEvent(VolumeDetachedEvent(EVT_VOLUME_DETACHED));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// printf("Unknown\n");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
#endif // WIN32
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
#include <dbt.h>
|
||||
#include <shlobj.h>
|
||||
#endif // _WIN32
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -127,6 +128,30 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
|||
// DEV_BROADCAST_HANDLE NotificationFilter = { 0 };
|
||||
// NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
|
||||
// NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
||||
|
||||
// Using Win32 Shell API to register for media insert / removal events.
|
||||
LPITEMIDLIST ppidl;
|
||||
if (SHGetSpecialFolderLocation(this->GetHWND(), CSIDL_DESKTOP, &ppidl) == NOERROR) {
|
||||
SHChangeNotifyEntry shCNE;
|
||||
shCNE.pidl = ppidl;
|
||||
shCNE.fRecursive = TRUE;
|
||||
// Returns a positive integer registration identifier (ID).
|
||||
// Returns zero if out of memory or in response to invalid parameters.
|
||||
m_ulSHChangeNotifyRegister = SHChangeNotifyRegister(this->GetHWND(), // Hwnd to receive notification
|
||||
SHCNE_DISKEVENTS, // Event types of interest (sources)
|
||||
SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED,
|
||||
//SHCNE_UPDATEITEM, // Events of interest - use SHCNE_ALLEVENTS for all events
|
||||
WM_USER_MEDIACHANGED, // Notification message to be sent upon the event
|
||||
1, // Number of entries in the pfsne array
|
||||
&shCNE); // Array of SHChangeNotifyEntry structures that
|
||||
// contain the notifications. This array should
|
||||
// always be set to one when calling SHChnageNotifyRegister
|
||||
// or SHChangeNotifyDeregister will not work properly.
|
||||
assert(m_ulSHChangeNotifyRegister != 0); // Shell notification failed
|
||||
} else {
|
||||
// Failed to get desktop location
|
||||
assert(false);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
// propagate event
|
||||
|
@ -161,8 +186,14 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
|||
void MainFrame::shutdown()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::UnregisterDeviceNotification(HDEVNOTIFY(m_hDeviceNotify));
|
||||
m_hDeviceNotify = nullptr;
|
||||
if (m_hDeviceNotify) {
|
||||
::UnregisterDeviceNotification(HDEVNOTIFY(m_hDeviceNotify));
|
||||
m_hDeviceNotify = nullptr;
|
||||
}
|
||||
if (m_ulSHChangeNotifyRegister) {
|
||||
SHChangeNotifyDeregister(m_ulSHChangeNotifyRegister);
|
||||
m_ulSHChangeNotifyRegister = 0;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
if (m_plater)
|
||||
|
|
|
@ -144,6 +144,8 @@ public:
|
|||
|
||||
#ifdef _WIN32
|
||||
void* m_hDeviceNotify { nullptr };
|
||||
uint32_t m_ulSHChangeNotifyRegister { 0 };
|
||||
static constexpr int WM_USER_MEDIACHANGED { 0x7FFF }; // WM_USER from 0x0400 to 0x7FFF, picking the last one to not interfere with wxWidgets allocation
|
||||
#endif // _WIN32
|
||||
};
|
||||
|
||||
|
|
|
@ -452,14 +452,8 @@ void RemovableDriveManager::thread_proc()
|
|||
{
|
||||
std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
|
||||
#ifdef _WIN32
|
||||
// Windows do not send an update on insert / eject of an SD card into an external SD card reader.
|
||||
// Windows also do not send an update on software eject of a FLASH drive.
|
||||
// We can likely use the Windows WMI API, but it will be quite time consuming to implement.
|
||||
// https://www.codeproject.com/Articles/10539/Making-WMI-Queries-In-C
|
||||
// https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-start-page
|
||||
// https://docs.microsoft.com/en-us/windows/win32/wmisdk/com-api-for-wmi
|
||||
// https://docs.microsoft.com/en-us/windows/win32/wmisdk/example--receiving-event-notifications-through-wmi-
|
||||
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(2), [this]{ return m_stop || m_wakeup; });
|
||||
// Reacting to updates by WM_DEVICECHANGE and WM_USER_MEDIACHANGED
|
||||
m_thread_stop_condition.wait(lck, [this]{ return m_stop || m_wakeup; });
|
||||
#else
|
||||
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(2), [this]{ return m_stop; });
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue