diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:08:06 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:08:06 +0200 |
commit | fbe76102e941b9f1edaf236788e42678f05fdf9a (patch) | |
tree | f5f538316019fa89be8dc478103490c3a826f3ac /RealtimeSync/notify.cpp | |
parent | 3.8 (diff) | |
download | FreeFileSync-fbe76102e941b9f1edaf236788e42678f05fdf9a.tar.gz FreeFileSync-fbe76102e941b9f1edaf236788e42678f05fdf9a.tar.bz2 FreeFileSync-fbe76102e941b9f1edaf236788e42678f05fdf9a.zip |
3.9
Diffstat (limited to 'RealtimeSync/notify.cpp')
-rw-r--r-- | RealtimeSync/notify.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/RealtimeSync/notify.cpp b/RealtimeSync/notify.cpp new file mode 100644 index 00000000..fc9ac8cc --- /dev/null +++ b/RealtimeSync/notify.cpp @@ -0,0 +1,268 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** +// +#include "notify.h" +#include <set> +#include "../shared/system_func.h" +#include "../shared/Loki/ScopeGuard.h" +#include <algorithm> +#include <boost/bind.hpp> +#include <dbt.h> + +using namespace ffs3; + + +/* +//convert bitmask into "real" drive-letter +Zstring getDriveFromMask(ULONG unitmask) +{ + for (int i = 0; i < 26; ++i) + { + if (unitmask & 0x1) + return Zstring() + static_cast<DefaultChar>(DefaultChar('A') + i) + DefaultStr(":\\"); + unitmask >>= 1; + } + return Zstring(); +} +*/ + +namespace +{ +bool messageProviderConstructed = false; +} + + +class MessageProvider //administrates a single dummy window to receive messages +{ +public: + static MessageProvider& instance() //throw (FileError) + { + static MessageProvider inst; + messageProviderConstructed = true; + return inst; + } + + class Listener + { + public: + virtual ~Listener() {} + virtual void onMessage(UINT message, WPARAM wParam, LPARAM lParam) = 0; //throw()! + }; + void registerListener(Listener& l); + void unregisterListener(Listener& l); //don't unregister objects with static lifetime + + HWND getWnd() const; //get handle in order to register additional notifications + +private: + MessageProvider(); + ~MessageProvider(); + MessageProvider(const MessageProvider&); + MessageProvider& operator=(const MessageProvider&); + + static const wchar_t WINDOW_NAME[]; + + friend LRESULT CALLBACK topWndProc(HWND, UINT, WPARAM, LPARAM); + void processMessage(UINT message, WPARAM wParam, LPARAM lParam); + + const HINSTANCE process; + HWND windowHandle; + + std::set<Listener*> listener; +}; + + +const wchar_t MessageProvider::WINDOW_NAME[] = L"E6AD5EB1-527B-4EEF-AC75-27883B233380"; //random name + + +LRESULT CALLBACK topWndProc( + HWND hwnd, //handle to window + UINT uMsg, //message identifier + WPARAM wParam, //first message parameter + LPARAM lParam) //second message parameter +{ + if (messageProviderConstructed) //attention: this callback is triggered in the middle of singleton construction! It is a bad idea to to call back at this time! + try + { + MessageProvider::instance().processMessage(uMsg, wParam, lParam); //not supposed to throw + } + catch (...) {} + + return ::DefWindowProc(hwnd, uMsg, wParam, lParam); +} + + +MessageProvider::MessageProvider() : + process(::GetModuleHandle(NULL)), //get program's module handle + windowHandle(NULL) +{ + if (process == NULL) + throw ffs3::FileError(wxString(wxT("Could not start monitoring window notifications:")) + wxT("\n\n") + + ffs3::getLastErrorFormatted() + wxT(" (GetModuleHandle)")); + + //register the main window class + WNDCLASS wc = {}; + wc.lpfnWndProc = topWndProc; + wc.hInstance = process; + wc.lpszClassName = WINDOW_NAME; + + if (::RegisterClass(&wc) == 0) + throw ffs3::FileError(wxString(wxT("Could not start monitoring window notifications:")) + wxT("\n\n") + + ffs3::getLastErrorFormatted() + wxT(" (RegisterClass)")); + + Loki::ScopeGuard guardClass = Loki::MakeGuard(::UnregisterClass, WINDOW_NAME, process); + + //create dummy-window + windowHandle = ::CreateWindow( + WINDOW_NAME, //LPCTSTR lpClassName OR ATOM in low-order word! + NULL, //LPCTSTR lpWindowName, + 0, //DWORD dwStyle, + 0, //int x, + 0, //int y, + 0, //int nWidth, + 0, //int nHeight, + 0, //note: we need a toplevel window to receive device arrival events, not a message-window (HWND_MESSAGE)! + NULL, //HMENU hMenu, + process, //HINSTANCE hInstance, + NULL); //LPVOID lpParam + if (windowHandle == NULL) + throw ffs3::FileError(wxString(wxT("Could not start monitoring window notifications:")) + wxT("\n\n") + + ffs3::getLastErrorFormatted() + wxT(" (CreateWindow)")); + + guardClass.Dismiss(); +} + + +MessageProvider::~MessageProvider() +{ + //clean-up in reverse order + ::DestroyWindow(windowHandle); + ::UnregisterClass(WINDOW_NAME, //LPCTSTR lpClassName OR ATOM in low-order word! + process); //HINSTANCE hInstance +} + + +inline +void MessageProvider::registerListener(Listener& l) +{ + listener.insert(&l); +} + + +inline +void MessageProvider::unregisterListener(Listener& l) //don't unregister objects with static lifetime +{ + listener.erase(&l); +} + + +inline +HWND MessageProvider::getWnd() const //get handle in order to register additional notifications +{ + return windowHandle; +} + + +void MessageProvider::processMessage(UINT message, WPARAM wParam, LPARAM lParam) +{ + std::for_each(listener.begin(), listener.end(), boost::bind(&Listener::onMessage, _1, message, wParam, lParam)); +} +//#################################################################################################### + + +class NotifyRequestDeviceRemoval::Pimpl : private MessageProvider::Listener +{ +public: + Pimpl(NotifyRequestDeviceRemoval& parent, const std::vector<HANDLE>& openHandles) : //throw (FileError) + parent_(parent) + { + MessageProvider::instance().registerListener(*this); //throw (FileError) + + //register handles to receive notifications + DEV_BROADCAST_HANDLE filter = {}; + filter.dbch_size = sizeof(filter); + filter.dbch_devicetype = DBT_DEVTYP_HANDLE; + + try + { + for (std::vector<HANDLE>::const_iterator i = openHandles.begin(); i != openHandles.end(); ++i) + { + filter.dbch_handle = *i; + + HDEVNOTIFY hNotfication = ::RegisterDeviceNotification( + MessageProvider::instance().getWnd(), //__in HANDLE hRecipient, + &filter, //__in LPVOID NotificationFilter, + DEVICE_NOTIFY_WINDOW_HANDLE); //__in DWORD Flags + if (hNotfication == NULL) + throw ffs3::FileError(wxString(wxT("Could not register device removal notifications:")) + wxT("\n\n") + ffs3::getLastErrorFormatted()); + + notifications.insert(hNotfication); + } + } + catch (...) + { + std::for_each(notifications.begin(), notifications.end(), ::UnregisterDeviceNotification); + MessageProvider::instance().unregisterListener(*this); //throw() in this case + throw; + } + } + + ~Pimpl() + { + std::for_each(notifications.begin(), notifications.end(), ::UnregisterDeviceNotification); + MessageProvider::instance().unregisterListener(*this); + } + +private: + virtual void onMessage(UINT message, WPARAM wParam, LPARAM lParam) //throw()! + { + //DBT_DEVICEQUERYREMOVE example: http://msdn.microsoft.com/en-us/library/aa363427(v=VS.85).aspx + if (message == WM_DEVICECHANGE) + { + if ( wParam == DBT_DEVICEQUERYREMOVE || + wParam == DBT_DEVICEQUERYREMOVEFAILED || + wParam == DBT_DEVICEREMOVECOMPLETE) + { + PDEV_BROADCAST_HDR header = reinterpret_cast<PDEV_BROADCAST_HDR>(lParam); + if (header->dbch_devicetype == DBT_DEVTYP_HANDLE) + { + PDEV_BROADCAST_HANDLE body = reinterpret_cast<PDEV_BROADCAST_HANDLE>(lParam); + +#ifdef __MINGW32__ + const HDEVNOTIFY requestNotification = reinterpret_cast<HDEVNOTIFY>(body->dbch_hdevnotify); +#else + const HDEVNOTIFY requestNotification = body->dbch_hdevnotify; +#endif + if (notifications.find(requestNotification) != notifications.end()) //is it for one of our notifications we registered? + switch (wParam) + { + case DBT_DEVICEQUERYREMOVE: + parent_.onRequestRemoval(body->dbch_handle); + break; + case DBT_DEVICEQUERYREMOVEFAILED: + parent_.onRemovalFinished(body->dbch_handle, false); + break; + case DBT_DEVICEREMOVECOMPLETE: + parent_.onRemovalFinished(body->dbch_handle, true); + break; + } + } + } + } + } + + NotifyRequestDeviceRemoval& parent_; + std::set<HDEVNOTIFY> notifications; +}; +//#################################################################################################### + + +NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(const std::vector<HANDLE>& openHandles) +{ + pimpl.reset(new Pimpl(*this, openHandles)); +} + + +NotifyRequestDeviceRemoval::~NotifyRequestDeviceRemoval() {} //make sure ~auto_ptr() works with complete type |