When I hide the app, the MainWindowHandle of runningProcess is showing as 0
You're right. If a process doesn't have a graphical interface associated with it (hidden/ minimized) then the MainWindowHandle
value is zero.
As workaround, you could try getting the HANDLE
for the hidden window by enumerating all open windows using EnumDesktopWindows
function and compare its process id with the hidden/ minimized windows's process id.
Update
The WPF's WIN32 window has a bit different behavior than the standard WIN32 window. It has the class name composed of the word HwndWrapper
, the name of AppDomain it was created in, and a unique random Guid
(which changes on every launch), e.g., HwndWrapper[WpfApp.exe;;4d426cdc-31cf-4e4c-88c7-ede846ab6d44].
Update 2
When WPF's window is hidden by using the Hide()
method, it internally calls UpdateVisibilityProperty(Visibility.Hidden)
, which in turns set the internal visibility flag for UIElement
to false. When we call the Show()
method of WPF Window
, UpdateVisibilityProperty(Visibility.Visible)
is called, and the internal visibility flag for UIElement
is toggled.
When we show the WPF window using the ShowWindow()
, the UpdateVisibilityProperty()
method is not trigerred, thus the internal visibility flag does not get reversed (which causes the window to be displayed with black backround).
By looking at the WPF Window
internal implementation, the only way to toggle the internal visiblity flag, without calling the Show()
or Hide()
method, is by sending a WM_SHOWWINDOW
message.
const int GWL_EXSTYLE = (-20);
const uint WS_EX_APPWINDOW = 0x40000;
const uint WM_SHOWWINDOW = 0x0018;
const int SW_PARENTOPENING = 3;
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumWindowsProc ewp, int lParam);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("user32.dll")]
private static extern uint GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern uint GetWindowText(IntPtr hWnd, StringBuilder lpString, uint nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern bool GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
static bool IsApplicationWindow(IntPtr hWnd) {
return (GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_APPWINDOW) != 0;
}
static IntPtr GetWindowHandle(int pid, string title) {
var result = IntPtr.Zero;
EnumWindowsProc enumerateHandle = delegate(IntPtr hWnd, int lParam)
{
int id;
GetWindowThreadProcessId(hWnd, out id);
if (pid == id) {
var clsName = new StringBuilder(256);
var hasClass = GetClassName(hWnd, clsName, 256);
if (hasClass) {
var maxLength = (int)GetWindowTextLength(hWnd);
var builder = new StringBuilder(maxLength + 1);
GetWindowText(hWnd, builder, (uint)builder.Capacity);
var text = builder.ToString();
var className = clsName.ToString();
// There could be multiple handle associated with our pid,
// so we return the first handle that satisfy:
// 1) the handle title/ caption matches our window title,
// 2) the window class name starts with HwndWrapper (WPF specific)
// 3) the window has WS_EX_APPWINDOW style
if (title == text && className.StartsWith("HwndWrapper") && IsApplicationWindow(hWnd))
{
result = hWnd;
return false;
}
}
}
return true;
};
EnumDesktopWindows(IntPtr.Zero, enumerateHandle, 0);
return result;
}
Usage Example
...
if (runningProcess.MainWindowHandle == IntPtr.Zero) {
var handle = GetWindowHandle(runningProcess.Id, runningProcess.MainWindowTitle);
if (handle != IntPtr.Zero) {
// show window
ShowWindow(handle, 5);
// send WM_SHOWWINDOW message to toggle the visibility flag
SendMessage(handle, WM_SHOWWINDOW, IntPtr.Zero, new IntPtr(SW_PARENTOPENING));
}
}
...