Windows の設定で、DPI が 100% より大きな値が設定されているとき、プログラムで表示するダイアログは自動的に拡大されて表示されますが、その拡大率は単純に DPI の拡大率で拡大されるわけではなく、ダイアログに設定されているフォントに応じてダイアログベース単位という単位が変わり、また、画面の DPI に応じてもダイアログベース単位が変わります。DPI が 100% のときのダイアログベース単位の値と、現在の DPI のダイアログベース単位の値を比較することでダイアログの拡大率が求まります。

DPI が 100% のときのダイアログベース単位を求める

#pragma pack(push, 1)
struct DLGTEMPLATEEX_PART1 {
	WORD      dlgVer;
	WORD      signature;
	DWORD     helpID;
	DWORD     exStyle;
	DWORD     style;
	WORD      cDlgItems;
	short     x;
	short     y;
	short     cx;
	short     cy;
};
#pragma pack(pop)

BYTE* AdvanceThrough_sz_Or_Ord(BYTE* pData)
{
	WORD* pWArr = (WORD*)pData;
	if (*pWArr == 0x0000)
	{
		pWArr++;
	}
	else if (*pWArr == 0xFFFF)
	{
		pWArr++;
		pWArr++;
	}
	else
	{
		WCHAR z;
		do
		{
			z = *pWArr;
			pWArr++;
		} while (z != 0);
	}
	return (BYTE*)pWArr;
}

BYTE* AdvanceThrough_String(BYTE* pData, LPWSTR pOutStr)
{
	WCHAR* pWStr = (WCHAR*)pData;
	WCHAR z;
	do
	{
		z = *pWStr;
		pWStr++;
	} while (z != 0);
	if (pOutStr)
	{
		int nLn = (int)(pWStr - (WCHAR*)pData);
		CopyMemory(pOutStr, pData, nLn * sizeof(WCHAR));
	}
	return (BYTE*)pWStr;
}

HFONT GetFontFromDialogTemplate(IN LPCTSTR lpszResourceID)
{
	HFONT hFont = NULL;
	HINSTANCE hInst = AfxGetResourceHandle();
	HRSRC hResource = FindResource(hInst, lpszResourceID, RT_DIALOG);
	if (hResource)
	{
		DWORD dwszDialogTemplate = SizeofResource(hInst, hResource);
		if (dwszDialogTemplate)
		{
			HGLOBAL hDialogTemplate = LoadResource(hInst, hResource);
			if (hDialogTemplate)
			{
				LPCDLGTEMPLATE lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);
				if (lpDialogTemplate)
				{
					DLGTEMPLATEEX_PART1* pDTX1 = (DLGTEMPLATEEX_PART1*)lpDialogTemplate;
					if (pDTX1->signature == 0xFFFF && pDTX1->dlgVer == 1)
					{
						BYTE* pData = (BYTE*)(pDTX1 + 1);
						pData = AdvanceThrough_sz_Or_Ord(pData);
						pData = AdvanceThrough_sz_Or_Ord(pData);
						pData = AdvanceThrough_String(pData, NULL);
						if (pDTX1->style & (DS_SETFONT | DS_SHELLFONT))
						{
							WORD ptFontSize = *(WORD*)pData;
							pData += sizeof(WORD);
							pData += sizeof(WORD);
							pData += sizeof(BYTE);
							pData += sizeof(BYTE);
							WCHAR strFontFaceName[LF_FACESIZE];
							pData = AdvanceThrough_String(pData, strFontFaceName);
							if ((pDTX1->style & DS_FIXEDSYS) && lstrcmpW(strFontFaceName, L"MS Shell Dlg") == 0)
							{
								lstrcpy(strFontFaceName, TEXT("MS Shell Dlg 2"));
							}
							hFont = CreateFontW(-MulDiv(ptFontSize, 96, 72), 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, strFontFaceName);
						}
					}
				}
			}
		}
	}
	return hFont;
}

BOOL GetActualDialogBaseUnitsBefore(IN LPCTSTR lpszResourceID, OUT LPSIZE baseUnit)
{
	HFONT hFont = (HFONT)GetFontFromDialogTemplate(lpszResourceID);
	if (hFont)
	{
		HDC hdc = GetDC(NULL);
		HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
		TEXTMETRIC tm = { 0 };
		GetTextMetrics(hdc, &tm);
		baseUnit->cy = (int)(tm.tmHeight);
		SIZE size = { 0 };
		GetTextExtentPoint32(hdc, TEXT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), 52, &size);
		SelectObject(hdc, hOldFont);
		baseUnit->cx = (int)((size.cx / 26 + 1) / 2);
		ReleaseDC(NULL, hdc);
		DeleteObject(hFont);
		return TRUE;
	}
	return FALSE;
}

現在の DPI のダイアログベース単位を求める

BOOL GetActualDialogBaseUnitsAfter(IN HWND hDialog, OUT LPSIZE baseUnit)
{
	RECT rect = { 4, 8, 0, 0 };
	BOOL result = MapDialogRect(hDialog, &rect);
	if (result)
	{
		baseUnit->cx = rect.left;
		baseUnit->cy = rect.top;
	}
	return result;
}

現在の DPI のダイアログの拡大率を求める

BOOL GetDialogScale(IN HWND hDialog, IN LPCTSTR lpszResourceID, OUT double* pdScaleX, OUT double* pdScaleY)
{
	SIZE BeforeBaseUnit = { 0 };
	SIZE AfterBaseUnit = { 0 };
	if (!GetActualDialogBaseUnitsBefore(lpszResourceID, &BeforeBaseUnit))
	{
		return FALSE;
	}
	if (!GetActualDialogBaseUnitsAfter(hDialog, &AfterBaseUnit))
	{
		return FALSE;
	}
	if (pdScaleX)
	{
		*pdScaleX = (double)AfterBaseUnit.cx / (double)BeforeBaseUnit.cx;
	}
	if (pdScaleY)
	{
		*pdScaleY = (double)AfterBaseUnit.cy / (double)BeforeBaseUnit.cy;
	}
	return TRUE;
}

使い方

double dScaleX = 1.0;
double dScaleY = 1.0;
HWND hDlg; // 拡大率を求めるダイアログのウィンドウハンドルを代入してください
LPCTSTR lpszResourceID = MAKEINTRESOURCE(IDD_DIALOG1); // 拡大率を求めるダイアログのリソースIDを代入してください
GetDialogScale(hDlg, lpszResourceID, &dScaleX, &dScaleY); // 拡大率を求める

参考サイト:
http://www5d.biglobe.ne.jp/~noocyte/Programming/Windows/WindowsTips.html
https://support.microsoft.com/ja-jp/help/125681/how-to-calculate-dialog-base-units-with-non-system-based-font
https://stackoverflow.com/questions/14370238/can-i-dynamically-change-the-font-size-of-a-dialog-window-created-with-c-in-vi


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です