[英]C# drawing on windows
我正在尝试在现有窗口上绘制一些线条和矩形。 我发现以下代码可以在桌面上绘制,效果非常好。
class Drawing {
[DLLImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hWnd);
public static void draw(Rectangle r, Brush b, IntPtr hWnd) {
using(Graphics g = Graphics.FromHdc(hwnd)) {
g.DrawRectangle(b, r);
}
}
}
Drawing.draw(new Rectangle(0, 0, 40, 80), Brushes.Red, Drawing.GetDC(IntPtr.Zero));
这是我如何修改它以在特定窗口上绘制的代码。
class Drawing {
public static IntPtr WinGetHandle(string wName) {
foreach (Process pList in Process.GetProcesses())
if (pList.MainWindowTitle.Contains(wName))
return pList.MainWindowHandle;
return IntPtr.Zero;
}
public static void draw(Rectangle r, Brush b, IntPtr hwnd) {
using(Graphics g = Graphics.FromHwnd(hwnd)) {
g.DrawRectangle(p, r);
}
}
}
Drawing.draw(new Rectangle(0, 0, 40, 80), Brushes.Red, Drawing.WinGetHandle("DrawingWindow"));
这段代码什么也没发生,在某些情况下,它会抛出 OutOfMemoryException 并立即崩溃。 在互联网上,我发现了获取窗口句柄然后在窗口上绘制的其他一些变体,但大多数崩溃都出现相同的异常或什么也不做。 我还循环尝试了它,因为我认为该应用程序可能会在每一帧中过度绘制。
所以我的问题是有人知道我如何解决这个问题或遇到同样的问题,而另一种方法可以做到这一点。
正如评论中所说,您不能直接在屏幕上绘图。 您可以做的是构建一些“叠加”窗口( 透明和点击)并在其上绘制。
这是一个 C# 控制台应用程序示例,它演示了这一点,还使用了 UI 自动化来跟踪打开的窗口并在它们周围绘制一个黄色矩形。
static class Program
{
[STAThread]
static void Main()
{
var overlay = new Overlay();
// track windows open (requires WindowsBase, UIAutomationTypes, UIAutomationClient)
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Subtree, (s, e) =>
{
var element = (AutomationElement)s;
if (element.Current.ProcessId != Process.GetCurrentProcess().Id)
{
Console.WriteLine("Added window '" + element.Current.Name + "'");
overlay.AddTrackedWindow(element);
// track window close
Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, element, TreeScope.Element, (s2, e2) =>
{
overlay.RemoveTrackedWindow(element);
});
}
});
Application.Run(overlay);
}
}
// adapted from https://stackoverflow.com/questions/11077236/transparent-window-layer-that-is-click-through-and-always-stays-on-top
public class Overlay : Form // standard Windows Form
{
private readonly HashSet<AutomationElement> _windows = new HashSet<AutomationElement>();
public Overlay()
{
TopMost = true;
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
MaximizeBox = false;
MinimizeBox = false;
ShowInTaskbar = false;
BackColor = Color.White;
TransparencyKey = BackColor;
}
protected override CreateParams CreateParams
{
get
{
var cp = base.CreateParams;
const int WS_EX_TRANSPARENT = 0x20;
const int WS_EX_LAYERED = 0x80000;
const int WS_EX_NOACTIVATE = 0x8000000;
cp.ExStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE;
return cp;
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
foreach (var window in _windows.ToArray())
{
Rect rect;
try
{
rect = window.Current.BoundingRectangle;
}
catch
{
// error, window's gone
_windows.Remove(window);
continue;
}
// draw a yellow rectangle around window
using (var pen = new Pen(Color.Yellow, 2))
{
e.Graphics.DrawRectangle(pen, (float)rect.X, (float)rect.Y, (float)rect.Width, (float)rect.Height);
}
}
}
// ensure we call Invalidate on UI thread
private void InvokeInvalidate() => BeginInvoke((Action)(() => { Invalidate(); }));
public void RemoveTrackedWindow(AutomationElement element)
{
_windows.Remove(element);
InvokeInvalidate();
}
public void AddTrackedWindow(AutomationElement element)
{
_windows.Add(element);
InvokeInvalidate();
// follow target window position
Automation.AddAutomationPropertyChangedEventHandler(element, TreeScope.Element, (s, e) =>
{
InvokeInvalidate();
}, AutomationElement.BoundingRectangleProperty);
}
}
要测试它,运行它,然后打开记事本。 这是你应该看到的:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.