【C#】获取电脑DPI
public static class DPIGeter { /// <summary> /// 获取DPI /// </summary> /// <param name="dpix"></param> /// <param name="dpiy"></param> public static void GetDPI(ref float dpix, ref float dpiy) { SetProcessDPIAware();//此处会忽视系统DWM虚拟化,不建议使用 IntPtr screenDC = GetDC(IntPtr.Zero); dpix = GetDeviceCaps(screenDC, LOGPIXELSX); dpiy = GetDeviceCaps(screenDC, LOGPIXELSY); ReleaseDC(IntPtr.Zero, screenDC); } /// <summary> /// 获取DPI缩放比例 /// </summary> /// <param name="dpiscalex"></param> /// <param name="dpiscaley"></param> public static void GetDPIScale(ref float dpiscalex, ref float dpiscaley) { int x = GetSystemMetrics(SM_CXSCREEN); int y = GetSystemMetrics(SM_CYSCREEN); IntPtr hdc = GetDC(IntPtr.Zero); int w = GetDeviceCaps(hdc, DESKTOPHORZRES); int h = GetDeviceCaps(hdc, DESKTOPVERTRES); ReleaseDC(IntPtr.Zero, hdc); dpiscalex = (float)w / x; dpiscaley = (float)h / y; } [DllImport("user32.dll")] static extern IntPtr GetDC(IntPtr ptr); [DllImport("user32.dll", EntryPoint = "ReleaseDC")] public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc); [DllImport("gdi32.dll")] public static extern IntPtr CreateDC( string lpszDriver, // driver name string lpszDevice, // device name string lpszOutput, // not used; should be NULL Int64 lpInitData // optional printer data ); [DllImport("gdi32.dll")] public static extern int GetDeviceCaps( IntPtr hdc, // handle to DC int nIndex // index of capability ); [DllImport("user32.dll")] internal static extern bool SetProcessDPIAware(); [DllImport("user32")] public static extern int GetSystemMetrics( int nIndex ); [DllImport("user32.dll", EntryPoint = "GetDesktopWindow", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr GetDesktopWindow(); [DllImport("user32.dll")] public static extern int GetWindowRect(IntPtr hwnd, out Rect lpRect); public struct Rect { public int Left; public int Top; public int Right; public int Bottom; } const int DRIVERVERSION = 0; const int TECHNOLOGY = 2; const int HORZSIZE = 4; const int VERTSIZE = 6; const int HORZRES = 8; const int VERTRES = 10; const int BITSPIXEL = 12; const int PLANES = 14; const int NUMBRUSHES = 16; const int NUMPENS = 18; const int NUMMARKERS = 20; const int NUMFONTS = 22; const int NUMCOLORS = 24; const int PDEVICESIZE = 26; const int CURVECAPS = 28; const int LINECAPS = 30; const int POLYGONALCAPS = 32; const int TEXTCAPS = 34; const int CLIPCAPS = 36; const int RASTERCAPS = 38; const int ASPECTX = 40; const int ASPECTY = 42; const int ASPECTXY = 44; const int SHADEBLENDCAPS = 45; const int LOGPIXELSX = 88; const int LOGPIXELSY = 90; const int SIZEPALETTE = 104; const int NUMRESERVED = 106; const int COLORRES = 108; const int PHYSICALWIDTH = 110; const int PHYSICALHEIGHT = 111; const int PHYSICALOFFSETX = 112; const int PHYSICALOFFSETY = 113; const int SCALINGFACTORX = 114; const int SCALINGFACTORY = 115; const int VREFRESH = 116; const int DESKTOPVERTRES = 117; const int DESKTOPHORZRES = 118; const int BLTALIGNMENT = 119; const int SM_CXSCREEN = 0;//'屏幕宽度 const int SM_CYSCREEN = 1;//'屏幕高度 const int SM_CXVSCROLL = 2;//'垂直滚动条的宽度 const int SM_CYHSCROLL = 3;//'水平滚动条的宽度 const int SM_CYCAPTION = 4;//'Height of windows caption 实际标题高度加上SM_CYBORDER const int SM_CXBORDER = 5;//'Width of no-sizable borders 无法测量的窗口框架宽度 const int SM_CYBORDER = 6;// 'Height of non-sizable borders 无法测量的窗口框架高度 const int SM_CXDLGFRAME = 7;// 'Width of dialog box borders const int SM_CYDLGFRAME = 8;//'Height of dialog box borders const int SM_CYHTHUMB = 9;//'Height of scroll box on horizontal scroll bar 水平滚动条上滑块的高度 const int SM_CXHTHUMB = 10;//' Width of scroll box on horizontal scroll bar 水平滚动条上滑块的宽度 const int SM_CXICON = 11;// 'Width of standard icon 图标宽度 const int SM_CYICON = 12;//'Height of standard icon 图标高度 const int SM_CXCURSOR = 13;//'Width of standard cursor 光标宽度 const int SM_CYCURSOR = 14;//'Height of standard cursor 光标高度 const int SM_CYMENU = 15;// 'Height of menu 以像素计算的单个菜单条的高度 const int SM_CXFULLSCREEN = 16;// 'Width of client area of maximized window const int SM_CYFULLSCREEN = 17;// 'Height of client area of maximized window const int SM_CYKANJIWINDOW = 18;//'Height of Kanji window const int SM_MOUSEPRESENT = 19;//'True is a mouse is present 如果为TRUE或不为0的值则安装了鼠标,否则没有安装。 const int SM_CYVSCROLL = 20;//'Height of arrow in vertical scroll bar const int SM_CXHSCROLL = 21;//'Width of arrow in vertical scroll bar const int SM_DEBUG = 22;//'True if deugging version of windows is running const int SM_SWAPBUTTON = 23;//'True if left and right buttons are swapped. const int SM_CXMIN = 28;// 'Minimum width of window const int SM_CYMIN = 29;//'Minimum height of window const int SM_CXSIZE = 30;//'Width of title bar bitmaps const int SM_CYSIZE = 31;//'height of title bar bitmaps const int SM_CXMINTRACK = 34;// 'Minimum tracking width of window const int SM_CYMINTRACK = 35;//'Minimum tracking height of window const int SM_CXDOUBLECLK = 36;// 'double click width const int SM_CYDOUBLECLK = 37;// 'double click height const int SM_CXICONSPACING = 38;//'width between desktop icons const int SM_CYICONSPACING = 39;//'height between desktop icons const int SM_MENUDROPALIGNMENT = 40;//'Zero if popup menus are aligned to the left of the memu bar item. True if it is aligned to the right. const int SM_PENWINDOWS = 41;// 'The handle of the pen windows DLL if loaded. const int SM_DBCSENABLED = 42;// 'True if double byte characteds are enabled const int SM_CMOUSEBUTTONS = 43;//'Number of mouse buttons. const int SM_CMETRICS = 44;// 'Number of system metrics const int SM_CLEANBOOT = 67;//'Windows 95 boot mode. 0 = normal, 1 = safe, 2 = safe with network const int SM_CXMAXIMIZED = 61;//'default width of win95 maximised window const int SM_CXMAXTRACK = 59;// 'maximum width when resizing win95 windows const int SM_CXMENUCHECK = 71;// 'width of menu checkmark bitmap const int SM_CXMENUSIZE = 54;//'width of button on menu bar const int SM_CXMINIMIZED = 57;// 'width of rectangle into which minimised windows must fit. const int SM_CYMAXIMIZED = 62;//'default height of win95 maximised window const int SM_CYMAXTRACK = 60;//'maximum width when resizing win95 windows const int SM_CYMENUCHECK = 72;//'height of menu checkmark bitmap const int SM_CYMENUSIZE = 55;//'height of button on menu bar const int SM_CYMINIMIZED = 58;// 'height of rectangle into which minimised windows must fit. const int SM_CYSMCAPTION = 51;// 'height of windows 95 small caption const int SM_MIDEASTENABLED = 74;// 'Hebrw and Arabic enabled for windows 95 const int SM_NETWORK = 63;//'bit o is set if a network is present. const int SM_SECURE = 44;//'True if security is present on windows 95 system const int SM_SLOWMACHINE = 73;// 'true if machine is too slow to run win95. }
附:
这里还有特殊情况也提一下: 我们在高DPI下通过窗口句柄取到的坐标信息是和目标程序是否支持DWM虚拟化相关联的, 我们对支持DWM虚拟化的程序窗口调用GetWindowRect, 取到的坐标也是经过DWM缩放后的坐标; 对禁用DWM虚拟化程序的窗口调用GetWindowRect, 取到的坐标则是没有经过缩放的原始坐标。
最后我们再讨论下Win8.1 对高DPI的支持, WIn8.1对高DPi以3种方式支持 Process_DPI_Awareness :
typedef enum _Process_DPI_Awareness { Process_DPI_Unaware = 0, Process_System_DPI_Aware = 1, Process_Per_Monitor_DPI_Aware = 2 } Process_DPI_Awareness;
下面我们依次讨论这3种方式:
第一种Unaware, 该种方式是告诉系统, 我的程序不支持DPI aware, 请通过DWM虚拟化帮我们实现。 该方式和上面Win7/Win8对高DPI的支持的实现基本一样,主要区别是它通过GetWindowRect取到的坐标都是经过DWM缩放后的, 无论对方窗口是不是支持DWM虚拟化。
第二种方式是System DPI aware, 该方式下告诉系统, 我的程序会在启动的显示器上自己支持DPI aware, 所以不需要对我进行DWM 虚拟化。 但是当我的程序被拖动到其他DPI不一样的显示器时, 请对我们先进行system DWM虚拟化缩放。
第三种方式是Per Monitor DPI aware, 该方式是告诉系统, 请永远不要对我进行DWM虚拟化,我会自己针对不同的Monitor的DPi缩放比率进行缩放。