【问题标题】:Restore minimized window of another, suspended application恢复另一个已挂起的应用程序的最小化窗口
【发布时间】:2019-10-11 09:27:24
【问题描述】:

我正在尝试从另一个应用程序恢复最小化、暂停的 UWP 应用程序, 但我无法找到可行的解决方案。

通过 PInvoke 使用 .NET Core 2.2 和 win32 API

我尝试设置窗口位置,设置窗口位置但没有任何效果。我发现的唯一原因是我要恢复的进程被挂起。 我做了一些研究,该应用程序是由 WinRT 堆栈管理的 UWP。遗憾的是 我找不到恢复进程并显示它的方法

感谢您的每一个帮助或建议。

        Process proc = Process.GetProcessesByName("HxOutlook").FirstOrDefault();

        if (proc != null)
        {
            ShowWindow(proc.MainWindowHandle, SW_SHOWNORMAL);   // Make the window visible if it was hidden
            ShowWindow(proc.MainWindowHandle, SW_RESTORE);      // Next, restore it if it was minimized
            SetForegroundWindow(proc.MainWindowHandle);         // Finally, activate the window 
        }

【问题讨论】:

  • 邮件是一个 UWP 应用。
  • No proc 不是没有并且有正确的句柄。此代码适用于非 UWP 应用,例如 spotify 的客户端。
  • 当线程挂起时 - 它不会处理 windows 消息。没有这个 - 窗口状态将不会改变。所以没有第一个取消暂停过程 - 没有解决方案
  • MainWindowHandle 是 .NET 的东西,本机 Win32 中不存在这个概念。您是否尝试过在实际应用程序窗口的实际 HWND 上调用 ShowWindow?你试过 ShowWindowAsync 了吗?

标签: c# windows winapi uwp .net-core


【解决方案1】:

我实际上并不认为窗口是 UWP 应用程序的一部分并不重要,确切地说。

您可以通过 Win32 调用 ShowWindow(hwnd, SW_RESTORE) 恢复 Mail 的主窗口。但是,诀窍是您必须找到正确的窗口。在 Spy++ 中,您可以看到某个窗口具有一个窗口类“ApplicationFrameWindow”,该窗口类与模块名称为“APPLICATIONFRAMEHOST”的进程相关联——这些必须是 UWP 实现的工件。

为了找到作为 Mail 主窗口的特定“ApplicationFrameWindow”,而不依赖于诸如窗口文本之类的可变或短暂的东西,我发现正确的窗口是与 HxOutlook.exe 进程关联的窗口之一的所有者.这样做可能不那么复杂,但以下工作。这显然是一个原生命令行应用程序:

#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>(&param));
    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;
}

它正在查找“HxOutlook.exe”的进程 ID,枚举该窗口的 WNDPROC 在该进程拥有的线程中运行的所有窗口,然后显示拥有这些窗口的所有窗口,其中一个是主邮件窗口。

您可以通过平台调用来执行上述操作,或者找到更简单的方法,或者将上面的代码放入 DLL 并通过 DLLImport 在 C# 中调用。

【讨论】:

  • 谢谢你的回答,效果很好,没想到我能做到这样。
  • 没问题...仅供参考,实际上我的 GetProcessByName() 函数中有一个错误:没有使用 bytes_returned 值仅查看进程[1024] 中实际返回的元素。上面是固定的。
【解决方案2】:

问题不在于进程被挂起,而是我无法通过 C# API (System.Diagnostics.Process) 获得正确的窗口句柄。所有功劳归于 jwezorek,这是 C# 实现。 (它有问题,但它有效)

    static UInt32 GetProccessByName(string targetProcessName)
    {
        UInt32[] processes = new UInt32[1024];
        UInt32 bytesCopied;

        if (!Psapi.EnumProcesses(processes, (UInt32)processes.Length, out bytesCopied))
        {
            return 0;
        }

        foreach (var pid in processes)
        {
            IntPtr handle = Kernel32.OpenProcess(
              (Kernel32.ProcessAccessFlags.QueryInformation |
                Kernel32.ProcessAccessFlags.VirtualMemoryRead),
                false,
                (int)pid);

            UInt32[] modules = new UInt32[1024];
            UInt32 bytesNeeeded;

            if (handle != null)
            {
                if (Psapi.EnumProcessModules(handle, modules, (UInt32)modules.Length, out bytesNeeeded))
                {
                    StringBuilder text = new StringBuilder(1024);
                    Psapi.GetModuleBaseName(handle, IntPtr.Zero, text, (UInt32)text.Capacity);

                    if (text.Equals(targetProcessName))
                    {
                        return pid;
                    }
                }
            }
        }

        return 0;
    }

    public static bool EnumProc(IntPtr hWnd, ref SearchData data)
    {
        UInt32 pid;

        User32.GetWindowThreadProcessId(hWnd, out pid);

        if(pid == data.PID)
        {
            data.Windows.Add(hWnd);
        }

        return true;
    }

    static List<IntPtr> GetWindowFromProcessID(UInt32 pid)
    {
        var searchData = new SearchData(pid);

        User32.EnumWindows(new User32.EnumWindowsProc(EnumProc), ref searchData);

        return searchData.Windows;
    }

    static void Main(string[] args)
    {
        var pid = GetProccessByName("HxOutlook.exe");

        var windows = GetWindowFromProcessID(pid);

        foreach (var window in windows)
        {
            var owner = User32.GetWindow(window, User32.GetWindowType.GW_OWNER);
            if(owner != null)
            {
                User32.ShowWindow(owner, SW_RESTORE);
            }
        }
    }

【讨论】:

    猜你喜欢
    • 2012-02-24
    • 2014-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多