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 assocated progid:

    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

Popular posts from this blog

java - Jasper subreport showing only one entry from the JSON data source when embedded in the Title band -

serialization - Convert Any type in scala to Array[Byte] and back -

SonarQube Plugin for Jenkins does not find SonarQube Scanner executable -