【问题标题】:C# - Docked External application - Mouse eventsC# - 停靠的外部应用程序 - 鼠标事件
【发布时间】:2020-09-04 21:50:44
【问题描述】:

我有一个 winform 应用程序 (.NET C#)。 从这个应用程序中,我启动了另一个进程 (notepad.exe) 并将其停靠到我的窗口 - 类似于这里的操作:Docking Window inside another Window

现在我的问题是,如何捕捉/处理在这个停靠的应用程序上发生的鼠标事件? 我试过的:

  • 在停靠面板上创建一个透明面板。当我无法“通过”隐形面板单击(或执行其他任何操作)时,问题就出现了
  • 全局鼠标钩子。我不喜欢这个解决方案,因为我只对表单中的鼠标位置感兴趣。另外,我需要鼠标相对于窗口的位置。

对于上下文,我想要实现的是在我的鼠标旁边有一个恒定的工具提示,通知我鼠标相对于面板的位置。见下面代码:

ToolTip trackTip;
public Form1
{
  trackTip = new ToolTip();
  transparentPanel1.MouseMove += new MouseEventHandler((object s, System.Windows.Forms.MouseEventArgs e) => trackTip.Hide(this));
  transparentPanel1.MouseLeave += new EventHandler(TransparentPanel1_MouseLeave);
}


void TransparentPanel1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
  String tipText = String.Format("({0}, {1})", e.X, e.Y);
  trackTip.Show(tipText, this, e.Location);
}

我找到了viable solution,但是我非常希望避免将代码注入到进程中,而且我觉得必须有更好的解决方案来解决我的具体问题。

如果您能给我任何建议,我将不胜感激。我是 .NET 编程新手。

【问题讨论】:

  • 你为什么要这样做?似乎需要做很多工作才能取得真正实质性的成果。记事本是一个相当普通的应用程序
  • @MichaelRandall 我使用记事本只是为了启动和运行。在我的开发阶段运行什么 .exe 并不重要。
  • 我认为您需要使用已建议的全局鼠标挂钩。我之所以这么说是因为 Windows API 可能不支持您想要获得的详细程度。例如广告鼠标相对于另一个窗口句柄的位置。您可以将全局鼠标位置与外部应用程序的窗口位置结合起来,一些基本的数学运算将为您提供相对坐标。即使有支持它的 Windows API。您可能会发现它仅在附加调试器或您的应用程序是最后一个活动应用程序时才有效。

标签: c# .net process mouseevent external


【解决方案1】:

作为参考,我在这里将两个答案合二为一:

Getting mouse position in c#

How to get window's position?

使用鼠标的全局位置和窗口的位置我们可以计算出鼠标的相对位置。下面是一些示例代码,它们具有非常基本的边界处理,并且对于句柄关闭等没有真正的错误处理。但是应该可以帮助您入门。

首先,基本上是上述答案的副本,引入 Windows API 来获取窗口和鼠标位置:

    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;

        public static implicit operator Point(POINT point)
        {
            return new Point(point.X, point.Y);
        }
    }

    [DllImport("user32.dll")]
    public static extern bool GetCursorPos(out POINT lpPoint);

    public static Point GetCursorPosition()
    {
        POINT lpPoint;
        GetCursorPos(out lpPoint);

        return lpPoint;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr FindWindow(string strClassName, string strWindowName);

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

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

然后在两者之间进行一些数学运算:

    public Point GetRelativeMousePosition(IntPtr windowPtr)
    {
        Rect windowPostion = new Rect();
        GetWindowRect(windowPtr, ref windowPostion);
        var mousePosition = GetCursorPosition();

        var result = new Point();
        result.X = mousePosition.X - windowPostion.Left;
        result.Y = mousePosition.Y - windowPostion.Top;

        // set some boundaries so we can't go outside.
        if (result.X < 0)
            result.X = 0;

        var maxWidth = windowPostion.Right - windowPostion.Left;
        if (result.X > maxWidth)
            result.X = maxWidth;

        if (result.Y < 0)
            result.Y = 0;

        var maxHeight = windowPostion.Bottom - windowPostion.Top;
        if (result.Y > maxHeight)
            result.Y = maxHeight;

        return result;
    }

然后把它们放在一起:

    private void Sample()
    {
        Process[] processes = Process.GetProcessesByName("notepad");
        Process lol = processes[0];
        var ptr = lol.MainWindowHandle; // getting this reference is expensive. Better to store and reuse if possible.
        var relativePoint = GetRelativeMousePosition(ptr);
        Console.WriteLine($"relative mouse x:{relativePoint.X} y:{relativePoint.Y}");
    }

【讨论】:

  • 我不想让它过于复杂。但是要考虑的一些事情是,如果记事本没有打开怎么办。如果窗口最小化怎么办。如果 X 超出边界,Y 是否也应显示为超出边界? Y 反之亦然。或者边界是否应该默认为 -1 或其他值以指示光标不在窗口内。这些都是可以根据您的需要进行调整的东西。
  • 感谢您的详细反馈!就像我说的,我不喜欢这个解决方案。它也可能被防病毒软件标记,所以总的来说我想远离全局鼠标钩子。我觉得好像必须有办法做到这一点。我认为我创建透明面板的想法可行,但我无法解决事件问题。无论如何,如果我不能让它工作,我可能最终会使用你的解决方案。 @Joe_DM
  • 将其他应用程序覆盖在主应用程序上可能会出现其他问题。例如覆盖层接受输入,然后需要将其复制到其下方的应用程序中。众所周知,Windows API 很难做这样的事情,因为对于什么是允许和不允许将密钥发送到另一个应用程序有严格的规定。如果你找到了一个好的答案,那么我总是很想看到这种东西的实际应用。
  • 我已经通过全局鼠标挂钩使用了您的解决方案。现在我还有一个问题。每当我从主窗口离开焦点时(例如,我将焦点设置为记事本),工具提示就会消失。我仍然可以将坐标打印到控制台(因此钩子正在工作),但是我的目标是拥有鼠标工具提示。你也许知道我怎么能做到这一点?我尝试将 tooltip.ShowAlways 分配为 true - 没有帮助。我是否必须将 Show() 函数中的窗口参数分配给我的主窗口以外的东西? @Joe_DM
  • @Naltamer14 这是一个新问题,但只是从内存中进行,我认为您可能需要导入一些额外的 Windows API DLL 以尝试告诉 Windows 本身它应该始终位于顶部。如果IsIconicSetForegroundWindow,可以尝试user32.dll 方法ShowWindow 和类型8。所有这些都以IntPtr 为目标。您还可以尝试使用 hack 向IntPtr 发送键盘命令,例如按下 ctrl,以欺骗窗口让您将焦点向前移动。
猜你喜欢
  • 1970-01-01
  • 2012-05-29
  • 2019-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-13
相关资源
最近更新 更多