鉴于文件类型(例如.txt
),我如何获得:
到文件类型的相关图标路径和索引,例如:
我想将.txt
转换为:
有了这些信息,我就可以提取图标(例如使用SHDefExtractIcon
)。
Windows中的每种类型的文件都在注册表中注册。当图标与文件关联时,会将其指定为包含图标的文件的路径,以及图标资源的索引(如果索引为负,则指定为资源ID)。
以.txt
文件为例,关联的 DefaultIcon 为:
%SystemRoot%\system32\imageres.dll,-102
首先是Win API函数ExtractAssociatedIcon:
检索文件中找到的索引图标的句柄或相关可执行文件中的图标。
这个想法是你传递路径和索引,然后它会为你获取图标:
String iconPath = "%SystemRoot%\system32\imageres.dll";
Word iIcon = -102;
HICON ico = ExtractAssociatedIcon(0, iconPath, iIcon);
仅当您已经知道所需图标的路径和索引时才有效。
幸运的是, ExtractAssociatedIcon 还可以告诉您您的图标文件的路径和索引:
如果该函数无法从该文件获取图标句柄,并且该文件具有关联的可执行文件,则会在该可执行文件中查找图标。
在这种情况下正确调用函数有点棘手,因为它会修改你提供的缓冲区(如果你没有将缓冲区填充到足够长的时间,导致缓冲区溢出):
String iconPath = "C:\Example.txt" + StringOfChar(\0, 32767); //pad the InOut buffer
Word iIcon = 0;
HICON ico = ExtractAssociatedIcon(0, iconPath, iIcon);
DestroyIcon(ico);
当函数返回时:
%SystemRoot%\system32\imageres.dll
为什么我没有使用ExtractAssociatedIcon返回的HICON
?因为ExtratAssociatedIcon不允许我指定我想要的Icon的大小。它返回" Shell大图标"就是这样。
此外,ExtractAssociatedIcon可以执行它的唯一方法是按文件类型查找,即文件实际是否存在。如果指定的文件不存在(它没有 - 因为没有foo.txt
),则该函数失败。
输入SHDefExtractIcon。它is able to extract any size of icon i want,你只需要传递图标资源的路径和索引:
String iconFile = "%SystemRoot%\system32\imageres.dll";
Int32 iIndex = -102;
HICON hLargeIcon;
if (SHDefExtractIcon(iconFile, iIndex, 0, out hLargeIcon, null, 256) == S_OK)
return hLargeIcon
唯一的问题是我必须为文件类型获取关联的路径和索引。与 ExtractAssociatedIcon 不同, SHDefExtractIcon 不会为您执行英雄查找。
为此我必须自己进行查找;这是我的问题。
我的第一次尝试是read the contract of file associations from the other side。我知道如何注册默认图标,我可以反过来。
将.ext
转换为关联的ProgID
:
HKEY_CLASSES_ROOT/.ext
(default) = [ProgID]
在DefaultIcon
[ProgID]
HKEY_CLASSES_ROOT/[progID]/DefaultIcon
(default) = [path],[index]
就我而言:
HKEY_CLASSES_ROOT/.txt
(default) = txtfile
HEKY_CLASSES_ROOT/txtfile/DefaultIcon
(default) = "%SystemRoot%\system32\imageres.dll,-102"
这是this accepted Stackoverflow背后的代码使用的方法回答同一个问题:
假设存在ext,并且progID存在,并且DefaultIcon存在,并且路径存在,并且我可以解析路径,则它是不正确的不支持的答案。有一些边缘情况,接受的代码不处理 1 。
我希望Windows API 支持的方式执行从.ext
到
有一个方便的功能SHGetFileInfo。它很方便,因为the filename doesn't need to actually exist。如果你传递SHGFI_USEFILEATTRIBUTES
标志,则表示:
不要访问磁盘。假设文件/目录存在,并且其文件属性是我作为dwFileAttributes参数传递的内容。无论它是否确实存在,都要这样做。
这很好:
SHELLFILEINFO sfi;
DWORD res = SHGetFileInfo("foo.txt",
FILE_ATTRIBUTE_NORMAL,
ref shellFileInfo,
sizeof(shellFileInfo),
SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SHELLICONSIZE | SHGFI_USEFILEATTRIBUTES);
if (res <> 0)
return shellFileInfo.hIcon;
唯一的问题是我无法指定我想要的图标大小。我仅限于 shell 决定它想要使用的图标大小。
IExtractImage很不错:
不幸的是,它需要一个实际存在的文件(它必须是shell命名空间中存在的东西)。当我有文件类型时,我不能使用 IExtractImage
IThumbnailProvider是 IExtractImage 的现代替代品:
Windows Vista IThumbnailProivder是Vista的新功能,取代了IExtractImage。 Vista仍然支持IExtractImage,但缺乏返回图像类型(alpha或不是)的能力。
IThumbnailProvider还允许我提供所需的图标大小。优良!
IThumbnailProvider 通常需要在shell命名空间中存在一个文件。但这仅仅是因为Shell API是唯一受支持的方式来获取ahold(&#34; bind&#34;)到由文件类型公开的 IThumbnailProvider shell接口。
幸运的是,我可以执行上面使用的相同可怕的黑客攻击,并手动抓取注册表:
HKEY_CLASSES_ROOT/.ext/ShellEx/[InterfaceID]
(default) = [ClassID]
如果它不存在:
HKEY_CLASSES_ROOT/.ext
(default) = [ProgID]
HKEY_CLASSES_ROOT/[ProgID]/ShellEx/[InterfaceID]
(default) = [ClassID]
对于我的.avi
文件:
HKEY_CLASSES_ROOT/.avi/ShellEx/{e357fccd-a995-4576-b01f-234630154e96}
(default) = "{9DBD2C50-62AD-11D0-B806-00C04FD706EC}"
现在我带着CLSID
来参加比赛!
不幸的是,它会在那里结束,因为 IThumbnailProvider 需要一个文件。更确切地说,它需要IInitializeWithStream
。
我没有流。我没有档案。我只有文件类型的概念。
也许AssocQueryString可以帮助我?我实际上并不知道 - 它是一个功能的野兽。我无法做出正面或反面。
鉴于文件类型(例如&#34; x.txt&#34;),我如何获得相关图标:
所以我可以提取我想要的大小的图标(可能使用SHDefExtractIcon)?
1 exefile和%1
答案 0 :(得分:0)
答案是我非常接近:
SHGetFileInfo
标记的SHGFI_ICONLOCATION
获取默认图标路径和索引 SHDefExtractIcon
以所需尺寸路径,索引提取默认图标或以函数形式:
HICON GetFileTypeDefaultIcon(String filename, Int32 iconSizePx)
{
//Filename is anything like "a.txt", "foo.xml", "x.zip"
//The file doesn't have to exist, but it can't be an invalid
//filename (e.g. "???.txt" is no good)
//Use SHGetFileInfo to get the path and index of our file type's icon
SHFILEINFO sfi;
//SHGFI_IconLocation means get me the path and icon index
//SHGFI_UseFileAttributes means the file doesn't have to exist
DWORD_PTR res := SHGetFileInfo(
filename,
FILE_ATTRIBUTE_NORMAL,
ref sfi,
sizeof(sfi),
SHGFI_ICONLOCATION or SHGFI_USEFILEATTRIBUTES);
if (res = 0) //"nonzero if successful"
return 0;
//The path and index are stuffed into the ShellFileInfo structure
String iconPath := sfi.szDisplayName;
Int32 iconIndex := sfi.iIcon;
//Now that we know the path and index, we can use SHDefExtractIcon
HICON largeIcon;
iconSizePx = iconSizePx and 0xFFFF; //preferred large icon size is in LOWORD 16-bits
HRESULT hr := SHDefExtractIcon(iconPath, iconIndex, 0,
out largeIcon, null, iconSizePx);
if (hr <> S_OK)
return 0;
return largeIcon;
}
我也figured out that there is a 20 year old Windows function可以执行对shell扩展的注册表的爬网。它还处理我错过的案例 - 因为它是规范正确的方式。
仅记录和解释shell类:
文件类型包含 ShellEx 键,其中包含{guid}
个子键。每个{guid}
键代表一个特定的 InterfaceID 。
有许多标准shell接口可以与文件类型相关联:
{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}
IExtractImage {953BB1EE-93B4-11d1-98A3-00C04FB687DA}
IExtractImage2 {e357fccd-a995-4576-b01f-234630154e96}
IThumbnailProvider {8895b1c6-b41f-4c1c-a562-0d564250836f}
IPreviewHandler 如果我想查找与.jpg
文件相关联的 IThumbnailProvider 的 clsid ,我会查看:
HKEY_CLASSES_ROOT/.jpg/ShellEx/{e357fccd-a995-4576-b01f-234630154e96}
(default) = [clsid]
但那不是我唯一能看的地方。我也可以看看:
HKEY_CLASSES_ROOT/.jpg
(default) = jpgfile
HKEY_CLASSES_ROOT/jpgfile/ShellEx/{e357fccd-a995-4576-b01f-234630154e96}
(default) = [clsid]
但那不是我唯一能看的地方。我也可以看看:
HKEY_CLASSES_ROOT/SystemFileAssociations/.jpg/ShellEx/{e357fccd-a995-4576-b01f-234630154e96}
(default) = [clsid]
但那不是我唯一能看的地方。我也可以看看:
HKEY_CLASSES_ROOT/SystemFileAssociations/jpegfile/ShellEx/{e357fccd-a995-4576-b01f-234630154e96}
(default) = [clsid]
但那不是我唯一能看的地方。如果我认为该文件是图像,我也可以查看:
HKEY_CLASSES_ROOT/SystemFileAssociations/image/ShellEx/{e357fccd-a995-4576-b01f-234630154e96}
(default) = [clsid]
我是如何找到这些地点的?我是否只关注记录和支持的位置?不,我在探索资源管理器时使用了Process Monitor,因为它正在寻找 IThumbnailProvider 。
所以现在我想自己使用标准shell接口作为文件类型。这意味着我必须抓取位置。但是,为什么要以无证件,不受支持的方式抓取这些位置。为什么会在the guy之上从the thing引发愤怒?使用AssocQueryString:
Guid GetShellClassIDForFileType(String fileExtension, Guid interfaceID)
{
//E.g.:
// String fileExtension = ".jpg"
// Guid interfaceID = "{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}"; //IExtractImage
//The interface we're after - in string form
String szInterfaceID := GuidToString(interfaceID);
//Buffer to receive the clsid string
DWORD bufferSize := 1024; //more than enough to hold a 38-character clsid
SetLength(buffer, bufferSize);
HRESULT hr := AssocQueryString(
ASSOCF_INIT_DEFAULTTOSTAR,
ASSOCSTR_SHELLEXTENSION, //for finding shell extensions
fileExtension, //e.g. ".txt"
szInterfaceID, //e.g. "{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}"
buffer, //will receive the clsid string
@bufferSize);
if (hr <> S_OK)
return Guid.Empty;
Guid clsid;
HRESULT hr = CLSIDFromString(buffer, out clsid);
if (hr <> NOERROR)
return Guid.Empty;
return clsid;
}