C# C语言中获取屏幕像素颜色的最快方法#
我正在寻找最快的方法来捕获一个单屏幕像素的颜色在c# 到目前为止,我正在使用GDI+方法和System.Threading.Timer,该方法在回调中调用捕获函数,但我正在寻找实现目标的最佳方法 我当前的代码是这样运行的C# C语言中获取屏幕像素颜色的最快方法#,c#,windows,optimization,screen-scraping,screen-capture,C#,Windows,Optimization,Screen Scraping,Screen Capture,我正在寻找最快的方法来捕获一个单屏幕像素的颜色在c# 到目前为止,我正在使用GDI+方法和System.Threading.Timer,该方法在回调中调用捕获函数,但我正在寻找实现目标的最佳方法 我当前的代码是这样运行的 System.Threading.Timer stTimer = new System.Threading.Timer(timerFired, null, 0, 1); 它调用包含此方法的函数 [DllImport("gdi32.dll")] private static e
System.Threading.Timer stTimer = new System.Threading.Timer(timerFired, null, 0, 1);
它调用包含此方法的函数
[DllImport("gdi32.dll")]
private static extern int BitBlt(IntPtr srchDC, int srcX, int srcY, int srcW, int srcH, IntPtr desthDC, int destX, int destY, int op);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
Bitmap screenPixel = new Bitmap(1, 1);
IntPtr hdcMem = CreateCompatibleDC(IntPtr.Zero);
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(appWindow))
{
int y = 540;
Point loc = new Point(xVal, y);
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, loc.X, loc.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
}
Color c = screenPixel.GetPixel(0, 0);
但我也想知道GetPixel方法
。。。
在这种情况下,仅获取单个像素的颜色可能会更快
我也在考虑尝试
[DllImport("user32.dll")]
static extern IntPtr GetWindowDC(IntPtr hWnd);
IntPtr hDC = GetWindowDC(appWindow);
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, loc.X, loc.Y, (int)CopyPixelOperation.SourceCopy);
甚至尝试
[DllImport("gdi32.dll")]
private static extern int BitBlt(IntPtr srchDC, int srcX, int srcY, int srcW, int srcH, IntPtr desthDC, int destX, int destY, int op);
[DllImport("gdi32.dll", SetLastError = true)]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("user32.dll")]
static extern IntPtr GetWindowDC(IntPtr hWnd);
IntPtr hDC = CreateCompatibleDC(GetWindowDC(appWindow));
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, loc.X, loc.Y, (int)CopyPixelOperation.SourceCopy);
但我甚至不确定如何在C#上下文中使用CreateCompatibleDC函数,或者它在这一点上是否真的做了任何有用的事情
我非常愿意接受任何关于优化的建议,包括GDI+库之外的方法,只要这些解决方案与C兼容,并且包含非常受欢迎的代码示例
此外,我并不太关心计时器的优化,但如果您确实在这方面进行了优化,请随时通过使用timespan类记录从GDI+和Managed DirectX之间的屏幕上仅获取一个像素所需的时间来共享这些优化,结果证明GDI+实际上要快得多 这两种方法均通过以下方法进行测试:
TimeSpan ts = new TimeSpan();
for (int tCount = 0; tCount < 1001; tCount++)
{
DateTime then = DateTime.Now;
METHOD_TO_TEST()
DateTime nNow = DateTime.Now;
TimeSpan tt = nNow - then;
ts += tt;
}
return (float)ts.Ticks / (float)(tCount - 1);
GDI+,至:
//global scope
Color c = Color.Black
PresentParameters parameters = new PresentParameters();
parameters.Windowed = true;
parameters.SwapEffect = SwapEffect.Discard;
Device d = new Device(0, DeviceType.Hardware, hWnd, CreateFlags.HardwareVertexProcessing, parameters);
Surface s = d.CreateOffscreenPlainSurface(Manager.Adapters.Default.CurrentDisplayMode.Width, Manager.Adapters.Default.CurrentDisplayMode.Height, Format.A8R8G8B8,
Pool.Scratch);
//method to test
d.GetFrontBufferData(0, s);
GraphicsStream gs = s.LockRectangle(LockFlags.None);
byte[] bu = new byte[4];
gs.Position = readPos;
gs.Read(bu, 0, 4);
int r = bu[2];
int g = bu[1];
int b = bu[0];
c = return Color.FromArgb(r, g, b);
s.UnlockRectangle();
s.ReleaseGraphics();
托管DirectX
GDI+在3000次迭代中平均运行20831.1953、18611.0566和20761.1914次,总平均运行20067.814433333次
而Managed DirectX在3000次迭代中的平均运行时间为489297.969、496458.4和494268.281次,总平均运行时间为493341.55次
这意味着,我使用的托管DirectX安装程序所需的时间是GDI+安装程序的24倍
现在,需要注意的是。。。
完全有可能有更有效的方法使用DirectX提取屏幕数据。我试着从后缓冲区而不是前缓冲区中提取数据,但是对于这个特定的例子,后缓冲区没有产生任何有价值的东西(它本质上只是一个黑屏)。另一件需要注意的事情是我实现设备句柄的方式,我很确定它捕获了整个桌面。这可能比仅为我试图捕获的任何特定窗口获取前缓冲区数据效率低。。。我没有这样做的唯一原因是,我自己试图解决这个问题的所有尝试都导致了失败(设备实例化中的某个地方出现了DirectX无效调用异常),并且因为我与任何资源交谈的人都不知道托管DirectX,更不用说如何将其用于我的目的了
还有一件事需要注意,我听说并读到有可能钩住已经运行的程序的DirectX api。这可能会产生更快的结果,并且对其他人来说是一个更好的解决方案,但是由于这样一个钩子的恶意性质以及我试图捕获的程序用来防止它的措施,这不适用于我的解决方案
最后,对于这种仅捕获一个屏幕像素的特定情况,GDI+的BitBlt似乎比Managed DirectX或至少是我的实现更快
//global scope
Bitmap screenPixel = new Bitmap(1, 1);
Color c = Color.Black
//method to test
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(hWnd))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, xVal, 540, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
}
c = screenPixel.GetPixel(0, 0);
//global scope
Color c = Color.Black
PresentParameters parameters = new PresentParameters();
parameters.Windowed = true;
parameters.SwapEffect = SwapEffect.Discard;
Device d = new Device(0, DeviceType.Hardware, hWnd, CreateFlags.HardwareVertexProcessing, parameters);
Surface s = d.CreateOffscreenPlainSurface(Manager.Adapters.Default.CurrentDisplayMode.Width, Manager.Adapters.Default.CurrentDisplayMode.Height, Format.A8R8G8B8,
Pool.Scratch);
//method to test
d.GetFrontBufferData(0, s);
GraphicsStream gs = s.LockRectangle(LockFlags.None);
byte[] bu = new byte[4];
gs.Position = readPos;
gs.Read(bu, 0, 4);
int r = bu[2];
int g = bu[1];
int b = bu[0];
c = return Color.FromArgb(r, g, b);
s.UnlockRectangle();
s.ReleaseGraphics();