2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Windows アイコンオーバーレイの上限を超えたアイコンを表示する方法

Last updated at Posted at 2018-05-26

Tablacus Explorerとアドオンの「アイコンオーバーレイ」で実現したアイコンオーバーレイの上限を超えたアイコンを表示する方法を紹介します。

#大雑把に言うと
レジストリからアイコンオーバーレイハンドラーを取得してカスタムドローでアイコンの上にアイコンオーバーレイを表示させています。

#一般的なアイコンオーバーレイの表示方法

  1. IShellIconOverlay::GetOverlayIndexでオーバーレイインデックスを取得
  2. ImageList_Draw で fStyleに
    INDEXTOOVERLAYMASK
    を付けてアイコンを表示

このイメージリストImageList_SetOverlayImage で設定できるアイコンオーバーレイの数がVersion 4.70以前が4個、Version 4.71以降が15個となっていてここがアイコンオーバーレイの限界になっています。
最初からショートカットやファイル共有のアイコンオーバーレイもこの15個の中に入りますし、現在では一つのシェル拡張でも10近いアイコンオーバーレイが登録されるのであっという間に上限を超えます。

#解説
##1. レジストリからアイコンオーバーレイハンドラー(IShellIconOverlayIdentifier) を取得

ticonoverlay.cpp 700行付近

アイコンオーバーレイハンドラーのレジストリ(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers) をRegOpenKeyExRegEnumKeyEx で列挙する

RegQueryValueEx で個々のアイコンオーバーレイハンドラーのクラスIDの文字列を取得

CLSIDFromString で文字列からクラスIDに変換

CoCreateInstance でクラスIDを使用してアイコンオーバーレイハンドラー(IShellIconOverlayIdentifier) を取得

IShellIconOverlayIdentifier::GetOverlayInfo でアイコンが取得できることを確認してリストに積む

ticonoverlay.cpp
		//Init
		case 0x60010000:
			if (nArg >= 0) {
				DWORD dwIndex = GetIntFromVariant(&pDispParams->rgvarg[nArg]);
				for (int i = g_pIconOverlayHandlers.size(); i-- > 0;) {
					SafeRelease(&g_pIconOverlayHandlers[i]);
				}
				g_pIconOverlayHandlers.clear();
				TCHAR pszName[MAX_PATH * 2], pszClsid[MAX_PATH];
				DWORD dwNameSize = MAX_PATH;
				DWORD dwSize = sizeof(pszClsid);
				FILETIME ftLastWriteTime;
				HKEY hKey, hKey1;
				LPWSTR pszKey = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers";
				lstrcpy(pszName, pszKey);
				lstrcat(pszName, L"\\");
				int nName = lstrlen(pszName); 
				if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszKey, 0, KEY_ENUMERATE_SUB_KEYS, &hKey) == ERROR_SUCCESS) {
					LONG lRet;
					while ((lRet = RegEnumKeyEx(hKey, dwIndex++, &pszName[nName], &dwNameSize, NULL, NULL, NULL, &ftLastWriteTime)) != ERROR_NO_MORE_ITEMS) {
						if (lRet == ERROR_SUCCESS) {
							if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszName, 0, KEY_READ, &hKey1) == ERROR_SUCCESS) {
								if (RegQueryValueEx(hKey1, NULL, NULL, NULL, (LPBYTE)&pszClsid, &dwSize) == ERROR_SUCCESS) {
									CLSID clsid;
									if SUCCEEDED(CLSIDFromString(pszClsid, &clsid)) {
										IShellIconOverlayIdentifier *psio;
										if SUCCEEDED(CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psio))) {
											TCHAR pszIconFile[MAX_PATH * 2];
											int iIndex;
											DWORD dwFlags;
											if (psio->GetOverlayInfo(pszIconFile, sizeof(pszIconFile), &iIndex, &dwFlags) == S_OK) {
												g_pIconOverlayHandlers.push_back(psio);
											}
										}
									}
								}
								RegCloseKey(hKey1);
							}
							dwNameSize = MAX_PATH;
						}
					}
					RegCloseKey(hKey);
				}					
			}
			return S_OK;

##2. アイコンオーバーレイハンドラーからアイコンを取得して、画像でリストに保存

script.js 25行付近、ticonoverlay.cpp 743行-772行

IShellIconOverlayIdentifier::GetOverlayInfo で個々のアイコン情報を取得して画像でリストに保存

script.js
		for (var i = 0, o = {}; Addons.IconOverlay.DLL.GetOverlayInfo(i, o) == S_OK; i++) {
			Addons.IconOverlay.Icon[i] = [MakeImgData("icon:" + o.IconFile + (o.dwFlags == 1 ? "" : "," + o.Index), 0, 16), o.dwFlags == 1 ? api.CreateObject("WICBitmap").FromFile(o.IconFile) : MakeImgData("icon:" + o.IconFile + "," + o.Index, 0, 32)];
}
ticonoverlay.cpp
		//GetOverlayInfo
		case 0x60010001:
			HRESULT hr;
			hr = E_FAIL;
			if (nArg >= 1) {
				DWORD dwIndex = GetIntFromVariant(&pDispParams->rgvarg[nArg]);
				IUnknown *punk;
				if (FindUnknown(&pDispParams->rgvarg[nArg - 1], &punk)) {
					TCHAR pszIconFile[MAX_PATH * 2];
					int iIndex;
					DWORD dwFlags;
					if (dwIndex < g_pIconOverlayHandlers.size()) {
						hr = g_pIconOverlayHandlers[dwIndex]->GetOverlayInfo(pszIconFile, sizeof(pszIconFile), &iIndex, &dwFlags);
						if SUCCEEDED(hr) {
							VARIANT v;
							teSetSZ(&v, pszIconFile);
							tePutProperty(punk, L"IconFile", &v);
							VariantClear(&v);
							teSetLong(&v, iIndex);
							tePutProperty(punk, L"Index", &v);
							VariantClear(&v);
							teSetLong(&v, dwFlags);
							tePutProperty(punk, L"dwFlags", &v);
							VariantClear(&v);
						}
					}
				}
			}
			teSetLong(pVarResult, hr);
			return S_OK;

##3. カスタムドローでアイコンオーバーレイを表示

script.js 33行付近、ticonoverlay.cpp 773行-788行

カスタムドローのCDDS_ITEMPOSTPAINT(既定の描画後)

積まれたリストのアイコンオーバーレイハンドラーを使い IShellIconOverlayIdentifier::IsMemberOf で表示項目がアイコン表示するかチェックし、表示するのであれば 2. で用意したアイコン画像を表示する

script.js
			AddEvent("ItemPostPaint2", function (Ctrl, pid, nmcd, vcd)
			{
				var hList = Ctrl.hwndList;
				if (hList && pid && Addons.IconOverlay.DLL) {
					var db = Addons.IconOverlay.FV[Ctrl.Id];
					if (!db) {
						db = {};
						Addons.IconOverlay.FV[Ctrl.Id] = db;
					}
					var i = db[pid.Path];
					if (isNaN(i)) {
						i = Addons.IconOverlay.DLL.GetOverlayIconIndex(pid.Path, api.GetAttributesOf(pid, SFGAO_STORAGECAPMASK));
						db[pid.Path] = i;
					}
					var icon = Addons.IconOverlay.Icon[i];
					if (icon) {
						var image = icon[Ctrl.IconSize < 32 ? 0 : 1];
						var rc = api.Memory("RECT");
						rc.Left = LVIR_ICON;
						api.SendMessage(hList, LVM_GETITEMRECT, nmcd.dwItemSpec, rc);
						var w = Ctrl.IconSize * screen.logicalYDPI / 96;
						image = GetThumbnail(image, Ctrl.IconSize < 32 ? w : (w * 5 / 16) + 16);
						var x = api.SendMessage(hList, LVM_GETVIEW, 0, 0) == 2 ? rc.Right - w : rc.Left + (rc.Right - rc.Left - w) / 2;
						var y = rc.Bottom - image.GetHeight() - 2;
						if (image) {
							image.DrawEx(nmcd.hdc, x, y, 0, 0, CLR_NONE, CLR_NONE, ILD_NORMAL);
						}
					}
				}
});
ticonoverlay.cpp
		//GetOverlayIconIndex
		case 0x60010002:
			int iResult;
			iResult = -1;
			if (nArg >= 1) {
				BSTR bsPath = GetLPWSTRFromVariant(&pDispParams->rgvarg[nArg]);
				DWORD dwAttrib = GetIntFromVariant(&pDispParams->rgvarg[nArg - 1]);
				for (UINT i = 0; i < g_pIconOverlayHandlers.size(); i++) {
					if (g_pIconOverlayHandlers[i]->IsMemberOf(bsPath, dwAttrib) == S_OK) {
						iResult = i;
						break;
					}
				}
			}
			teSetLong(pVarResult, iResult);
			return S_OK;

#参考
EternalWindows / シェル拡張 / アイコン オーバーレイ

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?