windows - How to get the icon path and index associated with a file type? -
given file type (e.g. .txt
) how can the:
- path
- index
to file type's associated icon path , index, e.g.:
i want convert .txt
into:
- path: %systemroot%\system32\imageres.dll
- index: -102
with information can extract icon (e.g. using shdefextracticon
).
background
every type of file in windows registered in registry. when icon assocated file, specified path file contains icon, , index of icon resource (or resource id if index negative).
using .txt
file example, associated defaulticon is:
%systemroot%\system32\imageres.dll,-102
extractassociatedicon
first there win api function extractassociatedicon:
retrieves handle indexed icon found in file or icon found in associated executable file.
the idea pass path, , index, , go icon you:
string iconpath = "%systemroot%\system32\imageres.dll"; word iicon = -102; hicon ico = extractassociatedicon(0, iconpath, iicon);
that works when know path , index of icon want.
fortunately, extractassociatedicon able tell path , index icon's file:
if function cannot obtain icon handle file, , file has associated executable file, looks in executable file icon.
correctly calling function in case bit tricky, modify supplied buffer (causing buffer overruns if didn't pad buffer long enough):
string iconpath = "c:\example.txt" + stringofchar(\0, 32767); //pad inout buffer word iicon = 0; hicon ico = extractassociatedicon(0, iconpath, iicon); destroyicon(ico);
when function returns:
- iconpath:
%systemroot%\system32\imageres.dll
- iicon: -102
why didn't use hicon
returned extractassociatedicon? because extratassociatedicon doesn't allow me specify size of icon want. returns "shell large icon" , that's it.
also, way extractassociatedicon can perform it's heroic efforts of looking file type if file actually exists. if specified file not exist (which doesn't - since there no foo.txt
), function fails.
shdefextracticon
enter shdefextracticon. is able extract size of icon want, have pass path , index of icon resource:
string iconfile = "%systemroot%\system32\imageres.dll"; int32 iindex = -102; hicon hlargeicon; if (shdefextracticon(iconfile, iindex, 0, out hlargeicon, null, 256) == s_ok) return hlargeicon
the problem have associated path , index file type already. , shdefextracticon, unlike extractassociatedicon, not perform heroic lookup you.
for have perform lookup myself; question.
buggy registry spelunking
my first attempt read contract of file associations other side. know how default icons registered, , can go in reverse.
convert
.ext
assocatedprogid
:hkey_classes_root/.ext (default) = [progid]
lookup
defaulticon
under[progid]
hkey_classes_root/[progid]/defaulticon (default) = [path],[index]
in case:
hkey_classes_root/.txt (default) = txtfile heky_classes_root/txtfile/defaulticon (default) = "%systemroot%\system32\imageres.dll,-102"
this approach used code behind this accepted stackoverflow answer same question:
- convert ext progid
- lookup progid's defaulticon key
assuming ext exists, , progid exists, , defaulticon exists, , path exists, , can parse path, it's incorrect unsupported answer. there edge cases accepted code not handle1.
i'd windows api supported way perform mapping .ext
- path
- index
shgetfileinfo
there handy function shgetfileinfo. it's handy because the filename doesn't need actually exist. if pass shgfi_usefileattributes
flag, means:
do not access disk. pretend file/directory exists, , file attributes passed dwfileattributes parameter. regardless of whether exists or not.
this good:
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;
the problem cannot specify icon size want. limited sizes of icons shell decides it wants use.
iextractimage
iextractimage nice:
- it can return [path],[index] of associated icon
- i can specify desired size
unfortunately requires file exist (it has exists in shell namespace). when have file-type can't use iextractimage
ithumbnailprovider
ithumbnailprovider, introduced windows vista, modern replacement iextractimage:
windows vista ithumbnailproivder new vista , replaces iextractimage. vista still supports iextractimage lacks ability return image type (alpha or not).
ithumbnailprovider lets me supply desired icon size. excellent!
ithumbnailprovider requires file exist in shell namespace. that's because shell api supported way ahold ("bind") ithumbnailprovider shell interface exposed file type.
fortunately can perform same horrible hacks used above, , crawl registry manually:
hkey_classes_root/.ext/shellex/[interfaceid] (default) = [classid]
if doesn't exist:
hkey_classes_root/.ext (default) = [progid] hkey_classes_root/[progid]/shellex/[interfaceid] (default) = [classid]
in case of .avi
file:
hkey_classes_root/.avi/shellex/{e357fccd-a995-4576-b01f-234630154e96} (default) = "{9dbd2c50-62ad-11d0-b806-00c04fd706ec}"
and i'm off races clsid
!
unfortunately ends there, ithumbnailprovider requires file. more precisely, requires iinitializewithstream
.
i don't have stream. don't have file. have notion of file type.
assocquerystring
perhaps assocquerystring can me? don't know - it's beast of function. , can't make head nor tails of it.
the question
given file type (e.g. "x.txt"), how can associated icon:
- path
- index
so may extract icon of own desired size (likely using shdefextracticon)?
footnotes
1 exefile , %1
the answer close:
- use
shgetfileinfo
shgfi_iconlocation
flag default icon path , index - use
shdefextracticon
extract default icon @ path,index @ desired size
or in function form:
hicon getfiletypedefaulticon(string filename, int32 iconsizepx) { //filename "a.txt", "foo.xml", "x.zip" //the file doesn't have exist, can't invalid //filename (e.g. "???.txt" no good) //use shgetfileinfo path , index of our file type's icon shfileinfo sfi; //shgfi_iconlocation means me path , icon index //shgfi_usefileattributes means file doesn't have 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 , index stuffed shellfileinfo structure string iconpath := sfi.szdisplayname; int32 iconindex := sfi.iicon; //now know path , index, can use shdefextracticon hicon largeicon; iconsizepx = iconsizepx , 0xffff; //preferred large icon size in loword 16-bits hresult hr := shdefextracticon(iconpath, iconindex, 0, out largeicon, null, iconsizepx); if (hr <> s_ok) return 0; return largeicon; }
stop spellunking shell objects
i figured out there 20 year old windows function can perform crawling of registry shell extensions. handles cases missed - because canonically correct way.
just document , explain shell classes:
file types have shellex key, {guid}
subkeys. each {guid}
key represents particular interfaceid.
there number of standard shell interfaces can associated file type:
{bb2e617c-0920-11d1-9a0b-00c04fc2d6c1}
iextractimage{953bb1ee-93b4-11d1-98a3-00c04fb687da}
iextractimage2{e357fccd-a995-4576-b01f-234630154e96}
ithumbnailprovider{8895b1c6-b41f-4c1c-a562-0d564250836f}
ipreviewhandler
if want find, example, clsid of ithumbnailprovider associated .jpg
file, in:
hkey_classes_root/.jpg/shellex/{e357fccd-a995-4576-b01f-234630154e96} (default) = [clsid]
but that's not place look. can in:
hkey_classes_root/.jpg (default) = jpgfile hkey_classes_root/jpgfile/shellex/{e357fccd-a995-4576-b01f-234630154e96} (default) = [clsid]
but that's not place look. can in:
hkey_classes_root/systemfileassociations/.jpg/shellex/{e357fccd-a995-4576-b01f-234630154e96} (default) = [clsid]
but that's not place look. can in:
hkey_classes_root/systemfileassociations/jpegfile/shellex/{e357fccd-a995-4576-b01f-234630154e96} (default) = [clsid]
but that's not place look. if think file image, can in:
hkey_classes_root/systemfileassociations/image/shellex/{e357fccd-a995-4576-b01f-234630154e96} (default) = [clsid]
how did find these locations? did follow documented , supported locations? no, spied on explorer using process monitor went hunting ithumbnailprovider.
so want use standard shell interface file-type myself. means have crawl locations. why crawl these locations in undocumented, unsupported way. why incur wrath the guy high atop the thing? use 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 receive clsid string dword buffersize := 1024; //more enough hold 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 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; }
Comments
Post a Comment