-
Notifications
You must be signed in to change notification settings - Fork 194
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: hide and show windows via set_cloak from COM lib
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
Showing
6 changed files
with
358 additions
and
11 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Cloaking functionality taken from https://github.com/Ciantic/AltTabAccessor/blob/main/src/lib.rs | ||
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() | ||
} | ||
}); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.