Skip to content

Commit

Permalink
feat: hide and show windows via set_cloak from COM lib
Browse files Browse the repository at this point in the history
Previous method of hiding the windows (SW_HIDE and SWP_HIDEWINDOW) would
completely remove the window from the taskbar, which would lead to
unexpected behavior for some people. set_cloak allows the hidden
windows to still be present in the task bar.

Other discussions on the topic: Ciantic/AltTabAccessor#1
  • Loading branch information
rpop0 committed Oct 15, 2024
1 parent 8eb71b8 commit 4728d1a
Show file tree
Hide file tree
Showing 6 changed files with 357 additions and 11 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/wm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ windows = { version = "0.52", features = [
"Win32_UI_TextServices",
"Win32_UI_WindowsAndMessaging",
] }
windows-interface = { version = "*"}
244 changes: 244 additions & 0 deletions packages/wm/src/common/com/interfaces.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
use std::{ffi::c_void, ops::Deref};
use windows::{
core::{IUnknown, IUnknown_Vtbl, GUID, HSTRING, HRESULT},
Win32::{UI::Shell::Common::IObjectArray, Foundation::HWND},
};


type DesktopID = GUID;

// Idea here is that the cloned ComIn instance lifetime is within the original ComIn instance lifetime
#[repr(transparent)]
pub struct ComIn<'a, T> {
data: T,
_phantom: std::marker::PhantomData<&'a T>,
}

impl<'a, T: Clone> ComIn<'a, T> {
pub fn new(t: &'a T) -> Self {
Self {
data: t.clone(),
_phantom: std::marker::PhantomData,
}
}

pub unsafe fn unsafe_new_no_clone(t: T) -> Self {
Self {
data: t,
_phantom: std::marker::PhantomData,
}
}
}

impl<'a, T> Deref for ComIn<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}

pub const CLSID_IMMERSIVE_SHELL: GUID = GUID {
data1: 0xC2F03A33,
data2: 0x21F5,
data3: 0x47FA,
data4: [0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39],
};


type BOOL = i32;
type DWORD = u32;
type INT = i32;
type LPVOID = *mut c_void;
type UINT = u32;
type ULONG = u32;
type WCHAR = u16;
type PCWSTR = *const WCHAR;
type PWSTR = *mut WCHAR;
type ULONGLONG = u64;
type LONG = i32;

type IAsyncCallback = UINT;
type IImmersiveMonitor = UINT;
type IApplicationViewOperation = UINT;
type IApplicationViewPosition = UINT;
type IImmersiveApplication = UINT;
type IApplicationViewChangeListener = UINT;
#[allow(non_camel_case_types)]
type APPLICATION_VIEW_COMPATIBILITY_POLICY = UINT;
#[allow(non_camel_case_types)]
type APPLICATION_VIEW_CLOAK_TYPE = UINT;

#[allow(dead_code)]
pub struct RECT {
left: LONG,
top: LONG,
right: LONG,
bottom: LONG,
}

#[allow(dead_code)]
pub struct SIZE {
cx: LONG,
cy: LONG,
}
#[windows_interface::interface("6D5140C1-7436-11CE-8034-00AA006009FA")]
pub unsafe trait IServiceProvider: IUnknown {
pub unsafe fn query_service(
&self,
guid_service: *const GUID,
riid: *const GUID,
ppv_object: *mut *mut c_void,
) -> HRESULT;
}

#[windows_interface::interface("372E1D3B-38D3-42E4-A15B-8AB2B178F513")]
pub unsafe trait IApplicationView: IUnknown {
/* IInspecateble */
pub unsafe fn get_iids(
&self,
out_iid_count: *mut ULONG,
out_opt_iid_array_ptr: *mut *mut GUID,
) -> HRESULT;
pub unsafe fn get_runtime_class_name(&self, out_opt_class_name: *mut HSTRING) -> HRESULT;
pub unsafe fn get_trust_level(&self, ptr_trust_level: LPVOID) -> HRESULT;

/* IApplicationView methods */
pub unsafe fn set_focus(&self) -> HRESULT;
pub unsafe fn switch_to(&self) -> HRESULT;

pub unsafe fn try_invoke_back(&self, ptr_async_callback: IAsyncCallback) -> HRESULT;
pub unsafe fn get_thumbnail_window(&self, out_hwnd: *mut HWND) -> HRESULT;
pub unsafe fn get_monitor(&self, out_monitors: *mut *mut IImmersiveMonitor) -> HRESULT;
pub unsafe fn get_visibility(&self, out_int: LPVOID) -> HRESULT;
pub unsafe fn set_cloak(
&self,
application_view_cloak_type: APPLICATION_VIEW_CLOAK_TYPE,
unknown: INT,
) -> HRESULT;
pub unsafe fn get_position(
&self,
unknowniid: *const GUID,
unknown_array_ptr: LPVOID,
) -> HRESULT;
pub unsafe fn set_position(&self, view_position: *mut IApplicationViewPosition) -> HRESULT;
pub unsafe fn insert_after_window(&self, window: HWND) -> HRESULT;
pub unsafe fn get_extended_frame_position(&self, rect: *mut RECT) -> HRESULT;
pub unsafe fn get_app_user_model_id(&self, id: *mut PWSTR) -> HRESULT; // Proc17
pub unsafe fn set_app_user_model_id(&self, id: PCWSTR) -> HRESULT;
pub unsafe fn is_equal_by_app_user_model_id(&self, id: PCWSTR, out_result: *mut INT)
-> HRESULT;

/*** IApplicationView methods ***/
pub unsafe fn get_view_state(&self, out_state: *mut UINT) -> HRESULT; // Proc20
pub unsafe fn set_view_state(&self, state: UINT) -> HRESULT; // Proc21
pub unsafe fn get_neediness(&self, out_neediness: *mut INT) -> HRESULT; // Proc22
pub unsafe fn get_last_activation_timestamp(&self, out_timestamp: *mut ULONGLONG) -> HRESULT;
pub unsafe fn set_last_activation_timestamp(&self, timestamp: ULONGLONG) -> HRESULT;
pub unsafe fn get_virtual_desktop_id(&self, out_desktop_guid: *mut DesktopID) -> HRESULT;
pub unsafe fn set_virtual_desktop_id(&self, desktop_guid: *const DesktopID) -> HRESULT;
pub unsafe fn get_show_in_switchers(&self, out_show: *mut INT) -> HRESULT;
pub unsafe fn set_show_in_switchers(&self, show: INT) -> HRESULT;
pub unsafe fn get_scale_factor(&self, out_scale_factor: *mut INT) -> HRESULT;
pub unsafe fn can_receive_input(&self, out_can: *mut BOOL) -> HRESULT;
pub unsafe fn get_compatibility_policy_type(
&self,
out_policy_type: *mut APPLICATION_VIEW_COMPATIBILITY_POLICY,
) -> HRESULT;
pub unsafe fn set_compatibility_policy_type(
&self,
policy_type: APPLICATION_VIEW_COMPATIBILITY_POLICY,
) -> HRESULT;

pub unsafe fn get_size_constraints(
&self,
monitor: *mut IImmersiveMonitor,
out_size1: *mut SIZE,
out_size2: *mut SIZE,
) -> HRESULT;
pub unsafe fn get_size_constraints_for_dpi(
&self,
dpi: UINT,
out_size1: *mut SIZE,
out_size2: *mut SIZE,
) -> HRESULT;
pub unsafe fn set_size_constraints_for_dpi(
&self,
dpi: *const UINT,
size1: *const SIZE,
size2: *const SIZE,
) -> HRESULT;

pub unsafe fn on_min_size_preferences_updated(&self, window: HWND) -> HRESULT;
pub unsafe fn apply_operation(&self, operation: *mut IApplicationViewOperation) -> HRESULT;
pub unsafe fn is_tray(&self, out_is: *mut BOOL) -> HRESULT;
pub unsafe fn is_in_high_zorder_band(&self, out_is: *mut BOOL) -> HRESULT;
pub unsafe fn is_splash_screen_presented(&self, out_is: *mut BOOL) -> HRESULT;
pub unsafe fn flash(&self) -> HRESULT;
pub unsafe fn get_root_switchable_owner(&self, app_view: *mut IApplicationView) -> HRESULT; // proc45
pub unsafe fn enumerate_ownership_tree(&self, objects: *mut IObjectArray) -> HRESULT; // proc46

pub unsafe fn get_enterprise_id(&self, out_id: *mut PWSTR) -> HRESULT; // proc47
pub unsafe fn is_mirrored(&self, out_is: *mut BOOL) -> HRESULT; //

pub unsafe fn unknown1(&self, arg: *mut INT) -> HRESULT;
pub unsafe fn unknown2(&self, arg: *mut INT) -> HRESULT;
pub unsafe fn unknown3(&self, arg: *mut INT) -> HRESULT;
pub unsafe fn unknown4(&self, arg: INT) -> HRESULT;
pub unsafe fn unknown5(&self, arg: *mut INT) -> HRESULT;
pub unsafe fn unknown6(&self, arg: INT) -> HRESULT;
pub unsafe fn unknown7(&self) -> HRESULT;
pub unsafe fn unknown8(&self, arg: *mut INT) -> HRESULT;
pub unsafe fn unknown9(&self, arg: INT) -> HRESULT;
pub unsafe fn unknown10(&self, arg: INT, arg2: INT) -> HRESULT;
pub unsafe fn unknown11(&self, arg: INT) -> HRESULT;
pub unsafe fn unknown12(&self, arg: *mut SIZE) -> HRESULT;
}


#[windows_interface::interface("1841c6d7-4f9d-42c0-af41-8747538f10e5")]
pub unsafe trait IApplicationViewCollection: IUnknown {
pub unsafe fn get_views(&self, out_views: *mut IObjectArray) -> HRESULT;

pub unsafe fn get_views_by_zorder(&self, out_views: *mut IObjectArray) -> HRESULT;

pub unsafe fn get_views_by_app_user_model_id(
&self,
id: PCWSTR,
out_views: *mut IObjectArray,
) -> HRESULT;

pub unsafe fn get_view_for_hwnd(
&self,
window: HWND,
out_view: *mut Option<IApplicationView>,
) -> HRESULT;

pub unsafe fn get_view_for_application(
&self,
app: ComIn<IImmersiveApplication>,
out_view: *mut IApplicationView,
) -> HRESULT;

pub unsafe fn get_view_for_app_user_model_id(
&self,
id: PCWSTR,
out_view: *mut IApplicationView,
) -> HRESULT;

pub unsafe fn get_view_in_focus(&self, out_view: *mut IApplicationView) -> HRESULT;

pub unsafe fn try_get_last_active_visible_view(
&self,
out_view: *mut IApplicationView,
) -> HRESULT;

pub unsafe fn refresh_collection(&self) -> HRESULT;

pub unsafe fn register_for_application_view_changes(
&self,
listener: ComIn<IApplicationViewChangeListener>,
out_id: *mut DWORD,
) -> HRESULT;

pub unsafe fn unregister_for_application_view_changes(&self, id: DWORD) -> HRESULT;
}
89 changes: 89 additions & 0 deletions packages/wm/src/common/com/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::ffi::c_void;
use windows::core::{ComInterface, Interface};
use windows::Win32::Foundation::HWND;
use windows::Win32::System::Com::{CoCreateInstance, CoInitializeEx, CoUninitialize, CLSCTX_ALL, COINIT_APARTMENTTHREADED};
use crate::common::com::interfaces::{IApplicationViewCollection, IServiceProvider, CLSID_IMMERSIVE_SHELL};

mod interfaces;

pub enum CloakVisibility {
HIDDEN,
VISIBLE
}

struct ComInit();

impl ComInit {
pub fn new() -> Self {
unsafe {
// Apparently only COINIT_APARTMENTTHREADED works correctly.
// Initialize the COM Library
// Handle the error differently maybe?
CoInitializeEx(None, COINIT_APARTMENTTHREADED).unwrap();
}
Self()
}
}

impl Drop for ComInit {
fn drop(&mut self) {
unsafe {
CoUninitialize();
}
}
}


fn get_iservice_provider() -> IServiceProvider {
COM_INIT.with(|_| unsafe {
CoCreateInstance(&CLSID_IMMERSIVE_SHELL, None, CLSCTX_ALL).unwrap()
})
}

fn get_iapplication_view_collection(provider: &IServiceProvider) -> IApplicationViewCollection {
COM_INIT.with(|_| {
let mut obj = std::ptr::null_mut::<c_void>();
unsafe {
provider
.query_service(
&IApplicationViewCollection::IID,
&IApplicationViewCollection::IID,
&mut obj,
)
.unwrap();
}

assert!(!obj.is_null());

unsafe { IApplicationViewCollection::from_raw(obj) }
})
}

// Each thread that accesses the COM_INIT variable gets a local instance of the variable.
// This is needed since the COM library requires the CoInitializeEx needs to be initialized per thread.
thread_local! {
static COM_INIT: ComInit = ComInit::new();
}

pub fn set_cloak(hwnd: HWND, cloak_visibility: &CloakVisibility) {
COM_INIT.with(|_| {
let provider = get_iservice_provider();
let view_collection = get_iapplication_view_collection(&provider);
let mut view = None;
unsafe {
view_collection.get_view_for_hwnd(hwnd, &mut view).unwrap()
};
let view = view.unwrap();


unsafe {
// https://github.com/Ciantic/AltTabAccessor/issues/1#issuecomment-1426877843
let flag = match cloak_visibility {
CloakVisibility::VISIBLE => 0,
CloakVisibility::HIDDEN => 2
};
view.set_cloak(1, flag).unwrap()
}
});

}
1 change: 1 addition & 0 deletions packages/wm/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod rect_delta;
mod tiling_direction;
mod try_warn;
mod vec_deque_ext;
mod com;

pub use color::*;
pub use direction::*;
Expand Down
Loading

0 comments on commit 4728d1a

Please sign in to comment.