概要

 エディットボックスを含むダイアログボックスのサンプルです。
 
 本プログラムはVistaからサポートしている高DPIに対応するため複雑ことを行っています。
 マニフェストでの高DPIの宣言は不要です。

SetThreadDpiAwarenessContext

 Windows 10 Version 1703より古い場合、プログラムの初期時にSetThreadDpiAwarenessContext APIが存在するかチェックし存在する場合、Windows10のバージョンをチェックします。
 バージョンが1607の場合は、非クライアント領域の大きさを変更させるためWM_NCCREATEメッセージ発生時にEnableNonClientDpiScalingを呼び出します。
 バージョンが1703以降の場合は、SetThreadDpiAwarenessContext APIでDPI_AWARENESS_PER_MONITOR_AWARE_V2を指定することにより Windowsに非クライアント領域の大きさの変更を自動にさせます。
 WM_CREATEメッセージに現在のDPIを取得しフォントの大きさ等を変更しています。

SetThreadDpiAwarenessContext

 SetThreadDpiAwarenessContext APIが使えない場合、GetDpiForMonitor API(Windows 8.1以降)が使えるか確認します。
 SetThreadDpiAwarenessContext APIが使える場合は、このAPIを実行し高DPIをサポートしたプログラムであることを宣言します。
 高DPIを使用することを宣言しないと常に96DPIが取得されWindowsが勝手にスケーリングしてしまいます。
GetDeviceCaps(hdc,LOGPIXELSX)によりディスクトップのDPIを取得します。

SetThreadDpiAwarenessContextが使えない場合

GetDeviceCaps(hdc,LOGPIXELSX)によりディスクトップのDPIを取得します。 事前にSetProcessDPIAware API(Windows Vista以降)を実行するとプログラム自身がスケーリング処理をすることになり正常なDPI値が返されます。Windows XPではこのAPIは存在しないので96DPIと仮定します。なおAPIが存在するかどうかは動的にライブラリをロードし関数のエントリポイントが存在するかどうかで判断しています。

解像度の変更等

 クライアント領域は初期化時とWM_DPICHANGEDメッセージ発生時(Windows 8.1以降)にフォントの大きさ等をDPI値(96DPIの時100%)に応じて変化させています。
 scaleChange関数を呼び出すと各コントロールウィンドウをEnumChildWindowsで列挙します。
 EnumChildWindowsから呼び出されるEnumWndProc関数によりスケールに応じた位置、サイズ、文字サイズを設定します

Windows 7の実行例

SetThreadDpiAwarenessContextを適用しない場合

SetThreadDpiAwarenessContextを適用した場合

テスト環境

コンパイラ

Visual C++ 2013 Express 32/64bit

プロジェクトの作成

Win32プロジェクト Windowsアプリケーション

プログラムソースの概要

_tWinMain関数

Windowsから最初に_tWinMain関数が呼び出されます。

DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)ダイアログボックスプロシージャー

case WM_INITDIALOG:

ダイアログボックスの初期化時に呼び出されます。
ここではエディットボックスの初期値を設定しています。

case WM_NCCREATE:

非クライアント領域の初期化時にEnableNonClientDpiScalingを呼び出します。

case WM_COMMAND:

各ボタンがクリックされた場合およびコンボボックスでアイテムが選択されたときやに呼び出されます。 wParamの下位ワード(16bit)にコントロールのID、wParamの上位ワードにメッセージが格納されています。 コントロールのID別に処理をswitchステートメントで記述しています。
IDOK
EnableNonClientDpiScalingを呼び出し非クライアント領域の扱いを元に戻します。
GetDlgItemTextによりエディットボックスの値を取得しMessageBoxで値を表示します。
IDCANCEL
プログラムを終了させます。

プログラムソース

edit3font.cpp

//	エディットボックスサンプル
//	Visual C++ 2013 32/64bit
#include <windows.h>
#include <commctrl.h> 
#include <tchar.h>
#include "resource.h"


#if 1500 < _MSC_VER
#include <shellscalingapi.h>

# pragma comment(lib, "Shcore.lib")
#else
#define WM_DPICHANGED                   0x02E0
typedef enum MONITOR_DPI_TYPE {
	MDT_EFFECTIVE_DPI = 0,
	MDT_ANGULAR_DPI = 1,
	MDT_RAW_DPI = 2,
	MDT_DEFAULT = MDT_EFFECTIVE_DPI
} MONITOR_DPI_TYPE;

#endif

//	ダイアログボックスプロシージャー
LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);

#ifndef _DPI_AWARENESS_CONTEXTS_
	DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
#endif

typedef HRESULT(_stdcall *GetDpiForMonitorFunc)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);
typedef HRESULT(_stdcall* EnableNonClientDpiScalingFunc)(HWND);
typedef HRESULT(_stdcall* SetThreadDpiAwarenessContextFunc)(DPI_AWARENESS_CONTEXT);
typedef BOOL(_stdcall *SetProcessDPIAwareFunc)(VOID);


static HMODULE hModUser32;
static EnableNonClientDpiScalingFunc NCCfunc = 0;
static SetThreadDpiAwarenessContextFunc ThreadAwareFunc = 0;

#ifndef _DPI_AWARENESS_CONTEXTS_

typedef enum _DPI_AWARENESS {
	DPI_AWARENESS_INVALID = -1,
	DPI_AWARENESS_UNAWARE = 0,
	DPI_AWARENESS_SYSTEM_AWARE = 1,
	DPI_AWARENESS_PER_MONITOR_AWARE = 2
} DPI_AWARENESS;



#define DPI_AWARENESS_CONTEXT_UNAWARE              ((DPI_AWARENESS_CONTEXT)-1)
#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE         ((DPI_AWARENESS_CONTEXT)-2)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE    ((DPI_AWARENESS_CONTEXT)-3)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
#define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED	   ((DPI_AWARENESS_CONTEXT)-5)
#endif

int win10ver2(void);
int WIN10VER;


int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst,
						TCHAR* lpszCmdLine, int nCmdShow){


	hModUser32 = LoadLibrary(L"User32.dll");

	NCCfunc = (EnableNonClientDpiScalingFunc)GetProcAddress(hModUser32, "EnableNonClientDpiScaling");
	if (WIN10VER == 1607)	//	AnniversaryUpdate
		NCCfunc = (EnableNonClientDpiScalingFunc)GetProcAddress(hModUser32, "EnableNonClientDpiScaling");
	else
		NCCfunc = 0;

	ThreadAwareFunc = (SetThreadDpiAwarenessContextFunc)GetProcAddress(hModUser32, "SetThreadDpiAwarenessContext");

	if (ThreadAwareFunc){
		if (WIN10VER<1703)
			(*ThreadAwareFunc)(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
		if (1703 <= WIN10VER && WIN10VER<1809)
			(*ThreadAwareFunc)(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
		if (1809 <= WIN10VER)
			(*ThreadAwareFunc)(DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED);
	}
	else{	//	Windows Vista以降
		SetProcessDPIAwareFunc ProcessAwareFunc = (SetProcessDPIAwareFunc)GetProcAddress(hModUser32, "SetProcessDPIAware");
		if (ProcessAwareFunc)
			(*ProcessAwareFunc)();
	}
	
	DialogBox(hInstance, TEXT("DLG1"), 0, (DLGPROC)DlgProc1);
	if (hModUser32)
		FreeLibrary(hModUser32);
	return (int)0;
}


BOOL GetActualDialogBaseUnits(HWND hDialog, SIZE *baseUnit){
	RECT rect;
	BOOL result;

	rect.left = 4;
	rect.top = 8;
	if (result = MapDialogRect(hDialog, &rect)) {
		baseUnit->cx = rect.left;
		baseUnit->cy = rect.top;
	}
	return result;
}

struct DLG_CHILD_SCALE{
	HWND hDlg;
	SIZE unit;
	UINT oldScale;
	UINT newScale;
	HFONT hFont;
};

BOOL CALLBACK EnumWndProc(HWND hWnd, LPARAM lParam){
	POINT pt;
	DLG_CHILD_SCALE* d = (DLG_CHILD_SCALE*)lParam;
	pt.x = 0;
	pt.y = 0;
	ClientToScreen(hWnd, &pt);
	ScreenToClient(d->hDlg, &pt);
	RECT rc;
	GetClientRect(hWnd, &rc);
	int cx, cy, w, h;
	cx = MulDiv(pt.x, 4, d->unit.cx);
	cy = MulDiv(pt.y, 8, d->unit.cy);

	cx = MulDiv(cx, d->newScale, d->oldScale);
	cy = MulDiv(cy, d->newScale, d->oldScale);

	cx = MulDiv(cx, d->unit.cx, 4);
	cy = MulDiv(cy, d->unit.cy, 8);

	w = MulDiv(rc.right, d->newScale, d->oldScale);
	h = MulDiv(rc.bottom, d->newScale, d->oldScale);

	rc.left = cx;
	rc.top = cy;
	rc.right = cx+w;
	rc.bottom = cy+h;
	LONG style = GetWindowLong(hWnd, GWL_STYLE);
	LONG exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
	AdjustWindowRectEx(&rc, style, FALSE, exStyle);
	SetWindowPos(hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER);
	SendMessage(hWnd, WM_SETFONT, (WPARAM)d->hFont, MAKELPARAM(TRUE, 0));
	UpdateWindow(hWnd);
	return TRUE;
}

HFONT scaleChange(HWND hDlg,int nowScale, int oldScale,SIZE& baseUnit,HFONT hFont){
	LOGFONT lf;
	HDC hdc = GetDC(GetDlgItem(hDlg, IDC_EDIT1));
	HFONT hfont9 = (HFONT)SendMessage(GetDlgItem(hDlg, IDOK), WM_GETFONT, 0, 0);
	GetObject(hfont9, sizeof(LOGFONT), &lf);
	TEXTMETRIC metrics;
	GetTextMetrics(hdc, &metrics);
	int height;
	height = lf.lfHeight*100/oldScale;
	memset(&lf, 0, sizeof(lf));
	lf.lfHeight = -(height - metrics.tmInternalLeading) *nowScale;
	lf.lfHeight = (height ) *nowScale; 
	lf.lfHeight /= 100;
	HFONT hFont2 = CreateFontIndirect(&lf);
	LOGFONT lfont;
	GetObject(hFont2, sizeof(LOGFONT), &lfont);
	SendMessage(hDlg, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
	HFONT hFontOld = (HFONT)SelectObject(hdc, hFont2);
	GetActualDialogBaseUnits(hDlg, &baseUnit);

	GetTextMetrics(hdc, &metrics);
	SelectObject(hdc, hFontOld);

	HFONT htemp=(HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0);
	GetObject(htemp, sizeof(LOGFONT), &lfont);
	ReleaseDC(hDlg, hdc);

	RECT rc;
	GetClientRect(hDlg, &rc);
	int w = rc.right - rc.left;
	int h = rc.bottom - rc.top;
	w = MulDiv(w, nowScale, oldScale);
	h = MulDiv(h, nowScale, oldScale);

	rc.left = 0;
	rc.top = 0;
	rc.right = w;
	rc.bottom = h;

	LONG style = GetWindowLong(hDlg, GWL_STYLE);
	AdjustWindowRectEx(&rc, style, FALSE, 0);

	SetWindowPos(hDlg, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);

	DLG_CHILD_SCALE d;
	d.hDlg = hDlg;
	d.hFont = hFont2;
	d.newScale = nowScale;
	d.oldScale = oldScale;
	d.unit = baseUnit;

	EnumChildWindows(hDlg, EnumWndProc, (LPARAM)&d);
	InvalidateRect(hDlg, NULL, FALSE);
	UpdateWindow(hDlg);
	if (hFont)
		DeleteObject(hFont);
	return hFont = hFont2;
}

//	ダイアログボックスプロシージャー

LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
	TCHAR buf[32];
	static HFONT hFont = 0;
	static int nowDPIx = 96;
	static int nowDPIy = 96;
	static UINT nowScale = 100;
	static UINT initScale = 100;
	static int height;
	static HMODULE hMod;
	static GetDpiForMonitorFunc func = 0;
	static SIZE baseUnit;

	switch (msg) {
		case WM_INITDIALOG:{
			HMONITOR hMonitor = MonitorFromWindow(hDlg, MONITOR_DEFAULTTONULL);
			hMod = LoadLibrary(L"Shcore.dll");

			func = (GetDpiForMonitorFunc)GetProcAddress(hMod, "GetDpiForMonitor");
			if (func == 0)
				FreeLibrary(hMod);

			UINT hDPI, vDPI;
			if (func)
				(*func)(hMonitor, MDT_EFFECTIVE_DPI, &hDPI, &vDPI);
			else{
				HDC hdc = GetDC(NULL);	//	ディスクトップ
				hDPI = GetDeviceCaps(hdc, LOGPIXELSX);
				vDPI = GetDeviceCaps(hdc, LOGPIXELSY);

			}
			nowDPIx = hDPI;
			nowDPIy = vDPI;
			nowScale = MulDiv(nowDPIx, 100, 96);
			SetWindowText(GetDlgItem(hDlg, IDC_EDIT1), _TEXT("初期値"));
			GetActualDialogBaseUnits(hDlg, &baseUnit);
			UINT oldScale = nowScale;
			hFont = scaleChange(hDlg, nowScale, oldScale, baseUnit, hFont);
			return TRUE;
		}
		case WM_DPICHANGED:{
			UINT oldScale = nowScale;
			nowDPIx = LOWORD(wParam);
			nowDPIy = HIWORD(wParam);
			nowScale = MulDiv(nowDPIx, 100, 96);
			hFont=scaleChange(hDlg, nowScale, oldScale, baseUnit, hFont);
			break;
		}

		case WM_NCCREATE:
		{
			if (NCCfunc)
			(*NCCfunc)(hDlg);
			return FALSE;
		}

		case WM_COMMAND:
			switch(LOWORD(wParam)){
				case IDOK:
					GetDlgItemText(hDlg,IDC_EDIT1,(TCHAR*)buf, sizeof(buf)/sizeof(TCHAR));
					MessageBox(hDlg, buf, _TEXT("エディットボックスの値"),MB_OK);
					if (func){
						FreeLibrary(hMod);
						hMod = 0;
						func = 0;
					}
					if (hFont)
						DeleteObject(hFont);
					EndDialog(hDlg, TRUE);
					return TRUE;
				case IDCANCEL:
					if (func){
						FreeLibrary(hMod);
						hMod = 0;
						func = 0;
					}
					if (hFont)
						DeleteObject(hFont);
					EndDialog(hDlg, FALSE);
					return FALSE;
				default:
					return FALSE;
			}
			default:
				return FALSE;
	}
	return TRUE;
}


//	DLL内の関数へのポインタ型を定義
typedef void (WINAPI *RtlGetVersion_FUNC)(OSVERSIONINFOEXW*);

int win10ver2(void){
	int ver = 0;
	HMODULE hMod;
	OSVERSIONINFOEXW osw;
	hMod = LoadLibrary(TEXT("ntdll.dll"));
	RtlGetVersion_FUNC func;
	if (hMod){
		func = (RtlGetVersion_FUNC)GetProcAddress(hMod, "RtlGetVersion");
		if (func == 0){
			FreeLibrary(hMod);
			return FALSE;
		}
		ZeroMemory(&osw, sizeof(osw));
		osw.dwOSVersionInfoSize = sizeof(osw);
		func(&osw);
		FreeLibrary(hMod);
		if (osw.dwMajorVersion == 10){
			switch (osw.dwBuildNumber){
			case 10240: ver = 1507; break;
			case 10586: ver = 1511; break;
			case 14393: ver = 1607; break;
			case 15063: ver = 1703; break;
			case 16299: ver = 1709; break;
			case 17134: ver = 1803; break;
			case 17763: ver = 1809; break;
			default: ver = 1809; break;
			}
			return ver;
		}
	}
	return -1;
}

resource.h

#define IDC_EDIT1	100

resource.rc

ソースファイルと実行ファイルのダウンロード

ダウンロード edit3font.zip(40.0kbyte)
ZIPファイルに含まれるファイル
edit3font.cpp
resource.h
resource.rc
edit3font.exe