This commit is contained in:
David Kocik 2019-12-13 11:52:08 +01:00
parent e1d9de3ca4
commit ff58fa99f4
2 changed files with 37 additions and 13 deletions

View file

@ -30,7 +30,7 @@ INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP
void RemovableDriveManager::search_for_drives() void RemovableDriveManager::search_for_drives()
{ {
m_current_drives.clear(); m_current_drives.clear();
m_current_drives.reserve(26); //get logical drives flags by letter in alphabetical order
DWORD drives_mask = GetLogicalDrives(); DWORD drives_mask = GetLogicalDrives();
for (size_t i = 0; i < 26; i++) for (size_t i = 0; i < 26; i++)
{ {
@ -39,6 +39,7 @@ void RemovableDriveManager::search_for_drives()
std::string path (1,(char)('A' + i)); std::string path (1,(char)('A' + i));
path+=":"; path+=":";
UINT drive_type = GetDriveTypeA(path.c_str()); UINT drive_type = GetDriveTypeA(path.c_str());
// DRIVE_REMOVABLE on W are sd cards and usb thumbnails (not usb harddrives)
if (drive_type == DRIVE_REMOVABLE) if (drive_type == DRIVE_REMOVABLE)
{ {
// get name of drive // get name of drive
@ -51,10 +52,12 @@ void RemovableDriveManager::search_for_drives()
BOOL error = GetVolumeInformationW(wpath.c_str(), &volume_name[0], sizeof(volume_name), NULL, NULL, NULL, &file_system_name[0], sizeof(file_system_name)); BOOL error = GetVolumeInformationW(wpath.c_str(), &volume_name[0], sizeof(volume_name), NULL, NULL, NULL, &file_system_name[0], sizeof(file_system_name));
if(error != 0) if(error != 0)
{ {
/*
if (volume_name == L"") if (volume_name == L"")
{ {
volume_name = L"REMOVABLE DRIVE"; volume_name = L"REMOVABLE DRIVE";
} }
*/
if (file_system_name != L"") if (file_system_name != L"")
{ {
ULARGE_INTEGER free_space; ULARGE_INTEGER free_space;
@ -78,6 +81,7 @@ void RemovableDriveManager::eject_drive(const std::string &path)
{ {
if ((*it).path == path) if ((*it).path == path)
{ {
// get handle to device
std::string mpath = "\\\\.\\" + path; std::string mpath = "\\\\.\\" + path;
mpath = mpath.substr(0, mpath.size() - 1); mpath = mpath.substr(0, mpath.size() - 1);
HANDLE handle = CreateFileA(mpath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); HANDLE handle = CreateFileA(mpath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
@ -87,8 +91,12 @@ void RemovableDriveManager::eject_drive(const std::string &path)
return; return;
} }
DWORD deviceControlRetVal(0); DWORD deviceControlRetVal(0);
//these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
//sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr); DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr); DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here but it returns error to me
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr); BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
if (error == 0) if (error == 0)
{ {
@ -130,11 +138,12 @@ std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
void RemovableDriveManager::register_window() void RemovableDriveManager::register_window()
{ {
//creates new unvisible window that is recieving callbacks from system //creates new unvisible window that is recieving callbacks from system
// structure to register
WNDCLASSEX wndClass; WNDCLASSEX wndClass;
wndClass.cbSize = sizeof(WNDCLASSEX); wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0)); wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProcCallback); wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProcCallback);//this is callback
wndClass.cbClsExtra = 0; wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0; wndClass.cbWndExtra = 0;
wndClass.hIcon = LoadIcon(0, IDI_APPLICATION); wndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
@ -169,6 +178,9 @@ void RemovableDriveManager::register_window()
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{ {
// here we need to catch messeges about device removal
// problem is that when ejecting usb (how is it implemented above) there is no messege dispached. Only after physical removal of the device.
//uncomment register_window() in init() to register and comment update() in GUI_App.cpp (only for windows!) to stop recieving periodical updates
LRESULT lRet = 1; LRESULT lRet = 1;
static HDEVNOTIFY hDeviceNotify; static HDEVNOTIFY hDeviceNotify;
@ -187,6 +199,7 @@ INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP
case WM_DEVICECHANGE: case WM_DEVICECHANGE:
{ {
// here is the important
if(wParam == DBT_DEVICEREMOVECOMPLETE) if(wParam == DBT_DEVICEREMOVECOMPLETE)
{ {
- RemovableDriveManager::get_instance().update(0, true); - RemovableDriveManager::get_instance().update(0, true);
@ -207,9 +220,9 @@ void RemovableDriveManager::search_for_drives()
{ {
m_current_drives.clear(); m_current_drives.clear();
m_current_drives.reserve(26);
#if __APPLE__ #if __APPLE__
// if on macos obj-c class will enumerate
if(m_rdmmm) if(m_rdmmm)
{ {
m_rdmmm->list_devices(); m_rdmmm->list_devices();
@ -287,6 +300,8 @@ void RemovableDriveManager::search_path(const std::string &path,const std::strin
} }
void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path) void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path)
{ {
//confirms if the file is removable drive and adds it to vector
//if not same file system - could be removable drive //if not same file system - could be removable drive
if(!compare_filesystem_id(path, parent_path)) if(!compare_filesystem_id(path, parent_path))
{ {
@ -335,7 +350,8 @@ void RemovableDriveManager::eject_drive(const std::string &path)
} }
} }
std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n"; std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
// there is no usable command in c++ so terminal command is used instead
// but neither triggers "succesful safe removal messege"
std::string command = ""; std::string command = "";
#if __APPLE__ #if __APPLE__
command = "diskutil unmount "; command = "diskutil unmount ";

View file

@ -29,26 +29,34 @@ public:
} }
RemovableDriveManager(RemovableDriveManager const&) = delete; RemovableDriveManager(RemovableDriveManager const&) = delete;
void operator=(RemovableDriveManager const&) = delete; void operator=(RemovableDriveManager const&) = delete;
//call only once. on apple register for unmnount callbacks. on windows register for device notification is prepared but not called (eject usb drive on widnows doesnt trigger the callback, sdc ard does), also enumerates devices for first time so init shoud be called on linux too.
//update() searches for removable devices, returns false if empty.
void init(); void init();
bool update(const long time = 0,const bool check = false); //time = 0 is forced update, time expects wxGetLocalTime() //update() searches for removable devices, returns false if empty. /time = 0 is forced update, time expects wxGetLocalTime()
bool update(const long time = 0,const bool check = false);
bool is_drive_mounted(const std::string &path); bool is_drive_mounted(const std::string &path);
void eject_drive(const std::string &path); void eject_drive(const std::string &path);
//returns path to last drive which was used, if none was used, returns device that was enumerated last
std::string get_last_save_path(); std::string get_last_save_path();
//returns path to last drive which was used, if none was used, returns empty string
std::string get_drive_path(); std::string get_drive_path();
std::vector<DriveData> get_all_drives(); std::vector<DriveData> get_all_drives();
bool is_path_on_removable_drive(const std::string &path); bool is_path_on_removable_drive(const std::string &path);
void add_callback(std::function<void()> callback); // callback will notify only if device with last save path was removed // callback will notify only if device with last save path was removed
void erase_callbacks(); // erases all callbacks added by add_callback() void add_callback(std::function<void()> callback);
// erases all callbacks added by add_callback()
void erase_callbacks();
// marks one of the eveices in vector as last used
void set_last_save_path(const std::string &path); void set_last_save_path(const std::string &path);
bool is_last_drive_removed(); bool is_last_drive_removed();
bool is_last_drive_removed_with_update(const long time = 0); // param as update() // param as update()
bool is_last_drive_removed_with_update(const long time = 0);
private: private:
RemovableDriveManager(); RemovableDriveManager();
void search_for_drives(); void search_for_drives();
//triggers callbacks if last used drive was removed
void check_and_notify(); void check_and_notify();
std::string get_drive_from_path(const std::string& path);//returns drive path (same as path in DriveData) if exists otherwise empty string "" //returns drive path (same as path in DriveData) if exists otherwise empty string ""
std::string get_drive_from_path(const std::string& path);
void reset_last_save_path(); void reset_last_save_path();
std::vector<DriveData> m_current_drives; std::vector<DriveData> m_current_drives;
@ -58,8 +66,8 @@ private:
std::string m_last_save_path; std::string m_last_save_path;
#if _WIN32 #if _WIN32
//registers for notifications by creating invisible window
void register_window(); void register_window();
//INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
#else #else
#if __APPLE__ #if __APPLE__
RDMMMWrapper * m_rdmmm; RDMMMWrapper * m_rdmmm;
@ -69,7 +77,7 @@ private:
void inspect_file(const std::string &path, const std::string &parent_path); void inspect_file(const std::string &path, const std::string &parent_path);
#endif #endif
}; };
// apple wrapper for RemovableDriveManagerMM which searches for drives and/or ejects them
#if __APPLE__ #if __APPLE__
class RDMMMWrapper class RDMMMWrapper
{ {