当我隐藏应用程序时,runningProcess 的 MainWindowHandle 显示为 0
你是对的。如果进程没有与之关联的图形界面(隐藏/最小化),则 MainWindowHandle 值为零。
作为解决方法,您可以尝试通过使用 EnumDesktopWindows 函数枚举所有打开的窗口并将其进程 ID 与隐藏/最小化窗口的进程 ID 进行比较来获取隐藏窗口的 HANDLE。
更新
WPF 的 WIN32 窗口与标准 WIN32 窗口的行为略有不同。它的类名由单词 HwndWrapper、创建它的 AppDomain 的名称和唯一的随机 Guid(每次启动时更改)组成,例如 HwndWrapper[WpfApp.exe;;4d426cdc -31cf-4e4c-88c7-ede846ab6d44]。
更新 2
当使用 Hide() 方法隐藏 WPF 的窗口时,它会在内部调用 UpdateVisibilityProperty(Visibility.Hidden),这反过来将 UIElement 的内部可见性标志设置为 false。当我们调用WPF Window的Show()方法时,UpdateVisibilityProperty(Visibility.Visible)被调用,UIElement的内部可见性标志被切换。
当我们使用ShowWindow() 显示 WPF 窗口时,UpdateVisibilityProperty() 方法不会被触发,因此内部可见性标志不会被反转(这会导致窗口以黑色背景显示)。
通过查看WPF Window 内部实现,在不调用Show() 或Hide() 方法的情况下切换内部可见性标志的唯一方法是发送WM_SHOWWINDOW 消息。
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;
}
使用示例
...
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));
}
}
...