320 lines
12 KiB
C#
320 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows;
|
|
using System.Windows.Interop;
|
|
using System.Windows.Threading;
|
|
using System.Timers;
|
|
using Yaulw.Thread;
|
|
|
|
namespace Yaulw.WPF
|
|
{
|
|
/// <remarks>
|
|
/// Class used to keep WPF Windows Hidden permanently.
|
|
/// This should ideally be called in the Constructor of a window you want to keep hidden.
|
|
/// <example>
|
|
/// <code>
|
|
/// private static KeepHidden _HiddenWindow = null;
|
|
/// public HiddenMainWindow()
|
|
/// {
|
|
/// // * Force this window to remain hidden indefinitly*
|
|
/// _HiddenWindow = new KeepHidden(this);
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
/// </remarks>
|
|
public class KeepHidden
|
|
{
|
|
#region Private Statics
|
|
|
|
// Static Map of All our Hidden Windows
|
|
private static Dictionary<IntPtr, Window> s_hWndToWindowMap = new Dictionary<IntPtr, Window>();
|
|
private static List<Window> s_WindowList = new List<Window>();
|
|
|
|
private Window _wpfWindow = null;
|
|
private object _lockObj = new Object();
|
|
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
|
|
/// <summary>
|
|
/// Get the WPF Object That is bound to this Keep Hidden Instance
|
|
/// </summary>
|
|
public Window wpfWindow { get { return _wpfWindow; } }
|
|
|
|
/// <summary>
|
|
/// Get the HWND Handle to the WPF Object bound to this Keep Hidden Instance
|
|
/// </summary>
|
|
public IntPtr hwndWindow
|
|
{
|
|
get
|
|
{
|
|
if (_wpfWindow != null)
|
|
{
|
|
WindowInteropHelper interop = new WindowInteropHelper(_wpfWindow);
|
|
return interop.Handle;
|
|
}
|
|
return IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
/// <summary>
|
|
/// When Passing in a Wpf Window in this Construction, it will ensure that
|
|
/// This Window will remain hidden during it's life span. If the Window is Not Loaded
|
|
/// yet, call KeepHidden's Show() method to show it, after construction.
|
|
/// </summary>
|
|
/// <param name="wpfWindow">a WPF Window Object that you want to keep Hidden</param>
|
|
public KeepHidden(Window wpfWindow)
|
|
{
|
|
if (wpfWindow != null && !wpfWindow.IsLoaded)
|
|
{
|
|
// Assign the Load Event Handler we care about
|
|
wpfWindow.Loaded += new RoutedEventHandler(wpfWindow_Loaded);
|
|
}
|
|
else if (wpfWindow != null && wpfWindow.IsLoaded)
|
|
{
|
|
// Call the Loaded Event Handler Directly
|
|
wpfWindow_Loaded((object)wpfWindow, null);
|
|
}
|
|
else
|
|
throw new ArgumentException("Please pass valid WPF Window Object");
|
|
|
|
// Make sure we aren't already tracking this window
|
|
if (s_WindowList.Contains(wpfWindow))
|
|
throw new ArgumentException("WPF Window Object already Kept Hidden");
|
|
|
|
// Assign remaining stuff
|
|
wpfWindow.Closing += new System.ComponentModel.CancelEventHandler(wpfWindow_Closing);
|
|
AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
|
|
_wpfWindow = wpfWindow;
|
|
|
|
// Add to WPF Window List
|
|
s_WindowList.Add(wpfWindow);
|
|
|
|
// Call this upon construction ~Done Way before Show() get's called
|
|
HideWPFWindow(_wpfWindow);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Call this to execute a Show() call on the WPF Window that you want to Keep Hidden
|
|
/// </summary>
|
|
public void Show()
|
|
{
|
|
if (_wpfWindow != null && !_wpfWindow.IsLoaded)
|
|
{
|
|
lock (_lockObj)
|
|
{
|
|
// Make sure it can't be seen or interacted with
|
|
HideWPFWindow(_wpfWindow);
|
|
|
|
// Show it
|
|
_wpfWindow.Show();
|
|
|
|
// Again... make sure...
|
|
HideWPFWindow(_wpfWindow);
|
|
|
|
// Hide it immediatly, make sure it can't be interacted with
|
|
_wpfWindow.Hide();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this to execute a Show() call on the WPF Window that you want to Keep Hidden
|
|
/// ~However, it will delay call the show allowing the hidden window's constructor to finish
|
|
/// </summary>
|
|
/// <param name="nDelayInMilliseconds">Milliseconds to delay show the HiddenWindow (at least 250 recommended)</param>
|
|
public void Show(uint nDelayInMilliseconds)
|
|
{
|
|
// Calling Show() directly in the Constructor causes a .Net Run-time Crash
|
|
// upon first launch only. Calling it .5 seconds later does the trick of avoiding that error,
|
|
// because by then the object is fully constructed
|
|
ElapsedEventHandler DelayShowHiddenWindow = delegate(object sender, ElapsedEventArgs e)
|
|
{
|
|
Timer timer = (Timer)sender;
|
|
timer.Stop();
|
|
|
|
// * Show the Hidden Window *
|
|
Show();
|
|
|
|
timer.Dispose();
|
|
timer = null;
|
|
};
|
|
TTimerDisp timerDelayShowHiddenWindow = new TTimerDisp(DelayShowHiddenWindow, (int) nDelayInMilliseconds, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this to execute a Close() call on the WPF Window that you previously had hidden
|
|
/// ~Should only call this when you are done with the Keep Hidden Object
|
|
/// </summary>
|
|
public void Close()
|
|
{
|
|
if (_wpfWindow != null && _wpfWindow.IsLoaded)
|
|
{
|
|
_wpfWindow.Close();
|
|
_wpfWindow = null;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Static Methods
|
|
|
|
/// <summary>
|
|
/// Call this function to determine if the passed in Window is a Hidden Window Object * Tracked by KeepHidden *
|
|
/// </summary>
|
|
/// <param name="_wpfWindow">pass in a wpf window object to check</param>
|
|
/// <returns></returns>
|
|
public static bool IsHiddenWindow(Window _wpfWindow)
|
|
{
|
|
if (_wpfWindow != null)
|
|
{
|
|
bool bIsFound = s_WindowList.Contains(_wpfWindow);
|
|
return bIsFound;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Keep Hidden Event Handlers
|
|
|
|
/// <summary>
|
|
/// Called by the WPFWindow's OnLoad Event
|
|
/// </summary>
|
|
private void wpfWindow_Loaded(object sender, RoutedEventArgs e)
|
|
{
|
|
lock (_lockObj)
|
|
{
|
|
Window wpfWindow = (Window)sender;
|
|
HideWPFWindow(wpfWindow);
|
|
|
|
// Add Window Source Message Hook * We'll track the hWnd for the Message Hook
|
|
// vie s_hWndToWindowMap
|
|
WindowInteropHelper interop = new WindowInteropHelper(wpfWindow);
|
|
s_hWndToWindowMap[interop.Handle] = wpfWindow;
|
|
HwndSource source = HwndSource.FromHwnd(interop.Handle);
|
|
source.AddHook(new HwndSourceHook(MessageHook));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the WPFWindow's Closing Event
|
|
/// </summary>
|
|
private void wpfWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
|
{
|
|
lock (_lockObj)
|
|
{
|
|
// Delete from the hWndMap, so Message Hook
|
|
// Stops processing messages
|
|
Window wpfWindow = (Window)sender;
|
|
if (s_hWndToWindowMap.ContainsValue(wpfWindow))
|
|
{
|
|
foreach (IntPtr hWnd in s_hWndToWindowMap.Keys)
|
|
{
|
|
if (s_hWndToWindowMap[hWnd] == wpfWindow)
|
|
{
|
|
s_hWndToWindowMap.Remove(hWnd);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// * Imp * Delete internal wpfWindow Reference,
|
|
// but DON'T Delete from the s_WindowList. Caller could still call IsHiddenWindow(),
|
|
// and we MUST return true if it is
|
|
_wpfWindow = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the Process Exiting * We want this to happen last *
|
|
/// This allows all unload events for each window to happen first
|
|
/// </summary>
|
|
void CurrentDomain_ProcessExit(object sender, EventArgs e)
|
|
{
|
|
// Make sure to clear all windows
|
|
s_hWndToWindowMap.Clear();
|
|
|
|
// Make sure to clear all windows
|
|
s_WindowList.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This Message Hook is for the WpfWindow to absolutely ensure that it remains Hidden.
|
|
/// We want to make sure that it is never displayed *It can occur that explorer.exe can show it, without this hook*
|
|
/// ~i.e. when explorer.exe crashes, and get's restarted, it can cause inconsitencies
|
|
/// </summary>
|
|
private IntPtr MessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
|
{
|
|
// Get the Proper WPF Object and make sure it is Hidden
|
|
if (s_hWndToWindowMap.ContainsKey(hwnd))
|
|
{
|
|
Window wpfWindow = s_hWndToWindowMap[hwnd];
|
|
if (wpfWindow != null)
|
|
HideWPFWindow(wpfWindow);
|
|
}
|
|
handled = false;
|
|
return System.IntPtr.Zero;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Hide Window Functionality
|
|
|
|
/// <summary>
|
|
/// * Core * 'Hiding' Function. Makes sure that the Window is and
|
|
/// stays hidden.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Height="2" Width="2" AllowsTransparency="True" Background="{x:Null}" WindowStyle="None" WindowStartupLocation="Manual" WindowState="Normal" ResizeMode="NoResize" Foreground="{x:Null}" ShowInTaskbar="False" ShowActivated="False" Left="0" Top="0" MaxWidth="2" MaxHeight="2" MinHeight="2" MinWidth="2" Visibility="Hidden" Opacity="0" IsHitTestVisible="False" IsTabStop="False" Focusable="False" IsEnabled="False"
|
|
/// </remarks>
|
|
/// <param name="wpfWindow">a WPF window object</param>
|
|
private void HideWPFWindow(Window wpfWindow)
|
|
{
|
|
// Force a tiny Window in top-left corner
|
|
wpfWindow.Width = 2;
|
|
wpfWindow.Height = 2;
|
|
wpfWindow.MinWidth = 2;
|
|
wpfWindow.MinHeight = 2;
|
|
wpfWindow.MaxWidth = 2;
|
|
wpfWindow.MaxHeight = 2;
|
|
wpfWindow.Top = 0;
|
|
wpfWindow.Left = 0;
|
|
|
|
// Force transparency
|
|
if (!wpfWindow.IsLoaded) // must not be already loaded
|
|
wpfWindow.AllowsTransparency = true;
|
|
wpfWindow.Background = null;
|
|
wpfWindow.Foreground = null;
|
|
|
|
// Force no user interaction
|
|
wpfWindow.WindowStartupLocation = WindowStartupLocation.Manual;
|
|
wpfWindow.WindowState = WindowState.Normal;
|
|
wpfWindow.ResizeMode = ResizeMode.NoResize;
|
|
wpfWindow.ShowInTaskbar = false;
|
|
wpfWindow.ShowActivated = false;
|
|
wpfWindow.Visibility = Visibility.Hidden;
|
|
wpfWindow.IsHitTestVisible = false;
|
|
wpfWindow.IsTabStop = false;
|
|
wpfWindow.Focusable = false;
|
|
|
|
// Force Opacity
|
|
wpfWindow.WindowStyle = WindowStyle.None;
|
|
wpfWindow.Opacity = 0.0;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|