ejecting via Shell COM Object
This commit is contained in:
parent
bb94e386d8
commit
9d34998ac3
@ -18,6 +18,10 @@
|
||||
#include <devpropdef.h>
|
||||
#include <devpkey.h>
|
||||
#include <usbioctl.h>
|
||||
|
||||
#include <atlbase.h>
|
||||
#include <atlcom.h>
|
||||
#include <shldisp.h>
|
||||
#else
|
||||
// unix, linux & OSX includes
|
||||
#include <errno.h>
|
||||
@ -80,7 +84,7 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
|
||||
namespace {
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
// From https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview
|
||||
typedef struct _STRING_DESCRIPTOR_NODE
|
||||
{
|
||||
@ -581,6 +585,57 @@ void eject_alt(std::string path, wxEvtHandler* callback_evt_handler, DriveData d
|
||||
if (callback_evt_handler)
|
||||
wxPostEvent(callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(drive_data), true)));
|
||||
}
|
||||
#endif // 0
|
||||
|
||||
// C++ equivavalent of PowerShell script:
|
||||
// $driveEject = New - Object - comObject Shell.Application
|
||||
// $driveEject.Namespace(17).ParseName("E:").InvokeVerb("Eject")
|
||||
// from https://superuser.com/a/1750403
|
||||
bool eject_inner(const std::string& path)
|
||||
{
|
||||
std::wstring wpath = boost::nowide::widen(path);
|
||||
CoInitialize(nullptr);
|
||||
CComPtr<IShellDispatch> pShellDisp;
|
||||
HRESULT hr = pShellDisp.CoCreateInstance(CLSID_Shell, nullptr, CLSCTX_INPROC_SERVER);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Attempt to get Shell pointer has failed.", path);
|
||||
CoUninitialize();
|
||||
return false;
|
||||
}
|
||||
CComPtr<Folder> pFolder;
|
||||
VARIANT vtDrives;
|
||||
VariantInit(&vtDrives);
|
||||
vtDrives.vt = VT_I4;
|
||||
vtDrives.lVal = ssfDRIVES;
|
||||
hr = pShellDisp->NameSpace(vtDrives, &pFolder);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Attempt to create Namespace has failed.", path);
|
||||
CoUninitialize();
|
||||
return false;
|
||||
}
|
||||
CComPtr<FolderItem> pItem;
|
||||
hr = pFolder->ParseName(static_cast<BSTR>(const_cast<wchar_t*>(wpath.c_str())), &pItem);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Attempt to Parse name has failed.", path);
|
||||
CoUninitialize();
|
||||
return false;
|
||||
}
|
||||
VARIANT vtEject;
|
||||
VariantInit(&vtEject);
|
||||
vtEject.vt = VT_BSTR;
|
||||
vtEject.bstrVal = SysAllocString(L"Eject");
|
||||
hr = pItem->InvokeVerb(vtEject);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Attempt to Invoke Verb has failed.", path);
|
||||
VariantClear(&vtEject);
|
||||
CoUninitialize();
|
||||
return false;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << "Ejecting via InvokeVerb has succeeded.";
|
||||
VariantClear(&vtEject);
|
||||
CoUninitialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
// Called from UI therefore it blocks the UI thread.
|
||||
@ -597,27 +652,19 @@ void RemovableDriveManager::eject_drive()
|
||||
BOOST_LOG_TRIVIAL(info) << "Ejecting started";
|
||||
std::scoped_lock<std::mutex> lock(m_drives_mutex);
|
||||
auto it_drive_data = this->find_last_save_path_drive_data();
|
||||
#if 1
|
||||
if (it_drive_data != m_current_drives.end()) {
|
||||
if (!eject_inner(m_last_save_path)) {
|
||||
if (eject_inner(m_last_save_path)) {
|
||||
// success
|
||||
BOOST_LOG_TRIVIAL(info) << "Ejecting has succeeded.";
|
||||
assert(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)));
|
||||
} 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
|
||||
// this should not happen, throwing exception might be the way here
|
||||
/*
|
||||
BOOST_LOG_TRIVIAL(error) << "Ejecting has failed.";
|
||||
assert(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)));
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
// drive not found in m_current_drives
|
||||
@ -626,47 +673,6 @@ void RemovableDriveManager::eject_drive()
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>({"",""}, false)));
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
// Implementation used until 2.5.x version
|
||||
// Some usb drives does not eject properly (still visible in file explorer). Some even does not write all content and eject.
|
||||
if (it_drive_data != m_current_drives.end()) {
|
||||
// get handle to device
|
||||
std::string mpath = "\\\\.\\" + m_last_save_path;
|
||||
mpath = mpath.substr(0, mpath.size() - 1);
|
||||
HANDLE handle = CreateFileW(boost::nowide::widen(mpath).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) << "Ejecting " << mpath << " failed (handle == INVALID_HANDLE_VALUE): " << GetLastError();
|
||||
assert(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)));
|
||||
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 = DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
BOOST_LOG_TRIVIAL(error) << "FSCTL_LOCK_VOLUME " << e1 << " ; " << deviceControlRetVal << " ; " << GetLastError();
|
||||
BOOL e2 = DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
BOOST_LOG_TRIVIAL(error) << "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) << "Ejecting " << mpath << " failed (IOCTL_STORAGE_EJECT_MEDIA)" << deviceControlRetVal << " " << GetLastError();
|
||||
assert(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)));
|
||||
return;
|
||||
}
|
||||
CloseHandle(handle);
|
||||
BOOST_LOG_TRIVIAL(info) << "Ejecting finished";
|
||||
assert(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)));
|
||||
m_current_drives.erase(it_drive_data);
|
||||
}
|
||||
#endif // 0
|
||||
}
|
||||
|
||||
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)
|
||||
|
@ -106,12 +106,6 @@ private:
|
||||
#endif /* _WIN32 */
|
||||
#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.
|
||||
std::vector<DriveData> search_for_removable_drives() const;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user