iconcache.c 32 KB
Newer Older
1 2 3
/*
 *	shell icon cache (SIC)
 *
4 5 6 7 8 9 10 11 12 13 14 15 16 17
 * Copyright 1998, 1999 Juergen Schmied
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
 */
20

21
#include "config.h"
22
#include "wine/port.h"
23

24
#include <stdarg.h>
25
#include <string.h>
26
#include <sys/types.h>
27 28 29
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
30 31 32

#define COBJMACROS

33
#include "windef.h"
34
#include "winbase.h"
35
#include "wingdi.h"
36
#include "winuser.h"
37
#include "winreg.h"
38
#include "wine/debug.h"
39 40

#include "shellapi.h"
41
#include "objbase.h"
42 43
#include "pidl.h"
#include "shell32_main.h"
44
#include "undocshell.h"
45
#include "shresdef.h"
46

47
WINE_DEFAULT_DEBUG_CHANNEL(shell);
48 49 50

/********************** THE ICON CACHE ********************************/

51 52
#define INVALID_INDEX -1

53
typedef struct
54
{
55
	LPWSTR sSourceFile;	/* file (not path!) containing the icon */
56
	DWORD dwSourceIndex;	/* index within the file, if it is a resource ID it will be negated */
57
	DWORD dwListIndex;	/* index within the iconlist */
58 59
	DWORD dwFlags;		/* GIL_* flags */
	DWORD dwAccessTime;
60 61
} SIC_ENTRY, * LPSIC_ENTRY;

62 63
static HDPA sic_hdpa;
static INIT_ONCE sic_init_once = INIT_ONCE_STATIC_INIT;
64
static HIMAGELIST shell_imagelists[SHIL_LAST+1];
65 66 67 68 69 70

static CRITICAL_SECTION SHELL32_SicCS;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
    0, 0, &SHELL32_SicCS,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
71
      0, 0, { (DWORD_PTR)(__FILE__ ": SHELL32_SicCS") }
72 73
};
static CRITICAL_SECTION SHELL32_SicCS = { &critsect_debug, -1, 0, 0, 0, 0 };
74

75 76 77 78 79

static const WCHAR WindowMetrics[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l','\\','D','e','s','k','t','o','p','\\',
                                      'W','i','n','d','o','w','M','e','t','r','i','c','s',0};
static const WCHAR ShellIconSize[] = {'S','h','e','l','l',' ','I','c','o','n',' ','S','i','z','e',0};

80 81
#define SIC_COMPARE_LISTINDEX 1

82
/*****************************************************************************
83
 * SIC_CompareEntries
84 85 86 87
 *
 * NOTES
 *  Callback for DPA_Search
 */
88
static INT CALLBACK SIC_CompareEntries( LPVOID p1, LPVOID p2, LPARAM lparam)
89 90 91
{
        LPSIC_ENTRY e1 = p1, e2 = p2;

92 93 94 95 96 97
	TRACE("%p %p %8lx\n", p1, p2, lparam);

	/* Icons in the cache are keyed by the name of the file they are
	 * loaded from, their resource index and the fact if they have a shortcut
	 * icon overlay or not. 
	 */
98 99 100 101

        if (lparam & SIC_COMPARE_LISTINDEX)
            return e1->dwListIndex != e2->dwListIndex;

102 103
	if (e1->dwSourceIndex != e2->dwSourceIndex || /* first the faster one */
	    (e1->dwFlags & GIL_FORSHORTCUT) != (e2->dwFlags & GIL_FORSHORTCUT)) 
104 105
	  return 1;

106
	if (strcmpiW(e1->sSourceFile,e2->sSourceFile))
107 108
	  return 1;

109
	return 0;
110
}
111

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
/**************************************************************************************
 *                      SIC_get_location
 *
 * Returns the source file and resource index of an icon with the given imagelist index
 */
HRESULT SIC_get_location( int list_idx, WCHAR *file, DWORD *size, int *res_idx )
{
    SIC_ENTRY seek, *found;
    DWORD needed;
    HRESULT hr = E_INVALIDARG;
    int dpa_idx;

    seek.dwListIndex = list_idx;

    EnterCriticalSection( &SHELL32_SicCS );

    dpa_idx = DPA_Search( sic_hdpa, &seek, 0, SIC_CompareEntries, SIC_COMPARE_LISTINDEX, 0 );
    if (dpa_idx != -1)
    {
131
        found = DPA_GetPtr( sic_hdpa, dpa_idx );
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
        needed = (strlenW( found->sSourceFile ) + 1) * sizeof(WCHAR);
        if (needed <= *size)
        {
            memcpy( file, found->sSourceFile, needed );
            *res_idx = found->dwSourceIndex;
            hr = S_OK;
        }
        else
        {
            *size = needed;
            hr = E_NOT_SUFFICIENT_BUFFER;
        }
    }
    LeaveCriticalSection( &SHELL32_SicCS );

    return hr;
}

150
/* declare SIC_LoadOverlayIcon() */
151
static int SIC_LoadOverlayIcon(int icon_idx);
152

153 154 155 156
/*****************************************************************************
 * SIC_OverlayShortcutImage			[internal]
 *
 * NOTES
157
 *  Creates a new icon as a copy of the passed-in icon, overlaid with a
158 159
 *  shortcut image. 
 */
160 161 162
static HICON SIC_OverlayShortcutImage(HICON SourceIcon, int type)
{
    ICONINFO SourceIconInfo, ShortcutIconInfo, TargetIconInfo;
163 164 165 166 167 168 169 170 171 172
	HICON ShortcutIcon, TargetIcon;
	BITMAP SourceBitmapInfo, ShortcutBitmapInfo;
	HDC SourceDC = NULL,
	  ShortcutDC = NULL,
	  TargetDC = NULL,
	  ScreenDC = NULL;
	HBITMAP OldSourceBitmap = NULL,
	  OldShortcutBitmap = NULL,
	  OldTargetBitmap = NULL;

173 174
	static int s_imgListIdx = -1;

175 176 177 178 179 180
	/* Get information about the source icon and shortcut overlay */
	if (! GetIconInfo(SourceIcon, &SourceIconInfo)
	    || 0 == GetObjectW(SourceIconInfo.hbmColor, sizeof(BITMAP), &SourceBitmapInfo))
	{
	  return NULL;
	}
181 182 183

	/* search for the shortcut icon only once */
	if (s_imgListIdx == -1)
184 185 186 187
	    s_imgListIdx = SIC_LoadOverlayIcon(- IDI_SHELL_SHORTCUT);
                           /* FIXME should use icon index 29 instead of the
                              resource id, but not all icons are present yet
                              so we can't use icon indices */
188

189 190 191 192 193 194 195 196 197 198
    if (s_imgListIdx != -1)
        ShortcutIcon = ImageList_GetIcon(shell_imagelists[type], s_imgListIdx, ILD_TRANSPARENT);
    else
        ShortcutIcon = NULL;

    if (NULL == ShortcutIcon || ! GetIconInfo(ShortcutIcon, &ShortcutIconInfo)
            || 0 == GetObjectW(ShortcutIconInfo.hbmColor, sizeof(BITMAP), &ShortcutBitmapInfo))
    {
        return NULL;
    }
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

	TargetIconInfo = SourceIconInfo;
	TargetIconInfo.hbmMask = NULL;
	TargetIconInfo.hbmColor = NULL;

	/* Setup the source, shortcut and target masks */
	SourceDC = CreateCompatibleDC(NULL);
	if (NULL == SourceDC) goto fail;
	OldSourceBitmap = SelectObject(SourceDC, SourceIconInfo.hbmMask);
	if (NULL == OldSourceBitmap) goto fail;

	ShortcutDC = CreateCompatibleDC(NULL);
	if (NULL == ShortcutDC) goto fail;
	OldShortcutBitmap = SelectObject(ShortcutDC, ShortcutIconInfo.hbmMask);
	if (NULL == OldShortcutBitmap) goto fail;

	TargetDC = CreateCompatibleDC(NULL);
	if (NULL == TargetDC) goto fail;
	TargetIconInfo.hbmMask = CreateCompatibleBitmap(TargetDC, SourceBitmapInfo.bmWidth,
	                                                SourceBitmapInfo.bmHeight);
	if (NULL == TargetIconInfo.hbmMask) goto fail;
	ScreenDC = GetDC(NULL);
	if (NULL == ScreenDC) goto fail;
	TargetIconInfo.hbmColor = CreateCompatibleBitmap(ScreenDC, SourceBitmapInfo.bmWidth,
	                                                 SourceBitmapInfo.bmHeight);
	ReleaseDC(NULL, ScreenDC);
	if (NULL == TargetIconInfo.hbmColor) goto fail;
	OldTargetBitmap = SelectObject(TargetDC, TargetIconInfo.hbmMask);
	if (NULL == OldTargetBitmap) goto fail;

	/* Create the target mask by ANDing the source and shortcut masks */
	if (! BitBlt(TargetDC, 0, 0, SourceBitmapInfo.bmWidth, SourceBitmapInfo.bmHeight,
	             SourceDC, 0, 0, SRCCOPY) ||
	    ! BitBlt(TargetDC, 0, SourceBitmapInfo.bmHeight - ShortcutBitmapInfo.bmHeight,
	             ShortcutBitmapInfo.bmWidth, ShortcutBitmapInfo.bmHeight,
	             ShortcutDC, 0, 0, SRCAND))
	{
	  goto fail;
	}

	/* Setup the source and target xor bitmap */
	if (NULL == SelectObject(SourceDC, SourceIconInfo.hbmColor) ||
	    NULL == SelectObject(TargetDC, TargetIconInfo.hbmColor))
	{
	  goto fail;
	}

	/* Copy the source xor bitmap to the target and clear out part of it by using
	   the shortcut mask */
	if (! BitBlt(TargetDC, 0, 0, SourceBitmapInfo.bmWidth, SourceBitmapInfo.bmHeight,
	             SourceDC, 0, 0, SRCCOPY) ||
	    ! BitBlt(TargetDC, 0, SourceBitmapInfo.bmHeight - ShortcutBitmapInfo.bmHeight,
	             ShortcutBitmapInfo.bmWidth, ShortcutBitmapInfo.bmHeight,
	             ShortcutDC, 0, 0, SRCAND))
	{
	  goto fail;
	}

	if (NULL == SelectObject(ShortcutDC, ShortcutIconInfo.hbmColor)) goto fail;

	/* Now put in the shortcut xor mask */
	if (! BitBlt(TargetDC, 0, SourceBitmapInfo.bmHeight - ShortcutBitmapInfo.bmHeight,
	             ShortcutBitmapInfo.bmWidth, ShortcutBitmapInfo.bmHeight,
	             ShortcutDC, 0, 0, SRCINVERT))
	{
	  goto fail;
	}

	/* Clean up, we're not goto'ing to 'fail' after this so we can be lazy and not set
	   handles to NULL */
	SelectObject(TargetDC, OldTargetBitmap);
	DeleteObject(TargetDC);
	SelectObject(ShortcutDC, OldShortcutBitmap);
	DeleteObject(ShortcutDC);
	SelectObject(SourceDC, OldSourceBitmap);
	DeleteObject(SourceDC);

	/* Create the icon using the bitmaps prepared earlier */
	TargetIcon = CreateIconIndirect(&TargetIconInfo);

	/* CreateIconIndirect copies the bitmaps, so we can release our bitmaps now */
	DeleteObject(TargetIconInfo.hbmColor);
	DeleteObject(TargetIconInfo.hbmMask);

	return TargetIcon;

fail:
	/* Clean up scratch resources we created */
	if (NULL != OldTargetBitmap) SelectObject(TargetDC, OldTargetBitmap);
	if (NULL != TargetIconInfo.hbmColor) DeleteObject(TargetIconInfo.hbmColor);
	if (NULL != TargetIconInfo.hbmMask) DeleteObject(TargetIconInfo.hbmMask);
	if (NULL != TargetDC) DeleteObject(TargetDC);
	if (NULL != OldShortcutBitmap) SelectObject(ShortcutDC, OldShortcutBitmap);
	if (NULL != ShortcutDC) DeleteObject(ShortcutDC);
	if (NULL != OldSourceBitmap) SelectObject(SourceDC, OldSourceBitmap);
	if (NULL != SourceDC) DeleteObject(SourceDC);

	return NULL;
}

299
/*****************************************************************************
300 301
 * SIC_IconAppend			[internal]
 */
302
static INT SIC_IconAppend (const WCHAR *sourcefile, INT src_index, HICON *hicons, DWORD flags)
303
{
304 305 306 307
    INT ret, index, index1;
    WCHAR path[MAX_PATH];
    SIC_ENTRY *entry;
    unsigned int i;
308

309
    TRACE("%s %i %p %#x\n", debugstr_w(sourcefile), src_index, hicons, flags);
310

311
    entry = SHAlloc(sizeof(*entry));
312

313 314 315
    GetFullPathNameW(sourcefile, MAX_PATH, path, NULL);
    entry->sSourceFile = heap_alloc( (strlenW(path)+1)*sizeof(WCHAR) );
    strcpyW( entry->sSourceFile, path );
316

317 318
    entry->dwSourceIndex = src_index;
    entry->dwFlags = flags;
319

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
    EnterCriticalSection(&SHELL32_SicCS);

    index = DPA_InsertPtr(sic_hdpa, 0x7fff, entry);
    if ( INVALID_INDEX == index )
    {
        heap_free(entry->sSourceFile);
        SHFree(entry);
        ret = INVALID_INDEX;
    }
    else
    {
        index = -1;
        for (i = 0; i < ARRAY_SIZE(shell_imagelists); i++)
        {
            index1 = ImageList_AddIcon(shell_imagelists[i], hicons[i]);
            if (index != -1 && index1 != index)
                WARN("Imagelists out of sync, list %d.\n", i);
            index = index1;
        }

        entry->dwListIndex = index;
        ret = entry->dwListIndex;
    }
343

344 345
    LeaveCriticalSection(&SHELL32_SicCS);
    return ret;
346
}
347 348 349

static BOOL get_imagelist_icon_size(int list, SIZE *size)
{
350
    if (list < 0 || list >= ARRAY_SIZE(shell_imagelists)) return FALSE;
351

352
    return ImageList_GetIconSize( shell_imagelists[list], &size->cx, &size->cy );
353 354
}

355
/****************************************************************************
356 357 358
 * SIC_LoadIcon				[internal]
 *
 * NOTES
359
 *  gets icons by index from the file
360
 */
361
static INT SIC_LoadIcon (const WCHAR *sourcefile, INT index, DWORD flags)
362
{
363
    HICON hicons[ARRAY_SIZE(shell_imagelists)] = { 0 };
364 365 366
    HICON hshortcuts[ARRAY_SIZE(hicons)] = { 0 };
    unsigned int i;
    SIZE size;
367
    INT ret = -1;
368 369 370 371 372 373

    for (i = 0; i < ARRAY_SIZE(hicons); i++)
    {
        get_imagelist_icon_size( i, &size );
        if (!PrivateExtractIconsW( sourcefile, index, size.cx, size.cy, &hicons[i], 0, 1, 0 ))
            WARN("Failed to load icon %d from %s.\n", index, debugstr_w(sourcefile));
374
        if (!hicons[i]) goto fail;
375
    }
376

377 378
    if (flags & GIL_FORSHORTCUT)
    {
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
        BOOL failed = FALSE;

        for (i = 0; i < ARRAY_SIZE(hshortcuts); i++)
        {
            if (!(hshortcuts[i] = SIC_OverlayShortcutImage(hicons[i], i)))
            {
                WARN("Failed to create shortcut overlaid icons.\n");
                failed = TRUE;
            }
        }

        if (failed)
        {
            for (i = 0; i < ARRAY_SIZE(hshortcuts); i++)
                DestroyIcon(hshortcuts[i]);
            flags &= ~GIL_FORSHORTCUT;
        }
        else
        {
            for (i = 0; i < ARRAY_SIZE(hicons); i++)
            {
                DestroyIcon(hicons[i]);
                hicons[i] = hshortcuts[i];
            }
        }
404
    }
405

406
    ret = SIC_IconAppend( sourcefile, index, hicons, flags );
407 408

fail:
409 410
    for (i = 0; i < ARRAY_SIZE(hicons); i++)
        DestroyIcon(hicons[i]);
411
    return ret;
412
}
413

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
static int get_shell_icon_size(void)
{
    WCHAR buf[32];
    DWORD value = 32, size = sizeof(buf), type;
    HKEY key;

    if (!RegOpenKeyW( HKEY_CURRENT_USER, WindowMetrics, &key ))
    {
        if (!RegQueryValueExW( key, ShellIconSize, NULL, &type, (BYTE *)buf, &size ) && type == REG_SZ)
        {
            if (size == sizeof(buf)) buf[size / sizeof(WCHAR) - 1] = 0;
            value = atoiW( buf );
        }
        RegCloseKey( key );
    }
    return value;
}

432
/*****************************************************************************
433 434
 * SIC_Initialize			[internal]
 */
435
static BOOL WINAPI SIC_Initialize( INIT_ONCE *once, void *param, void **context )
436
{
437 438 439 440
    HICON hicons[ARRAY_SIZE(shell_imagelists)];
    SIZE sizes[ARRAY_SIZE(shell_imagelists)];
    BOOL failed = FALSE;
    unsigned int i;
441

442 443 444 445 446 447 448 449 450 451 452 453 454
    if (!IsProcessDPIAware())
    {
        sizes[SHIL_LARGE].cx = sizes[SHIL_LARGE].cy = get_shell_icon_size();
        sizes[SHIL_SMALL].cx = GetSystemMetrics( SM_CXSMICON );
        sizes[SHIL_SMALL].cy = GetSystemMetrics( SM_CYSMICON );
    }
    else
    {
        sizes[SHIL_LARGE].cx = GetSystemMetrics( SM_CXICON );
        sizes[SHIL_LARGE].cy = GetSystemMetrics( SM_CYICON );
        sizes[SHIL_SMALL].cx = sizes[SHIL_LARGE].cx / 2;
        sizes[SHIL_SMALL].cy = sizes[SHIL_LARGE].cy / 2;
    }
455

456 457
    sizes[SHIL_EXTRALARGE].cx = (GetSystemMetrics( SM_CXICON ) * 3) / 2;
    sizes[SHIL_EXTRALARGE].cy = (GetSystemMetrics( SM_CYICON ) * 3) / 2;
458 459
    sizes[SHIL_SYSSMALL].cx = GetSystemMetrics( SM_CXSMICON );
    sizes[SHIL_SYSSMALL].cy = GetSystemMetrics( SM_CYSMICON );
460
    sizes[SHIL_JUMBO].cx = sizes[SHIL_JUMBO].cy = 256;
461

462
    TRACE("large %dx%d small %dx%d\n", sizes[SHIL_LARGE].cx, sizes[SHIL_LARGE].cy, sizes[SHIL_SMALL].cx, sizes[SHIL_SMALL].cy);
463

464 465 466
    sic_hdpa = DPA_Create(16);
    if (!sic_hdpa)
        return(FALSE);
467

468 469 470 471
    for (i = 0; i < ARRAY_SIZE(shell_imagelists); i++)
    {
        shell_imagelists[i] = ImageList_Create(sizes[i].cx, sizes[i].cy, ILC_COLOR32 | ILC_MASK, 0, 0x20);
        ImageList_SetBkColor(shell_imagelists[i], CLR_NONE);
472

473 474
        /* Load the generic file icon, which is used as the default if an icon isn't found. */
        if (!(hicons[i] = LoadImageA(shell32_hInstance, MAKEINTRESOURCEA(IDI_SHELL_FILE),
475
            IMAGE_ICON, sizes[i].cx, sizes[i].cy, LR_SHARED)))
476
        {
477
            failed = TRUE;
478
        }
479
    }
480

481 482
    if (failed)
    {
483
        FIXME("Failed to load IDI_SHELL_FILE icon!\n");
484 485 486
        return FALSE;
    }

487 488
    SIC_IconAppend(swShell32Name, IDI_SHELL_FILE - 1, hicons, 0);
    SIC_IconAppend(swShell32Name, -IDI_SHELL_FILE, hicons, 0);
489

490 491 492
    TRACE("small list=%p, large list=%p\n", shell_imagelists[SHIL_SMALL], shell_imagelists[SHIL_LARGE]);

    return TRUE;
493
}
494

495 496 497 498 499
/*************************************************************************
 * SIC_Destroy
 *
 * frees the cache
 */
500
static INT CALLBACK sic_free( LPVOID ptr, LPVOID lparam )
501
{
502
	heap_free(((LPSIC_ENTRY)ptr)->sSourceFile);
503 504
	SHFree(ptr);
	return TRUE;
505
}
506

507 508
void SIC_Destroy(void)
{
509
    unsigned int i;
510

511
    TRACE("\n");
512

513
    EnterCriticalSection(&SHELL32_SicCS);
514

515
    if (sic_hdpa) DPA_DestroyCallback(sic_hdpa, sic_free, NULL );
516

517 518 519 520 521 522 523 524
    for (i = 0; i < ARRAY_SIZE(shell_imagelists); i++)
    {
        if (shell_imagelists[i])
            ImageList_Destroy(shell_imagelists[i]);
    }

    LeaveCriticalSection(&SHELL32_SicCS);
    DeleteCriticalSection(&SHELL32_SicCS);
525
}
526

527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
/*****************************************************************************
 * SIC_GetIconIndex			[internal]
 *
 * Parameters
 *	sSourceFile	[IN]	filename of file containing the icon
 *	index		[IN]	index/resID (negated) in this file
 *
 * NOTES
 *  look in the cache for a proper icon. if not available the icon is taken
 *  from the file and cached
 */
INT SIC_GetIconIndex (LPCWSTR sSourceFile, INT dwSourceIndex, DWORD dwFlags )
{
	SIC_ENTRY sice;
	INT ret, index = INVALID_INDEX;
	WCHAR path[MAX_PATH];

	TRACE("%s %i\n", debugstr_w(sSourceFile), dwSourceIndex);

	GetFullPathNameW(sSourceFile, MAX_PATH, path, NULL);
	sice.sSourceFile = path;
	sice.dwSourceIndex = dwSourceIndex;
	sice.dwFlags = dwFlags;

        InitOnceExecuteOnce( &sic_init_once, SIC_Initialize, NULL, NULL );

	EnterCriticalSection(&SHELL32_SicCS);

	if (NULL != DPA_GetPtr (sic_hdpa, 0))
	{
	  /* search linear from position 0*/
	  index = DPA_Search (sic_hdpa, &sice, 0, SIC_CompareEntries, 0, 0);
	}

	if ( INVALID_INDEX == index )
	{
          ret = SIC_LoadIcon (sSourceFile, dwSourceIndex, dwFlags);
	}
	else
	{
	  TRACE("-- found\n");
	  ret = ((LPSIC_ENTRY)DPA_GetPtr(sic_hdpa, index))->dwListIndex;
	}

	LeaveCriticalSection(&SHELL32_SicCS);
	return ret;
}

575 576 577 578 579
/*****************************************************************************
 * SIC_LoadOverlayIcon			[internal]
 *
 * Load a shell overlay icon and return its icon cache index.
 */
580
static int SIC_LoadOverlayIcon(int icon_idx)
581 582 583 584 585 586 587 588 589 590 591 592 593 594
{
	WCHAR buffer[1024], wszIdx[8];
	HKEY hKeyShellIcons;
	LPCWSTR iconPath;
	int iconIdx;

	static const WCHAR wszShellIcons[] = {
	    'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
	    'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
	    'E','x','p','l','o','r','e','r','\\','S','h','e','l','l',' ','I','c','o','n','s',0
	}; 
	static const WCHAR wszNumFmt[] = {'%','d',0};

	iconPath = swShell32Name;	/* default: load icon from shell32.dll */
595
	iconIdx = icon_idx;
596 597 598 599 600

	if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszShellIcons, 0, KEY_READ, &hKeyShellIcons) == ERROR_SUCCESS)
	{
	    DWORD count = sizeof(buffer);

601
	    sprintfW(wszIdx, wszNumFmt, icon_idx);
602 603 604 605 606 607

	    /* read icon path and index */
	    if (RegQueryValueExW(hKeyShellIcons, wszIdx, NULL, NULL, (LPBYTE)buffer, &count) == ERROR_SUCCESS)
	    {
		LPWSTR p = strchrW(buffer, ',');

608 609 610 611 612 613 614
		if (!p)
		{
		    ERR("Icon index in %s/%s corrupted, no comma.\n", debugstr_w(wszShellIcons),debugstr_w(wszIdx));
		    RegCloseKey(hKeyShellIcons);
		    return -1;
		}
		*p++ = 0;
615 616 617 618 619 620 621
		iconPath = buffer;
		iconIdx = atoiW(p);
	    }

	    RegCloseKey(hKeyShellIcons);
	}

622 623
        InitOnceExecuteOnce( &sic_init_once, SIC_Initialize, NULL, NULL );

624 625 626
	return SIC_LoadIcon(iconPath, iconIdx, 0);
}

627
/*************************************************************************
628
 * Shell_GetImageLists			[SHELL32.71]
629 630
 *
 * PARAMETERS
631
 *  imglist[1|2] [OUT] pointer which receives imagelist handles
632 633
 *
 */
634
BOOL WINAPI Shell_GetImageLists(HIMAGELIST *large_list, HIMAGELIST *small_list)
635
{
636 637
    TRACE("(%p, %p)\n", large_list, small_list);

638
    InitOnceExecuteOnce( &sic_init_once, SIC_Initialize, NULL, NULL );
639 640
    if (large_list) *large_list = shell_imagelists[SHIL_LARGE];
    if (small_list) *small_list = shell_imagelists[SHIL_SMALL];
641
    return TRUE;
642
}
643

644 645 646 647 648 649 650
/*************************************************************************
 * PidlToSicIndex			[INTERNAL]
 *
 * PARAMETERS
 *	sh	[IN]	IShellFolder
 *	pidl	[IN]
 *	bBigIcon [IN]
Juergen Schmied's avatar
Juergen Schmied committed
651
 *	uFlags	[IN]	GIL_*
652 653 654
 *	pIndex	[OUT]	index within the SIC
 *
 */
Juergen Schmied's avatar
Juergen Schmied committed
655 656
BOOL PidlToSicIndex (
	IShellFolder * sh,
657
	LPCITEMIDLIST pidl,
Juergen Schmied's avatar
Juergen Schmied committed
658 659
	BOOL bBigIcon,
	UINT uFlags,
660
	int * pIndex)
661
{
662 663
	IExtractIconW	*ei;
	WCHAR		szIconFile[MAX_PATH];	/* file containing the icon */
664 665 666
	INT		iSourceIndex;		/* index or resID(negated) in this file */
	BOOL		ret = FALSE;
	UINT		dwFlags = 0;
667
	int		iShortcutDefaultIndex = INVALID_INDEX;
668

Juergen Schmied's avatar
Juergen Schmied committed
669
	TRACE("sf=%p pidl=%p %s\n", sh, pidl, bBigIcon?"Big":"Small");
670

671 672
        InitOnceExecuteOnce( &sic_init_once, SIC_Initialize, NULL, NULL );

673
	if (SUCCEEDED (IShellFolder_GetUIObjectOf(sh, 0, 1, &pidl, &IID_IExtractIconW, 0, (void **)&ei)))
674
	{
675
	  if (SUCCEEDED(IExtractIconW_GetIconLocation(ei, uFlags, szIconFile, MAX_PATH, &iSourceIndex, &dwFlags)))
Juergen Schmied's avatar
Juergen Schmied committed
676
	  {
677
	    *pIndex = SIC_GetIconIndex(szIconFile, iSourceIndex, uFlags);
678
	    ret = TRUE;
679
	  }
680
	  IExtractIconW_Release(ei);
681 682 683
	}

	if (INVALID_INDEX == *pIndex)	/* default icon when failed */
684 685 686 687 688 689 690 691 692 693 694 695 696 697
	{
	  if (0 == (uFlags & GIL_FORSHORTCUT))
	  {
	    *pIndex = 0;
	  }
	  else
	  {
	    if (INVALID_INDEX == iShortcutDefaultIndex)
	    {
	      iShortcutDefaultIndex = SIC_LoadIcon(swShell32Name, 0, GIL_FORSHORTCUT);
	    }
	    *pIndex = (INVALID_INDEX != iShortcutDefaultIndex ? iShortcutDefaultIndex : 0);
	  }
	}
698 699 700 701

	return ret;

}
702 703

/*************************************************************************
704
 * SHMapPIDLToSystemImageListIndex	[SHELL32.77]
705 706
 *
 * PARAMETERS
707
 *	sh	[IN]		pointer to an instance of IShellFolder
708 709
 *	pidl	[IN]
 *	pIndex	[OUT][OPTIONAL]	SIC index for big icon
710 711
 *
 */
712
int WINAPI SHMapPIDLToSystemImageListIndex(
713
	IShellFolder *sh,
714
	LPCITEMIDLIST pidl,
715
	int *pIndex)
716
{
717
	int Index;
718
	UINT uGilFlags = 0;
719

720
	TRACE("(SF=%p,pidl=%p,%p)\n",sh,pidl,pIndex);
721
	pdump(pidl);
722

723 724 725
	if (SHELL_IsShortcut(pidl))
	    uGilFlags |= GIL_FORSHORTCUT;

726
	if (pIndex)
727 728 729 730 731 732
	    if (!PidlToSicIndex ( sh, pidl, 1, uGilFlags, pIndex))
	        *pIndex = -1;

	if (!PidlToSicIndex ( sh, pidl, 0, uGilFlags, &Index))
	    return -1;

733
	return Index;
734 735
}

736 737 738 739 740 741 742 743 744 745 746 747 748
/*************************************************************************
 * SHMapIDListToImageListIndexAsync  [SHELL32.148]
 */
HRESULT WINAPI SHMapIDListToImageListIndexAsync(IUnknown *pts, IShellFolder *psf,
                                                LPCITEMIDLIST pidl, UINT flags,
                                                void *pfn, void *pvData, void *pvHint,
                                                int *piIndex, int *piIndexSel)
{
    FIXME("(%p, %p, %p, 0x%08x, %p, %p, %p, %p, %p)\n",
            pts, psf, pidl, flags, pfn, pvData, pvHint, piIndex, piIndexSel);
    return E_FAIL;
}

749
/*************************************************************************
750
 * Shell_GetCachedImageIndex		[SHELL32.72]
751 752
 *
 */
753
static INT Shell_GetCachedImageIndexA(LPCSTR szPath, INT nIndex, BOOL bSimulateDoc)
754
{
755 756 757
	INT ret, len;
	LPWSTR szTemp;

758
	WARN("(%s,%08x,%08x) semi-stub.\n",debugstr_a(szPath), nIndex, bSimulateDoc);
759 760

	len = MultiByteToWideChar( CP_ACP, 0, szPath, -1, NULL, 0 );
761
	szTemp = heap_alloc( len * sizeof(WCHAR) );
762 763
	MultiByteToWideChar( CP_ACP, 0, szPath, -1, szTemp, len );

764
	ret = SIC_GetIconIndex( szTemp, nIndex, 0 );
765

766
	heap_free( szTemp );
767 768

	return ret;
769 770
}

771
static INT Shell_GetCachedImageIndexW(LPCWSTR szPath, INT nIndex, BOOL bSimulateDoc)
772
{
773
	WARN("(%s,%08x,%08x) semi-stub.\n",debugstr_w(szPath), nIndex, bSimulateDoc);
774

775
	return SIC_GetIconIndex(szPath, nIndex, 0);
776 777
}

778
INT WINAPI Shell_GetCachedImageIndexAW(LPCVOID szPath, INT nIndex, BOOL bSimulateDoc)
779
{	if( SHELL_OsIsUnicode())
780 781
	  return Shell_GetCachedImageIndexW(szPath, nIndex, bSimulateDoc);
	return Shell_GetCachedImageIndexA(szPath, nIndex, bSimulateDoc);
782 783
}

784
/*************************************************************************
785
 * ExtractIconExW			[SHELL32.@]
786
 * RETURNS
787
 *  0 no icon found
788 789
 *  -1 file is not valid
 *  or number of icons extracted
790
 */
791
UINT WINAPI ExtractIconExW(LPCWSTR lpszFile, INT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIcons)
792
{
793 794 795
	TRACE("%s %i %p %p %i\n", debugstr_w(lpszFile), nIconIndex, phiconLarge, phiconSmall, nIcons);

	return PrivateExtractIconExW(lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
796
}
797

798
/*************************************************************************
799
 * ExtractIconExA			[SHELL32.@]
800
 */
801
UINT WINAPI ExtractIconExA(LPCSTR lpszFile, INT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIcons)
802
{
803
    UINT ret = 0;
804
    INT len = MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, NULL, 0);
805
    LPWSTR lpwstrFile = heap_alloc( len * sizeof(WCHAR));
806

807
    TRACE("%s %i %p %p %i\n", lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
808

809 810 811 812
    if (lpwstrFile)
    {
        MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, lpwstrFile, len);
        ret = ExtractIconExW(lpwstrFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
813
        heap_free(lpwstrFile);
814
    }
815
    return ret;
816
}
817 818 819 820 821 822 823 824

/*************************************************************************
 *				ExtractAssociatedIconA (SHELL32.@)
 *
 * Return icon for given file (either from file itself or from associated
 * executable) and patch parameters if needed.
 */
HICON WINAPI ExtractAssociatedIconA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIcon)
825
{	
826 827
    HICON hIcon = NULL;
    INT len = MultiByteToWideChar(CP_ACP, 0, lpIconPath, -1, NULL, 0);
828 829 830 831 832
    /* Note that we need to allocate MAX_PATH, since we are supposed to fill
     * the correct executable if there is no icon in lpIconPath directly.
     * lpIconPath itself is supposed to be large enough, so make sure lpIconPathW
     * is large enough too. Yes, I am puking too.
     */
833
    LPWSTR lpIconPathW = heap_alloc(MAX_PATH * sizeof(WCHAR));
834 835 836 837 838 839 840

    TRACE("%p %s %p\n", hInst, debugstr_a(lpIconPath), lpiIcon);

    if (lpIconPathW)
    {
        MultiByteToWideChar(CP_ACP, 0, lpIconPath, -1, lpIconPathW, len);
        hIcon = ExtractAssociatedIconW(hInst, lpIconPathW, lpiIcon);
841
        WideCharToMultiByte(CP_ACP, 0, lpIconPathW, -1, lpIconPath, MAX_PATH , NULL, NULL);
842
        heap_free(lpIconPathW);
843 844 845
    }
    return hIcon;
}
846

847 848 849 850 851 852
/*************************************************************************
 *				ExtractAssociatedIconW (SHELL32.@)
 *
 * Return icon for given file (either from file itself or from associated
 * executable) and patch parameters if needed.
 */
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
HICON WINAPI ExtractAssociatedIconW(HINSTANCE hInst, LPWSTR lpIconPath, LPWORD lpiIcon)
{
    HICON hIcon = NULL;
    WORD wDummyIcon = 0;

    TRACE("%p %s %p\n", hInst, debugstr_w(lpIconPath), lpiIcon);

    if(lpiIcon == NULL)
        lpiIcon = &wDummyIcon;

    hIcon = ExtractIconW(hInst, lpIconPath, *lpiIcon);

    if( hIcon < (HICON)2 )
    { if( hIcon == (HICON)1 ) /* no icons found in given file */
      { WCHAR tempPath[MAX_PATH];
        HINSTANCE uRet = FindExecutableW(lpIconPath,NULL,tempPath);

        if( uRet > (HINSTANCE)32 && tempPath[0] )
        { lstrcpyW(lpIconPath,tempPath);
          hIcon = ExtractIconW(hInst, lpIconPath, *lpiIcon);
          if( hIcon > (HICON)2 )
            return hIcon;
        }
      }

      if( hIcon == (HICON)1 )
        *lpiIcon = 2;   /* MSDOS icon - we found .exe but no icons in it */
      else
        *lpiIcon = 6;   /* generic icon - found nothing */

      if (GetModuleFileNameW(hInst, lpIconPath, MAX_PATH))
        hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(*lpiIcon));
    }
    return hIcon;
887 888 889
}

/*************************************************************************
890
 *				ExtractAssociatedIconExW (SHELL32.@)
891 892 893 894
 *
 * Return icon for given file (either from file itself or from associated
 * executable) and patch parameters if needed.
 */
895
HICON WINAPI ExtractAssociatedIconExW(HINSTANCE hInst, LPWSTR lpIconPath, LPWORD lpiIconIdx, LPWORD lpiIconId)
896
{
897
  FIXME("%p %s %p %p): stub\n", hInst, debugstr_w(lpIconPath), lpiIconIdx, lpiIconId);
898 899 900 901
  return 0;
}

/*************************************************************************
902
 *				ExtractAssociatedIconExA (SHELL32.@)
903 904 905 906
 *
 * Return icon for given file (either from file itself or from associated
 * executable) and patch parameters if needed.
 */
907
HICON WINAPI ExtractAssociatedIconExA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIconIdx, LPWORD lpiIconId)
908
{
909 910
  HICON ret;
  INT len = MultiByteToWideChar( CP_ACP, 0, lpIconPath, -1, NULL, 0 );
911
  LPWSTR lpwstrFile = heap_alloc( len * sizeof(WCHAR) );
912 913 914 915 916

  TRACE("%p %s %p %p)\n", hInst, lpIconPath, lpiIconIdx, lpiIconId);

  MultiByteToWideChar( CP_ACP, 0, lpIconPath, -1, lpwstrFile, len );
  ret = ExtractAssociatedIconExW(hInst, lpwstrFile, lpiIconIdx, lpiIconId);
917
  heap_free(lpwstrFile);
918
  return ret;
919
}
920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936


/****************************************************************************
 * SHDefExtractIconW		[SHELL32.@]
 */
HRESULT WINAPI SHDefExtractIconW(LPCWSTR pszIconFile, int iIndex, UINT uFlags,
                                 HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize)
{
	UINT ret;
	HICON hIcons[2];
	WARN("%s %d 0x%08x %p %p %d, semi-stub\n", debugstr_w(pszIconFile), iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);

	ret = PrivateExtractIconsW(pszIconFile, iIndex, nIconSize, nIconSize, hIcons, NULL, 2, LR_DEFAULTCOLOR);
	/* FIXME: deal with uFlags parameter which contains GIL_ flags */
	if (ret == 0xFFFFFFFF)
	  return E_FAIL;
	if (ret > 0) {
937 938 939 940 941 942 943 944
	  if (phiconLarge)
	    *phiconLarge = hIcons[0];
	  else
	    DestroyIcon(hIcons[0]);
	  if (phiconSmall)
	    *phiconSmall = hIcons[1];
	  else
	    DestroyIcon(hIcons[1]);
945 946 947 948 949 950 951 952 953 954 955 956 957
	  return S_OK;
	}
	return S_FALSE;
}

/****************************************************************************
 * SHDefExtractIconA		[SHELL32.@]
 */
HRESULT WINAPI SHDefExtractIconA(LPCSTR pszIconFile, int iIndex, UINT uFlags,
                                 HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize)
{
  HRESULT ret;
  INT len = MultiByteToWideChar(CP_ACP, 0, pszIconFile, -1, NULL, 0);
958
  LPWSTR lpwstrFile = heap_alloc(len * sizeof(WCHAR));
959 960 961 962 963

  TRACE("%s %d 0x%08x %p %p %d\n", pszIconFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);

  MultiByteToWideChar(CP_ACP, 0, pszIconFile, -1, lpwstrFile, len);
  ret = SHDefExtractIconW(lpwstrFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
964
  heap_free(lpwstrFile);
965 966
  return ret;
}
967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991


/****************************************************************************
 * SHGetIconOverlayIndexA    [SHELL32.@]
 *
 * Returns the index of the overlay icon in the system image list.
 */
INT WINAPI SHGetIconOverlayIndexA(LPCSTR pszIconPath, INT iIconIndex)
{
  FIXME("%s, %d\n", debugstr_a(pszIconPath), iIconIndex);

  return -1;
}

/****************************************************************************
 * SHGetIconOverlayIndexW    [SHELL32.@]
 *
 * Returns the index of the overlay icon in the system image list.
 */
INT WINAPI SHGetIconOverlayIndexW(LPCWSTR pszIconPath, INT iIconIndex)
{
  FIXME("%s, %d\n", debugstr_w(pszIconPath), iIconIndex);

  return -1;
}
992 993 994 995

/****************************************************************************
 * SHGetStockIconInfo [SHELL32.@]
 *
996
 * Receive information for builtin icons
997 998
 *
 * PARAMS
999 1000
 *  id      [I]  selected icon-id to get information for
 *  flags   [I]  selects the information to receive
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
 *  sii     [IO] SHSTOCKICONINFO structure to fill
 *
 * RETURNS
 *  Success: S_OK
 *  Failure: A HRESULT failure code
 *
 */
HRESULT WINAPI SHGetStockIconInfo(SHSTOCKICONID id, UINT flags, SHSTOCKICONINFO *sii)
{
    static const WCHAR shell32dll[] = {'\\','s','h','e','l','l','3','2','.','d','l','l',0};

    FIXME("(%d, 0x%x, %p) semi-stub\n", id, flags, sii);
    if ((id < 0) || (id >= SIID_MAX_ICONS) || !sii || (sii->cbSize != sizeof(SHSTOCKICONINFO))) {
        return E_INVALIDARG;
    }

    GetSystemDirectoryW(sii->szPath, MAX_PATH);

    /* no icons defined: use default */
1020
    sii->iIcon = -IDI_SHELL_FILE;
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
    lstrcatW(sii->szPath, shell32dll);

    if (flags)
        FIXME("flags 0x%x not implemented\n", flags);

    sii->hIcon = NULL;
    sii->iSysImageIndex = -1;

    TRACE("%3d: returning %s (%d)\n", id, debugstr_w(sii->szPath), sii->iIcon);

    return S_OK;
}
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045

/*************************************************************************
 *              SHGetImageList (SHELL32.727)
 *
 * Returns a copy of a shell image list.
 *
 * NOTES
 *   Windows XP features 4 sizes of image list, and Vista 5. Wine currently
 *   only supports the traditional small and large image lists, so requests
 *   for the others will currently fail.
 */
HRESULT WINAPI SHGetImageList(int iImageList, REFIID riid, void **ppv)
{
1046 1047 1048
    TRACE("(%d, %s, %p)\n", iImageList, debugstr_guid(riid), ppv);

    if (iImageList < 0 || iImageList > SHIL_LAST)
1049 1050 1051 1052 1053
        return E_FAIL;

    InitOnceExecuteOnce( &sic_init_once, SIC_Initialize, NULL, NULL );
    return HIMAGELIST_QueryInterface(shell_imagelists[iImageList], riid, ppv);
}