From 6168ea633eb9697d78f4ee0ce17bb075787f0c95 Mon Sep 17 00:00:00 2001
From: Jake Shadle <jake.shadle@embark-studios.com>
Date: Wed, 26 Apr 2023 21:08:49 +0200
Subject: [PATCH] Replace `windows` with `windows-sys` (#118)

---
 .github/workflows/rust.yml                    |   4 +-
 Cargo.toml                                    |   5 +-
 build.rs                                      |  23 +-
 src/backend/win_cid/file_dialog.rs            |   5 +-
 src/backend/win_cid/file_dialog/com.rs        | 268 ++++++++++++++
 src/backend/win_cid/file_dialog/dialog_ffi.rs | 328 +++++++++++-------
 .../win_cid/file_dialog/dialog_future.rs      |  10 +-
 src/backend/win_cid/message_dialog.rs         | 122 ++++---
 src/backend/win_cid/utils.rs                  |  30 +-
 9 files changed, 565 insertions(+), 230 deletions(-)
 create mode 100644 src/backend/win_cid/file_dialog/com.rs

diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 8b38323..cd21fcd 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -36,14 +36,14 @@ jobs:
     name: ${{ matrix.name }}
     runs-on: ${{ matrix.os }}
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: "[Ubuntu] install dependencies"
       if: matrix.name == 'Ubuntu GTK'
       run: sudo apt update && sudo apt install libgtk-3-dev
     - name: "[WASM] rustup"
       if: matrix.name == 'WASM32'
       run: rustup target add wasm32-unknown-unknown
-    - uses: Swatinem/rust-cache@v1
+    - uses: Swatinem/rust-cache@v2
     - name: Build
       run: cargo build --target ${{ matrix.target }} ${{ matrix.flags }}
     - name: Test
diff --git a/Cargo.toml b/Cargo.toml
index 06d23df..d860baa 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,7 +15,7 @@ default = ["gtk3"]
 file-handle-inner = []
 gtk3 = ["gtk-sys", "glib-sys", "gobject-sys"]
 xdg-portal = ["ashpd", "urlencoding", "pollster"]
-common-controls-v6 = ["windows/Win32_UI_Controls"]
+common-controls-v6 = ["windows-sys/Win32_UI_Controls"]
 
 [dev-dependencies]
 futures = "0.3.12"
@@ -32,7 +32,7 @@ block = "0.1.6"
 objc-foundation = "0.1.1"
 
 [target.'cfg(target_os = "windows")'.dependencies]
-windows = { version = "0.44", features = [
+windows-sys = { version = "0.48", features = [
   "Win32_Foundation",
   "Win32_System_Com",
   "Win32_UI_Shell_Common",
@@ -76,4 +76,3 @@ name = "async"
 
 [package.metadata.docs.rs]
 features = ["file-handle-inner"]
-
diff --git a/build.rs b/build.rs
index cf8219a..6ec72c3 100644
--- a/build.rs
+++ b/build.rs
@@ -1,13 +1,18 @@
 fn main() {
-    let target = std::env::var("TARGET").unwrap();
+    let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("target OS not detected");
 
-    if target.contains("darwin") {
-        println!("cargo:rustc-link-lib=framework=AppKit");
-    }
-
-    #[cfg(all(feature = "gtk3", feature = "xdg-portal"))]
-    compile_error!("You can't enable both GTK3 & XDG Portal features at once");
+    match target_os.as_str() {
+        "macos" => println!("cargo:rustc-link-lib=framework=AppKit"),
+        "windows" => {}
+        _ => {
+            let gtk = std::env::var_os("CARGO_FEATURE_GTK3").is_some();
+            let xdg = std::env::var_os("CARGO_FEATURE_XDG_PORTAL").is_some();
 
-    #[cfg(not(any(feature = "gtk3", feature = "xdg-portal")))]
-    compile_error!("You need to choose at least one backend: `gtk3` or `xdg-portal` features");
+            if gtk && xdg {
+                panic!("You can't enable both `gtk3` and `xdg-portal` features at once");
+            } else if !gtk && !xdg {
+                panic!("You need to choose at least one backend: `gtk3` or `xdg-portal` features");
+            }
+        }
+    }
 }
diff --git a/src/backend/win_cid/file_dialog.rs b/src/backend/win_cid/file_dialog.rs
index ae4481d..d5c414a 100644
--- a/src/backend/win_cid/file_dialog.rs
+++ b/src/backend/win_cid/file_dialog.rs
@@ -1,7 +1,8 @@
+mod com;
 pub mod dialog_ffi;
 mod dialog_future;
 
-use dialog_ffi::IDialog;
+use dialog_ffi::{IDialog, Result};
 use dialog_future::{multiple_return_future, single_return_future};
 
 use crate::backend::DialogFutureType;
@@ -10,8 +11,6 @@ use crate::FileHandle;
 
 use std::path::PathBuf;
 
-use windows::core::Result;
-
 use super::utils::init_com;
 
 //
diff --git a/src/backend/win_cid/file_dialog/com.rs b/src/backend/win_cid/file_dialog/com.rs
new file mode 100644
index 0000000..acb192c
--- /dev/null
+++ b/src/backend/win_cid/file_dialog/com.rs
@@ -0,0 +1,268 @@
+#![allow(non_snake_case)]
+
+use std::ffi::c_void;
+use windows_sys::core::{HRESULT, PCWSTR, PWSTR};
+pub use windows_sys::{
+    core::GUID,
+    Win32::{
+        Foundation::HWND,
+        UI::Shell::{Common::COMDLG_FILTERSPEC, FILEOPENDIALOGOPTIONS, SIGDN, SIGDN_FILESYSPATH},
+    },
+};
+
+pub(crate) type Result<T> = std::result::Result<T, HRESULT>;
+
+#[inline]
+pub(super) fn wrap_err(hresult: HRESULT) -> Result<()> {
+    if hresult >= 0 {
+        Ok(())
+    } else {
+        Err(hresult)
+    }
+}
+
+#[inline]
+unsafe fn read_to_string(ptr: *const u16) -> String {
+    let mut cursor = ptr;
+
+    while *cursor != 0 {
+        cursor = cursor.add(1);
+    }
+
+    let slice = std::slice::from_raw_parts(ptr, cursor.offset_from(ptr) as usize);
+    String::from_utf16(slice).unwrap()
+}
+
+#[repr(C)]
+pub(super) struct Interface<T> {
+    vtable: *mut T,
+}
+
+impl<T> Interface<T> {
+    #[inline]
+    pub(super) fn vtbl(&self) -> &T {
+        unsafe { &*self.vtable }
+    }
+}
+
+#[repr(C)]
+pub(super) struct IUnknownV {
+    __query_interface: usize,
+    __add_ref: usize,
+    pub(super) release: unsafe extern "system" fn(this: *mut c_void) -> u32,
+}
+
+pub(super) type IUnknown = Interface<IUnknownV>;
+
+#[inline]
+fn drop_impl(ptr: *mut c_void) {
+    unsafe {
+        ((*ptr.cast::<IUnknown>()).vtbl().release)(ptr);
+    }
+}
+
+#[repr(C)]
+pub(super) struct IShellItemV {
+    base: IUnknownV,
+    BindToHandler: unsafe extern "system" fn(
+        this: *mut c_void,
+        pbc: *mut c_void,
+        bhid: *const GUID,
+        riid: *const GUID,
+        ppv: *mut *mut c_void,
+    ) -> HRESULT,
+    GetParent: unsafe extern "system" fn(this: *mut c_void, ppsi: *mut *mut c_void) -> HRESULT,
+    GetDisplayName: unsafe extern "system" fn(
+        this: *mut c_void,
+        sigdnname: SIGDN,
+        ppszname: *mut PWSTR,
+    ) -> HRESULT,
+    GetAttributes: usize,
+    Compare: unsafe extern "system" fn(
+        this: *mut c_void,
+        psi: *mut c_void,
+        hint: u32,
+        piorder: *mut i32,
+    ) -> HRESULT,
+}
+
+#[repr(transparent)]
+pub(super) struct IShellItem(pub(super) *mut Interface<IShellItemV>);
+
+impl IShellItem {
+    pub(super) fn get_path(&self) -> Result<std::path::PathBuf> {
+        let filename = unsafe {
+            let mut dname = std::mem::MaybeUninit::uninit();
+            wrap_err(((*self.0).vtbl().GetDisplayName)(
+                self.0.cast(),
+                SIGDN_FILESYSPATH,
+                dname.as_mut_ptr(),
+            ))?;
+
+            let dname = dname.assume_init();
+            let fname = read_to_string(dname);
+            windows_sys::Win32::System::Com::CoTaskMemFree(dname.cast());
+            fname
+        };
+
+        Ok(filename.into())
+    }
+}
+
+impl Drop for IShellItem {
+    fn drop(&mut self) {
+        drop_impl(self.0.cast());
+    }
+}
+
+#[repr(C)]
+struct IShellItemArrayV {
+    base: IUnknownV,
+    BindToHandler: unsafe extern "system" fn(
+        this: *mut c_void,
+        pbc: *mut c_void,
+        bhid: *const GUID,
+        riid: *const GUID,
+        ppvout: *mut *mut c_void,
+    ) -> HRESULT,
+    GetPropertyStore: usize,
+    GetPropertyDescriptionList: usize,
+    GetAttributes: usize,
+    GetCount: unsafe extern "system" fn(this: *mut c_void, pdwnumitems: *mut u32) -> HRESULT,
+    GetItemAt: unsafe extern "system" fn(
+        this: *mut c_void,
+        dwindex: u32,
+        ppsi: *mut IShellItem,
+    ) -> HRESULT,
+    EnumItems:
+        unsafe extern "system" fn(this: *mut c_void, ppenumshellitems: *mut *mut c_void) -> HRESULT,
+}
+
+#[repr(transparent)]
+pub(super) struct IShellItemArray(*mut Interface<IShellItemArrayV>);
+
+impl IShellItemArray {
+    #[inline]
+    pub(super) fn get_count(&self) -> Result<u32> {
+        let mut count = 0;
+        unsafe {
+            wrap_err(((*self.0).vtbl().GetCount)(self.0.cast(), &mut count))?;
+        }
+        Ok(count)
+    }
+
+    #[inline]
+    pub(super) fn get_item_at(&self, index: u32) -> Result<IShellItem> {
+        let mut item = std::mem::MaybeUninit::uninit();
+        unsafe {
+            wrap_err(((*self.0).vtbl().GetItemAt)(
+                self.0.cast(),
+                index,
+                item.as_mut_ptr(),
+            ))?;
+            Ok(item.assume_init())
+        }
+    }
+}
+
+impl Drop for IShellItemArray {
+    fn drop(&mut self) {
+        drop_impl(self.0.cast());
+    }
+}
+
+#[repr(C)]
+pub(super) struct IModalWindowV {
+    base: IUnknownV,
+    pub(super) Show: unsafe extern "system" fn(this: *mut c_void, owner: HWND) -> HRESULT,
+}
+
+/// <https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifiledialog>
+#[repr(C)]
+pub(super) struct IFileDialogV {
+    pub(super) base: IModalWindowV,
+    pub(super) SetFileTypes: unsafe extern "system" fn(
+        this: *mut c_void,
+        cfiletypes: u32,
+        rgfilterspec: *const COMDLG_FILTERSPEC,
+    ) -> HRESULT,
+    SetFileTypeIndex: unsafe extern "system" fn(this: *mut c_void, ifiletype: u32) -> HRESULT,
+    GetFileTypeIndex: unsafe extern "system" fn(this: *mut c_void, pifiletype: *mut u32) -> HRESULT,
+    Advise: unsafe extern "system" fn(
+        this: *mut c_void,
+        pfde: *mut c_void,
+        pdwcookie: *mut u32,
+    ) -> HRESULT,
+    Unadvise: unsafe extern "system" fn(this: *mut c_void, dwcookie: u32) -> HRESULT,
+    pub(super) SetOptions:
+        unsafe extern "system" fn(this: *mut c_void, fos: FILEOPENDIALOGOPTIONS) -> HRESULT,
+    GetOptions:
+        unsafe extern "system" fn(this: *mut c_void, pfos: *mut FILEOPENDIALOGOPTIONS) -> HRESULT,
+    SetDefaultFolder: unsafe extern "system" fn(this: *mut c_void, psi: *mut c_void) -> HRESULT,
+    pub(super) SetFolder: unsafe extern "system" fn(this: *mut c_void, psi: *mut c_void) -> HRESULT,
+    GetFolder: unsafe extern "system" fn(this: *mut c_void, ppsi: *mut *mut c_void) -> HRESULT,
+    GetCurrentSelection:
+        unsafe extern "system" fn(this: *mut c_void, ppsi: *mut *mut c_void) -> HRESULT,
+    pub(super) SetFileName:
+        unsafe extern "system" fn(this: *mut c_void, pszname: PCWSTR) -> HRESULT,
+    GetFileName: unsafe extern "system" fn(this: *mut c_void, pszname: *mut PWSTR) -> HRESULT,
+    pub(super) SetTitle: unsafe extern "system" fn(this: *mut c_void, psztitle: PCWSTR) -> HRESULT,
+    SetOkButtonLabel: unsafe extern "system" fn(this: *mut c_void, psztext: PCWSTR) -> HRESULT,
+    SetFileNameLabel: unsafe extern "system" fn(this: *mut c_void, pszlabel: PCWSTR) -> HRESULT,
+    pub(super) GetResult:
+        unsafe extern "system" fn(this: *mut c_void, ppsi: *mut IShellItem) -> HRESULT,
+    AddPlace: unsafe extern "system" fn(
+        this: *mut c_void,
+        psi: *mut c_void,
+        fdap: windows_sys::Win32::UI::Shell::FDAP,
+    ) -> HRESULT,
+    pub(super) SetDefaultExtension:
+        unsafe extern "system" fn(this: *mut c_void, pszdefaultextension: PCWSTR) -> HRESULT,
+    Close: unsafe extern "system" fn(this: *mut c_void, hr: HRESULT) -> HRESULT,
+    SetClientGuid: unsafe extern "system" fn(this: *mut c_void, guid: *const GUID) -> HRESULT,
+    ClearClientData: unsafe extern "system" fn(this: *mut c_void) -> HRESULT,
+    SetFilter: unsafe extern "system" fn(this: *mut c_void, pfilter: *mut c_void) -> HRESULT,
+}
+
+#[repr(transparent)]
+pub(super) struct IFileDialog(pub(super) *mut Interface<IFileDialogV>);
+
+impl Drop for IFileDialog {
+    fn drop(&mut self) {
+        drop_impl(self.0.cast());
+    }
+}
+
+/// <https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifileopendialog>
+#[repr(C)]
+pub(super) struct IFileOpenDialogV {
+    pub(super) base: IFileDialogV,
+    /// <https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileopendialog-getresults>
+    GetResults:
+        unsafe extern "system" fn(this: *mut c_void, ppenum: *mut IShellItemArray) -> HRESULT,
+    GetSelectedItems:
+        unsafe extern "system" fn(this: *mut c_void, ppsai: *mut *mut c_void) -> HRESULT,
+}
+
+#[repr(transparent)]
+pub(super) struct IFileOpenDialog(pub(super) *mut Interface<IFileOpenDialogV>);
+
+impl IFileOpenDialog {
+    #[inline]
+    pub(super) fn get_results(&self) -> Result<IShellItemArray> {
+        let mut res = std::mem::MaybeUninit::uninit();
+        unsafe {
+            wrap_err((((*self.0).vtbl()).GetResults)(
+                self.0.cast(),
+                res.as_mut_ptr(),
+            ))?;
+            Ok(res.assume_init())
+        }
+    }
+}
+
+impl Drop for IFileOpenDialog {
+    fn drop(&mut self) {
+        drop_impl(self.0.cast());
+    }
+}
diff --git a/src/backend/win_cid/file_dialog/dialog_ffi.rs b/src/backend/win_cid/file_dialog/dialog_ffi.rs
index 5a87ed9..c0f73a4 100644
--- a/src/backend/win_cid/file_dialog/dialog_ffi.rs
+++ b/src/backend/win_cid/file_dialog/dialog_ffi.rs
@@ -1,161 +1,257 @@
+use super::super::utils::str_to_vec_u16;
+pub(crate) use super::com::Result;
+use super::com::{
+    wrap_err, IFileDialog, IFileDialogV, IFileOpenDialog, IShellItem, COMDLG_FILTERSPEC,
+    FILEOPENDIALOGOPTIONS, HWND,
+};
 use crate::FileDialog;
 
-use std::{ffi::OsStr, iter::once, os::windows::ffi::OsStrExt, path::PathBuf};
-
-use windows::core::{Result, PCWSTR, PWSTR};
-use windows::Win32::{
-    Foundation::HWND,
-    System::Com::{CoCreateInstance, CoTaskMemFree, CLSCTX_INPROC_SERVER},
-    UI::Shell::{
-        Common::COMDLG_FILTERSPEC, FileOpenDialog, FileSaveDialog, IFileDialog, IFileOpenDialog,
-        IFileSaveDialog, IShellItem, SHCreateItemFromParsingName, FILEOPENDIALOGOPTIONS,
-        FOS_ALLOWMULTISELECT, FOS_PICKFOLDERS, SIGDN_FILESYSPATH,
+use windows_sys::{
+    core::GUID,
+    Win32::{
+        System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER},
+        UI::Shell::{
+            FileOpenDialog, FileSaveDialog, SHCreateItemFromParsingName, FOS_ALLOWMULTISELECT,
+            FOS_PICKFOLDERS,
+        },
     },
 };
 
+use std::{ffi::c_void, path::PathBuf};
+
 use raw_window_handle::RawWindowHandle;
 
-unsafe fn read_to_string(ptr: PWSTR) -> String {
-    let mut len = 0usize;
-    let mut cursor = ptr;
-    loop {
-        let val = cursor.0.read();
-        if val == 0 {
-            break;
+enum DialogInner {
+    Open(IFileOpenDialog),
+    Save(IFileDialog),
+}
+
+impl DialogInner {
+    unsafe fn new(open: bool) -> Result<Self> {
+        const FILE_OPEN_DIALOG_IID: GUID = GUID::from_u128(0xd57c7288_d4ad_4768_be02_9d969532d960);
+        const FILE_SAVE_DIALOG_IID: GUID = GUID::from_u128(0x84bccd23_5fde_4cdb_aea4_af64b83d78ab);
+
+        unsafe {
+            let (cls_id, iid) = if open {
+                (&FileOpenDialog, &FILE_OPEN_DIALOG_IID)
+            } else {
+                (&FileSaveDialog, &FILE_SAVE_DIALOG_IID)
+            };
+
+            let mut iptr = std::mem::MaybeUninit::uninit();
+            wrap_err(CoCreateInstance(
+                cls_id,
+                std::ptr::null_mut(),
+                CLSCTX_INPROC_SERVER,
+                iid,
+                iptr.as_mut_ptr(),
+            ))?;
+
+            let iptr = iptr.assume_init();
+
+            Ok(if open {
+                Self::Open(IFileOpenDialog(iptr.cast()))
+            } else {
+                Self::Save(IFileDialog(iptr.cast()))
+            })
         }
-        len += 1;
-        cursor = PWSTR(cursor.0.add(1));
     }
 
-    let slice = std::slice::from_raw_parts(ptr.0, len);
-    String::from_utf16(slice).unwrap()
-}
+    #[inline]
+    unsafe fn open() -> Result<Self> {
+        unsafe { Self::new(true) }
+    }
 
-pub enum DialogKind {
-    Open(IFileOpenDialog),
-    Save(IFileSaveDialog),
-}
+    #[inline]
+    unsafe fn save() -> Result<Self> {
+        unsafe { Self::new(false) }
+    }
 
-impl DialogKind {
-    fn as_dialog(&self) -> IFileDialog {
+    #[inline]
+    unsafe fn fd(&self) -> (*mut c_void, &IFileDialogV) {
         match self {
-            Self::Open(d) => d.into(),
-            Self::Save(d) => d.into(),
+            Self::Save(s) => unsafe { (s.0.cast(), (*s.0).vtbl()) },
+            Self::Open(o) => unsafe { (o.0.cast(), &(*o.0).vtbl().base) },
         }
     }
+
+    #[inline]
+    unsafe fn set_options(&self, opts: FILEOPENDIALOGOPTIONS) -> Result<()> {
+        let (d, v) = self.fd();
+        wrap_err((v.SetOptions)(d, opts))
+    }
+
+    #[inline]
+    unsafe fn set_title(&self, title: &[u16]) -> Result<()> {
+        let (d, v) = self.fd();
+        wrap_err((v.SetTitle)(d, title.as_ptr()))
+    }
+
+    #[inline]
+    unsafe fn set_default_extension(&self, extension: &[u16]) -> Result<()> {
+        let (d, v) = self.fd();
+        wrap_err((v.SetDefaultExtension)(d, extension.as_ptr()))
+    }
+
+    #[inline]
+    unsafe fn set_file_types(&self, specs: &[COMDLG_FILTERSPEC]) -> Result<()> {
+        let (d, v) = self.fd();
+        wrap_err((v.SetFileTypes)(d, specs.len() as _, specs.as_ptr()))
+    }
+
+    #[inline]
+    unsafe fn set_filename(&self, fname: &[u16]) -> Result<()> {
+        let (d, v) = self.fd();
+        wrap_err((v.SetFileName)(d, fname.as_ptr()))
+    }
+
+    #[inline]
+    unsafe fn set_folder(&self, folder: &IShellItem) -> Result<()> {
+        let (d, v) = self.fd();
+        wrap_err((v.SetFolder)(d, folder.0.cast()))
+    }
+
+    #[inline]
+    unsafe fn show(&self, parent: Option<HWND>) -> Result<()> {
+        let (d, v) = self.fd();
+        wrap_err((v.base.Show)(d, parent.unwrap_or_default()))
+    }
+
+    #[inline]
+    unsafe fn get_result(&self) -> Result<PathBuf> {
+        let (d, v) = self.fd();
+        let mut res = std::mem::MaybeUninit::uninit();
+        wrap_err((v.GetResult)(d, res.as_mut_ptr()))?;
+        let res = res.assume_init();
+        res.get_path()
+    }
+
+    #[inline]
+    unsafe fn get_results(&self) -> Result<Vec<PathBuf>> {
+        let Self::Open(od) = self else { unreachable!() };
+
+        let items = od.get_results()?;
+        let count = items.get_count()?;
+
+        let mut paths = Vec::with_capacity(count as usize);
+        for index in 0..count {
+            let item = items.get_item_at(index)?;
+
+            let path = item.get_path()?;
+            paths.push(path);
+        }
+
+        Ok(paths)
+    }
 }
 
-pub struct IDialog(pub DialogKind, Option<HWND>);
+pub struct IDialog(DialogInner, Option<HWND>);
 
 impl IDialog {
     fn new_open_dialog(opt: &FileDialog) -> Result<Self> {
-        let dialog: IFileOpenDialog =
-            unsafe { CoCreateInstance(&FileOpenDialog, None, CLSCTX_INPROC_SERVER)? };
+        let dialog = unsafe { DialogInner::open()? };
 
         let parent = match opt.parent {
-            Some(RawWindowHandle::Win32(handle)) => Some(HWND(handle.hwnd as _)),
+            Some(RawWindowHandle::Win32(handle)) => Some(handle.hwnd as _),
             None => None,
             _ => unreachable!("unsupported window handle, expected: Windows"),
         };
 
-        Ok(Self(DialogKind::Open(dialog), parent))
+        Ok(Self(dialog, parent))
     }
 
     fn new_save_dialog(opt: &FileDialog) -> Result<Self> {
-        let dialog: IFileSaveDialog =
-            unsafe { CoCreateInstance(&FileSaveDialog, None, CLSCTX_INPROC_SERVER)? };
+        let dialog = unsafe { DialogInner::save()? };
 
         let parent = match opt.parent {
-            Some(RawWindowHandle::Win32(handle)) => Some(HWND(handle.hwnd as _)),
+            Some(RawWindowHandle::Win32(handle)) => Some(handle.hwnd as _),
             None => None,
             _ => unreachable!("unsupported window handle, expected: Windows"),
         };
 
-        Ok(Self(DialogKind::Save(dialog), parent))
+        Ok(Self(dialog, parent))
     }
 
     fn add_filters(&self, filters: &[crate::file_dialog::Filter]) -> Result<()> {
-        if let Some(first_filter) = filters.first() {
+        {
+            let Some(first_filter) = filters.first() else { return Ok(()) };
             if let Some(first_extension) = first_filter.extensions.first() {
-                let mut extension: Vec<u16> =
-                    first_extension.encode_utf16().chain(Some(0)).collect();
-                unsafe {
-                    self.0
-                        .as_dialog()
-                        .SetDefaultExtension(PCWSTR(extension.as_mut_ptr()))?;
-                }
+                let extension = str_to_vec_u16(first_extension);
+                unsafe { self.0.set_default_extension(&extension)? }
             }
         }
 
-        let mut f_list = {
+        let f_list = {
             let mut f_list = Vec::new();
+            let mut ext_string = String::new();
 
             for f in filters.iter() {
-                let name: Vec<u16> = OsStr::new(&f.name).encode_wide().chain(once(0)).collect();
-                let ext_string = f
-                    .extensions
-                    .iter()
-                    .map(|item| format!("*.{}", item))
-                    .collect::<Vec<_>>()
-                    .join(";");
-
-                let ext: Vec<u16> = OsStr::new(&ext_string)
-                    .encode_wide()
-                    .chain(once(0))
-                    .collect();
-
-                f_list.push((name, ext));
+                let name = str_to_vec_u16(&f.name);
+                ext_string.clear();
+
+                for ext in &f.extensions {
+                    use std::fmt::Write;
+                    // This is infallible for String (barring OOM)
+                    let _ = write!(&mut ext_string, "*.{ext};");
+                }
+
+                // pop trailing ;
+                ext_string.pop();
+
+                f_list.push((name, str_to_vec_u16(&ext_string)));
             }
             f_list
         };
 
         let spec: Vec<_> = f_list
-            .iter_mut()
+            .iter()
             .map(|(name, ext)| COMDLG_FILTERSPEC {
-                pszName: PCWSTR(name.as_mut_ptr()),
-                pszSpec: PCWSTR(ext.as_mut_ptr()),
+                pszName: name.as_ptr(),
+                pszSpec: ext.as_ptr(),
             })
             .collect();
 
         unsafe {
-            if !spec.is_empty() {
-                self.0.as_dialog().SetFileTypes(&spec)?;
-            }
+            self.0.set_file_types(&spec)?;
         }
         Ok(())
     }
 
     fn set_path(&self, path: &Option<PathBuf>) -> Result<()> {
-        if let Some(path) = path {
-            if let Some(path) = path.to_str() {
-                // Strip Win32 namespace prefix from the path
-                let path = path.strip_prefix(r"\\?\").unwrap_or(path);
-
-                let mut wide_path: Vec<u16> =
-                    OsStr::new(path).encode_wide().chain(once(0)).collect();
-
-                unsafe {
-                    let item: Option<IShellItem> =
-                        SHCreateItemFromParsingName(PCWSTR(wide_path.as_mut_ptr()), None).ok();
-
-                    if let Some(item) = item {
-                        // For some reason SetDefaultFolder(), does not guarantees default path, so we use SetFolder
-                        self.0.as_dialog().SetFolder(&item)?;
-                    }
-                }
+        const SHELL_ITEM_IID: GUID = GUID::from_u128(0x43826d1e_e718_42ee_bc55_a1e261c37bfe);
+
+        let Some(path) = path.as_ref().and_then(|p| p.to_str()) else { return Ok(()) };
+
+        // Strip Win32 namespace prefix from the path
+        let path = path.strip_prefix(r"\\?\").unwrap_or(path);
+
+        let wide_path = str_to_vec_u16(path);
+
+        unsafe {
+            let mut item = std::mem::MaybeUninit::uninit();
+            if wrap_err(SHCreateItemFromParsingName(
+                wide_path.as_ptr(),
+                std::ptr::null_mut(),
+                &SHELL_ITEM_IID,
+                item.as_mut_ptr(),
+            ))
+            .is_ok()
+            {
+                let item = IShellItem(item.assume_init().cast());
+                // For some reason SetDefaultFolder(), does not guarantees default path, so we use SetFolder
+                self.0.set_folder(&item)?;
             }
         }
+
         Ok(())
     }
 
     fn set_file_name(&self, file_name: &Option<String>) -> Result<()> {
         if let Some(path) = file_name {
-            let mut wide_path: Vec<u16> = OsStr::new(path).encode_wide().chain(once(0)).collect();
+            let wide_path = str_to_vec_u16(path);
 
             unsafe {
-                self.0
-                    .as_dialog()
-                    .SetFileName(PCWSTR(wide_path.as_mut_ptr()))?;
+                self.0.set_filename(&wide_path)?;
             }
         }
         Ok(())
@@ -163,61 +259,25 @@ impl IDialog {
 
     fn set_title(&self, title: &Option<String>) -> Result<()> {
         if let Some(title) = title {
-            let mut wide_title: Vec<u16> = OsStr::new(title).encode_wide().chain(once(0)).collect();
+            let wide_title = str_to_vec_u16(title);
 
             unsafe {
-                self.0
-                    .as_dialog()
-                    .SetTitle(PCWSTR(wide_title.as_mut_ptr()))?;
+                self.0.set_title(&wide_title)?;
             }
         }
         Ok(())
     }
 
     pub fn get_results(&self) -> Result<Vec<PathBuf>> {
-        unsafe {
-            let dialog = if let DialogKind::Open(ref d) = self.0 {
-                d
-            } else {
-                unreachable!()
-            };
-
-            let items = dialog.GetResults()?;
-
-            let count = items.GetCount()?;
-
-            let mut paths = Vec::new();
-            for id in 0..count {
-                let res_item = items.GetItemAt(id)?;
-
-                let display_name = res_item.GetDisplayName(SIGDN_FILESYSPATH)?;
-
-                let filename = read_to_string(display_name);
-
-                CoTaskMemFree(Some(display_name.0 as _));
-
-                let path = PathBuf::from(filename);
-                paths.push(path);
-            }
-
-            Ok(paths)
-        }
+        unsafe { self.0.get_results() }
     }
 
     pub fn get_result(&self) -> Result<PathBuf> {
-        unsafe {
-            let res_item = self.0.as_dialog().GetResult()?;
-            let display_name = res_item.GetDisplayName(SIGDN_FILESYSPATH)?;
-
-            let filename = read_to_string(display_name);
-            CoTaskMemFree(Some(display_name.0 as _));
-
-            Ok(PathBuf::from(filename))
-        }
+        unsafe { self.0.get_result() }
     }
 
     pub fn show(&self) -> Result<()> {
-        unsafe { self.0.as_dialog().Show(self.1) }
+        unsafe { self.0.show(self.1) }
     }
 }
 
@@ -251,7 +311,7 @@ impl IDialog {
         dialog.set_title(&opt.title)?;
 
         unsafe {
-            dialog.0.as_dialog().SetOptions(FOS_PICKFOLDERS as _)?;
+            dialog.0.set_options(FOS_PICKFOLDERS)?;
         }
 
         Ok(dialog)
@@ -262,10 +322,10 @@ impl IDialog {
 
         dialog.set_path(&opt.starting_directory)?;
         dialog.set_title(&opt.title)?;
-        let opts = FILEOPENDIALOGOPTIONS(FOS_PICKFOLDERS.0 | FOS_ALLOWMULTISELECT.0);
+        let opts = FOS_PICKFOLDERS | FOS_ALLOWMULTISELECT;
 
         unsafe {
-            dialog.0.as_dialog().SetOptions(opts)?;
+            dialog.0.set_options(opts)?;
         }
 
         Ok(dialog)
@@ -280,7 +340,7 @@ impl IDialog {
         dialog.set_title(&opt.title)?;
 
         unsafe {
-            dialog.0.as_dialog().SetOptions(FOS_ALLOWMULTISELECT as _)?;
+            dialog.0.set_options(FOS_ALLOWMULTISELECT)?;
         }
 
         Ok(dialog)
diff --git a/src/backend/win_cid/file_dialog/dialog_future.rs b/src/backend/win_cid/file_dialog/dialog_future.rs
index 2ab5f14..2ebf72c 100644
--- a/src/backend/win_cid/file_dialog/dialog_future.rs
+++ b/src/backend/win_cid/file_dialog/dialog_future.rs
@@ -2,15 +2,13 @@ use super::super::thread_future::ThreadFuture;
 use super::super::utils::init_com;
 use super::dialog_ffi::IDialog;
 
-use windows::core::Result;
-
 use crate::file_handle::FileHandle;
 
-pub fn single_return_future<F: FnOnce() -> Result<IDialog> + Send + 'static>(
+pub fn single_return_future<F: FnOnce() -> Result<IDialog, i32> + Send + 'static>(
     build: F,
 ) -> ThreadFuture<Option<FileHandle>> {
     ThreadFuture::new(move |data| {
-        let ret: Result<()> = (|| {
+        let ret: Result<(), i32> = (|| {
             init_com(|| {
                 let dialog = build()?;
                 dialog.show()?;
@@ -28,11 +26,11 @@ pub fn single_return_future<F: FnOnce() -> Result<IDialog> + Send + 'static>(
     })
 }
 
-pub fn multiple_return_future<F: FnOnce() -> Result<IDialog> + Send + 'static>(
+pub fn multiple_return_future<F: FnOnce() -> Result<IDialog, i32> + Send + 'static>(
     build: F,
 ) -> ThreadFuture<Option<Vec<FileHandle>>> {
     ThreadFuture::new(move |data| {
-        let ret: Result<()> = (|| {
+        let ret: Result<(), i32> = (|| {
             init_com(|| {
                 let dialog = build()?;
                 dialog.show()?;
diff --git a/src/backend/win_cid/message_dialog.rs b/src/backend/win_cid/message_dialog.rs
index 0f33c97..a9f6300 100644
--- a/src/backend/win_cid/message_dialog.rs
+++ b/src/backend/win_cid/message_dialog.rs
@@ -1,24 +1,20 @@
 use super::thread_future::ThreadFuture;
+use super::utils::str_to_vec_u16;
 use crate::message_dialog::{MessageButtons, MessageDialog, MessageLevel};
 
-use windows::{
-    core::PCWSTR,
-    Win32::{
-        Foundation::HWND,
-        UI::WindowsAndMessaging::{IDOK, IDYES},
-    },
+use windows_sys::Win32::{
+    Foundation::HWND,
+    UI::WindowsAndMessaging::{IDOK, IDYES},
 };
 
 #[cfg(not(feature = "common-controls-v6"))]
-use windows::Win32::UI::WindowsAndMessaging::{
+use windows_sys::Win32::UI::WindowsAndMessaging::{
     MessageBoxW, MB_ICONERROR, MB_ICONINFORMATION, MB_ICONWARNING, MB_OK, MB_OKCANCEL, MB_YESNO,
     MESSAGEBOX_STYLE,
 };
 
 use raw_window_handle::RawWindowHandle;
 
-use std::{ffi::OsStr, iter::once, os::windows::ffi::OsStrExt};
-
 pub struct WinMessageDialog {
     parent: Option<HWND>,
     text: Vec<u16>,
@@ -33,10 +29,6 @@ pub struct WinMessageDialog {
 // fingers crossed
 unsafe impl Send for WinMessageDialog {}
 
-fn str_to_vec_u16(str: &str) -> Vec<u16> {
-    OsStr::new(str).encode_wide().chain(once(0)).collect()
-}
-
 impl WinMessageDialog {
     pub fn new(opt: MessageDialog) -> Self {
         let text: Vec<u16> = str_to_vec_u16(&opt.description);
@@ -57,7 +49,7 @@ impl WinMessageDialog {
         };
 
         let parent = match opt.parent {
-            Some(RawWindowHandle::Win32(handle)) => Some(HWND(handle.hwnd as _)),
+            Some(RawWindowHandle::Win32(handle)) => Some(handle.hwnd as _),
             None => None,
             _ => unreachable!("unsupported window handle, expected: Windows"),
         };
@@ -74,57 +66,31 @@ impl WinMessageDialog {
     }
 
     #[cfg(feature = "common-controls-v6")]
-    pub fn run(mut self) -> bool {
-        use windows::Win32::{
-            Foundation::BOOL,
-            UI::Controls::{
-                TaskDialogIndirect, TASKDIALOGCONFIG, TASKDIALOG_BUTTON,
-                TASKDIALOG_COMMON_BUTTON_FLAGS, TDCBF_CANCEL_BUTTON, TDCBF_NO_BUTTON,
-                TDCBF_OK_BUTTON, TDCBF_YES_BUTTON, TDF_ALLOW_DIALOG_CANCELLATION,
-            },
+    pub fn run(self) -> bool {
+        use windows_sys::Win32::UI::Controls::{
+            TaskDialogIndirect, TASKDIALOGCONFIG, TASKDIALOGCONFIG_0, TASKDIALOGCONFIG_1,
+            TASKDIALOG_BUTTON, TDCBF_CANCEL_BUTTON, TDCBF_NO_BUTTON, TDCBF_OK_BUTTON,
+            TDCBF_YES_BUTTON, TDF_ALLOW_DIALOG_CANCELLATION, TD_ERROR_ICON, TD_INFORMATION_ICON,
+            TD_WARNING_ICON,
         };
 
-        let mut pf_verification_flag_checked = BOOL(0);
+        let mut pf_verification_flag_checked = 0;
         let mut pn_button = 0;
         let mut pn_radio_button = 0;
 
         let id_custom_ok = 1000;
         let id_custom_cancel = 1001;
 
-        let mut task_dialog_config = TASKDIALOGCONFIG {
-            cbSize: core::mem::size_of::<TASKDIALOGCONFIG>() as u32,
-            hwndParent: self.parent.unwrap_or_default(),
-            dwFlags: TDF_ALLOW_DIALOG_CANCELLATION,
-            cButtons: 0,
-            pszWindowTitle: PCWSTR(self.caption.as_mut_ptr()),
-            pszContent: PCWSTR(self.text.as_mut_ptr()),
-            ..Default::default()
-        };
-
         let main_icon_ptr = match self.opt.level {
-            // `TD_WARNING_ICON` / `TD_ERROR_ICON` / `TD_INFORMATION_ICON` are missing in windows-rs
-            // https://github.com/microsoft/win32metadata/issues/968
-            // Workaround via hard code:
-            // TD_WARNING_ICON
-            MessageLevel::Warning => -1 as i16 as u16,
-            // TD_ERROR_ICON
-            MessageLevel::Error => -2 as i16 as u16,
-            // TD_INFORMATION_ICON
-            MessageLevel::Info => -3 as i16 as u16,
+            MessageLevel::Warning => TD_WARNING_ICON,
+            MessageLevel::Error => TD_ERROR_ICON,
+            MessageLevel::Info => TD_INFORMATION_ICON,
         };
 
-        task_dialog_config.Anonymous1.pszMainIcon = PCWSTR(main_icon_ptr as *const u16);
-
         let (system_buttons, custom_buttons) = match self.opt.buttons {
             MessageButtons::Ok => (TDCBF_OK_BUTTON, vec![]),
-            MessageButtons::OkCancel => (
-                TASKDIALOG_COMMON_BUTTON_FLAGS(TDCBF_OK_BUTTON.0 | TDCBF_CANCEL_BUTTON.0),
-                vec![],
-            ),
-            MessageButtons::YesNo => (
-                TASKDIALOG_COMMON_BUTTON_FLAGS(TDCBF_YES_BUTTON.0 | TDCBF_NO_BUTTON.0),
-                vec![],
-            ),
+            MessageButtons::OkCancel => (TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON, vec![]),
+            MessageButtons::YesNo => (TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, vec![]),
             MessageButtons::OkCustom(ok_text) => (
                 Default::default(),
                 vec![(id_custom_ok, str_to_vec_u16(&ok_text))],
@@ -142,32 +108,60 @@ impl WinMessageDialog {
             .iter()
             .map(|(id, text)| TASKDIALOG_BUTTON {
                 nButtonID: *id,
-                pszButtonText: PCWSTR(text.as_ptr()),
+                pszButtonText: text.as_ptr(),
             })
             .collect::<Vec<_>>();
-        task_dialog_config.dwCommonButtons = system_buttons;
-        task_dialog_config.pButtons = p_buttons.as_ptr();
-        task_dialog_config.cButtons = custom_buttons.len() as u32;
+
+        let task_dialog_config = TASKDIALOGCONFIG {
+            cbSize: core::mem::size_of::<TASKDIALOGCONFIG>() as u32,
+            hwndParent: self.parent.unwrap_or_default(),
+            dwFlags: TDF_ALLOW_DIALOG_CANCELLATION,
+            pszWindowTitle: self.caption.as_ptr(),
+            pszContent: self.text.as_ptr(),
+            Anonymous1: TASKDIALOGCONFIG_0 {
+                pszMainIcon: main_icon_ptr,
+            },
+            Anonymous2: TASKDIALOGCONFIG_1 {
+                pszFooterIcon: std::ptr::null(),
+            },
+            dwCommonButtons: system_buttons,
+            pButtons: p_buttons.as_ptr(),
+            cButtons: custom_buttons.len() as u32,
+            pRadioButtons: std::ptr::null(),
+            cRadioButtons: 0,
+            cxWidth: 0,
+            hInstance: 0,
+            pfCallback: None,
+            lpCallbackData: 0,
+            nDefaultButton: 0,
+            nDefaultRadioButton: 0,
+            pszCollapsedControlText: std::ptr::null(),
+            pszExpandedControlText: std::ptr::null(),
+            pszExpandedInformation: std::ptr::null(),
+            pszMainInstruction: std::ptr::null(),
+            pszVerificationText: std::ptr::null(),
+            pszFooter: std::ptr::null(),
+        };
 
         let ret = unsafe {
             TaskDialogIndirect(
                 &task_dialog_config,
-                Some(&mut pn_button),
-                Some(&mut pn_radio_button),
-                Some(&mut pf_verification_flag_checked),
+                &mut pn_button,
+                &mut pn_radio_button,
+                &mut pf_verification_flag_checked,
             )
         };
 
-        ret.is_ok() && (pn_button == id_custom_ok || pn_button == IDYES.0 || pn_button == IDOK.0)
+        ret == 0 && (pn_button == id_custom_ok || pn_button == IDYES || pn_button == IDOK)
     }
 
     #[cfg(not(feature = "common-controls-v6"))]
-    pub fn run(mut self) -> bool {
+    pub fn run(self) -> bool {
         let ret = unsafe {
             MessageBoxW(
-                self.parent,
-                PCWSTR(self.text.as_mut_ptr()),
-                PCWSTR(self.caption.as_mut_ptr()),
+                self.parent.unwrap_or_default(),
+                self.text.as_ptr(),
+                self.caption.as_ptr(),
                 self.flags,
             )
         };
diff --git a/src/backend/win_cid/utils.rs b/src/backend/win_cid/utils.rs
index d31b7cd..dd7b6a1 100644
--- a/src/backend/win_cid/utils.rs
+++ b/src/backend/win_cid/utils.rs
@@ -1,18 +1,30 @@
-use windows::core::Result;
-
-use windows::Win32::System::Com::{
-    CoInitializeEx, CoUninitialize, COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE,
+use windows_sys::{
+    core::HRESULT,
+    Win32::System::Com::{
+        CoInitializeEx, CoUninitialize, COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE,
+    },
 };
 
-/// Makes sure that COM lib is initialized long enought
-pub fn init_com<T, F: FnOnce() -> T>(f: F) -> Result<T> {
-    unsafe {
+#[inline]
+pub(crate) fn str_to_vec_u16(s: &str) -> Vec<u16> {
+    let mut v: Vec<_> = s.encode_utf16().collect();
+    v.push(0);
+    v
+}
+
+/// Makes sure that COM lib is initialized long enough
+pub fn init_com<T, F: FnOnce() -> T>(f: F) -> Result<T, HRESULT> {
+    let res = unsafe {
         CoInitializeEx(
-            None,
+            std::ptr::null(),
             COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE,
-        )?
+        )
     };
 
+    if res < 0 {
+        return Err(res);
+    }
+
     let out = f();
 
     unsafe {