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缩放比率进行缩放。