@@ -67,91 +67,137 @@ public static class QuickSwitch
6767 private static HWND _hookedDialogWindowHandle = HWND.Null;
6868 private static readonly object _hookedDialogWindowHandleLock = new();*/
6969
70- private static bool _isInitialized = false ;
70+ private static bool _initialized = false ;
71+ private static bool _enabled = false ;
7172
7273 #endregion
7374
74- #region Initialization
75+ #region Initialize & Setup
7576
76- public static void Initialize ( )
77+ public static void InitializeQuickSwitch ( )
7778 {
78- if ( _isInitialized ) return ;
79+ if ( _initialized ) return ;
7980
80- // Check all foreground windows and check if there are explorer windows
81- lock ( _lastExplorerViewLock )
81+ // Initialize main window handle
82+ _mainWindowHandle = Win32Helper . GetMainWindowHandle ( ) ;
83+
84+ // Initialize timer
85+ _dragMoveTimer = new DispatcherTimer { Interval = TimeSpan . FromMilliseconds ( 10 ) } ;
86+ _dragMoveTimer . Tick += ( s , e ) => InvokeUpdateQuickSwitchWindow ( ) ;
87+
88+ _initialized = true ;
89+ }
90+
91+ public static void SetupQuickSwitch ( bool enabled )
92+ {
93+ if ( enabled == _enabled ) return ;
94+
95+ if ( enabled )
8296 {
83- var explorerInitialized = false ;
84- EnumerateShellWindows ( ( shellWindow ) =>
97+ // Check all foreground windows and check if there are explorer windows
98+ lock ( _lastExplorerViewLock )
8599 {
86- if ( shellWindow is not IWebBrowser2 explorer )
100+ var explorerInitialized = false ;
101+ EnumerateShellWindows ( ( shellWindow ) =>
87102 {
88- return ;
89- }
103+ if ( shellWindow is not IWebBrowser2 explorer )
104+ {
105+ return ;
106+ }
90107
91- // Initialize one explorer window even if it is not foreground
92- if ( ! explorerInitialized )
93- {
94- _lastExplorerView = explorer ;
108+ // Initialize one explorer window even if it is not foreground
109+ if ( ! explorerInitialized )
110+ {
111+ _lastExplorerView = explorer ;
95112
96- Log . Debug ( ClassName , $ "Explorer Window: { explorer . HWND . Value } ") ;
97- }
98- // Force update explorer window if it is foreground
99- else if ( Win32Helper . IsForegroundWindow ( explorer . HWND . Value ) )
100- {
101- _lastExplorerView = explorer ;
113+ Log . Debug ( ClassName , $ "Explorer Window: { explorer . HWND . Value } ") ;
114+ }
102115
103- Log . Debug ( ClassName , $ "Explorer Window: { explorer . HWND . Value } " ) ;
104- }
105- } ) ;
106- }
116+ // Force update explorer window if it is foreground
117+ else if ( Win32Helper . IsForegroundWindow ( explorer . HWND . Value ) )
118+ {
119+ _lastExplorerView = explorer ;
107120
108- // Call ForegroundChange when the foreground window changes
109- _foregroundChangeHook = PInvoke . SetWinEventHook (
110- PInvoke . EVENT_SYSTEM_FOREGROUND ,
111- PInvoke . EVENT_SYSTEM_FOREGROUND ,
112- PInvoke . GetModuleHandle ( ( PCWSTR ) null ) ,
113- ForegroundChangeCallback ,
114- 0 ,
115- 0 ,
116- PInvoke . WINEVENT_OUTOFCONTEXT ) ;
117-
118- // Call LocationChange when the location of the window changes
119- _locationChangeHook = PInvoke . SetWinEventHook (
120- PInvoke . EVENT_OBJECT_LOCATIONCHANGE ,
121- PInvoke . EVENT_OBJECT_LOCATIONCHANGE ,
122- PInvoke . GetModuleHandle ( ( PCWSTR ) null ) ,
123- LocationChangeCallback ,
124- 0 ,
125- 0 ,
126- PInvoke . WINEVENT_OUTOFCONTEXT ) ;
127-
128- // Call DestroyChange when the window is destroyed
129- _destroyChangeHook = PInvoke . SetWinEventHook (
130- PInvoke . EVENT_OBJECT_DESTROY ,
131- PInvoke . EVENT_OBJECT_DESTROY ,
132- PInvoke . GetModuleHandle ( ( PCWSTR ) null ) ,
133- DestroyChangeCallback ,
134- 0 ,
135- 0 ,
136- PInvoke . WINEVENT_OUTOFCONTEXT ) ;
137-
138- if ( _foregroundChangeHook . IsNull ||
139- _locationChangeHook . IsNull ||
140- _destroyChangeHook . IsNull )
141- {
142- Log . Error ( ClassName , "Failed to initialize QuickSwitch" ) ;
143- return ;
144- }
121+ Log . Debug ( ClassName , $ "Explorer Window: { explorer . HWND . Value } ") ;
122+ }
123+ } ) ;
124+ }
145125
146- // Initialize main window handle
147- _mainWindowHandle = Win32Helper . GetMainWindowHandle ( ) ;
126+ // Unhook events
127+ if ( ! _foregroundChangeHook . IsNull )
128+ {
129+ PInvoke . UnhookWinEvent ( _foregroundChangeHook ) ;
130+ _foregroundChangeHook = HWINEVENTHOOK . Null ;
131+ }
132+ if ( ! _locationChangeHook . IsNull )
133+ {
134+ PInvoke . UnhookWinEvent ( _locationChangeHook ) ;
135+ _locationChangeHook = HWINEVENTHOOK . Null ;
136+ }
137+ if ( ! _destroyChangeHook . IsNull )
138+ {
139+ PInvoke . UnhookWinEvent ( _destroyChangeHook ) ;
140+ _destroyChangeHook = HWINEVENTHOOK . Null ;
141+ }
148142
149- // Initialize timer
150- _dragMoveTimer = new DispatcherTimer { Interval = TimeSpan . FromMilliseconds ( 10 ) } ;
151- _dragMoveTimer . Tick += ( s , e ) => InvokeUpdateQuickSwitchWindow ( ) ;
143+ // Hook events
144+ _foregroundChangeHook = PInvoke . SetWinEventHook (
145+ PInvoke . EVENT_SYSTEM_FOREGROUND ,
146+ PInvoke . EVENT_SYSTEM_FOREGROUND ,
147+ PInvoke . GetModuleHandle ( ( PCWSTR ) null ) ,
148+ ForegroundChangeCallback ,
149+ 0 ,
150+ 0 ,
151+ PInvoke . WINEVENT_OUTOFCONTEXT ) ;
152+ _locationChangeHook = PInvoke . SetWinEventHook (
153+ PInvoke . EVENT_OBJECT_LOCATIONCHANGE ,
154+ PInvoke . EVENT_OBJECT_LOCATIONCHANGE ,
155+ PInvoke . GetModuleHandle ( ( PCWSTR ) null ) ,
156+ LocationChangeCallback ,
157+ 0 ,
158+ 0 ,
159+ PInvoke . WINEVENT_OUTOFCONTEXT ) ;
160+ _destroyChangeHook = PInvoke . SetWinEventHook (
161+ PInvoke . EVENT_OBJECT_DESTROY ,
162+ PInvoke . EVENT_OBJECT_DESTROY ,
163+ PInvoke . GetModuleHandle ( ( PCWSTR ) null ) ,
164+ DestroyChangeCallback ,
165+ 0 ,
166+ 0 ,
167+ PInvoke . WINEVENT_OUTOFCONTEXT ) ;
168+
169+ if ( _foregroundChangeHook . IsNull ||
170+ _locationChangeHook . IsNull ||
171+ _destroyChangeHook . IsNull )
172+ {
173+ Log . Error ( ClassName , "Failed to enable QuickSwitch" ) ;
174+ return ;
175+ }
176+ }
177+ else
178+ {
179+ // Unhook events
180+ if ( ! _foregroundChangeHook . IsNull )
181+ {
182+ PInvoke . UnhookWinEvent ( _foregroundChangeHook ) ;
183+ _foregroundChangeHook = HWINEVENTHOOK . Null ;
184+ }
185+ if ( ! _locationChangeHook . IsNull )
186+ {
187+ PInvoke . UnhookWinEvent ( _locationChangeHook ) ;
188+ _locationChangeHook = HWINEVENTHOOK . Null ;
189+ }
190+ if ( ! _destroyChangeHook . IsNull )
191+ {
192+ PInvoke . UnhookWinEvent ( _destroyChangeHook ) ;
193+ _destroyChangeHook = HWINEVENTHOOK . Null ;
194+ }
152195
153- _isInitialized = true ;
154- return ;
196+ // Stop drag move timer
197+ _dragMoveTimer ? . Stop ( ) ;
198+ }
199+
200+ _enabled = enabled ;
155201 }
156202
157203 #endregion
@@ -243,10 +289,7 @@ private static void InvokeHideQuickSwitchWindow()
243289
244290 public static void OnToggleHotkey ( object sender , HotkeyEventArgs args )
245291 {
246- if ( _isInitialized )
247- {
248- NavigateDialogPath ( Win32Helper . GetForegroundWindowHWND ( ) ) ;
249- }
292+ NavigateDialogPath ( Win32Helper . GetForegroundWindowHWND ( ) ) ;
250293 }
251294
252295 #endregion
@@ -620,10 +663,11 @@ private static unsafe void EnumerateShellWindows(Action<object> action)
620663
621664 public static void Dispose ( )
622665 {
623- // Reset initialize flag
624- _isInitialized = false ;
666+ // Reset flags
667+ _enabled = false ;
668+ _initialized = false ;
625669
626- // Dispose handle
670+ // Unhook events
627671 if ( ! _foregroundChangeHook . IsNull )
628672 {
629673 PInvoke . UnhookWinEvent ( _foregroundChangeHook ) ;
@@ -646,9 +690,16 @@ public static void Dispose()
646690 }
647691
648692 // Release ComObjects
649- if ( _lastExplorerView != null )
693+ try
694+ {
695+ if ( _lastExplorerView != null )
696+ {
697+ Marshal . ReleaseComObject ( _lastExplorerView ) ;
698+ _lastExplorerView = null ;
699+ }
700+ }
701+ catch ( COMException )
650702 {
651- Marshal . ReleaseComObject ( _lastExplorerView ) ;
652703 _lastExplorerView = null ;
653704 }
654705
0 commit comments