-1

下面的复杂解决方案需要将浏览器窗口置于前面。它的工作时间约为 90%。问题是那 10%,其实不然。

我有一个应用程序在与用户的活动桌面不同的桌面上运行(它是一个屏幕保护程序)。我还有一个从屏幕保护程序接收事件的 Windows 服务。该服务然后执行以下操作:

  • 模拟当前登录的用户并使用命令行参数中的 URL 启动帮助应用程序。
  • 助手应用程序由 CreateProcessAsUser 启动 - 这也是助手的理由,我需要使用 ShellExec,因此必须使用单独的进程。

此帮助应用程序执行以下操作:

  • 等到用户的当前桌面变为活动状态。在此之前,它会进行一段时间的循环并进行一些睡眠。
  • 然后它找出用户的默认浏览器
  • 使用 ShellExec(C# 中的 Process.Start)启动默认浏览器,并向浏览器传递一些命令行参数和 URL。

助手应用程序调用的实际命令行是这样的:

cmd /C start "" C:\PathToBrowser\Browser.exe URL -someargument

到目前为止,除了一件重要的事情之外,一切都在工作:浏览器并不是在所有可能的情况下都被放在前面。

还有什么比这更进一步的,我可以用这些浏览器来强迫它们出现在前面吗?我的问题是这样的:

假设我从命令行启动 Chrome。Chrome 只会向已经运行的实例发送一条消息,然后退出。所以我不能依赖我启动的进程的 PID 和 hWnd,它与实际显示网页的进程不同。

任何帮助将非常感激。

4

1 回答 1

2

感谢 cubrr 的帮助,他的想法与我的一些扩展一起奏效。首先,我必须找出将在浏览器中显示的网页的标题。在此之后,我必须使用 EnumWindows 找到新打开的浏览器窗口,并在其上调用 SetForegroundWindow。我的解决方案基于这些其他来源:

如何使用 EnumWindows 通过部分标题找到某个窗口。

从网页中获取标题。

最小化时转到前向窗口

cubrr 建议的解决方案,使用 FindWindow (您必须知道确切的窗口标题才能使用它):

[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);

[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]
public static extern bool IsIconic(IntPtr handle);

[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr handle, int nCmdShow);

void Main()
{
    const int SW_RESTORE = 9;
    var hWnd = FindWindow(null, "Google - Google Chrome");
    if (IsIconic(hWnd))
        ShowWindow(hWnd, SW_RESTORE);
    SetForegroundWindow(hWnd);
}

这是我最终使用的最终代码:

public class MyClass
{
    private const int SW_RESTORE = 9;
    [DllImport("User32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool BringWindowToTop(IntPtr hWnd);
    [DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
    private static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
    [DllImport("user32.dll")]
    private static extern bool IsIconic(IntPtr handle);
    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetWindowTextLength(IntPtr hWnd);
    [DllImport("user32.dll")]
    private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);

    private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

    public static string GetWebPageTitle(string url)
    {
        // Create a request to the url
        HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;

        // If the request wasn't an HTTP request (like a file), ignore it
        if (request == null) return null;

        // Use the user's credentials
        request.UseDefaultCredentials = true;

        // Obtain a response from the server, if there was an error, return nothing
        HttpWebResponse response = null;
        try { response = request.GetResponse() as HttpWebResponse; }
        catch (WebException) { return null; }

        // Regular expression for an HTML title
        string regex = @"(?<=<title.*>)([\s\S]*)(?=</title>)";

        // If the correct HTML header exists for HTML text, continue
        if (new List<string>(response.Headers.AllKeys).Contains("Content-Type"))
            if (response.Headers["Content-Type"].StartsWith("text/html"))
            {
                // Download the page
                WebClient web = new WebClient();
                web.UseDefaultCredentials = true;
                string page = web.DownloadString(url);

                // Extract the title
                Regex ex = new Regex(regex, RegexOptions.IgnoreCase);
                return ex.Match(page).Value.Trim();
            }

        // Not a valid HTML page
        return null;
    }

    public static void BringToFront(string title)
    {
        try
        {
            if (!String.IsNullOrEmpty(title))
            {
                IEnumerable<IntPtr> listPtr = null;

                // Wait until the browser is started - it may take some time
                // Maximum wait is (200 + some) * 100 milliseconds > 20 seconds
                int retryCount = 100;
                do
                {
                    listPtr = FindWindowsWithText(title);
                    if (listPtr == null || listPtr.Count() == 0)
                    {
                        Thread.Sleep(200);
                    }
                } while (--retryCount > 0 || listPtr == null || listPtr.Count() == 0);

                if (listPtr == null)
                    return;

                foreach (var hWnd in listPtr)
                {
                    if (IsIconic(hWnd))
                        ShowWindow(hWnd, SW_RESTORE);
                    SetForegroundWindow(hWnd);
                }
            }
        }
        catch (Exception)
        {
            // If it fails at least we tried
        }
    }

    public static string GetWindowText(IntPtr hWnd)
    {
        int size = GetWindowTextLength(hWnd);
        if (size++ > 0)
        {
            var builder = new StringBuilder(size);
            GetWindowText(hWnd, builder, builder.Capacity);
            return builder.ToString();
        }

        return String.Empty;
    }

    public static IEnumerable<IntPtr> FindWindowsWithText(string titleText)
    {
        IntPtr found = IntPtr.Zero;
        List<IntPtr> windows = new List<IntPtr>();

        EnumWindows(delegate(IntPtr wnd, IntPtr param)
        {
            if (GetWindowText(wnd).Contains(titleText))
            {
                windows.Add(wnd);
            }
            return true;
        }, IntPtr.Zero);

        return windows;
    }

    [STAThread]
    public static int Main(string[] args)
    {
        try
        {
            if (args.Count() == 0)
                return 0;

            // ...
            // Wait until the user's desktop is inactive (outside the scope of this solution)
            // ...

            String url = args[0];


            System.Diagnostics.Process process = new System.Diagnostics.Process();
            System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
            startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
            startInfo.FileName = "cmd.exe";

            // ...
            // Get the path to the default browser from registry, and create a StartupInfo object with it.
            // ...

            process.StartInfo = startInfo;
            process.Start();
            try
            {
                process.WaitForInputIdle();
            }
            catch (InvalidOperationException)
            {
                // if the process exited then it passed the URL on to the other browser process.
            }

            String title = GetWebPageTitle(url);
            if (!String.IsNullOrEmpty(title))
            {
                BringToFront(title);
            }

            return 0;
        }
        catch (System.Exception ex)
        {
            return -1;
        }
    }
}
于 2015-06-18T13:35:33.050 回答