如何获取我的表单父级窗口的当前标题?

2024-01-09

我有一个 WinForm 应用程序,它是其他进程(例如 Google Chrome)的 Windows 的父级。我使用以下代码将 Windows 设为我的表单的父级,使用返回的句柄[Process].MainWindowHandle.
我正在努力寻找MainWindowTitle是我的表单的所有 Windows 的父级,因此我可以在标签上显示它们的名称。

当 Web 浏览器的窗口为embedded,当选择不同的网页时,标题将发生变化,切换选项卡。

我用于启动程序的代码确实可以正常工作:

ProcessStartInfo ps1 = new ProcessStartInfo(@"C:/Users/Jacob/AppData/Roaming/Spotify/Spotify.exe");
ps1.WindowStyle = ProcessWindowStyle.Minimized;
Process p1 = Process.Start(ps1);
// Allow the process to open it's window
Thread.Sleep(1000);
appWin1 = p1.MainWindowHandle;
spotify = p1;

// Put it into this form
SetParent(appWin1, this.Handle);
// Move the window to overlay it on this window
MoveWindow(appWin1, 0, 70, this.Width / 2, this.Height/2, true);

既然你愿意用UI自动化 https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation来处理这个养育子女事情,我建议完全使用自动化方法来处理这个问题。几乎,SetParent仍然需要:)。

此处显示的类使用WindowPatter.WindowOpenedEvent https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation.windowpattern.windowopenedevent检测并通知系统中何时打开新窗口。
它可以是任何窗口,包括控制台(仍然是窗口)。
此方法允许在已创建窗口句柄时识别窗口,因此您不需要任意time-out或尝试使用Process.WaitForInputIdle(),这可能不会达到预期的结果。

您可以将进程名称列表传递给ProcessNames类的属性:当打开属于这些进程之一的任何窗口时,UIAutomation 会检测到它并引发公共事件。它通知订阅者列表中的进程之一打开了一个窗口,这是ProcessId所有者和 Windows 句柄。
这些值在自定义中传递EventArgs class, ProcessStartedArgs当。。。的时候ProcessStarted事件被引发。

由于自动化事件是在 UI 线程以外的线程中引发的,因此该类捕获同步上下文 https://learn.microsoft.com/en-us/dotnet/api/system.threading.synchronizationcontext创建类的位置(UI 线程,因为您可能在表单中创建此类)并将事件编组到该线程,调用其Post() https://learn.microsoft.com/en-us/dotnet/api/system.threading.synchronizationcontext.post方法传递一个发送或后回调 http://SendOrPostCallback代表。
这样,您可以安全地将表单的句柄和窗口的句柄传递给SetParent().

要检索父窗口的当前标题 (Caption),请将先前在事件参数中返回的句柄传递给GetCurrentWindowTitle()方法。如果窗口包含选项卡式子窗口,作为 Web 浏览器,此方法将返回与当前所选选项卡相关的标题。

▶ 该类是一次性的,您需要调用它的 publicDispose()方法。这将删除自动化事件处理程序以及您已订阅的公共事件的调用列表中的所有事件。这样,您就可以使用 Lambda 来订阅事件。


使用 Field 来存储此类的实例。在需要时创建实例,并传递您感兴趣的进程名称列表。
订阅ProcessStarted event.
当这些进程中的一个打开一个新窗口时,您将收到一条通知,并且可以执行父子关系:

public partial class SomeForm : Form
{
    private WindowWatcher watcher = null;

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        watcher = new WindowWatcher();
        watcher.ProcessNames.AddRange(new[] { "msedge", "firefox", "chrome", "notepad" });

        watcher.ProcessStarted += (o, ev) => {
            SetParent(ev.WindowHandle, this.Handle);
            MoveWindow(ev.WindowHandle, 0, 70, this.Width / 2, this.Height / 2, true);
            string windowTitle = WindowWatcher.GetCurrentWindowTitle(ev.WindowHandle);
        };
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        watcher.Dispose();
        base.OnFormClosed(e);
    }
}

WindowWatcher class:

NOTE: UI 自动化组件 https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation是一部分Windows Presentation Framework.
当 WinForms 应用程序中引用这些程序集之一时,WinForms 应用程序将变为 DpiAware (SystemAware),如果还不是 DpiAware。
这可能会对一个或多个并非设计用于处理 Dpi 感知更改和通知的表单的布局产生影响。

需要项目参考:

  • UI自动化客户端
  • UI自动化类型
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Automation;

public class WindowWatcher : IDisposable
{
    private SynchronizationContext context = null;
    private readonly SendOrPostCallback eventCallback;
    public event EventHandler<ProcessStartedArgs> ProcessStarted;
    private AutomationElement uiaWindow;
    private AutomationEventHandler WindowOpenedHandler;

    public WindowWatcher() {
        context = SynchronizationContext.Current;
        eventCallback = new SendOrPostCallback(EventHandlersInvoker);
        InitializeWatcher();
    }

    public List<string> ProcessNames { get; set; } = new List<string>();

    private void InitializeWatcher()
    {
        Automation.AddAutomationEventHandler(
            WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
            TreeScope.Children, WindowOpenedHandler = new AutomationEventHandler(OnWindowOpenedEvent));
    }

    public static string GetCurrentWindowTitle(IntPtr handle)
    {
        if (handle == IntPtr.Zero) return string.Empty;
        var element = AutomationElement.FromHandle(handle);
        if (element != null) {
            return element.Current.Name;
        }
        return string.Empty;
    }

    private void OnWindowOpenedEvent(object uiaElement, AutomationEventArgs e)
    {
        uiaWindow = uiaElement as AutomationElement;
        if (uiaWindow == null || uiaWindow.Current.ProcessId == Process.GetCurrentProcess().Id) return;
        var window = uiaWindow.Current;

        var procName = string.Empty;
        using (var proc = Process.GetProcessById(window.ProcessId)) {
            if (proc == null) throw new InvalidOperationException("Invalid Process");
            procName = proc.ProcessName;
        }

        if (ProcessNames.IndexOf(procName) >= 0) {
            var args = new ProcessStartedArgs(procName, window.ProcessId, (IntPtr)window.NativeWindowHandle);
            context.Post(eventCallback, args);
        }
    }

    public class ProcessStartedArgs : EventArgs
    {
        public ProcessStartedArgs(string procName, int procId, IntPtr windowHandle)
        {
            ProcessName = procName;
            ProcessId = procId;
            WindowHandle = windowHandle;
        }

        public string ProcessName { get; }
        public int ProcessId { get; }
        public IntPtr WindowHandle { get; }
    }

    private void EventHandlersInvoker(object state)
    {
        if (!(state is ProcessStartedArgs args)) return;
        ProcessStarted?.Invoke(this, args);
    }

    ~WindowWatcher() { Dispose(false); }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected void Dispose(bool disposing)
    {
        if (uiaWindow != null && WindowOpenedHandler != null) {
            Automation.RemoveAutomationEventHandler(
                WindowPattern.WindowOpenedEvent, uiaWindow, WindowOpenedHandler);
        }
            
        if (ProcessStarted != null) {
            var invList = ProcessStarted.GetInvocationList();
            if (invList != null && invList.Length > 0) {
                for (int i = invList.Length - 1; i >= 0; i--) {
                    ProcessStarted -= (EventHandler<ProcessStartedArgs>)invList[i];
                }
            }
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何获取我的表单父级窗口的当前标题? 的相关文章

  • 秒表有最长运行时间吗?

    多久可以Stopwatch在 NET 中运行 如果达到该限制 它会回绕到负数还是从 0 重新开始 Stopwatch Elapsed返回一个TimeSpan From MSDN https learn microsoft com en us
  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • 从Web API同步调用外部api

    我需要从我的 Web API 2 控制器调用外部 api 类似于此处的要求 使用 HttpClient 从 Web API 操作调用外部 HTTP 服务 https stackoverflow com questions 13222998
  • 如何使用 ICU 解析汉字数字字符?

    我正在编写一个使用 ICU 来解析由汉字数字字符组成的 Unicode 字符串的函数 并希望返回该字符串的整数值 五 gt 5 三十一 gt 31 五千九百七十二 gt 5972 我将区域设置设置为 Locale getJapan 并使用
  • 在 Windows 窗体中保存带有 Alpha 通道的单色位图会保存不同(错误)的颜色

    在 C NET 2 0 Windows 窗体 Visual Studio Express 2010 中 我保存由相同颜色组成的图像 Bitmap bitmap new Bitmap width height PixelFormat Form
  • 如何从 appsettings.json 文件中的对象数组读取值

    我的 appsettings json 文件 StudentBirthdays Anne 01 11 2000 Peter 29 07 2001 Jane 15 10 2001 John Not Mentioned 我有一个单独的配置类 p
  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • 重载<<的返回值

    include
  • WCF 中 SOAP 消息的数字签名

    我在 4 0 中有一个 WCF 服务 我需要向 SOAP 响应添加数字签名 我不太确定实际上应该如何完成 我相信响应应该类似于下面的链接中显示的内容 https spaces internet2 edu display ISWG Signe
  • 显示UnityWebRequest的进度

    我正在尝试使用下载 assetbundle统一网络请求 https docs unity3d com ScriptReference Networking UnityWebRequest GetAssetBundle html并显示进度 根
  • IEnumerable 与 IReadOnlyList

    选择有什么区别IEnumerable
  • 转发声明和包含

    在使用库时 无论是我自己的还是外部的 都有很多带有前向声明的类 根据情况 相同的类也包含在内 当我使用某个类时 我需要知道该类使用的某些对象是前向声明的还是 include d 原因是我想知道是否应该包含两个标题还是只包含一个标题 现在我知
  • 通过指向其基址的指针删除 POD 对象是否安全?

    事实上 我正在考虑那些微不足道的可破坏物体 而不仅仅是POD http en wikipedia org wiki Plain old data structure 我不确定 POD 是否可以有基类 当我读到这个解释时is triviall
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • 基于 OpenCV 边缘的物体检测 C++

    我有一个应用程序 我必须检测场景中某些项目的存在 这些项目可以旋转并稍微缩放 更大或更小 我尝试过使用关键点检测器 但它们不够快且不够准确 因此 我决定首先使用 Canny 或更快的边缘检测算法 检测模板和搜索区域中的边缘 然后匹配边缘以查
  • C# 模拟VolumeMute按下

    我得到以下代码来模拟音量静音按键 DllImport coredll dll SetLastError true static extern void keybd event byte bVk byte bScan int dwFlags
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • Windows 和 Linux 上的线程

    我在互联网上看到过在 Windows 上使用 C 制作多线程应用程序的教程 以及在 Linux 上执行相同操作的其他教程 但不能同时用于两者 是否存在即使在 Linux 或 Windows 上编译也能工作的函数 您需要使用一个包含两者的实现
  • C++ 标准是否指定了编译器的 STL 实现细节?

    在写答案时this https stackoverflow com questions 30909296 can you put a pimpl class inside a vector我遇到了一个有趣的情况 这个问题演示了这样一种情况

随机推荐

  • Django/Python:如何迭代字典中的列表以进行迁移/迁移数据

    我正在尝试设置迁移文件以将值加载到我的表中 我正在我的表格中创建国家 州 我想以一种可以将每个国家 地区放入自己的文件中的方式进行设置 然后遍历所有移民国家 地区 我成功地分别输入了所有的名字 但我正在努力让它变得更容易 UPDATE 感谢
  • 当使用Server.Transfer时,整个Asp.Net生命周期是否再次执行?

    我知道 Server Transfer 不会往返返回请求客户端 我无法了解到的是 控制权是否只是直接传递给您要转移到的新请求处理程序 或者是否再次执行整个请求生命周期 我假设使用传输 URL 再次执行整个生命周期 但想验证情况是否如此 这是
  • Postgres 中整理的目的

    我是 Postgres 新手 我刚刚开始学习here http www w3resource com PostgreSQL pl pgsql declarations php 我从 select 语句中找到 COLLATE C SELECT
  • 从 Postman 生成 Azure 访问令牌

    我想从 Postman 生成 Azure 令牌 以便在我的项目中进行 API 授权 我可以使用下面的 API 请求生成令牌 但收到以下错误消息 此请求的授权被拒绝 在另一个 API 请求中使用生成的令牌时 端点 https login mi
  • 为什么字符串上的 + + 会产生奇怪的结果

    我正在努力使用 jQuery 动态附加元素 发现使用时 表明NaN并且不会添加下一个文本 我能以某种方式猜到 在这里作为算术加运算符并返回NaN 这不是增量运算符 因为两者之间有空格 我的问题是 这里实际发生了什么所以它返回NaN Why
  • sqlalchemy、postgresql 和关系陷入“事务中空闲”状态

    我有一个与 sqlalchemy 和 postgresql 相关的问题 class Profile Base roles relationship Role secondary role profiles backref profiles
  • 在 Windows 中检查端口可用性的 VBS 脚本

    我正在尝试检查某个软件的先决条件 我有一组脚本来检查并检查磁盘空间 内存可用性等内容 我需要创建一个脚本来检查某些端口是否打开且可访问 我正在使用 WMI 检查其他网络配置项 但找不到任何检查端口可用性的参考 任何人都知道我可以在哪里找到
  • 如何在正方形中显示图像的特定部分

    我在反应本机中的图像组件有问题 我想在某个正方形中显示图像的特定部分 F e 假设我的图像分辨率为 1280x720 private someImage require Assets someImage jpg 我想以方形组件显示此图像 大
  • 如果从电子邮件中单击链接,则引用 URL

    我有一个用户注册过程 其中我向用户发送一封电子邮件以进行电子邮件验证并激活他们的帐户 现在当activate link单击电子邮件中的链接后 如何获取引荐来源网址来告诉我用户是否通过单击电子邮件中的链接来到该页面 在生成电子邮件之前 您需要
  • 您可能需要一个适当的加载器来处理此文件类型上传图像文件

    我在react babel webpack 中使用图像文件 但它显示一个错误 ERROR in public assets scissors png Module parse failed home rohit Desktop game p
  • 如何强制 Maven 使用 maven-install-plugin 版本 2.5?

    我想使用 maven install plugin 将一些 jar 安装到我的本地 Maven 存储库中 我的maven 3 2 1安装使用这个插件的2 4版本 它需要指定很多参数 我想使用需要较少参数的 2 5 版本 如上所述http m
  • scala 中的 Hello World 有什么问题?

    我在学习scala来自 docs scala lang org 有一个example https docs scala lang org overviews scala book hello world 2 html object Hell
  • 一个月中的周数

    我有以下代码 NSDate dateNow NSDate alloc init NSTimeInterval timeDifference usersDate timeIntervalSinceDate dateNow Get the sy
  • Postgres 选择当前小时数据

    我有一个带有表 token 的 postgres 数据库 它有 token id 及其生成时间 token id generated time 196618 2016 10 15 01 02 48 963 196619 2016 10 15
  • PostgreSQL 9.4.2 中“CREATE TRIGGER”使用什么锁(如果有)

    根据Postgres XL http files postgres xl org documentation explicit locking html CREATE TRIGGER使用SHARE ROW EXCLUSIVE锁定 但根据Po
  • 数据表类型错误:c 未定义

    我尝试使用 jQuery DataTables 但出现错误 类型错误 c 未定义 我不知道我的代码有什么问题 因为我可以看到 JSON 正确检索并且格式也正确 但我不知道我收到上述错误有什么问题 我的 JSON Data LOGIN 101
  • 消除宝石错误

    我有使用 RVM 的 2 个版本的红宝石 我正在尝试删除此红宝石中的所有宝石version 1 8 7 p302 首先我尝试了这个 但出现错误 gem list cut d f1 xargs gem uninstall aIx Succes
  • airflow.exceptions.AirflowException:在 DAG 中检测到循环。任务错误

    我正在运行气流管道 但代码看起来不错 但实际上我收到了airflow exceptions AirflowException 在 DAG 中检测到循环 错误任务 你能帮忙解决这个问题吗 发生这种情况的原因是多个任务中有重复的 task id
  • C语言中如何将线程分配给不同的核心?

    我创建了一个程序 使用 4 个线程对 8 个数字进行加法 然后计算结果的乘积 如何确保每个线程使用单独的核心以获得最大性能提升 我是 pthreads 新手 所以我真的不知道如何正确使用它 请提供尽可能简单的答案 My code inclu
  • 如何获取我的表单父级窗口的当前标题?

    我有一个 WinForm 应用程序 它是其他进程 例如 Google Chrome 的 Windows 的父级 我使用以下代码将 Windows 设为我的表单的父级 使用返回的句柄 Process MainWindowHandle 我正在努