WMIを使用してディスプレイサイズ,接続方法を取得(Windows 10高DPI対応)

icon 項目のみ表示/展開表示の切り替え

概要

 WMI(Windows Management Instrumentation)を使用して接続されている全部のディスプレイサイズ,接続方法を取得してウィンドウに表示します。Windows Vista以降の高DPIに対応しているため文字が綺麗に表示されます。
Windows 10(Version 1607 Anniversary Update)以降では非クライアント領域(タイトルバー,枠等)を動的に変更しています。
 以下に本プログラムを実行結果を記載します。

 WMIはWindowsのシステム管理を行うためのインターフェースで主にスクリプトやプログラム言語でシステムイベント、システムの設定や状態の変更が可能である。
 WMIは名前空間(このプログラムではroot¥¥WMIを指定)とクラスを指定して情報を取得する。
 動作確認は、以下の環境で実施しました。
 lenovo YogaPro3の内臓ディスプレイ(13.3インチ 3200*1800画素)とHDMI接続で BenQ XL2420(24型 1920*1080画素 53㎝*30㎝)を使用したデュアルディスプレイ環境。

 なお、WMIの名前空間とクラス名を調べるにはhttps://forest.watch.impress.co.jp/docs/review/702170.html SimpleWMIViewというフリーソフトが便利である。

 本ページではWindows10の高DPI対応をメインに記述しています。なおWMIでの値の取得方法は以下のページを参照されたい。
WMIを使用してディスプレイ名と製造者名を取得
WMIを使用してディスプレイの接続方法を取得
WMIを使用してディスプレイサイズ,接続方法を取得
WMIを使用してディスプレイのデバイス名を取得
 本プログラムは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%)に応じて変化させています。
 クライアント領域にリストビューを表示しているので初期化時とWM_DPICHANGEDメッセージ発生時(Windows 8.1以降)にリストビューのフォントの大きさをDPI値(96DPIの時100%)に応じて変化させています。  

高解像度環境での挙動(Windows 8.1以降)

高解像度側にウィンドウが表示される

 文字が小さくならないようにこの環境では文字タイトルバーが2倍(192DPI/96DPI=2)の大きさで表示する。

低解像度側にウィンドウが表示される

 文字タイトルバーが1倍(96DPI)の大きさで表示する。

高解像度側よりにウィンドウが表示される

 文字が小さくならないようにこの環境では文字タイトルバーが2倍(192DPI/96DPI=2)の大きさで表示する。

低解像度側よりにウィンドウが表示される

 文字タイトルバーが1倍(96DPI)の大きさで表示する。

Windows 7(200% 192DPI)の表示例

OSでスケーリングした場合

プログラムでスケーリングした場合

コンパイル方法

 Visual C++ 2008 Express Edition又はVisual C++ 2013 Expressで動作確認をしている。動作OSはWindows 32bit/64bit

プログラムソースの概要

gmonitorinfo.cpp

_tWinMain関数

 Windowsから最初に_tWinMain関数が呼び出されます。  ウィンドウを作成する場合は、RegisterClass APIによりウィンドウクラスを定義してからCreateWindow APIを呼び出しウィンドウを作成します。 Windowsは入力等のイベントが発生するとアプリケーションにメッセージを送付します。 メッセージはキューに保管されます。アプリケーションはメッセージを取り出し、該当ウィンドウにメッセージを配信します。 メッセージの取り出しから配信までループで処理を行いウィンドウから終了メッセージが届くと、ループを抜けるように記述します。 これらの一連の処理は、通常にCreateWindow APIの後に記述しこれをメッセージループと呼んでいます。 詳細は以下を参照してください。
メッセージループについて

WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)ウィンドウプロシージャー

 RegisterClass APIにより登録することによりWindowsから呼び出されます。
 第2引数にメッセージの種類が格納されていますので、switchステートメントによりメッセージごとの処理を振り分けます。
 自分で処理しないメッセージはDefWindowProc APIに渡せばWindowsが標準的な処理を行ってくれます。

case WM_CREATE:

 ウィンドウの初期化時に呼び出されます。
 現在の位置のディスプレイのDPIを取得します。
 DPIに応じてリストビューのフォントをCreateFontIndirectで新規に作成します。
 リストビューをディスプレイ数に応じてset_lvs関数で作成します。
 その他の処理は、DefWindowProc APIにそのままメッセージを渡して処理しています。

case WM_DPICHANGED:

 ディスプレイのDPIが変更された場合は、ウィンドウの移動により異なるDPIのディスプレイ上の場合に呼び出されます。
 wParamよりDPIを取得しリストビューの文字サイズを変更し再描画します。なお文字サイズが変わるのでリストビューの列幅をlist2fit関数で修正しています。

case WM_SIZE:

 ウィンドウのサイズが変更されたときに呼び出されます。リストビューの大きさをウィンドウサイズに合わせるためMoveWindowを実行しています。

case WM_DESTROY:

 ウィンドウが閉じるときに呼び出されます。
 PostQuitMessage APIにより終了コードを指定して、ウィンドウプロシージャーを終了させます。

ソースコード

gmonitorinfo.cpp

//	WMIからモニターの情報を取得してリストビューに表示する。
//  Visual C++ 2008/2013 UNICODE専用
//	Windows Vista/7/8
//	Windows 8.1
//	Windows 10 Per-Monitor and Per-Monitor (V2) DPI Awarenessに対応

#include <windows.h>
#define _WIN32_DCOM

#include <comdef.h>
#include <wbemidl.h>
#include <commctrl.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

#include <tchar.h>
#include "resource.h"

# pragma comment(lib, "wbemuuid.lib")

#ifndef _DPI_AWARENESS_CONTEXTS_
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
#endif

typedef HRESULT(_stdcall* EnableNonClientDpiScalingFunc)(HWND);
typedef HRESULT(_stdcall* SetThreadDpiAwarenessContextFunc)(DPI_AWARENESS_CONTEXT);

HMODULE hModUser32 = 0;
EnableNonClientDpiScalingFunc NcDpifunc = 0;
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


#define ID_LISTVIEW 100

TCHAR* szClassName=TEXT("GMONITORINFO");
HINSTANCE hInst;

//	ウィンドウプロシージャー
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

//	リストビューの表示内容を設定
int set_lvs(HWND hList);

// int型のSAFEARRAYから文字列を取り出す。

WCHAR* safearrayi4_str(VARIANT& v, WCHAR* str, int len);

// D3DKMDT_VIDEO_OUTPUT_TECHNOLOGYの値に該当する文字列を検索する。

WCHAR* video_out_type_get(int x);

typedef HRESULT(_stdcall *GetDpiForMonitorFunc)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);

typedef BOOL(_stdcall *SetProcessDPIAwareFunc)(VOID);

int win10ver2(void);

int WIN10VER;

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst,TCHAR* lpszCmdLine, int nCmdShow){
	HWND hWnd;
	MSG lpMsg;
	WNDCLASS myProg;
	hInst=hInstance;
	WIN10VER = win10ver2();
	hModUser32 = LoadLibrary(L"User32.dll");
	if (hModUser32){
		if (WIN10VER == 1607)	//	AnniversaryUpdate
			NcDpifunc = (EnableNonClientDpiScalingFunc)GetProcAddress(hModUser32, "EnableNonClientDpiScaling");
		else
			NcDpifunc = 0;
		ThreadAwareFunc = (SetThreadDpiAwarenessContextFunc)GetProcAddress(hModUser32, "SetThreadDpiAwarenessContext");
		if (ThreadAwareFunc){	//	Windows 10
			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)();
		}
	}
	if (!hPreInst) {
		myProg.style=CS_HREDRAW | CS_VREDRAW;
		myProg.lpfnWndProc=WndProc;
		myProg.cbClsExtra=0;
		myProg.cbWndExtra=0;
		myProg.hInstance=hInstance;
		myProg.hIcon=NULL;
		myProg.hCursor=LoadCursor(NULL, IDC_ARROW);
		myProg.hbrBackground=(HBRUSH__ *)GetStockObject(WHITE_BRUSH);
		myProg.lpszMenuName=NULL;
		myProg.lpszClassName=szClassName;
		if (!RegisterClass(&myProg))
			return FALSE;
	}
	HMENU hMenu = LoadMenu(hInst, TEXT("IDM_MENU"));
	hWnd = CreateWindow(szClassName,
		TEXT("GMonitorInfo"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		NULL,
		hMenu,
		hInstance,
		NULL);
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
	while (GetMessage(&lpMsg, NULL, 0, 0)) {
		TranslateMessage(&lpMsg);
		DispatchMessage(&lpMsg);
	}

	if (hModUser32)
		FreeLibrary(hModUser32);

	return int(lpMsg.wParam);
}

int auto_width(HWND hList){
	HWND hHeader=(HWND)SendMessage(hList,LVM_GETHEADER, 0, 0);
	int cmax=(int)SendMessage(hHeader, HDM_GETITEMCOUNT, 0, 0);
	int n;
	int all=0;
	for(n=0;n<cmax-1;n++){
		ListView_SetColumnWidth(hList,n,LVSCW_AUTOSIZE_USEHEADER);
	}
	for(n=0;n<cmax-1;n++){
		all+=ListView_GetColumnWidth(hList,n);
	}
	return all;
}


void list2fit(HWND hWnd,HWND hList){
	int w=auto_width(hList);
	int h;
	HWND hHeader=(HWND)SendMessage(hList,LVM_GETHEADER, 0, 0);
	RECT rc;
	GetClientRect(hHeader,&rc);
	h=rc.bottom-rc.top;
	SetWindowPos(hWnd, NULL, 0, 0, w+30, h*11, SWP_NOMOVE | SWP_NOZORDER);
}


//	ウィンドウプロシージャー

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
	LV_COLUMN lvcol;
	static int mon_max=0;
	static HWND hList;	//	リストビューのハンドル
	int n;
	TCHAR buf[256];
	static HFONT hFont=0;
	static int nowDPIx = 96;
	static int nowDPIy = 96;
	static UINT nowScale = 100;
	static UINT initScale = 100;
	static int height;
	static int txtPt = 12;	//	文字のポイント数
	static HMODULE hMod;
	static GetDpiForMonitorFunc func = 0;
	switch (msg){
	case WM_CREATE:{
		HMONITOR hMonitor = MonitorFromWindow(hWnd, 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;
		initScale=nowScale = MulDiv(nowDPIx, 100, 96);

		hList = CreateWindowEx(0,
			WC_LISTVIEW, TEXT("GMONITORINFO"),
			WS_CHILD | WS_VISIBLE | LVS_REPORT,
			0, 0, 0, 0,
			hWnd,
			(HMENU)ID_LISTVIEW,
			hInst,
			NULL);

		HDC hdc = GetDC(hList);

		TEXTMETRIC metrics;
		GetTextMetrics(hdc, &metrics);
		height = metrics.tmHeight;
		LOGFONT lf;
		memset(&lf, 0, sizeof(lf));
		height = 96*txtPt/72;
		lf.lfHeight = height *nowScale;
		lf.lfHeight /= 100;
		hFont = CreateFontIndirect(&lf);
		ReleaseDC(hList, hdc);
		SendMessage(hList, WM_SETFONT, (WPARAM)hFont, 0);
		lvcol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
		lvcol.fmt = LVCFMT_LEFT;
		lvcol.cx = 80;
		lvcol.pszText = TEXT("namespace");
		lvcol.iSubItem = 0;
		ListView_InsertColumn(hList, 0, &lvcol);

		lvcol.cx = 260;
		lvcol.pszText = TEXT("class");
		lvcol.iSubItem = 1;
		ListView_InsertColumn(hList, 1, &lvcol);

		lvcol.cx = 150;
		lvcol.fmt = LVCFMT_LEFT;
		lvcol.pszText = TEXT("名称");
		lvcol.iSubItem = 2;
		ListView_InsertColumn(hList, 2, &lvcol);

		mon_max = GetSystemMetrics(SM_CMONITORS);	//	モニター数取得
		for (n = 0; n < mon_max; n++){
			_stprintf_s(buf, sizeof(buf) / sizeof(buf[0]), _TEXT("モニター%d"), n);
			lvcol.cx = 120;
			lvcol.fmt = LVCFMT_LEFT;
			lvcol.pszText = buf;
			lvcol.iSubItem = 3 + n;
			ListView_InsertColumn(hList, 3 + n, &lvcol);
		}
		lvcol.cx = 0;
		lvcol.fmt = LVCFMT_LEFT;
		lvcol.pszText = buf;
		lvcol.iSubItem = 3 + n+1;
		ListView_InsertColumn(hList, 3 + n+1, &lvcol);
		set_lvs(hList);
		list2fit(hWnd,hList);

		break; 
	}
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDM_STD_RES:
			SendMessage(hWnd,WM_DPICHANGED,MAKEWPARAM(96,96),0);
			break;
		case IDM_HI_RES:
			SendMessage(hWnd,WM_DPICHANGED,MAKEWPARAM(192,192),0);
			break;
		}
		break;
		case WM_NCCREATE: {	//	Windows 10 Version 1607のみ呼び出す
		if (NcDpifunc)
			(*NcDpifunc)(hWnd);
		return DefWindowProc(hWnd, msg, wParam, lParam);
		}
	case WM_DPICHANGED:{
		nowDPIx = LOWORD(wParam);
		nowDPIy = HIWORD(wParam);
		nowScale = MulDiv(nowDPIx, 100, 96);

		HDC hdc = GetDC(hList);

		TEXTMETRIC metrics;
		GetTextMetrics(hdc, &metrics);
		height = metrics.tmHeight;
		LOGFONT lf;
		memset(&lf, 0, sizeof(lf));
		height = 96 * txtPt / 72;
		lf.lfHeight = height *nowScale;
		lf.lfHeight /= 100;
		HFONT hFont2 = CreateFontIndirect(&lf);
		ReleaseDC(hList, hdc);
		SendMessage(hList, WM_SETFONT, (WPARAM)hFont2, 0);

		InvalidateRect(hWnd, NULL, FALSE);
		UpdateWindow(hWnd);

		DeleteObject(hFont);
		hFont = hFont2;

		list2fit(hWnd,hList);

		break;
	}
	case WM_SIZE:
		MoveWindow(hList, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		break;
	case WM_DESTROY:
			if (func){
				FreeLibrary(hMod);
				hMod = 0;
				func = 0;
			}
			DeleteObject(hFont);
		PostQuitMessage(0);
		break; 
	default:
		return(DefWindowProc(hWnd, msg, wParam, lParam));
	}
	return (0L);
}

//	リストビューの表示内容を設定

int set_lvs(HWND hList){
	LV_ITEM item;
	HRESULT hres;
	TCHAR buf[128];
	// COM の実行環境を作成
	hres =  CoInitializeEx(0, COINIT_MULTITHREADED); 
	if (FAILED(hres))
		return 1;

	// COMのセキュリティの設定

	hres =  CoInitializeSecurity(NULL,-1,NULL,NULL,RPC_C_AUTHN_LEVEL_DEFAULT,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE,NULL);
	if (FAILED(hres)){
		CoUninitialize();
		return 1;
	}

	// IWbemLocator インターフェースを取得(WMI クライアント)

	IWbemLocator *pLoc = NULL;

	hres = CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER,IID_IWbemLocator, (LPVOID *) &pLoc);
	if(FAILED(hres)){
		CoUninitialize();
		return 1;
	}

	// 指定したnamespaceでWMIに接続

	IWbemServices *pSvc = NULL;
 
	hres = pLoc->ConnectServer(_bstr_t(L"root¥¥WMI"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
	
	if(FAILED(hres)){
		pLoc->Release();
		CoUninitialize();
		return 1;
	}

	// セキュリティの設定
	hres = CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL,RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE);

	if(FAILED(hres)){
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		return 1;
	}

	// WMIクラスの取得
	IEnumWbemClassObject* pEnumerator = NULL;
	hres = pSvc->ExecQuery( bstr_t("WQL"),bstr_t("Select * From WmiMonitorID"),WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,NULL,&pEnumerator);
	if(FAILED(hres)){
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		return 1;
	}

	// プロパティを検索
 
	IWbemClassObject *pclsObj = NULL;
	ULONG uReturn = 0;
	WCHAR as[64];

	item.mask = LVIF_TEXT  | LVIF_PARAM;
	item.pszText = L"root¥¥WMI";
	item.iItem = 0;
	item.iSubItem = 0;
	item.lParam = 0;
	ListView_InsertItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"WmiMonitorID";
	item.iItem = 0;
	item.iSubItem = 1;
	ListView_SetItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"UserFriendlyName";
	item.iItem = 0;
	item.iSubItem = 2;
	ListView_SetItem(hList, &item);

	item.mask = LVIF_TEXT  | LVIF_PARAM;
	item.pszText = L"root¥¥WMI";
	item.iItem = 1;
	item.iSubItem = 0;
	item.lParam = 0;
	ListView_InsertItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"WmiMonitorID";
	item.iItem = 1;
	item.iSubItem = 1;
	ListView_SetItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"ManufacturerName";
	item.iItem = 1;
	item.iSubItem = 2;
	ListView_SetItem(hList, &item);

	int mon_num=0;
	while (pEnumerator){
		HRESULT hr = pEnumerator->Next(WBEM_INFINITE,1,&pclsObj, &uReturn);
		if(0 == uReturn)
			break;
		VARIANT vtProp;
		WCHAR* p;
		hr = pclsObj->Get(p=L"UserFriendlyName", 0, &vtProp, 0, 0);
		if (vtProp.vt != VARENUM::VT_NULL){
			if (vtProp.vt & VT_ARRAY){
				safearrayi4_str(vtProp, as, sizeof(as) / sizeof(as[0]) - 1);
				item.mask = LVIF_TEXT;
				item.pszText = as;
				item.iItem = 0;
				item.iSubItem = 3+mon_num;
				ListView_SetItem(hList, &item);
			}
		}
		VariantClear(&vtProp);
		hr = pclsObj->Get(p=L"ManufacturerName", 0, &vtProp, 0, 0);
		if (vtProp.vt != VARENUM::VT_NULL){
			if (vtProp.vt & VT_ARRAY){
				safearrayi4_str(vtProp, as, sizeof(as) / sizeof(as[0]) - 1);
				item.mask = LVIF_TEXT;
				item.pszText = as;
				item.iItem = 1;
				item.iSubItem = 3+mon_num;
				ListView_SetItem(hList, &item);
			}
		}
		VariantClear(&vtProp);
		pclsObj->Release();
		++mon_num;
	}
	pEnumerator->Release();

	// WMIクラスの取得
	pEnumerator = NULL;
	hres = pSvc->ExecQuery( bstr_t("WQL"),bstr_t("Select * From WmiMonitorBasicDisplayParams"),WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,NULL,&pEnumerator);
	if (FAILED(hres)){
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		return 1;
	}

	pclsObj = NULL;
	uReturn = 0;

	item.mask = LVIF_TEXT  | LVIF_PARAM;
	item.pszText = L"root¥¥WMI";
	item.iItem = 2;
	item.iSubItem = 0;
	item.lParam = 0;
	ListView_InsertItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"WmiMonitorBasicDisplayParams";
	item.iItem = 2;
	item.iSubItem = 1;
	ListView_SetItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"MaxHorizontalImageSize";
	item.iItem = 2;
	item.iSubItem = 2;
	ListView_SetItem(hList, &item);

	item.mask = LVIF_TEXT  | LVIF_PARAM;
	item.pszText = L"root¥¥WMI";
	item.iItem = 3;
	item.iSubItem = 0;
	item.lParam = 0;
	ListView_InsertItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"WmiMonitorBasicDisplayParams";
	item.iItem = 3;
	item.iSubItem = 1;
	ListView_SetItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"MaxVerticalImageSize";
	item.iItem = 3;
	item.iSubItem = 2;
	ListView_SetItem(hList, &item);

	item.mask = LVIF_TEXT  | LVIF_PARAM;
	item.pszText = L"root¥¥WMI";
	item.iItem = 4;
	item.iSubItem = 0;
	item.lParam = 0;
	ListView_InsertItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"WmiMonitorBasicDisplayParams";
	item.iItem = 4;
	item.iSubItem = 1;
	ListView_SetItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"VideoInputType";
	item.iItem = 4;
	item.iSubItem = 2;
	ListView_SetItem(hList, &item);

	mon_num=0;
	while (pEnumerator){
		HRESULT hr = pEnumerator->Next(WBEM_INFINITE,1,&pclsObj, &uReturn);
		if(0 == uReturn)
			break;
		VARIANT vtProp;
		WCHAR* p;

		hr = pclsObj->Get(p=L"MaxHorizontalImageSize", 0, &vtProp, 0, 0);
		if(hr==S_OK){
			UINT b=0xff;
			if (vtProp.vt == VARENUM::VT_UI1)
				b = vtProp.bVal;
			_stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),_TEXT("%d"),b);
			item.mask = LVIF_TEXT;
			item.pszText = buf;
			item.iItem = 2;
			item.iSubItem = 3+mon_num;
			ListView_SetItem(hList, &item);
		}
		VariantClear(&vtProp);

		hr = pclsObj->Get(p=L"MaxVerticalImageSize", 0, &vtProp, 0, 0);
		if(hr==S_OK){
			UINT b=0xff;
			if (vtProp.vt == VARENUM::VT_UI1)
				b = vtProp.bVal;
			_stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),_TEXT("%d"),b);
			item.mask = LVIF_TEXT;
			item.pszText = buf;
			item.iItem = 3;
			item.iSubItem = 3+mon_num;
			ListView_SetItem(hList, &item);
		}
		VariantClear(&vtProp);

		hr = pclsObj->Get(p=L"VideoInputType", 0, &vtProp, 0, 0);
		if(hr==S_OK){
			UINT b=0xff;
			if(vtProp.vt == VARENUM::VT_UI1)
				b = vtProp.bVal;
			TCHAR* t=L"不明";
			switch(b){
				case 0:
					t=L"アナログ";
					break;
				case 1:
					t=L"デジタル";
					break;
			}
			item.mask = LVIF_TEXT;
			item.pszText = t;
			item.iItem = 4;
			item.iSubItem = 3+mon_num;
			ListView_SetItem(hList, &item);
		}
		VariantClear(&vtProp);
		pclsObj->Release();
		++mon_num;
	}
	pEnumerator->Release();

	pEnumerator = NULL;
	hres = pSvc->ExecQuery( bstr_t("WQL"),bstr_t("Select * From WmiMonitorConnectionParams"),WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,NULL,&pEnumerator);
	if (FAILED(hres)){
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		return 1;
	}

	// プロパティを検索
 
	pclsObj = NULL;
	uReturn = 0;
  	item.mask = LVIF_TEXT  | LVIF_PARAM;
	item.pszText = L"root¥¥WMI";
	item.iItem = 5;
	item.iSubItem = 0;
	item.lParam = 0;
	ListView_InsertItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"WmiMonitorConnectionParams";
	item.iItem = 5;
	item.iSubItem = 1;
	ListView_SetItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"VideoOutputTechnology";
	item.iItem = 5;
	item.iSubItem = 2;
	ListView_SetItem(hList, &item);

 	mon_num=0;
	while (pEnumerator){
		HRESULT hr = pEnumerator->Next(WBEM_INFINITE,1,&pclsObj, &uReturn);
		if(0 == uReturn)
			break;
		VARIANT vtProp;
		TCHAR* p;
		hr = pclsObj->Get(p=L"VideoOutputTechnology", 0, &vtProp, 0, 0);
		if(hr == S_OK){
			INT b;
			if (vtProp.vt == VARENUM::VT_I4)
				b = vtProp.intVal;
			WCHAR* t = video_out_type_get(b);
			item.mask = LVIF_TEXT;
			item.pszText = t;
			item.iItem = 5;
			item.iSubItem = 3+mon_num;
			ListView_SetItem(hList, &item);
		}
		VariantClear(&vtProp);

		pclsObj->Release();
		++mon_num;
	}
	// メモリの解放
	
	pSvc->Release();
	pEnumerator->Release();

	// 指定したnamespaceでWMIに接続

	pSvc = NULL;
 
	hres = pLoc->ConnectServer(_bstr_t(L"root¥¥CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
	
	if (FAILED(hres)){
			pLoc->Release();
		CoUninitialize();
		return 1;
	}

	// セキュリティの設定
	hres = CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL,RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE);

	if (FAILED(hres)){
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		return 1;
	}

	// WMIクラスの取得
	pEnumerator = NULL;
	hres = pSvc->ExecQuery( bstr_t("WQL"),bstr_t("Select * From Win32_PnPEntity WHERE Service=¥"monitor¥""),WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,NULL,&pEnumerator);
	if (FAILED(hres)){
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		return 1;
	}

	// プロパティを検索
 
	pclsObj = NULL;
	uReturn = 0;
  	item.mask = LVIF_TEXT  | LVIF_PARAM;
	item.pszText = L"root¥¥CIMV2";
	item.iItem =6;
	item.iSubItem = 0;
	item.lParam = 0;
	ListView_InsertItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"Win32_PnPEntity WHERE Service=¥"monitor¥"";
	item.iItem = 6;
	item.iSubItem = 1;
	ListView_SetItem(hList, &item);

	item.mask = LVIF_TEXT;
	item.pszText = L"Name";
	item.iItem = 6;
	item.iSubItem = 2;
	ListView_SetItem(hList, &item);

 	mon_num=0;
	while (pEnumerator){
		HRESULT hr = pEnumerator->Next(WBEM_INFINITE,1,&pclsObj, &uReturn);
		if(0 == uReturn)
			break;
		VARIANT vtProp;
		hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
		item.mask = LVIF_TEXT;
		item.pszText = vtProp.bstrVal;
		item.iItem = 6;
		item.iSubItem = 3+mon_num;
		ListView_SetItem(hList, &item);
		VariantClear(&vtProp);
		pclsObj->Release();
		++mon_num;
	}

	// メモリの解放
	
	pSvc->Release();
	pLoc->Release();
	pEnumerator->Release();
	CoUninitialize();

	return 0;
}

// int型のSAFEARRAYから文字列を取り出す。

WCHAR* safearrayi4_str(VARIANT& v, WCHAR* str, int len){
	int *i4;
	SafeArrayAccessData(v.parray, (void**)&i4);

	int i;
	for (i = 0; i4[i] && i<len-1; i++)
		str[i] = i4[i];
	str[i] = L'¥0';

	SafeArrayUnaccessData(v.parray);
	return str;
}


typedef enum _D3DKMDT_VIDEO_OUTPUT_TECHNOLOGY{
	D3DKMDT_VOT_UNINITIALIZED = -2,
	D3DKMDT_VOT_OTHER = -1,
	D3DKMDT_VOT_HD15 = 0,
	D3DKMDT_VOT_SVIDEO = 1,
	D3DKMDT_VOT_COMPOSITE_VIDEO = 2,
	D3DKMDT_VOT_COMPONENT_VIDEO = 3,
	D3DKMDT_VOT_DVI = 4,
	D3DKMDT_VOT_HDMI = 5,
	D3DKMDT_VOT_LVDS = 6,
	D3DKMDT_VOT_D_JPN = 8,
	D3DKMDT_VOT_SDI = 9,
	D3DKMDT_VOT_DISPLAYPORT_EXTERNAL = 10,
	D3DKMDT_VOT_DISPLAYPORT_EMBEDDED = 11,
	D3DKMDT_VOT_UDI_EXTERNAL = 12,
	D3DKMDT_VOT_UDI_EMBEDDED = 13,
	D3DKMDT_VOT_SDTVDONGLE = 14,
	D3DKMDT_VOT_MIRACAST = 15,
	D3DKMDT_VOT_INTERNAL = 0x80000000,

	D3DKMDT_VOT_SVIDEO_4PIN = D3DKMDT_VOT_SVIDEO,
	D3DKMDT_VOT_SVIDEO_7PIN = D3DKMDT_VOT_SVIDEO,
	D3DKMDT_VOT_RF = D3DKMDT_VOT_COMPOSITE_VIDEO,
	D3DKMDT_VOT_RCA_3COMPONENT = D3DKMDT_VOT_COMPONENT_VIDEO,
	D3DKMDT_VOT_BNC = D3DKMDT_VOT_COMPONENT_VIDEO,
}
D3DKMDT_VIDEO_OUTPUT_TECHNOLOGY;

struct VIDEO_OUT_TYPE{
	INT i;
	WCHAR* l;
};

VIDEO_OUT_TYPE video_out_type[] = {
	{ D3DKMDT_VOT_OTHER, L"OTHER" },
	{ D3DKMDT_VOT_HD15, L"アナログRGB" },
	{ D3DKMDT_VOT_SVIDEO, L"SVIDEO" },
	{ D3DKMDT_VOT_COMPOSITE_VIDEO, L"COMPOSITE_VIDEO" },
	{ D3DKMDT_VOT_COMPONENT_VIDEO, L"COMPONENT_VIDEO" },
	{ D3DKMDT_VOT_DVI, L"DVI" },
	{ D3DKMDT_VOT_HDMI, L"HDMI" },
	{ D3DKMDT_VOT_LVDS, L"LVDS" },
	{ D3DKMDT_VOT_D_JPN, L"D_JPN" },
	{ D3DKMDT_VOT_SDI, L"SDI" },
	{ D3DKMDT_VOT_DISPLAYPORT_EXTERNAL, L"DISPLAYPORT_EXTERNAL" },
	{ D3DKMDT_VOT_DISPLAYPORT_EMBEDDED, L"DISPLAYPORT_EMBEDDED" },
	{ D3DKMDT_VOT_UDI_EXTERNAL, L"UDI_EXTERNAL" },
	{ D3DKMDT_VOT_UDI_EMBEDDED, L"UDI_EMBEDDED" },
	{ D3DKMDT_VOT_SDTVDONGLE, L"SDTVDONGLE" },
	{ D3DKMDT_VOT_MIRACAST, L"MIRACAST" },
	{ D3DKMDT_VOT_INTERNAL, L"INTERNAL" },
	{ D3DKMDT_VOT_SVIDEO_4PIN, L"SVIDEO_4PIN" },
	{ D3DKMDT_VOT_SVIDEO_7PIN, L"SVIDEO_7PIN" },
	{ D3DKMDT_VOT_RF, L"RF" },
	{ D3DKMDT_VOT_RCA_3COMPONENT, L"RCA_3COMPONENT" },
	{ D3DKMDT_VOT_BNC, L"BNC" },
	{ 0, 0 } };


// D3DKMDT_VIDEO_OUTPUT_TECHNOLOGYの値に該当する文字列を検索する。

WCHAR* video_out_type_get(int x){
	VIDEO_OUT_TYPE* p = video_out_type;
	int n = 0;
	while (p[n].l){
		if (x == p[n].i)
			return p[n].l;
		++n;
	}
	return 0;
}


//	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;
}

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

ダウンロード gmonitorinfo.zip(55.3kByte)

ZIPファイルに含まれるファイル
gmonitorinfo.cpp
gmonitorinfo.exe