【问题标题】:Drawing over all windows on multiple monitors在多个显示器上绘制所有窗口
【发布时间】:2009-03-30 03:07:53
【问题描述】:

我正在使用以下代码在单个显示器上绘图:

Point cursorLocation;
NativeMethods.GetCursorPos(out cursorLocation);
Screen screen = Screen.FromPoint(cursorLocation);

Point h1 = new Point(screen.Bounds.Left, cursorLocation.Y);
Point h2 = new Point(screen.Bounds.Right, cursorLocation.Y);
Point v1 = new Point(cursorLocation.X, screen.Bounds.Top);
Point v2 = new Point(cursorLocation.X, screen.Bounds.Bottom);

using (Graphics graphics = Graphics.FromHwnd(NativeMethods.GetDesktopWindow())) {
    NativeMethods.SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero);
    graphics.DrawLine(Pens.Red, h1, h2);
    graphics.DrawLine(Pens.Red, v1, v2);
}

从本质上讲,这应该 理论上在任一监视器上绘制。但是,它只利用初级。所以,为了解决这个问题,我得到了所有显示器的 DC 并尝试这样做。

IntPtr hdc = NativeMethods.CreateDC("DISPLAY", IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
Graphics graphics = Graphics.FromHdc(hdc);
graphics.DrawLine(Pens.Red, h1, h2);
graphics.DrawLine(Pens.Red, v1, v2);
graphics.Dispose();
NativeMethods.ReleaseDC(IntPtr.Zero, hdc);

想想看,这甚至根本不会在屏幕上绘制。我已经为 CreateDC 尝试了各种重载,并搜索了 SO 和其他资源,但我很难过。


一旦解决了这个问题,有人知道如何通过使用 SHCHangeNotify 刷新桌面来消除闪烁吗?我只画了两条线,它像疯了一样闪烁..)

【问题讨论】:

    标签: c# gdi+ desktop


    【解决方案1】:

    这对我有用。我使用 EnumDisplayDevices 来获取名称。

    [DllImport("gdi32.dll")]
    public static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);
    
    var hdc = CreateDC(@"\\.\DISPLAY1", "", "", IntPtr.Zero);
    Graphics g = Graphics.FromHdc(hdc);
    var pt = Cursor.Position;
    g.DrawEllipse(Pens.BlueViolet, pt.X - 100, pt.Y - 100, 200, 200);
    

    更多信息在这里: http://msdn.microsoft.com/en-us/library/windows/desktop/dd145179(v=vs.85).aspx

    【讨论】:

    • 您的代码没有按原样编译——您的图形对象被命名为“graphics”,但后来您将其用作“g”。
    • Graphics.FromHwnd(hdc) 为我抛出了 OutOfMemoryException - 既适用于从“\\.DISPLAY1”创建的 hdc,也适用于由 EnumDisplayDevices 返回的字符串创建的所有非零 dc。 .
    • 您的解决方案以及this one 帮助我在每个监视器的基础上解决了这个问题。此外,我还在最后做一个DeleteDC。而不是使用EnumDisplayDevices,而是使用Screen.FromControl(c).DeviceName 来获取某个控件/表单所在的显示器的显示名称。
    【解决方案2】:

    这不是您问题的答案,但如果您对此没有任何问题,我会建议您尝试使用 WPF 来解决此问题。我玩过其他类型的桌面交互,比如透明度,与 GDI 替代方案相比,WPF 的速度快如闪电。

    您放置您的 wpf 应用程序并调整它的大小以适合您需要绘画的所有区域。然后将其设置为透明,并确保其点击(我认为这是 100% 透明度的默认设置)。这样,只要您在这个大的 WPF 画布/表单上没有任何东西,所有鼠标事件都会点击到桌面。

    然后开始在上面画画。 (我喜欢它,因为它很容易为线条和图像添加效果)。保证不闪烁。

    或者,您可以在普通 Windows 窗体上使用相同的方案。

    这样您就不必求助于 GDI 来完成您的工作。

    我认为,当您现在正在接近它时,您能够做到这一点的唯一方法是连接到桌面 WM_PAINT 上的窗口消息并在那里完成您的工作。

    【讨论】:

      【解决方案3】:

      很遗憾,这两个答案都没有任何帮助,原因如下:

      我仅限于 Windows 窗体,因为这是为 WF 构建的基础框架。 我已经尝试过 TransparencyKey/Form 技巧,但仍然闪烁太多。

      我认真地尝试了近十种不同的方法,甚至我在 Google 中搜索 DirectX/Direct3D、C# Desktop Hooks 和其他方法都无济于事。

      我要做的就是画一个横跨整个桌面的十字准线,1px x 1px.. 肯定有人必须有一些例子来说明如何做到这一点而不闪烁和良好的性能:? D3D、DX、GDI 还是其他? :\

      【讨论】:

        【解决方案4】:

        在第一部分无法提供帮助。我假设您正在 Paint 事件中绘制线条。使用双缓冲消除闪烁。

        【讨论】:

          猜你喜欢
          • 2012-11-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-02
          • 2012-04-07
          相关资源
          最近更新 更多