Merge branch 'dk_alt_eject'

This commit is contained in:
David Kocik 2023-04-05 10:32:22 +02:00
commit bf87b89ea2
2 changed files with 86 additions and 28 deletions

View File

@ -78,32 +78,7 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
} }
namespace { namespace {
int eject_alt(const std::wstring& volume_access_path)
{
HANDLE handle = CreateFileW(volume_access_path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
BOOST_LOG_TRIVIAL(error) << "Alt Ejecting " << volume_access_path << " failed (handle == INVALID_HANDLE_VALUE): " << GetLastError();
return 1;
}
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
BOOL e1 = DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
BOOST_LOG_TRIVIAL(debug) << "FSCTL_LOCK_VOLUME " << e1 << " ; " << deviceControlRetVal << " ; " << GetLastError();
BOOL e2 = DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
BOOST_LOG_TRIVIAL(debug) << "FSCTL_DISMOUNT_VOLUME " << e2 << " ; " << deviceControlRetVal << " ; " << GetLastError();
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here with FALSE as third parameter, which should set PreventMediaRemoval
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
if (error == 0) {
CloseHandle(handle);
BOOST_LOG_TRIVIAL(error) << "Alt Ejecting " << volume_access_path << " failed (IOCTL_STORAGE_EJECT_MEDIA)" << deviceControlRetVal << " " << GetLastError();
return 1;
}
CloseHandle(handle);
BOOST_LOG_TRIVIAL(info) << "Alt Ejecting finished";
return 0;
}
// From https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview // From https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview
@ -485,7 +460,7 @@ int eject_inner(const std::string& path)
DEVINST dev_inst = get_dev_inst_by_device_number(device_number, drive_type, dos_device_name); DEVINST dev_inst = get_dev_inst_by_device_number(device_number, drive_type, dos_device_name);
if (dev_inst == 0) { if (dev_inst == 0) {
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1%: Invalid device instance handle. Going to try alternative ejecting method.", path); BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1%: Invalid device instance handle. Going to try alternative ejecting method.", path);
return eject_alt(volume_access_path); return 1;
} }
PNP_VETO_TYPE veto_type = PNP_VetoTypeUnknown; PNP_VETO_TYPE veto_type = PNP_VetoTypeUnknown;
@ -524,7 +499,7 @@ int eject_inner(const std::string& path)
if (res == CR_SUCCESS && veto_type == PNP_VetoTypeUnknown) { if (res == CR_SUCCESS && veto_type == PNP_VetoTypeUnknown) {
return 0; return 0;
} }
BOOST_LOG_TRIVIAL(warning) << GUI::format("Ejecting of %1% has failed: Request to eject device has failed. Another request will follow. Veto type: %2%", path, veto_type); BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Request to eject device has failed. Another request will follow. Veto type: %2%", path, veto_type);
// But on some PC, SD cards ejects only with its own dev_inst. // But on some PC, SD cards ejects only with its own dev_inst.
res = CM_Request_Device_EjectW(dev_inst, &veto_type, veto_name, MAX_PATH, 0); res = CM_Request_Device_EjectW(dev_inst, &veto_type, veto_name, MAX_PATH, 0);
@ -537,6 +512,76 @@ int eject_inner(const std::string& path)
return 1; return 1;
} }
// this method should be called in worker thread. It does SLEEP.
// Alternative ejecting to eject_inner method.
// Success or Fail is passed directly to UI by events
void eject_alt(std::string path, wxEvtHandler* callback_evt_handler, DriveData drive_data)
{
// Transform path to correct form
std::wstring wpath = std::wstring();
wpath += boost::nowide::widen(path)[0]; // drive letter wide
wpath[0] &= ~0x20; // make sure drive letter is uppercase
std::wstring volume_access_path = L"\\\\.\\" + wpath + L":"; // for CreateFile
HANDLE handle = CreateFileW(volume_access_path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
BOOST_LOG_TRIVIAL(error) << "Alt ejecting " << volume_access_path << " failed (handle == INVALID_HANDLE_VALUE): " << GetLastError();
assert(callback_evt_handler);
if (callback_evt_handler)
wxPostEvent(callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(std::move(drive_data), false)));
return;
}
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
BOOL e1;
for (int i = 0; i < 10; i++) {
e1 = DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
if (e1)
break;
BOOST_LOG_TRIVIAL(warning) << "Alt Ejecting: FSCTL_LOCK_VOLUME failed. Try " << i << ". " << GetLastError();
Sleep(500);
}
if (e1 == 0) {
CloseHandle(handle);
BOOST_LOG_TRIVIAL(error) << "Alt Ejecting " << volume_access_path << " failed to Lock the device. Ejecting has failed. " << GetLastError();
assert(callback_evt_handler);
if (callback_evt_handler)
wxPostEvent(callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(std::move(drive_data), false)));
return;
}
BOOST_LOG_TRIVIAL(info) << "Alt Ejecting: FSCTL_LOCK_VOLUME success.";
BOOL e2 = DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
if (e2 == 0) {
CloseHandle(handle);
BOOST_LOG_TRIVIAL(error) << "Alt Ejecting " << volume_access_path << " failed to dismount the volume. Ejecting has failed. " << GetLastError();
assert(callback_evt_handler);
if (callback_evt_handler)
wxPostEvent(callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(std::move(drive_data), false)));
return;
}
BOOST_LOG_TRIVIAL(info) << "Alt Ejecting: FSCTL_DISMOUNT_VOLUME success.";
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here with FALSE as third parameter, which should set PreventMediaRemoval
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
if (error == 0) {
CloseHandle(handle);
BOOST_LOG_TRIVIAL(error) << "Alt Ejecting " << volume_access_path << " failed (IOCTL_STORAGE_EJECT_MEDIA)" << deviceControlRetVal << " " << GetLastError();
assert(callback_evt_handler);
if (callback_evt_handler)
wxPostEvent(callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(std::move(drive_data), false)));
return;
}
CloseHandle(handle);
BOOST_LOG_TRIVIAL(info) << "Alt Ejecting finished";
assert(callback_evt_handler);
if (callback_evt_handler)
wxPostEvent(callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(drive_data), true)));
}
} // namespace } // namespace
// Called from UI therefore it blocks the UI thread. // Called from UI therefore it blocks the UI thread.
// It also blocks updates at the worker thread. // It also blocks updates at the worker thread.
@ -561,12 +606,18 @@ void RemovableDriveManager::eject_drive()
if (m_callback_evt_handler) if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(*it_drive_data), true))); wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(*it_drive_data), true)));
} else { } else {
if (m_eject_thread.joinable())
m_eject_thread.join();
m_eject_thread = boost::thread(eject_alt, m_last_save_path, m_callback_evt_handler, std::move(*it_drive_data));
// failed to eject // failed to eject
// this should not happen, throwing exception might be the way here // this should not happen, throwing exception might be the way here
/*
BOOST_LOG_TRIVIAL(error) << "Ejecting has failed."; BOOST_LOG_TRIVIAL(error) << "Ejecting has failed.";
assert(m_callback_evt_handler); assert(m_callback_evt_handler);
if (m_callback_evt_handler) if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false))); wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
*/
} }
} else { } else {
// drive not found in m_current_drives // drive not found in m_current_drives

View File

@ -93,6 +93,7 @@ private:
bool m_initialized { false }; bool m_initialized { false };
wxEvtHandler* m_callback_evt_handler { nullptr }; wxEvtHandler* m_callback_evt_handler { nullptr };
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS #ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
// Worker thread, worker thread synchronization and callbacks to the UI thread. // Worker thread, worker thread synchronization and callbacks to the UI thread.
void thread_proc(); void thread_proc();
@ -105,6 +106,12 @@ private:
#endif /* _WIN32 */ #endif /* _WIN32 */
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS #endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
#ifdef _WIN32
// Another worker thread, used only to perform alt_eject method (external SD cards only).
// Does not share data with m_thread
boost::thread m_eject_thread;
#endif /* _WIN32 */
// Called from update() to enumerate removable drives. // Called from update() to enumerate removable drives.
std::vector<DriveData> search_for_removable_drives() const; std::vector<DriveData> search_for_removable_drives() const;