如何将我的表单粘贴到第三方应用程序的窗口?

Posted

技术标签:

【中文标题】如何将我的表单粘贴到第三方应用程序的窗口?【英文标题】:How to stick my form to a window of a thirdparty application? 【发布时间】:2015-10-28 05:23:36 【问题描述】:

我正在尝试将我的表单粘贴到另一个应用程序的窗口(比如说 Microsoft Outlook)。当我移动 Outlook 窗口时,我的表单应该仍然停留在它的右侧。

目前,我正在监视 Outlook 在 while(true) 循环中的位置(使用 sleep())并调整我的表单位置。

这里有两个问题:

如果sleep() 的持续时间太短,那么检查 Outlook 的位置并经常调整我的表单会需要很多性能。 如果 sleep() 持续时间太长,我的表单在适应 Outlook 时太慢(它滞后)。

没有本地解决方案吗?

【问题讨论】:

不要在带有睡眠的 while 循环中进行调整:使用计时器。另外,在设置属性之前测试位置是否不变。 你可以通过 SetWinEventHook(), EVENT_OBJECT_LOCATIONCHANGE 通知来做到这一点。 Move window when external application's window moves的可能重复 @MA-Maddin 您的链接问题与我的问题相当重复,因为我的问题已经超过 2 年了。 @halloei 完全正确,先用另一种方式做,然后发现这个 Meta QA:Should I vote to close a duplicate question, even though it's much newer, and has more up to date answers? 【参考方案1】:

弗雷杜的回答几乎就是答案。

我在我的应用程序中使用了它,但遇到了异常“callbackoncollecteddelegate”。 要使其正常工作,必须将 WinEventProc 附加到属性上。

...
private NativeMethods.WinEventDelegate winEventProc;
private void Form1_Load(object sender, EventArgs e)
    
        this.Width = 100;
        this.Height = 100;
        this.TopMost = true;
        int processId = Process.GetProcessesByName("OUTLOOK")[0].Id;

        //this will also be triggered by mouse moving over the process windows
        //NativeMethods.SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, WinEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);
        this.winEventProc = new NativeMethods.WinEventDelegate(WinEventProc);

        NativeMethods.SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, IntPtr.Zero, this.winEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);
    
...

见CallbackOnCollectedDelegate at Application.Run(new Form1())

【讨论】:

【参考方案2】:

你必须钩住进程并监听事件

这应该给你一个很好的起点

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApplication1

    public partial class Form1 : Form
    
        private const uint WINEVENT_OUTOFCONTEXT = 0x0000;
        private const uint EVENT_OBJECT_LOCATIONCHANGE = 0x800B;

        private const uint EVENT_SYSTEM_MOVESIZESTART = 0x000A;
        private const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B;

        public Form1()
        
            InitializeComponent();
        

        private void Form1_Load(object sender, EventArgs e)
        
            this.Width = 100;
            this.Height = 100;
            this.TopMost = true;
            int processId = Process.GetProcessesByName("OUTLOOK")[0].Id;

            //this will also be triggered by mouse moving over the process windows
            //NativeMethods.SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, WinEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);

            NativeMethods.SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, IntPtr.Zero, WinEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);
        

        private void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
        
            Rect move = new Rect();

            if (eventType == EVENT_SYSTEM_MOVESIZESTART)
            
                NativeMethods.GetWindowRect(hwnd, ref move);

                Debug.WriteLine("EVENT_SYSTEM_MOVESIZESTART");
            
            else if (eventType == EVENT_SYSTEM_MOVESIZEEND)
            
                NativeMethods.GetWindowRect(hwnd, ref move);

                Debug.WriteLine("EVENT_SYSTEM_MOVESIZEEND");
            

            this.Left = move.Left;
            this.Top = move.Top;
        
    

    public struct Rect
    
        public int Left  get; set; 
        public int Top  get; set; 
        public int Right  get; set; 
        public int Bottom  get; set; 
    

    static class NativeMethods
    
        [DllImport("user32.dll")]
        public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);

        public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);

        [DllImport("user32.dll")]
        public static extern bool GetWindowRect(IntPtr hwnd, ref Rect rectangle);
    

【讨论】:

有没有办法监听 RESIZING 事件?例如,如果用户将 Outlook 表单的顶部边框向下拖动,则您的表单只有在调整大小完成后才会重新定位,因此会有明显的延迟。【参考方案3】:

您可以使用任何 Windows 挂钩函数 WH_GETMESSAGEWH_GETMESSAGERETWH_SYSMSGFILTERWH_MSGFILTER

在这种情况下,您会对WM_MOVEWM_MOVING 感兴趣,其中WM_MOVE 在窗口移动(也就是移动完成)后发送,WM_MOVING 在窗口移动时发送(并且所以你会得到很多)。

从阅读开始:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms644959(v=vs.85).aspx

【讨论】:

以上是关于如何将我的表单粘贴到第三方应用程序的窗口?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 Selenium 切换到新窗口

在发布到 MySQL 之前合并两个字段

如何使用 SwiftUI 将我的按钮粘贴到视图底部?

python安装第三方库

单击提交重力表单时显示加载动画

在winform嵌入外部应用程序