通过设备描述表句柄(HDC)获取设备DPI

3 篇文章 0 订阅
2 篇文章 0 订阅

通过设备描述表句柄(HDC)获取设备DPI
如何解决在不同windows版本和不同分辨率下设备的DPI(每英寸像素数)或DPM(每毫米像素数)是保证绘图尺寸正当确的关键,本文记录了我在解决这一问题时心路历程。

  1. 使用系统函数直接获取DPI
    xDPI = GetDeviceCaps(pdc, LOGPIXELSX);
    yDPI = GetDeviceCaps(pdc, LOGPIXELSY);
    刚开始的时,没太注意,反正获取的值是用于绘图计算,显示的图形相对关系基本正确,也没太去关注其绝对尺寸或大小的正确性。不过后来发现无论如何修改系统显示的分辨率,这个函数返回值都是96,所以觉得这个函数用的不对,仔细研究了这个参数,LOGPIXELS 沿屏幕宽度的每个逻辑英寸的像素数。问题就出在逻辑英寸这个词上。关于这个问题,请参考什么是逻辑英寸?这个DPI 在看VC++中关于模式映射的时候看见
  2. 使用系统函数间接获取DPI
    /* HDC pdc 设备描术表句柄
    float widthP = GetDeviceCaps(pdc, HORZRES);/屏幕宽度,像素/
    float heightP = GetDeviceCaps(pdc, VERTRES);/屏幕高度,像素/
    float widthMM = GetDeviceCaps(pdc, HORZSIZE);/屏幕宽度,毫米/
    float heightMM = GetDeviceCaps(pdc, VERTSIZE);/屏幕高度,毫米/
    float dpi = sqrt(widthPwidthP + heightP * heightP) / sqrt(widthMMwidthMM + heightMM * heightMM)*25.4;
    貌似比较完美的解决了在不同分辨率下图形显示绝对尺寸的正确性的问题,但又发现这个函数在win7上返回的参数不正确。网上也有这样的问题,典型见GetDeviceCaps 函数获取屏幕物理宽度(毫米)不准,求解决。,有朋友也给出了一些解决方案。
  3. 解决方案
    如果不是Win7系统,第二部分所述就比较好了,如果是Win7系统, 我参考了Ofek’s Visual C++ stuff,不过这种方式不确保始终有值,如果显示器不支持,或者注册表中的EDID值由于某种原因不存在,那就取不到。现在我也没有更好的解决办法。最后给出自己的处理方法;
    不罗嗦,直接上代码:
#include <atlstr.h>;
#include <SetupApi.h>;
#include <cfgmgr32.h>;   // for MAX_DEVICE_ID_LEN
#pragma comment(lib, "setupapi.lib")

//#define NAME_SIZE 128
#define DEFALDPI 96.

const GUID GUID_CLASS_MONITOR = { 0x4d36e96e, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 };

CString Get2ndSlashBlock(const CString& sIn)
{
	int FirstSlash = sIn.Find(_T('\\'));
	CString sOut = sIn.Right(sIn.GetLength() - FirstSlash - 1);
	FirstSlash = sOut.Find(_T('\\'));
	sOut = sOut.Left(FirstSlash);
	return sOut;
}

// Assumes hEDIDRegKey is valid
bool GetMonitorSizeFromEDID(const HKEY hEDIDRegKey, short& WidthMm, short& HeightMm)
{
	BYTE EDIDdata[1024];
	DWORD edidsize = sizeof(EDIDdata);

	if (ERROR_SUCCESS != RegQueryValueEx(hEDIDRegKey, _T("EDID"), NULL, NULL, EDIDdata, &edidsize))
		return false;
	WidthMm = ((EDIDdata[68] & 0xF0) << 4) + EDIDdata[66];
	HeightMm = ((EDIDdata[68] & 0x0F) << 8) + EDIDdata[67];

	return true; // valid EDID found
}

bool GetSizeForDevID(const CString& TargetDevID, short& WidthMm, short& HeightMm)
{
	HDEVINFO devInfo = SetupDiGetClassDevsEx(
		&GUID_CLASS_MONITOR, //class GUID
		NULL, //enumerator
		NULL, //HWND
		DIGCF_PRESENT | DIGCF_PROFILE, // Flags //DIGCF_ALLCLASSES|
		NULL, // device info, create a new one.
		NULL, // machine name, local machine
		NULL);// reserved

	if (NULL == devInfo)
		return false;

	bool bRes = false;

	for (ULONG i = 0; ERROR_NO_MORE_ITEMS != GetLastError(); ++i)
	{
		SP_DEVINFO_DATA devInfoData;
		memset(&devInfoData, 0, sizeof(devInfoData));
		devInfoData.cbSize = sizeof(devInfoData);

		if (SetupDiEnumDeviceInfo(devInfo, i, &devInfoData))
		{
			TCHAR Instance[MAX_DEVICE_ID_LEN];
			SetupDiGetDeviceInstanceId(devInfo, &devInfoData, Instance, MAX_PATH, NULL);

			CString sInstance(Instance);
			if (-1 == sInstance.Find(TargetDevID))
				continue;

			HKEY hEDIDRegKey = SetupDiOpenDevRegKey(devInfo, &devInfoData,
				DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);

			if (!hEDIDRegKey || (hEDIDRegKey == INVALID_HANDLE_VALUE))
				continue;

			bRes = GetMonitorSizeFromEDID(hEDIDRegKey, WidthMm, HeightMm);

			RegCloseKey(hEDIDRegKey);
		}
	}
	SetupDiDestroyDeviceInfoList(devInfo);
	return bRes;
}

HMONITOR  g_hMonitor;

BOOL CALLBACK MyMonitorEnumProc(
	_In_  HMONITOR hMonitor,
	_In_  HDC hdcMonitor,
	_In_  LPRECT lprcMonitor,
	_In_  LPARAM dwData
)

{
	// Use this function to identify the monitor of interest: MONITORINFO contains the Monitor RECT.
	MONITORINFOEX mi;
	mi.cbSize = sizeof(MONITORINFOEX);

	GetMonitorInfo(hMonitor, &mi);
	OutputDebugString(mi.szDevice);

	// For simplicity, we set the last monitor to be the one of interest
	g_hMonitor = hMonitor;

	return TRUE;
}

BOOL DisplayDeviceFromHMonitor(HMONITOR hMonitor, DISPLAY_DEVICE& ddMonOut)
{
	MONITORINFOEX mi;
	mi.cbSize = sizeof(MONITORINFOEX);
	GetMonitorInfo(hMonitor, &mi);

	DISPLAY_DEVICE dd;
	dd.cb = sizeof(dd);
	DWORD devIdx = 0; // device index

	CString DeviceID;
	bool bFoundDevice = false;
	while (EnumDisplayDevices(0, devIdx, &dd, 0))
	{
		devIdx++;
		if (0 != _tcscmp(dd.DeviceName, mi.szDevice))
			continue;

		DISPLAY_DEVICE ddMon;
		ZeroMemory(&ddMon, sizeof(ddMon));
		ddMon.cb = sizeof(ddMon);
		DWORD MonIdx = 0;

		while (EnumDisplayDevices(dd.DeviceName, MonIdx, &ddMon, 0))
		{
			MonIdx++;

			ddMonOut = ddMon;
			return TRUE;

			ZeroMemory(&ddMon, sizeof(ddMon));
			ddMon.cb = sizeof(ddMon);
		}

		ZeroMemory(&dd, sizeof(dd));
		dd.cb = sizeof(dd);
	}

	return FALSE;
}


//-------------------------------------------------------------------------
// 函数    : IsWinVerEqualTo
// 功能    : 判断是否=某个特定的系统版本
// 返回值  : BOOL
// 参数    : DWORD dwMajorVersion
// 参数    : DWORD dwMinorVersion
// 附注    :
//-------------------------------------------------------------------------

BOOL IsWinVerEqualTo(DWORD dwMajorVersion, DWORD dwMinorVersion)
{
	OSVERSIONINFOEXW osvi = { 0 };
	DWORDLONG dwlConditionMask = 0;

	// 1、初始化系统版本信息数据结构
	ZeroMemory(&osvi, sizeof(osvi));
	osvi.dwOSVersionInfoSize = sizeof(osvi);
	osvi.dwMajorVersion = dwMajorVersion;
	osvi.dwMinorVersion = dwMinorVersion;

	// 2、初始化条件掩码
	VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL);
	VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);

	return ::VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask);
}


float getDPI(HDC pdc)
{
	if (pdc == NULL)
		return DEFALDPI .;

	float widthP = GetDeviceCaps(pdc, HORZRES);			/*屏幕宽度,像素*/
	float heightP = GetDeviceCaps(pdc, VERTRES);		/*屏幕高度,像素*/
	float widthMM = GetDeviceCaps(pdc, HORZSIZE);		/*屏幕宽度,毫米*/
	float heightMM = GetDeviceCaps(pdc, VERTSIZE);		/*屏幕高度,毫米*/
	int DevTech = GetDeviceCaps(pdc, TECHNOLOGY);		/*设备技术。它可以是以下值之一*/
														//	DT_PLOTTER	矢量绘图仪
														//	DT_RASDISPLAY	栅格显示
														//	DT_RASPRINTER	光栅打印机
														//	DT_RASCAMERA	光栅相机
														//	DT_CHARSTREAM	字符流
														//	DT_METAFILE	图元文件
														//	DT_DISPFILE	显示文件

	if(DevTech !=DT_RASDISPLAY||!IsWinVerEqualTo(6, 1))
	{
		return sqrt((widthP*widthP + heightP * heightP) / (widthMM*widthMM + heightMM * heightMM))*25.4;
	}
	else
	{
		EnumDisplayMonitors(NULL, NULL, MyMonitorEnumProc, NULL);
		DISPLAY_DEVICE ddMon;
		if (FALSE == DisplayDeviceFromHMonitor(g_hMonitor, ddMon))
			return DEFALDPI .;

		CString DeviceID;
		DeviceID.Format(_T("%s"), ddMon.DeviceID);
		DeviceID = Get2ndSlashBlock(DeviceID);

		short WidthMm, HeightMm;
		bool bFoundDevice = GetSizeForDevID(DeviceID, WidthMm, HeightMm);
		if (!bFoundDevice)
			return DEFALDPI .;
		return sqrt((widthP*widthP + heightP * heightP) / (WidthMm*WidthMm + HeightMm * HeightMm))*25.4;
	}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值