Restore a Minimized Window of Another Application

Restore a minimized window of another application

... Apparently you cannot trust the information a Process gives you.

Process.MainWindowHandle returns the window handle of the first window created by the application, which is USUALLY that app's main top-level window. However, in my case, a call to FindWindow() shows that the handle of the actual window I want to restore is not what MainWindowHandle is pointing to. It appears that the window handle from the Process, in this case, is that of the splash screen shown as the program loads the main form.

If I call ShowWindow on the handle that FindWindow returned, it works perfectly.

What's even more unusual is that when the window's open, the call to SetForegroundWindow(), when given the process's MainWindowHandle (which should be invalid as that window has closed), works fine. So obviously that handle has SOME validity, just not when the window's minimized.

In summary, if you find yourself in my predicament, call FindWindow, passing it the known name of your external app's main window, to get the handle you need.

Restore a minimized window of another application (C++ WinAPI)

I've had similar problems in the past that I've solved using Get/SetWindowPlacement():

// Ensure that the given window is not minimized.
// If it is minimized, restore it to its normal state.
void EnsureNotMinimized(HWND hWnd)
{
WINDOWPLACEMENT placement;
placement.length = sizeof(placement);

if(!GetWindowPlacement(hWnd, &placement))
return;

BOOL minimized = (placement.showCmd & SW_SHOWMINIMIZED) != 0;
if(!minimized)
return;

placement.showCmd = SW_SHOWNORMAL;
SetWindowPlacement(hWnd, &placement);
}

However, I've only used this for windows that belong to my own application. I don't know if security would allow it to be used on outsiders.

Restore minimized window of another, suspended application

I don't actually think that it matters that the window is part of a UWP app, exactly.

You can restore Mail's main window via the Win32 call ShowWindow(hwnd, SW_RESTORE). However, the trick is you have to find the correct window. In Spy++ you can see that it is some window that has a window class "ApplicationFrameWindow" that is associated with the process with module name "APPLICATIONFRAMEHOST" -- these must be artifacts of UWP's implementation.

In order to find the particular "ApplicationFrameWindow" that is Mail's main window, without relying on something mutable or ephemeral like window text, I found that the correct window is the owner of one of the windows associated with the HxOutlook.exe process. There may be a less convoluted to do this but the following works. This is a native command line application obviously:

#include <Windows.h>
#include <psapi.h>
#include <tchar.h>
#include <vector>

DWORD GetProcessByName(const TCHAR* target_process_name)
{
DWORD processes[1024], bytes_returned;

if (!EnumProcesses(processes, sizeof(processes), &bytes_returned))
return 0;
int n = bytes_returned / sizeof(DWORD);
for (int i = 0; i < n; i++) {
auto pid = processes[i];
TCHAR process_name[MAX_PATH] = TEXT("");
HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (process) {
HMODULE module;
DWORD bytes_needed;
if (EnumProcessModules(process, &module, sizeof(module), &bytes_needed)) {
GetModuleBaseName(process, module, process_name, sizeof(process_name) / sizeof(TCHAR));
if (_tcscmp(process_name, target_process_name) == 0)
return pid;
}
}
}
return 0;
}

struct HwndFinder {
std::vector<HWND> windows;
DWORD pid;

HwndFinder(DWORD pid) : pid(pid)
{}
};

BOOL CALLBACK EnumWindowsFindProcessWindow(HWND hwnd, LPARAM lParam)
{
DWORD pid;
HwndFinder* param = reinterpret_cast<HwndFinder*>(lParam);
GetWindowThreadProcessId(hwnd, &pid);
if (pid == param->pid) {
param->windows.push_back(hwnd);
}
return TRUE;
}

std::vector<HWND> GetWindowsFromProcessID(DWORD pid)
{
HwndFinder param(pid);
EnumWindows(EnumWindowsFindProcessWindow, reinterpret_cast<LPARAM>(¶m));
return param.windows;
}

int main()
{
auto mail_process = GetProcessByName(TEXT("HxOutlook.exe"));
auto windows = GetWindowsFromProcessID(mail_process);
for (auto window : windows) {
auto owner = GetWindow(window, GW_OWNER);
if (owner)
ShowWindow(owner, SW_RESTORE);
}
return 0;
}

It's finding the process ID for "HxOutlook.exe", enumerating all the windows in which the WNDPROC for the window runs in a thread owned by that process, and then ShowWindow-ing all windows that own those windows, one of which is the main Mail window.

You could do something like the above via platform invoking, or find a simpler way, or put the code above in a DLL and call into it in C# via DLLImport.

How can I restore a window of a program minimized into tray

The thing is, that Windows does not have a support to minimize the window into system tray. There is no such state. And in order to simulate this behavior applications simply hide the windows totally.
You can use Spy++ tool to find the window of your target app while it is visible.
Then "minimize" it to tray, and see what was changed in the attributes. Then in your app you should revert the attributes. This is needed, because there several ways to hide a window and different apps use one of them.

For example Windows Task Manager change the style from VS_VISIBLE to VS_MINIMIZED (and remove VS_VISIBLE).

How to restore minimized/hidden window?

Change your ExecuteOpenWindowsCommand to only set the Activated property.

private void ExecuteOpenWindowsCommand()
{
Activated = true;
}

Adapt the OnActivatedChanged method in the ActivateBehavior like this.

private static void OnActivatedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var behavior = (ActivateBehavior)dependencyObject;
if (!behavior.Activated || behavior.isActivated)
return;

// The Activated property is set to true but the Activated event (tracked by the
// isActivated field) hasn't been fired. Go ahead and activate the window.
var window = behavior.AssociatedObject;
if (window.WindowState == WindowState.Minimized)
{
window.WindowState = WindowState.Normal;
SystemCommands.RestoreWindow(window);
}

window.Activate();
}

The essential part is restoring the window, which did not happen before, it was hidden. Then it needs to be activated to come to foreground. The window state asssignment is not needed, but makes the restore transition of the window more pleasant.

Minimized window needs two clicks to restore

The problem is related to a UI component that You are populating, or invoking it at runtime. It is quite monkey job, but what you can do to find what is hanging is comment some UI components, and try to see if the problem persists.

Usually this happens because the Main UI Thread when you are running STA, is waiting for a focus, or for an user action. Since you double focus it, it might proceed.

How to restore a minimized Window in code-behind?

Not sure this will work for everybody, but I ran into this today and someone on the team suggested "have you tried Normal"?

Turns out he was right. The following seems to nicely restore your window:

if (myWindow.WindowState == WindowState.Minimized)
myWindow.WindowState = WindowState.Normal;

That works just fine, restoring the window to Maximized if needed. It seems critical to check for the minimized state first as calling WindowState.Normal a second time will "restore" your window to its non-maximized state.

Hope this helps.



Related Topics



Leave a reply



Submit