将由 ShellExec/Process.Start 启动的浏览器置于最前面
Posted
技术标签:
【中文标题】将由 ShellExec/Process.Start 启动的浏览器置于最前面【英文标题】:Bring browser to front that is started by ShellExec/Process.Start 【发布时间】:2015-06-18 10:13:54 【问题描述】:下面的复杂解决方案需要将浏览器窗口置于前面。它的工作时间约为 90%。问题是那 10%,其实不然。
我有一个应用程序在与用户的活动桌面不同的桌面上运行(它是一个屏幕保护程序)。 我还有一个从屏幕保护程序接收事件的 Windows 服务。该服务然后执行以下操作:
模拟当前登录的用户并使用命令行参数中的 URL 启动帮助应用程序。 助手应用程序由 CreateProcessAsUser 启动 - 这也是助手的理由,我需要使用 ShellExec,因此必须使用单独的进程。此帮助应用程序执行以下操作:
等待用户的当前桌面变为活动状态。在此之前,它会执行一个 while 循环并进行一些睡眠。 然后找出用户的默认浏览器 使用 ShellExec(C# 中的 Process.Start)启动默认浏览器,并向浏览器传递一些命令行参数和 URL。帮助应用程序调用的实际命令行是这样的:
cmd /C start "" C:\PathToBrowser\Browser.exe URL -someargument
到目前为止,一切正常,除了一件重要的事情:浏览器并非在所有可能的情况下都放在前面。
还有什么比这更进一步,我可以使用这些浏览器来强制它们出现在前面吗?我的问题是这样的:
假设我从命令行启动 Chrome。 Chrome 只会向已经运行的实例发送一条消息,然后退出。所以我不能依赖我启动的进程的PID和hWnd,它不会和实际显示网页的一样。
任何帮助将不胜感激。
【问题讨论】:
我认为答案here 应该可以完成这项工作! 谢谢cubrr,我已经看过那个帖子了。实际上,*** 上的帖子已经帮助了我很多。使用 FindWindow 和 SetForeground 不是选项,因为我不知道最后哪个窗口会显示我的网页(请阅读我帖子的结尾) 这就是为什么 SetForegroundWindow 不能在浏览器上工作的原因:“所以我不能依赖我启动的进程的 PID 和 hWnd,它与实际显示的进程不同网页。” 你不应该使用来自Process.Id
的PID,你应该从FindWindow
获取窗口句柄。如果您知道网页的 html 标题,则可以使用 FindWindow(null, "Page title - Google Chrome");
获取 Chrome 窗口的句柄。如果已最小化,请使用here 中提到的功能。
嗨,cubrr,如果它有效,那也不是一个坏主意。但是,我事先不知道页面的标题 - 在尝试将 URL 发送到浏览器之前,我需要自己下载并解析 html。我要试一试。
【参考方案1】:
感谢 cubrr 的帮助,他的想法与我的一些扩展相得益彰。首先,我必须找出将在浏览器中显示的网页的标题。在此之后,我必须使用 EnumWindows 找到新打开的浏览器窗口,并在其上调用 SetForegroundWindow。 我的解决方案基于以下其他来源:
How to use EnumWindows to find a certain window by partial title.
Get the title from a webpage.
Bring to forward window when minimized
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;
【讨论】:
太棒了!很高兴看到我能提供帮助。以上是关于将由 ShellExec/Process.Start 启动的浏览器置于最前面的主要内容,如果未能解决你的问题,请参考以下文章