MainWindowHandle 为 0 时将窗口置于前台

Posted

技术标签:

【中文标题】MainWindowHandle 为 0 时将窗口置于前台【英文标题】:Bring Window To Foreground When MainWindowHandle Is 0 【发布时间】:2017-12-16 05:41:49 【问题描述】:

如果MainWindowHandle 不为0,则以下代码将窗口置于前台。

如何将MainWindowHandle = 0 的窗口置于最前面?

这适用于 Microsoft Excel - 兼容性检查器窗口,该窗口显示一个 GUI,但在任务栏中没有图标,并且 MainWindowHandle = 0。

我没有运行其他 Excel 实例。

Add-Type @"
  using System;
  using System.Runtime.InteropServices;
  public class Tricks 
     [DllImport("user32.dll")]
     [return: MarshalAs(UnmanagedType.Bool)]
     public static extern bool SetForegroundWindow(IntPtr hWnd);
  
"@

$excel = (Get-Process | Where-Object  $_.ProcessName -eq 'EXCEL' ).MainWindowHandle
[void] [Tricks]::SetForegroundWindow($excel)

在 Windows 任务管理器中,我可以右键单击“Microsoft Excel - 兼容性检查器”并单击“置于前面”,这样就可以了。如何在 Powershell 中模拟此功能?

【问题讨论】:

每个窗口都有一个窗口句柄,不管它是否有任务栏按钮。您需要找到正确的窗口句柄。 我的代码没有得到正确的 MainWindowHandle 吗?如果 MainWindowHandle 不正确,我该如何获取它? MainWindowHandle 是一个谎言。 Windows API 不模拟主窗口 的概念。这纯粹是在 .NET 框架中实现的,它应用启发式方法来确定用户将什么视为主窗口。这不能保证成功。您需要使用不同的方法来找到您感兴趣的窗口句柄(例如EnumWindows)。 【参考方案1】:

感谢 IInspectable 为我指明了正确的方向。

此代码获取真正的MainWindowHandle 值:

$TypeDef2 = @"

using System;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Api


public class WinStruct

   public string WinTitle get; set; 
   public int MainWindowHandle  get; set; 


public class ApiDef

   private delegate bool CallBackPtr(int hwnd, int lParam);
   private static CallBackPtr callBackPtr = Callback;
   private static List<WinStruct> _WinStructList = new List<WinStruct>();

   [DllImport("User32.dll")]
   [return: MarshalAs(UnmanagedType.Bool)]
   private static extern bool EnumWindows(CallBackPtr lpEnumFunc, IntPtr lParam);

   [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
   static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

   private static bool Callback(int hWnd, int lparam)
   
       StringBuilder sb = new StringBuilder(256);
       int res = GetWindowText((IntPtr)hWnd, sb, 256);
      _WinStructList.Add(new WinStruct  MainWindowHandle = hWnd, WinTitle = sb.ToString() );
       return true;
     

   public static List<WinStruct> GetWindows()
   
      _WinStructList = new List<WinStruct>();
      EnumWindows(callBackPtr, IntPtr.Zero);
      return _WinStructList;
   



"@

Add-Type -TypeDefinition $TypeDef2 -Language CSharpVersion3

$excelInstance = [Api.Apidef]::GetWindows() | Where-Object  $_.WinTitle.ToUpper() -eq "Microsoft Excel - Compatibility Checker".ToUpper() 

所以现在使用这个正确的值,我可以调用SetForegroundWindow() 函数:

Add-Type @"
  using System;
  using System.Runtime.InteropServices;
  public class Tricks 
     [DllImport("user32.dll")]
     [return: MarshalAs(UnmanagedType.Bool)]
     public static extern bool SetForegroundWindow(IntPtr hWnd);
  
"@
[void] [Tricks]::SetForegroundWindow($excelInstance.MainWindowHandle)

我在website 上写了一篇详细的博客。

我在GitHub 上提供了一个完整示例,说明如何创建 Excel 文件、对其进行编辑并在另一个线程中运行上述代码,因为 Excel 弹出窗口阻塞了主线程,因此您必须这样做。

【讨论】:

以上是关于MainWindowHandle 为 0 时将窗口置于前台的主要内容,如果未能解决你的问题,请参考以下文章

C# 根据进程ID获取进程主窗口句柄

Windows 服务:获取所有用户的进程,包括 MainWindowHandle

在运行时将一个窗口的控件替换为另一个

如何在按钮单击时将部分视图呈现为模式弹出窗口?

在VB6.0中,运行一个窗体时,放大该窗体的尺寸时将触发的事件是

在 Electron 中创建新的浏览器窗口时将 node-integration 设置为 false 的结果是啥?