闲来研究下非神经网络的computer vision,啥库都不用,先C#试试手撸截屏器,抄起代码来可快了(见下面的代码)。可是使用的时候发现,为啥截屏只截了一部分呢?
public static Rectangle GetScreenSize(Form from)
{
Rectangle rect = Screen.FromControl(from).Bounds;
return rect;
}
public static Bitmap CaptureScreen(Form from, string outputFilename)
{
Rectangle rect = GetScreenSize(from);
using (Bitmap bitmap = new Bitmap(rect.Width, rect.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(new Point(0, 0), Point.Empty, rect.Size);
}
if (outputFilename.Length > 0)
{
bitmap.Save(outputFilename, ImageFormat.Png);
}
return bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.PixelFormat.DontCare);
}
}
然后搜索了各种方案,要么引用PresentationCore的,要么引用GetDpiForSystem的,一堆乱遭遭的;我只想知道我 2560x1600
的屏幕为啥 Screen
只返回 1707x1067
…
这个应该是DPI的问题,有点时间又没有接触windows了,想当初还是GetDC
的年代…突然意识到我的显示屏的DPI应该是比默认值96
高的…
所以查了下,默认原来Windows应用并没有给开启这个high DPI awareness…所以不管用什么API统一都是返回的96
…
所以这里应该有两种解决方案,一个是直接去读取Windows注册表,找到屏幕缩放比例设置的那个位置,另一个是给应用开启high DPI awareness,就是有一个叫SetProcessDpiAwareness
的Windows API,在 Shcore.dll
里:
[DllImport("shcore.dll")]
public static extern uint SetProcessDpiAwareness(uint mode);
但是文档里写了,Windows 8.1 / Server 2012R2 才支持,那之前的是不是就不用考虑DPI的问题了,但是也得保证能运行呀。所以改成LoadLibrary
的形式:
static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
// v--- in main form scope
private void EnableDpiAwareness()
{
// skip this procedure if Windows version is below
// Shcore.dll#SetProcessDpiAwareness [min] Windows 8.1 / Windows Server 2012 R2
IntPtr shcoreDll = NativeMethods.LoadLibrary(@"Shcore.dll");
if (shcoreDll != IntPtr.Zero) return;
IntPtr ptrSetProcessDpiAwareness = NativeMethods.GetProcAddress(shcoreDll, "SetProcessDpiAwareness");
if (ptrSetProcessDpiAwareness != IntPtr.Zero)
{
SetProcessDpiAwareness fnSetProcessDpiAwareness = (SetProcessDpiAwareness)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(ptrSetProcessDpiAwareness, typeof(SetProcessDpiAwareness));
fnSetProcessDpiAwareness(/* DPI_AWARENESS_SYSTEM_AWARE */ 1);
}
NativeMethods.FreeLibrary(shcoreDll);
}
然后把EnableDpiAwareness
放在main
的 InitializeComponent
之前运行就好了…所有都正常了…