Sökum þvílíks greinarleysis á þessu áhugamáli ákvað ég að skrifa “litla” grein um síðasta vandamál sem ég var að glíma við. Verkefnið var að finna þau icon sem windowsið notar í windows expolorer til að auðkenna ýmsar skrár. Auðvitað var tilgangurinn að búa til “file explorer” sem líkist honum.

Eitt vandamálið var að þær skrár sem ég þurfti að birta voru ekki til á tölvunni sem viðkomandi forrit er keyrt af, heldur á miðlægum server sem átt var samkipti við í gegnum web services. Ég fæ semsagt file lista frá web service sem ég þarf að birta í listview boxi með tilheyrandi iconum.

Ég komst að því að í registryinu undir HKEY_CLASSES_ROOT er að finna registry lykla sem heita þeim endingum sem windows þekkir. T.d. “.asp”, “.htm”, “.doc” og svo framvegis. Við nánari athugun á þessum lyklum kemur í ljós að gildið fyrir lykilinn er heiti skrártegundarinnar. Til dæmis undir “.asp” er gildið “aspfile”.

Neðar í HEKY_CLASSES_ROOT er svo að finna lykla sem innihalda þessi gildi, eins og “aspfile” og undir þeim lykli er annar lykill sem heitir “DefaultIcon” og gildið hans vísar í skrá sem geymir icon-ið sem windows notar til að auðkennar skránna. Fyrir aftan skráarnafnið kemur svo komma og númer, en það er resource-id-ið.

Þegar þetta er komið á hreint, þá er bara að skrifa lítinn klasa til að sækja þessi resource. Ég valdi mér auðvitað C# sem forritunarmál og hér kemur mín útfærsla á þessu.

Fyrsta var að gera sér grein fyrir hvaða reference þarf til að framkvæma þessar aðgerðir. Það er ljóst að við þurfum að reference-a System.Drawing.dll til að fá aðgang að Icon klasanum. Annað þurfum við ekki til viðbótar System.dll.

Það sem þarf að koma fram í using kaflanum, er eftirfarandi:
using System; // af augljósum ástæðum
using System.Drawing; // Geymir Icon klasann
using System.Runtime.InteropServices; // Til að geta kallað í windows api
using Microsoft.Win32; // Til að fá aðgang að registry-inu
using System.IO; // til að fá aðgang að skrárvinnslu klösum.

Því næst skilgreini ég namespace og enumerator til að skilgreina hvaða stærð af icon-i við viljum fá:
namespace abh.iconlibrary
{
    public enum ICON_SIZE
    {
        ICON_SMALL = 1,
        ICON_LARGE = 2
    }

Þá bý ég mér næst til klasann og skilgreini eitt external kall, sem sér um að sækja icon-ið frá ákveðnu library, hvort sem er exe eða dll skrá.
public class DefaultIcon
{
    [DllImport("shell32.dll", CharSet=CharSet.Auto)]
    private extern static int ExtractIconEx([MarshalAs(UnManagedType.LPTStr)] string sFilename, int iIndex, IntPtr[] piLargeVersion, IntPtr[] piSmallVersion, int nIcons);

Ég besta leiðin sem ég fann til að skilgreina slóðina á shell32.dll sem er staðsett í system32 möppunni undir windows möppunni var að búa til private strengja property. Það gerði ég svona:

private string _shell32Location
{
    get { return Environment.SystemDirectory + “\\shell32.dll”; }
}

Svo skilgreini ég breytu sem geymir upplýsingar um hvort notandi vilji lítið eða stórt icon.

private ICON_SIZE _iconSize = ICON_SIZE.ICON_SMALL;

Þarna sést að default set ég stærðina í lítið icon. Þetta er reyndar óþarfi þar sem með minni útfærslu getur notandinn ekki stofnað klasann án þess að skilgreina stærðina. En það er aldrei að vita nema maður búa til constructor sem þarf ekki á því að halda og þá kemur þetta default value til góða. En það er allavega ljóst að við viljum búa til property svo notandi geti skipt um skoðun.

public ICON_SIZE IconSize
{
    get { return _iconSize; }
    set { _iconSize = value; }
}

Þá er kominn tími til að búa constructor fyrir klasann.

public DefaultIcon(ICON_SIZE iconSize)
{
    _iconSize = iconSize;
}

Því næst búum við til fall sem extract-ar iconið úr library-inu.

public Icon LoadIconFromLibrary(string sFilename, int iIconIndex)
{
    // Fyrst athuga hvort skráin er til eða ekki
    // ef hún er ekki til, þá skila default iconi.
    if (!File.Exists(sFilename))
        return DefaultUnknownIcon();

    // Búa til pointera til að taka við iconum
    IntPtr[] ipLarge, ipSmall;

    // Því næst athuga ég hversu mörg icon eru í library-inu
    int iIconCount = ExtractIconEx(sFilename, 1, null, null, 0);
    if (iIconCount > 0)
    {
        // Stofna fylkin
        ipLarge = new IntPtr[iIconCount];
        ipSmall = new IntPtr[iIconCount];
        // Sækja svo iconin
        ExtractIconEx(sFilename, iIconIndex, ipLarge, ipSmall, 1);
        // Svo skila til baka litlu eða stóri iconi
        if (_iconSize == ICON_SIZE.ICON_SMALL)
            return Icon.FromHandle(piSmall);
        return Icon.FromHandle(piLarge);
    }
    // Skila null ef iIconCount er ekki stærra en 0
    return null;
}

Þetta fall þarf kannski að útskýra örlítið. Það fyrsta sem við sjáum er kall í fallið DefaultUnknownIcon(), þetta fall eigum við eftir að útfæra, en það mun hafa það hlutverk að skila til baka því icon-i sem windows notar á skrár sem það þekkir ekki.

Svo sjáum við tvö mismunandi köll í ExtractIconEx, þetta fall vorum við þegar búin að skilgreina sem external fall í shell32.dll. Þetta fall tekur við 5 parameterum, fyrsti parameterinn er augljóslega bara slóðin á library-ið sem við viljum fá iconið úr, næsti parameter er index á iconinum sem við viljum fá, næstu tveir parametrar eru fylki af IntPtr sem eiga að geyma pointer í iconin eftir extract, það fyrra fyrir stóra útgáfu af iconinu og seinna fyrir litla útgáfu. Síðasta fallið segir svo hversu mörg icon við viljum fá.

Ef báðir IntPtr-arnir eru null og síðasti parameter er 0, þá skilar fallið til baka fjölda icon-a í library-inu.

Annað í þessu falli ætti að vera nokkuð auðskilið.

Þá skulum við útfæra hitt fallið, eða DefaultUnknownIcon, þetta fall á bara að sækja það icon sem windows notar til að auðkenna skrár sem það þekkir ekki. Þetta icon er geymt í shell32.dll sem er í windows/system32 möppunni, og er fyrsta iconið.

public icon DefaultUnknownIcon()
{
    return LoadIconFromLibrary(_shell32Location, 0);
}

Ekki var þetta flókið kall. Eitt ber þó að varast og er rétt að benda á. Í þessu getur verið mjög hættulegur kóði sem veldur infinite loop. Við munum að það fyrsta sem LoadIconFromLibrary gerir er að athuga hvort library sé til eða ekki, ef það er ekki til þá kallar það í DefaultUnknownIcon(), sem aftur kallar á LoadIconFromLibrary. Þannig að ef _shell32Location inniheldur vísum í skrá sem er ekki til, þá er hér endalaus lúppa komin af stað. Þetta ætla ég ekki að laga :D

Þá er allur galdurinn við að sækja icon kominn. Ég set aðra grein inn fljótlega sem inniheldur eitt aukafall og það er til að finna icon-ið eftir endingu, það er að segja icon fyrir .txt skrár, .htm skrár og svo framvegis.

Höfundur tekur enga ábyrg á fyrrgreindum kóða eða aðferðum. Sömuleiðis er höfundur ekki ábyrgur fyrir hvort hugi ákveður að brengla greinina.