ejecting via Shell COM Object
This commit is contained in:
parent
bb94e386d8
commit
9d34998ac3
@ -18,6 +18,10 @@
|
|||||||
#include <devpropdef.h>
|
#include <devpropdef.h>
|
||||||
#include <devpkey.h>
|
#include <devpkey.h>
|
||||||
#include <usbioctl.h>
|
#include <usbioctl.h>
|
||||||
|
|
||||||
|
#include <atlbase.h>
|
||||||
|
#include <atlcom.h>
|
||||||
|
#include <shldisp.h>
|
||||||
#else
|
#else
|
||||||
// unix, linux & OSX includes
|
// unix, linux & OSX includes
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -80,7 +84,7 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
#if 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
|
||||||
typedef struct _STRING_DESCRIPTOR_NODE
|
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)
|
if (callback_evt_handler)
|
||||||
wxPostEvent(callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(drive_data), true)));
|
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
|
} // namespace
|
||||||
// Called from UI therefore it blocks the UI thread.
|
// Called from UI therefore it blocks the UI thread.
|
||||||
@ -597,27 +652,19 @@ void RemovableDriveManager::eject_drive()
|
|||||||
BOOST_LOG_TRIVIAL(info) << "Ejecting started";
|
BOOST_LOG_TRIVIAL(info) << "Ejecting started";
|
||||||
std::scoped_lock<std::mutex> lock(m_drives_mutex);
|
std::scoped_lock<std::mutex> lock(m_drives_mutex);
|
||||||
auto it_drive_data = this->find_last_save_path_drive_data();
|
auto it_drive_data = this->find_last_save_path_drive_data();
|
||||||
#if 1
|
|
||||||
if (it_drive_data != m_current_drives.end()) {
|
if (it_drive_data != m_current_drives.end()) {
|
||||||
if (!eject_inner(m_last_save_path)) {
|
if (eject_inner(m_last_save_path)) {
|
||||||
// success
|
// success
|
||||||
BOOST_LOG_TRIVIAL(info) << "Ejecting has succeeded.";
|
BOOST_LOG_TRIVIAL(info) << "Ejecting has succeeded.";
|
||||||
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 >(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
|
|
||||||
/*
|
|
||||||
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
|
||||||
@ -626,47 +673,6 @@ 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>({"",""}, false)));
|
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)
|
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)
|
||||||
|
@ -106,12 +106,6 @@ 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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user