Skip to content

Commit

Permalink
Now purge_all doesn't show a dialog on windows.
Browse files Browse the repository at this point in the history
  • Loading branch information
ArturKovacs committed Nov 7, 2019
1 parent 22d5181 commit 07e3bc2
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 32 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ scopeguard = "1.0.0"

[target.'cfg(windows)'.dependencies.winapi]
git = "https://github.com/ArturKovacs/winapi-rs.git"
rev = "b5c196ad2aa27b26e0ed0ae1f35ac790d677a3b5"
rev = "fe297b9830c804b274e2206d3aebfe9dc33e39cb"
features = [
"combaseapi", "objbase", "shlobj", "shellapi", "winerror", "shlobj", "shlwapi", "shobjidl_core",
"shobjidl", "oleauto", "oaidl", "wtypes", "errhandlingapi", "timezoneapi", "winuser"
Expand Down
5 changes: 0 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,3 @@ where
{
platform::remove_all(paths)
}

/// Returns true if the functions are implemented on the current platform.
pub fn is_implemented() -> bool {
platform::is_implemented()
}
8 changes: 0 additions & 8 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,6 @@ fn purge() {
.collect();
assert_eq!(targets.len(), batches * files_per_batch);
trash::purge_all(targets).unwrap();
// Ugly hack but need to wait for example on Windows one must wait a bit
// before the items acctually leave the trash
//std::thread::sleep(std::time::Duration::from_secs(8));
let remaining = trash::list()
.unwrap()
.into_iter()
Expand Down Expand Up @@ -146,11 +143,6 @@ fn restore() {
.collect();
assert_eq!(targets.len(), file_count);
trash::restore_all(targets).unwrap();

// Ugly hack but need to wait for example on Windows one must wait a bit
// before the items acctually leave the trash
//std::thread::sleep(std::time::Duration::from_secs(8));

let remaining = trash::list()
.unwrap()
.into_iter()
Expand Down
86 changes: 68 additions & 18 deletions src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use winapi::{
shared::windef::HWND,
shared::winerror::{HRESULT_FROM_WIN32, SUCCEEDED, S_OK},
shared::wtypes::{VT_BSTR, VT_DATE},
um::combaseapi::{CoInitializeEx, CoTaskMemFree, CoUninitialize},
um::combaseapi::{CoInitializeEx, CoTaskMemFree, CoUninitialize, CoCreateInstance, CLSCTX_ALL},
um::errhandlingapi::GetLastError,
um::minwinbase::SYSTEMTIME,
um::oaidl::VARIANT,
Expand All @@ -24,20 +24,21 @@ use winapi::{
},
um::oleauto::{VariantChangeType, VariantClear, VariantTimeToSystemTime},
um::shellapi::{
SHFileOperationW, FOF_ALLOWUNDO, FOF_SILENT, FOF_WANTNUKEWARNING, FO_DELETE,
SHFileOperationW, FOF_ALLOWUNDO, FOF_NO_UI, FOF_SILENT, FOF_WANTNUKEWARNING, FO_DELETE,
SHFILEOPSTRUCTW,
},
um::winuser::{CreatePopupMenu, DestroyMenu},
um::shlobj::CSIDL_BITBUCKET,
um::shlwapi::StrRetToStrW,
um::shobjidl_core::{
IContextMenu, IEnumIDList, IShellFolder, IShellFolder2, SHCONTF_FOLDERS, SHCONTF_NONFOLDERS, SHGDNF,
SHGDN_FORPARSING, SHGDN_INFOLDER, CMF_NORMAL, CMINVOKECOMMANDINFO
IContextMenu, IEnumIDList, IShellFolder, IShellFolder2, IFileOperation, FileOperation, IShellItem, SHCONTF_FOLDERS, SHCONTF_NONFOLDERS, SHGDNF,
SHGDN_FORPARSING, SHGDN_INFOLDER, CMF_NORMAL, CMINVOKECOMMANDINFO, CMINVOKECOMMANDINFOEX, CMIC_MASK_FLAG_NO_UI, SHCreateItemWithParent
},
um::shtypes::{PCUITEMID_CHILD, PCUITEMID_CHILD_ARRAY, PIDLIST_RELATIVE, PIDLIST_ABSOLUTE, PITEMID_CHILD, SHCOLUMNID, STRRET},
um::timezoneapi::SystemTimeToFileTime,
um::winnt::{PCZZWSTR, PWSTR, ULARGE_INTEGER},
Interface,
Class
};

use crate::{Error, TrashItem};
Expand Down Expand Up @@ -68,10 +69,6 @@ macro_rules! return_err_on_fail {
})
}

pub fn is_implemented() -> bool {
true
}

pub fn remove_all<I, T>(paths: I) -> Result<(), Error>
where
I: IntoIterator<Item = T>,
Expand Down Expand Up @@ -177,7 +174,61 @@ pub fn purge_all<I>(items: I) -> Result<(), Error>
where
I: IntoIterator<Item = TrashItem>,
{
execute_verb_on_all(items.into_iter(), "delete")
ensure_com_initialized();
unsafe {
let mut recycle_bin = MaybeUninit::<*mut IShellFolder2>::uninit();
bind_to_csidl(
CSIDL_BITBUCKET,
&IShellFolder2::uuidof() as *const _,
recycle_bin.as_mut_ptr() as *mut *mut c_void,
)?;
let recycle_bin = recycle_bin.assume_init();
defer! {{ (*recycle_bin).Release(); }}
let mut pfo = MaybeUninit::<*mut IFileOperation>::uninit();
return_err_on_fail! {
CoCreateInstance(
&FileOperation::uuidof() as *const _,
std::ptr::null_mut(),
CLSCTX_ALL,
&IFileOperation::uuidof() as *const _,
pfo.as_mut_ptr() as *mut *mut c_void,
)
};
let pfo = pfo.assume_init();
defer!{{ (*pfo).Release(); }}
return_err_on_fail! { (*pfo).SetOperationFlags(FOF_NO_UI as DWORD) };
for item in items {
let mut id_wstr: Vec<_> = item.id.encode_wide().chain(std::iter::once(0)).collect();
let mut pidl = MaybeUninit::<PIDLIST_RELATIVE>::uninit();
return_err_on_fail! {
(*recycle_bin).ParseDisplayName(
0 as _,
std::ptr::null_mut(),
id_wstr.as_mut_ptr(),
std::ptr::null_mut(),
pidl.as_mut_ptr(),
std::ptr::null_mut(),
)
};
let pidl = pidl.assume_init();
defer! {{ CoTaskMemFree(pidl as LPVOID); }}
let mut shi = MaybeUninit::<*mut IShellItem>::uninit();
return_err_on_fail! {
SHCreateItemWithParent(
std::ptr::null_mut(),
recycle_bin as *mut _,
pidl,
&IShellItem::uuidof() as *const _,
shi.as_mut_ptr() as *mut *mut c_void,
)
};
let shi = shi.assume_init();
defer!{{ (*shi).Release(); }}
return_err_on_fail! { (*pfo).DeleteItem(shi, std::ptr::null_mut()) };
}
return_err_on_fail! { (*pfo).PerformOperations() };
Ok(())
}
}

pub fn restore_all<I>(items: I) -> Result<(), Error>
Expand All @@ -192,10 +243,10 @@ impl CoInitializer {
fn new() -> CoInitializer {
//let first = INITIALIZER_THREAD_COUNT.fetch_add(1, Ordering::SeqCst) == 0;
let mut init_mode = 0;
if cfg!(coinit_apartmentthreaded) {
init_mode |= COINIT_APARTMENTTHREADED;
} else if cfg!(coinit_multithreaded) {
if cfg!(coinit_multithreaded) {
init_mode |= COINIT_MULTITHREADED;
} else if cfg!(coinit_apartmentthreaded) {
init_mode |= COINIT_APARTMENTTHREADED;
}
// else missing intentionaly. These flags can be combined
if cfg!(coinit_disable_ole1dde) {
Expand All @@ -206,7 +257,7 @@ impl CoInitializer {
}
let hr = unsafe { CoInitializeEx(std::ptr::null_mut(), init_mode) };
if !SUCCEEDED(hr) {
panic!(format!("Call to CoInitializeEx failed. HRESULT: {:X}", hr));
panic!(format!("Call to CoInitializeEx failed. HRESULT: {:X}. Consider using `trash` with the feature `coinit_multithreaded`", hr));
}
CoInitializer {}
}
Expand Down Expand Up @@ -331,8 +382,6 @@ unsafe fn get_date_unix(
Ok(unix_time)
}



unsafe fn invoke_verb(pcm: *mut IContextMenu, verb: &'static str) -> Result<(), Error> {
// Recycle bin verbs:
// undelete
Expand All @@ -349,10 +398,11 @@ unsafe fn invoke_verb(pcm: *mut IContextMenu, verb: &'static str) -> Result<(),
defer! {{ DestroyMenu(hmenu); }}
return_err_on_fail!{(*pcm).QueryContextMenu(hmenu, 0, 1, 0x7FFF, CMF_NORMAL)};
let zero_terminated_verb: Vec<_> = verb.bytes().chain(std::iter::once(0)).collect();
let mut info = MaybeUninit::<CMINVOKECOMMANDINFO>::zeroed().assume_init();
info.cbSize = std::mem::size_of::<CMINVOKECOMMANDINFO>() as u32;
let mut info = MaybeUninit::<CMINVOKECOMMANDINFOEX>::zeroed().assume_init();
info.cbSize = std::mem::size_of::<CMINVOKECOMMANDINFOEX>() as u32;
info.lpVerb = zero_terminated_verb.as_ptr() as *const i8;
return_err_on_fail!{(*pcm).InvokeCommand(&mut info as *mut _)};
info.fMask = CMIC_MASK_FLAG_NO_UI;
return_err_on_fail!{(*pcm).InvokeCommand(&mut info as *mut _ as *mut _)};
Ok(())
}

Expand Down

0 comments on commit 07e3bc2

Please sign in to comment.