/*++ Copyright (c) 1993-2000 Microsoft Corporation Module Name: devicon.c Abstract: Device Installer routines dealing with retrieval/display of icons. Author: Lonny McMichael (lonnym) 28-Aug--1995 Notes: You must include "basetyps.h" first. --*/ #include "precomp.h" #pragma hdrstop #include MINI_ICON_LIST GlobalMiniIconList; /*++ Class-to-Icon conversion tables exist in two places. First, there is the built-in one defined below that is based on a hard-wired bitmap in the resource. The second is a linked list of CLASSICON structures that is created every time a new class with an icon comes along. Check out resource\ilwinmsd.bmp to see the mini-icons. The icons are referenced via their (zero-based) index (e.g., computer is 0, chip is 1, display is 2, etc.). Today, the bitmap indexes look as follows: 0 - computer 1 - chip 2 - display 3 - network 4 - windows 5 - mouse 6 - keyboard 7 - phone 8 - sound 9 - drives 10 - plugs 11 - generic 12 - check 13 - uncheck 14 - printer 15 - nettrans 16 - netclient 17 - netservice 18 - unknown 19 - Fax machine 20 - greyed check 21 - dial up networking 22 - direct cable connection 23 - briefcase (filesync) 24 - Exchange 25 - partial check 26 - Generic folder / Accessories 27 - media (music) 28 - Quick View 29 - old MSN 30 - calculator 31 - FAT32 Converter 32 - Document Templates 33 - disk compression 34 - Games 35 - HyperTerminal 36 - package 37 - mspaint 38 - Screensavers 39 - WordPad 40 - Clipboard Viewer 41 - Accessibility Options 42 - backup 43 - Desktop Wallpaper 44 - Character Map 45 - Mouse Pointers 46 - Net Watcher 47 - phone dialer 48 - resource monitor 49 - Online User's Guide 50 - Multilanguage Support 51 - Audio Compression 52 - CD player 53 - Media Player 54 - WAV sounds 55 - Sample Sounds 56 - Video Compression 57 - Volume Control 58 - Musica sound scheme 59 - Jungle sound scheme 60 - Robotz sound scheme 61 - Utopia sound scheme 62 - Eudcedit 63 - Minesweeper 64 - Pinball 65 - Imaging 66 - Clock 67 - Infrared 68 - MS Wallet 69 - FrontPage Express (aka FrontPad) 70 - MS Agent 71 - Internet Tools 72 - NetShow Player 73 - Net Meeting 74 - DVD Player 75 - Freecell 76 - Athena / Outlook Express / Internet Mail and News 77 - Desktop Themes 78 - Baseball theme 79 - Dangerous Creatures theme 80 - Inside your Computer theme 81 - Jungle theme 82 - Leonardo da Vinci theme 83 - More Windows theme 84 - Mystery theme 85 - Nature theme 86 - Science theme 87 - Space theme 88 - Sports theme 89 - The 60's USA theme 90 - The Golden Era theme 91 - Travel theme 92 - Underwater theme 93 - Windows 95 theme 94 - Personal Web Server 95 - Real Audio 96 - Web Publisher / WebPost 97 - WaveTop 98 - WebTV --*/ CONST INT UnknownClassMiniIconIndex = 11; CONST CLASSICON MiniIconXlate[] = { {&GUID_DEVCLASS_COMPUTER, 0, NULL}, {&GUID_DEVCLASS_DISPLAY, 2, NULL}, {&GUID_DEVCLASS_MOUSE, 5, NULL}, {&GUID_DEVCLASS_KEYBOARD, 6, NULL}, {&GUID_DEVCLASS_FDC, 9, NULL}, {&GUID_DEVCLASS_HDC, 9, NULL}, {&GUID_DEVCLASS_PORTS, 10, NULL}, {&GUID_DEVCLASS_NET, 15, NULL}, {&GUID_DEVCLASS_SYSTEM, 0, NULL}, {&GUID_DEVCLASS_SOUND, 8, NULL}, {&GUID_DEVCLASS_PRINTER, 14, NULL}, {&GUID_DEVCLASS_MONITOR, 2, NULL}, {&GUID_DEVCLASS_NETTRANS, 3, NULL}, {&GUID_DEVCLASS_NETCLIENT, 16, NULL}, {&GUID_DEVCLASS_NETSERVICE, 17, NULL}, {&GUID_DEVCLASS_UNKNOWN, 18, NULL}, {&GUID_DEVCLASS_LEGACYDRIVER, 11, NULL}, {&GUID_DEVCLASS_MTD, 9, NULL} }; #define MINIX 16 #define MINIY 16 #define RGB_WHITE (RGB(255, 255, 255)) #define RGB_BLACK (RGB(0, 0, 0)) #define RGB_TRANSPARENT (RGB(0, 128, 128)) // This should removed when WINVER defined >= 0x0500 for this project. #ifndef NOMIRRORBITMAP #ifdef UNICODE #define NOMIRRORBITMAP (DWORD)0x80000000 /* Do not Mirror the bitmap in this call */ #else #define NOMIRRORBITMAP (DWORD)0x0 #endif //UNICODE #endif // // Private function prototypes. // INT NewMiniIcon( IN CONST GUID *ClassGuid, IN HICON hIcon OPTIONAL ); INT WINAPI SetupDiDrawMiniIcon( IN HDC hdc, IN RECT rc, IN INT MiniIconIndex, IN DWORD Flags ) /*++ Routine Description: This routine draws the specified mini-icon at the requested location. Arguments: hdc - Supplies the handle of the device context in which the mini-icon will be drawn. rc - Supplies the rectangle in the specified HDC to draw the icon in. MiniIconIndex - The index of the mini-icon, as retrieved from SetupDiLoadClassIcon or SetupDiGetClassBitmapIndex. The following are pre-defined indices that may be used. 0 Computer 2 display 5 mouse 6 keyboard 9 fdc 9 hdc 10 ports 15 net 0 system 8 sound 14 printer 2 monitor 3 nettrans 16 netclient 17 netservice 18 unknown Flags - Controls the drawing operation. The LOWORD contains the actual flags defined as follows: DMI_MASK - Draw the mini-icon's mask into HDC. DMI_BKCOLOR - Use the system color index specified in the HIWORD of Flags as the background color. If not specified, COLOR_WINDOW is used. DMI_USERECT - If set, SetupDiDrawMiniIcon will use the supplied rect, stretching the icon to fit as appropriate. Return Value: This function returns the offset from the left of rc where the string should start. Remarks: By default, the icon will be centered vertically and butted against the left corner of the specified rectangle. --*/ { HBITMAP hbmOld; DWORD rgbBk, rgbText; INT ret = 0; if(!LockMiniIconList(&GlobalMiniIconList)) { return 0; } CreateMiniIcons(); if(GlobalMiniIconList.hbmMiniImage) { // // Set the Foreground and background color for the // conversion of the Mono Mask image // if(Flags & DMI_MASK) { rgbBk = SetBkColor(hdc, RGB_WHITE); } else { rgbBk = SetBkColor(hdc, GetSysColor(((int)(Flags & DMI_BKCOLOR ? HIWORD(Flags) : COLOR_WINDOW))) ); } rgbText = SetTextColor(hdc, RGB_BLACK); if(Flags & DMI_USERECT) { // // Copy the converted mask into the dest. The transparent // areas will be drawn with the current window color. // hbmOld = SelectObject(GlobalMiniIconList.hdcMiniMem, GlobalMiniIconList.hbmMiniMask ); StretchBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, GlobalMiniIconList.hdcMiniMem, MINIX * MiniIconIndex, 0, MINIX, MINIY, SRCCOPY | NOMIRRORBITMAP); if(!(Flags & DMI_MASK)) { // // OR the image into the dest // SelectObject(GlobalMiniIconList.hdcMiniMem, GlobalMiniIconList.hbmMiniImage ); StretchBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, GlobalMiniIconList.hdcMiniMem, MINIX * MiniIconIndex, 0, MINIX, MINIY, SRCPAINT | NOMIRRORBITMAP); } } else { // // Copy the converted mask into the dest. The transparent // areas will be drawn with the current window color. // hbmOld = SelectObject(GlobalMiniIconList.hdcMiniMem, GlobalMiniIconList.hbmMiniMask ); BitBlt(hdc, rc.left, rc.top + (rc.bottom - rc.top - MINIY)/2, MINIX, MINIY, GlobalMiniIconList.hdcMiniMem, MINIX * MiniIconIndex, 0, SRCCOPY | NOMIRRORBITMAP ); if(!(Flags & DMI_MASK)) { // // OR the image into the dest // SelectObject(GlobalMiniIconList.hdcMiniMem, GlobalMiniIconList.hbmMiniImage ); BitBlt(hdc, rc.left, rc.top + (rc.bottom - rc.top - MINIY)/2, MINIX, MINIY, GlobalMiniIconList.hdcMiniMem, MINIX * MiniIconIndex, 0, SRCPAINT | NOMIRRORBITMAP ); } } SetBkColor(hdc, rgbBk); SetTextColor(hdc, rgbText); SelectObject(GlobalMiniIconList.hdcMiniMem, hbmOld); if(Flags & DMI_USERECT) { ret = rc.right - rc.left + 2; // offset to string from left edge } else { ret = MINIX + 2; // offset to string from left edge } } UnlockMiniIconList(&GlobalMiniIconList); return ret; } BOOL WINAPI pSetupDiLoadClassIcon( IN CONST GUID *ClassGuid, OUT HICON *LargeIcon, OPTIONAL OUT HICON *SmallIcon, OPTIONAL OUT LPINT MiniIconIndex OPTIONAL ) /*++ Routine Description: This routine loads both the large and mini-icons for the specified class Arguments: ClassGuid - Supplies the GUID of the class for which the icon(s) should be loaded. LargeIcon - Optionally, supplies a pointer to a variable that will receive a handle to the loaded large icon for the specified class. If this parameter is not specified, the large icon will not be loaded. SmallIcon - Optionally, supplies a pointer to a variable that will receive a handle to the loaded small icon for the specified class. If this parameter is not specified, the small icon will not be loaded. MiniIconIndex - Optionally, supplies a pointer to a variable that will receive the index of the mini-icon for the specified class. The mini-icon is stored in the device installer's mini-icon cache. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: The icons of the class are either pre-defined, and loaded from the device installer's internal cache, or they are loaded directly from the class installer's executable. This function will query the registry value ICON in the specified class's section. If this value is specified then it indicates what mini icon to load. If the icon value is negative the absolute value represents a predefined icon. See SetupDiDrawMiniIcon for a list of pre-defined mini icons. If the icon value is positive it represents an icon in the class installer's executable, and will be extracted (the number one is reserved). This function also uses the INSTALLER value first and ENUMPROPPAGES value second to determine what executable to extract the icon(s) from. --*/ { HKEY hk = INVALID_HANDLE_VALUE; DWORD Err; HICON hRetLargeIcon = NULL; HICON hRetSmallIcon = NULL; INT ClassIconIndex; DWORD RegDataType, StringSize; PTSTR EndPtr; BOOL bGetMini = FALSE; BOOL bDestroyLargeIcon = FALSE, bDestroySmallIcon = FALSE; TCHAR TempString[MAX_PATH]; TCHAR FullPath[MAX_PATH]; UINT IconsExtracted; if(!LockMiniIconList(&GlobalMiniIconList)) { SetLastError(ERROR_CANT_LOAD_CLASS_ICON); return FALSE; } try { if(MiniIconIndex) { *MiniIconIndex = -1; } if((hk = SetupDiOpenClassRegKey(ClassGuid, KEY_READ)) == INVALID_HANDLE_VALUE) { goto clean0; } StringSize = sizeof(TempString); Err = RegQueryValueEx(hk, pszInsIcon, NULL, &RegDataType, (PBYTE)TempString, &StringSize ); if((Err == ERROR_SUCCESS) && (RegDataType == REG_SZ) && (StringSize > sizeof(TCHAR))) { if((ClassIconIndex = _tcstol(TempString, &EndPtr, 10)) == 1) { // // Positive values indicate that we should access an icon // directly using its ID. Since ExtractIconEx uses negative // values to indicate these IDs, and since the value -1 has // a special meaning to that API, we must disallow a ClassIconIndex // of +1. // goto clean1; } } else { // // Icon index not specified, so assume index is 0. // ClassIconIndex = 0; } if(MiniIconIndex) { // // If mini-icon is already around, then we're done with it. // if(!SetupDiGetClassBitmapIndex(ClassGuid, MiniIconIndex)) { // // mini-icon not around--set flag to show we didn't get it. // bGetMini = TRUE; } } if(ClassIconIndex < 0) { INT ClassIconIndexNeg = -ClassIconIndex; // // Icon index is negative. This means that this class is one // of our special classes with icons in setupapi.dll // if(LargeIcon) { hRetLargeIcon = LoadIcon(MyDllModuleHandle, MAKEINTRESOURCE(ClassIconIndexNeg) ); } if(bGetMini || SmallIcon) { // // Retrieve the small icon as well. If the resource doesn't // have a small icon then the big one will get smushed, but it's // better than getting the default icon. // hRetSmallIcon = LoadImage(MyDllModuleHandle, MAKEINTRESOURCE(ClassIconIndexNeg), IMAGE_ICON, MINIX, MINIY, 0 ); if(hRetSmallIcon && bGetMini) { *MiniIconIndex = NewMiniIcon(ClassGuid, hRetSmallIcon); } } } else if(bGetMini || LargeIcon || SmallIcon) { // // Look for the binary containing the icon(s) first in the // "Installer32" entry, and if not found, then in the "EnumPropPages32" // entry. // lstrcpyn(FullPath, SystemDirectory, MAX_PATH); StringSize = sizeof(TempString); Err = RegQueryValueEx(hk, pszInstaller32, NULL, &RegDataType, (PBYTE)TempString, &StringSize ); if((Err != ERROR_SUCCESS) || (RegDataType != REG_SZ) || (StringSize < sizeof(TCHAR))) { StringSize = sizeof(TempString); Err = RegQueryValueEx(hk, pszEnumPropPages32, NULL, &RegDataType, (PBYTE)TempString, &StringSize ); if((Err != ERROR_SUCCESS) || (RegDataType != REG_SZ) || (StringSize < sizeof(TCHAR))) { goto clean1; } } // // Remove function name, if present, from installer name. // for(EndPtr = TempString + ((StringSize / sizeof(TCHAR)) - 1); EndPtr >= TempString; EndPtr--) { if(*EndPtr == TEXT(',')) { *EndPtr = TEXT('\0'); break; } // // If we hit a double-quote mark, we terminate the search. // if(*EndPtr == TEXT('\"')) { break; } } pSetupConcatenatePaths(FullPath, TempString, MAX_PATH, NULL); IconsExtracted = ExtractIconEx(FullPath, -ClassIconIndex, LargeIcon ? &hRetLargeIcon : NULL, (bGetMini || SmallIcon) ? &hRetSmallIcon : NULL, 1 ); if((IconsExtracted != (UINT)-1) && (IconsExtracted > 0)) { if(hRetLargeIcon) { bDestroyLargeIcon = TRUE; } if(hRetSmallIcon) { *MiniIconIndex = NewMiniIcon(ClassGuid, hRetSmallIcon); bDestroySmallIcon = TRUE; } } } clean1: RegCloseKey(hk); hk = INVALID_HANDLE_VALUE; clean0: // // Assume success, unless we hit some really big problem below. // Err = NO_ERROR; if(LargeIcon && !hRetLargeIcon) { // // We didn't retrieve a large icon, so use a default. // if(!(hRetLargeIcon = LoadIcon(MyDllModuleHandle, MAKEINTRESOURCE(ICON_DEFAULT)))) { Err = GetLastError(); } } if (SmallIcon && !hRetSmallIcon) { // // We didn't retrieve a small icon, so use a default. // if (!(hRetSmallIcon = LoadImage(MyDllModuleHandle, MAKEINTRESOURCE(ICON_DEFAULT), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0 ))) { Err = GetLastError(); } } if(Err == NO_ERROR) { if(LargeIcon) { *LargeIcon = hRetLargeIcon; } if (SmallIcon) { *SmallIcon = hRetSmallIcon; } if(MiniIconIndex && (*MiniIconIndex == -1)) { SetupDiGetClassBitmapIndex(NULL, MiniIconIndex); } } } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; if(hk != INVALID_HANDLE_VALUE) { RegCloseKey(hk); } } if(Err != NO_ERROR) { if (bDestroyLargeIcon) { DestroyIcon(hRetLargeIcon); } if (bDestroySmallIcon) { DestroyIcon(hRetSmallIcon); } } UnlockMiniIconList(&GlobalMiniIconList); SetLastError(Err); return (Err == NO_ERROR); } BOOL WINAPI SetupDiLoadClassIcon( IN CONST GUID *ClassGuid, OUT HICON *LargeIcon, OPTIONAL OUT LPINT MiniIconIndex OPTIONAL ) /*++ Routine Description: This routine loads both the large and mini-icons for the specified class Arguments: ClassGuid - Supplies the GUID of the class for which the icon(s) should be loaded. LargeIcon - Optionally, supplies a pointer to a variable that will receive a handle to the loaded large icon for the specified class. If this parameter is not specified, the large icon will not be loaded. MiniIconIndex - Optionally, supplies a pointer to a variable that will receive the index of the mini-icon for the specified class. The mini-icon is stored in the device installer's mini-icon cache. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: The icons of the class are either pre-defined, and loaded from the device installer's internal cache, or they are loaded directly from the class installer's executable. This function will query the registry value ICON in the specified class's section. If this value is specified then it indicates what mini icon to load. If the icon value is negative the absolute value represents a predefined icon. See SetupDiDrawMiniIcon for a list of pre-defined mini icons. If the icon value is positive it represents an icon in the class installer's executable, and will be extracted (the number one is reserved). This function also uses the INSTALLER value first and ENUMPROPPAGES value second to determine what executable to extract the icon(s) from. --*/ { return (pSetupDiLoadClassIcon(ClassGuid, LargeIcon, NULL, MiniIconIndex )); } BOOL WINAPI SetupDiGetClassBitmapIndex( IN CONST GUID *ClassGuid, OPTIONAL OUT PINT MiniIconIndex ) /*++ Routine Description: This routine retrieves the index of the mini-icon for the specified class. Arguments: ClassGuid - Optionally, supplies the GUID of the class to get the mini-icon index for. If this parameter is not specified, the index of the 'unknown' icon is returned. MiniIconIndex - Supplies a pointer to a variable that receives the index of the mini-icon for the specified class. This buffer is always filled in, and receives the index of the unknown mini-icon if there is no mini-icon for the specified class (i.e., the return value is FALSE). Return Value: If there was a mini-icon for the specified class, the return value is TRUE. If there was not a mini-icon for the specified class, or if no class was specified, the return value is FALSE (and GetLastError returns ERROR_NO_DEVICE_CLASS_ICON). In this case, MiniIconIndex will contain the index of the unknown mini-icon. --*/ { BOOL bRet = FALSE; // assume not found int i; PCLASSICON pci; DWORD Err; if(ClassGuid) { // // First check the built-in list. // for(i = 0; !bRet && (i < ARRAYSIZE(MiniIconXlate)); i++) { if(IsEqualGUID(MiniIconXlate[i].ClassGuid, ClassGuid)) { *MiniIconIndex = MiniIconXlate[i].MiniBitmapIndex; bRet = TRUE; } } // // Next check the "new stuff" list to see if it's there. // if(!bRet && LockMiniIconList(&GlobalMiniIconList)) { for(pci = GlobalMiniIconList.ClassIconList; !bRet && pci; pci = pci->Next) { if(IsEqualGUID(pci->ClassGuid, ClassGuid)) { *MiniIconIndex = pci->MiniBitmapIndex; bRet = TRUE; } } UnlockMiniIconList(&GlobalMiniIconList); } } // // If no match was found, snag the "unknown" class. // if(!bRet) { *MiniIconIndex = UnknownClassMiniIconIndex; Err = ERROR_NO_DEVICE_ICON; } else { Err = NO_ERROR; } SetLastError(Err); return bRet; } BOOL CreateMiniIcons( VOID ) /*++ Routine Description: This routine loads the default bitmap of mini-icons and turns it into the image/mask pair that will be the cornerstone of mini-icon management. THIS FUNCTION ASSUMES THE MINI-ICON LOCK HAS ALREADY BEEN ACQUIRED! Arguments: None. Return Value: If the function succeeds, the return value is TRUE, otherwise it is FALSE. --*/ { HDC hdc, hdcMem; HBITMAP hbmOld; BITMAP bm; BOOL bRet = FALSE; // assume failure if(GlobalMiniIconList.hdcMiniMem) { // // Then the mini-icon list has already been built, so // return success. // return TRUE; } hdc = GetDC(NULL); if (!hdc) { goto clean0; } GlobalMiniIconList.hdcMiniMem = CreateCompatibleDC(hdc); ReleaseDC(NULL, hdc); if(!GlobalMiniIconList.hdcMiniMem) { goto clean0; } if(!(hdcMem = CreateCompatibleDC(GlobalMiniIconList.hdcMiniMem))) { goto clean0; } if(!(GlobalMiniIconList.hbmMiniImage = LoadBitmap(MyDllModuleHandle, MAKEINTRESOURCE(BMP_DRIVERTYPES)))) { goto clean1; } GetObject(GlobalMiniIconList.hbmMiniImage, sizeof(bm), &bm); if(!(GlobalMiniIconList.hbmMiniMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL))) { goto clean1; } hbmOld = SelectObject(hdcMem, GlobalMiniIconList.hbmMiniImage); SelectObject(GlobalMiniIconList.hdcMiniMem, GlobalMiniIconList.hbmMiniMask ); // // make the mask. white where transparent, black where opaque // SetBkColor(hdcMem, RGB_TRANSPARENT); BitBlt(GlobalMiniIconList.hdcMiniMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY ); // // black-out all of the transparent parts of the image, in preparation // for drawing. // SetBkColor(hdcMem, RGB_BLACK); SetTextColor(hdcMem, RGB_WHITE); BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, GlobalMiniIconList.hdcMiniMem, 0, 0, SRCAND); SelectObject(GlobalMiniIconList.hdcMiniMem, hbmOld); GlobalMiniIconList.NumClassImages = bm.bmWidth/MINIX; bRet = TRUE; SelectObject(hdcMem, hbmOld); clean1: DeleteObject(hdcMem); clean0: // // If failure, clean up anything we might have built // if(!bRet) { DestroyMiniIcons(); } return bRet; } INT NewMiniIcon( IN CONST GUID *ClassGuid, IN HICON hIcon OPTIONAL ) /*++ Routine Description: It's a new class, and we have a mini-icon for it, so let's add it to the mini-icon database. First pull out the image and mask bitmaps. Then add these to the mini-icon bitmap. If this all works, add the new class to the list THIS FUNCTION ASSUMES THE MINI-ICON LOCK HAS ALREADY BEEN ACQUIRED! Arguments: ClassGuid - Supplies a pointer to the class GUID for this mini-icon. hIcon - Optionally, supplies a handle to the mini-icon to be added to the database. If this parameter is not specified, then the index for the "unknown class" icon will be returned. Return Value: Index for class (set to "unknown class" if error) --*/ { INT iBitmap = -1; ICONINFO ii; PCLASSICON pci = NULL; if(hIcon && GetIconInfo(hIcon, &ii)) { try { if((iBitmap = pSetupAddMiniIconToList(ii.hbmColor, ii.hbmMask)) != -1) { // // Allocate an extra GUID's worth of memory, so we can store // the class GUID in the same chunk of memory as the CLASSICON // node. // if(pci = (PCLASSICON)MyMalloc(sizeof(CLASSICON) + sizeof(GUID))) { CopyMemory((PBYTE)pci + sizeof(CLASSICON), ClassGuid, sizeof(GUID) ); pci->ClassGuid = (LPGUID)((PBYTE)pci + sizeof(CLASSICON)); pci->MiniBitmapIndex = iBitmap; pci->Next = GlobalMiniIconList.ClassIconList; GlobalMiniIconList.ClassIconList = pci; } } } except(EXCEPTION_EXECUTE_HANDLER) { if(pci) { MyFree(pci); } iBitmap = -1; } DeleteObject(ii.hbmColor); DeleteObject(ii.hbmMask); } if(iBitmap == -1) { SetupDiGetClassBitmapIndex(NULL, &iBitmap); } return iBitmap; } INT pSetupAddMiniIconToList( IN HBITMAP hbmImage, IN HBITMAP hbmMask ) /*++ Routine Description: Given the image and mask bitmaps of a mini-icon, add these to the mini-icon bitmap. THIS FUNCTION ASSUMES THE MINI-ICON LOCK HAS ALREADY BEEN ACQUIRED! Arguments: hbmImage - Supplies the handle of the bitmap for the mini-icon. hbmMask - Supplies the handle of the bitmap for the mini-icon's mask. Return Value: If success, returns the index of the added mini-icon. If failure, returns -1. --*/ { HBITMAP hbmNewImage, hbmNewMask, hbmOld; HDC hdcMem; BITMAP bm; INT iIcon = -1; // assume failure if(!CreateMiniIcons()) { goto AddIcon_Exit; } MYASSERT(GlobalMiniIconList.hdcMiniMem); if(!(hdcMem = CreateCompatibleDC(GlobalMiniIconList.hdcMiniMem))) { goto AddIcon_Exit; } // // Create a New global Bitmap for the minis // hbmOld = SelectObject(GlobalMiniIconList.hdcMiniMem, GlobalMiniIconList.hbmMiniImage ); if(!(hbmNewImage = CreateCompatibleBitmap(GlobalMiniIconList.hdcMiniMem, MINIX * (GlobalMiniIconList.NumClassImages + 1), MINIY))) { goto AddIcon_Exit1; } // // Copy the current Mini bitmap // SelectObject(hdcMem, hbmNewImage); BitBlt(hdcMem, 0, 0, MINIX * GlobalMiniIconList.NumClassImages, MINIY, GlobalMiniIconList.hdcMiniMem, 0, 0, SRCCOPY ); // // Fit the New icon into ours. We need to stretch it to fit correctly. // SelectObject(GlobalMiniIconList.hdcMiniMem, hbmImage); GetObject(hbmImage, sizeof(bm), &bm); StretchBlt(hdcMem, MINIX * GlobalMiniIconList.NumClassImages, 0, MINIX, MINIY, GlobalMiniIconList.hdcMiniMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY ); SelectObject(GlobalMiniIconList.hdcMiniMem, hbmOld); DeleteObject(GlobalMiniIconList.hbmMiniImage); GlobalMiniIconList.hbmMiniImage = hbmNewImage; // // Next, copy the mask. // hbmOld = SelectObject(GlobalMiniIconList.hdcMiniMem, GlobalMiniIconList.hbmMiniMask ); if(!(hbmNewMask = CreateBitmap(MINIX * (GlobalMiniIconList.NumClassImages + 1), MINIY, 1, 1, NULL))) { goto AddIcon_Exit1; } SelectObject(hdcMem, hbmNewMask); BitBlt(hdcMem, 0, 0, MINIX * GlobalMiniIconList.NumClassImages, MINIY, GlobalMiniIconList.hdcMiniMem, 0, 0, SRCCOPY ); SelectObject(GlobalMiniIconList.hdcMiniMem, hbmMask); GetObject(hbmMask, sizeof(bm), &bm); StretchBlt(hdcMem, MINIX * GlobalMiniIconList.NumClassImages, 0, MINIX, MINIY, GlobalMiniIconList.hdcMiniMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY ); SelectObject(GlobalMiniIconList.hdcMiniMem, hbmOld); DeleteObject(GlobalMiniIconList.hbmMiniMask); GlobalMiniIconList.hbmMiniMask = hbmNewMask; iIcon = GlobalMiniIconList.NumClassImages; GlobalMiniIconList.NumClassImages++; // one more image on the table AddIcon_Exit1: DeleteDC(hdcMem); AddIcon_Exit: return iIcon; } VOID DestroyMiniIcons( VOID ) /*++ Routine Description: This routine destroys the mini-icon bitmaps, if they exist. THIS FUNCTION ASSUMES THE MINI-ICON LOCK HAS ALREADY BEEN ACQUIRED! Arguments: None. Return Value: None. --*/ { PCLASSICON pci; if(GlobalMiniIconList.hdcMiniMem) { DeleteDC(GlobalMiniIconList.hdcMiniMem); GlobalMiniIconList.hdcMiniMem = NULL; } if(GlobalMiniIconList.hbmMiniImage) { DeleteObject(GlobalMiniIconList.hbmMiniImage); GlobalMiniIconList.hbmMiniImage = NULL; } if(GlobalMiniIconList.hbmMiniMask) { DeleteObject(GlobalMiniIconList.hbmMiniMask); GlobalMiniIconList.hbmMiniMask = NULL; } GlobalMiniIconList.NumClassImages = 0; // // Free up any additional class icon guys that were created // while(GlobalMiniIconList.ClassIconList) { pci = GlobalMiniIconList.ClassIconList; GlobalMiniIconList.ClassIconList = pci->Next; MyFree(pci); } } BOOL WINAPI SetupDiGetClassImageList( OUT PSP_CLASSIMAGELIST_DATA ClassImageListData ) /*++ Routine Description: See SetupDiGetClassImageListEx for details. --*/ { return SetupDiGetClassImageListEx(ClassImageListData, NULL, NULL); } #ifdef UNICODE // // ANSI version // BOOL WINAPI SetupDiGetClassImageListExA( OUT PSP_CLASSIMAGELIST_DATA ClassImageListData, IN PCSTR MachineName, OPTIONAL IN PVOID Reserved ) { PCWSTR UnicodeMachineName; DWORD rc; BOOL b; b = FALSE; if(MachineName) { rc = pSetupCaptureAndConvertAnsiArg(MachineName, &UnicodeMachineName); } else { UnicodeMachineName = NULL; rc = NO_ERROR; } if(rc == NO_ERROR) { b = SetupDiGetClassImageListExW(ClassImageListData, UnicodeMachineName, Reserved); rc = GetLastError(); if(UnicodeMachineName) { MyFree(UnicodeMachineName); } } SetLastError(rc); return b; } #else // // Unicode version // BOOL WINAPI SetupDiGetClassImageListExW( OUT PSP_CLASSIMAGELIST_DATA ClassImageListData, IN PCWSTR MachineName, OPTIONAL IN PVOID Reserved ) { UNREFERENCED_PARAMETER(ClassImageListData); UNREFERENCED_PARAMETER(MachineName); UNREFERENCED_PARAMETER(Reserved); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL WINAPI SetupDiGetClassImageListEx( OUT PSP_CLASSIMAGELIST_DATA ClassImageListData, IN PCTSTR MachineName, OPTIONAL IN PVOID Reserved ) /*++ Routine Description: This routine builds an image list containing bitmaps for every installed class, and returns a data structure containing the list. Arguments: ClassImageListData - Supplies the address of a SP_CLASSIMAGELIST_DATA structure that will receive information regarding the class list (including a handle to the image list). The cbSize field of this structure must be initialized with the size of the structure (in bytes) before calling this routine, or the API will fail. MachineName - Optionally, specifies the name of the remote machine whose installed classes are to be used in building the class image list. If this parameter is not specified, the local machine is used. NOTE: Presently, class-specific icons can only be displayed if the class is also present on the local machine. Thus, if the remote machine has class x, but class x is not installed locally, then the generic (unknown) icon will be returned. Reserved - Reserved for future use--must be NULL. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: The image list contained in structure filled in by this API should NOT be destroyed by calling ImageList_Destroy. Instead, SetupDiDestroyClassImageList should be called, for proper clean-up to occur. --*/ { DWORD Err = NO_ERROR; int cxMiniIcon, cyMiniIcon; int MiniIconIndex = 0, DefaultIndex = 0; int GuidCount, i; int iIcon, iIndex; HDC hDC = NULL, hMemImageDC = NULL; HBITMAP hbmMiniImage = NULL, hbmMiniMask = NULL, hbmOldImage = NULL; RECT rc; CONST GUID *pClassGuid = NULL; BOOL bUseBitmap, ComputerClassFound = FALSE; HICON hiLargeIcon = NULL, hiSmallIcon = NULL, hIcon = NULL; HBRUSH hOldBrush; PCLASSICON pci = NULL; PCLASS_IMAGE_LIST ImageData = NULL; BOOL DestroyLock = FALSE; HIMAGELIST ImageList = NULL; DWORD dwLayout = 0; UINT ImageListFlags = 0; // // Make sure the caller didn't pass us anything in the Reserved parameter. // if(Reserved) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } try { if(ClassImageListData->cbSize != sizeof(SP_CLASSIMAGELIST_DATA)) { Err = ERROR_INVALID_USER_BUFFER; goto clean0; } // // Allocate and initialize the image list, including setting up the // synchronization lock. Destroy it when done. // if(ImageData = MyMalloc(sizeof(CLASS_IMAGE_LIST))) { ZeroMemory(ImageData, sizeof(CLASS_IMAGE_LIST)); } else { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } if(InitializeSynchronizedAccess(&ImageData->Lock)) { DestroyLock = TRUE; } // // Build the class image list. Create an Image List with no mask, // 1 image, and a growth factor of 1. // cxMiniIcon = GetSystemMetrics(SM_CXSMICON); cyMiniIcon = GetSystemMetrics(SM_CYSMICON); ImageListFlags = ILC_MASK; // // Check which color depth we are running in. Set the ILC_COLOR32 // imagelist create flag if we are running at greate than 8bit (256) // color depth. // hDC = GetDC(NULL); if (hDC) { if (GetDeviceCaps(hDC, BITSPIXEL) > 8) { ImageListFlags |= ILC_COLOR32; } ReleaseDC(NULL, hDC); hDC = NULL; } #ifdef UNICODE // // If we are running on an RTL build then we need to set the ILC_MIRROR // flag when calling ImageList_Create to un-mirror the icons. By // default the icons are mirrored. // if (GetProcessDefaultLayout(&dwLayout) && (dwLayout & LAYOUT_RTL)) { ImageListFlags |= ILC_MIRROR; } #endif if(!(ImageList = ImageList_Create(cxMiniIcon, cyMiniIcon, ImageListFlags, 1, 1))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } ImageList_SetBkColor(ImageList, GetSysColor(COLOR_WINDOW)); // // Create a DC to draw the mini icons into. This is needed // for the system defined Minis // if(!(hDC = GetDC(HWND_DESKTOP)) || !(hMemImageDC = CreateCompatibleDC(hDC))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } // // Create a bitmap to draw the icons on. Defer checking for creation // of bitmap until afer freeing DC, so it only has to be done once. // hbmMiniImage = CreateCompatibleBitmap(hDC, cxMiniIcon, cyMiniIcon); hbmMiniMask = CreateCompatibleBitmap(hDC, cxMiniIcon, cyMiniIcon); ReleaseDC(HWND_DESKTOP, hDC); hDC = NULL; // // Did the bitmap get created? // if (!hbmMiniImage || ! hbmMiniMask) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } // // Select our bitmap into the memory DC. // hbmOldImage = SelectObject(hMemImageDC, hbmMiniImage); // // Prepare to draw the mini icon onto the memory DC // rc.left = 0; rc.top = 0; rc.right = cxMiniIcon; rc.bottom = cyMiniIcon; // // Get the Index of the Default mini icon. // SetupDiGetClassBitmapIndex(NULL, &DefaultIndex); // // Enumerate all classes, and for each class, draw its bitmap. // GuidCount = 32; ImageData->ClassGuidList = (LPGUID)MyMalloc(sizeof(GUID) * GuidCount); if (!SetupDiBuildClassInfoListEx(0, ImageData->ClassGuidList, GuidCount, &GuidCount, MachineName, NULL)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // // Realloc buffer and try again. // MyFree(ImageData->ClassGuidList); if(!(ImageData->ClassGuidList = MyMalloc(sizeof(GUID) * GuidCount))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } if (!SetupDiBuildClassInfoListEx(0, ImageData->ClassGuidList, GuidCount, &GuidCount, MachineName, NULL)) { Err = GetLastError(); goto clean0; } } else { Err = GetLastError(); goto clean0; } } // // Retrieve the icon for each class in the class list. // for (pClassGuid = ImageData->ClassGuidList, i = 0; i < GuidCount; pClassGuid++, i++) { if (!pSetupDiLoadClassIcon(pClassGuid, &hiLargeIcon, &hiSmallIcon, &MiniIconIndex)) { Err = GetLastError(); goto clean0; } // // If the returned Mini Icon index is not the Default one, then // we use the MiniBitmap, since it is a pre-defined one in SETUPAPI. // If the Mini is not pre-defined, and there is no Class Installer // then we use the Mini, since it is a valid default. If there // is no Mini, and there is a class installer, then we will use // the Class installer's big Icon, and have the Image list crunch // it for us. // bUseBitmap = FALSE; if (DefaultIndex != MiniIconIndex) { SetupDiDrawMiniIcon(hMemImageDC, rc, MiniIconIndex, DMI_USERECT); SelectObject(hMemImageDC, hbmMiniMask); SetupDiDrawMiniIcon(hMemImageDC, rc, MiniIconIndex, DMI_MASK | DMI_USERECT); bUseBitmap = TRUE; } // // Deselect the bitmap from our DC BEFORE calling ImageList // functions. // SelectObject(hMemImageDC, hbmOldImage); // // Add the image. Allocate a new PCI. // if(!(pci = (PCLASSICON)MyMalloc(sizeof(CLASSICON)))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } if (hiSmallIcon) { pci->MiniBitmapIndex = (UINT)ImageList_AddIcon(ImageList, hiSmallIcon); } else if (bUseBitmap) { pci->MiniBitmapIndex = (UINT)ImageList_Add(ImageList, hbmMiniImage, hbmMiniMask); } else { pci->MiniBitmapIndex = (UINT)ImageList_AddIcon(ImageList, hiLargeIcon); } if (hiLargeIcon) { DestroyIcon(hiLargeIcon); hiLargeIcon = NULL; } if (hiSmallIcon) { DestroyIcon(hiSmallIcon); hiSmallIcon = NULL; } if(pci->MiniBitmapIndex == (UINT)-1) { Err = ERROR_NOT_ENOUGH_MEMORY; MyFree(pci); pci = NULL; goto clean0; } pci->ClassGuid = pClassGuid; // // Link it in. // pci->Next = ImageData->ClassIconList; ImageData->ClassIconList = pci; // // Reset pci to NULL so we won't try to free it later. // pci = NULL; // // Select our bitmap back for the next ICON. // SelectObject(hMemImageDC, hbmMiniImage); if(IsEqualGUID(pClassGuid, &GUID_DEVCLASS_UNKNOWN)) { ImageData->UnknownImageIndex = i; } // // Check to see if we've encountered the computer class. This used // to be a special pseudo-class used solely by DevMgr to retrieve // the icon for the root of the device tree. Now, we use this class // for specifying the 'drivers' for the computer itself (i.e., the // HALs and the appropriate versions of files that are different for // MP vs. UP. // // We should encounter this class GUID, but if we don't, then we // want to maintain the old behavior of adding this in manually // later on. // if(!ComputerClassFound && IsEqualGUID(pClassGuid, &GUID_DEVCLASS_COMPUTER)) { ComputerClassFound = TRUE; } } if(!ComputerClassFound) { // // Special Case for the Internal Class "Computer" // if(!(pci = (PCLASSICON)MyMalloc(sizeof(CLASSICON)))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } pci->ClassGuid = &GUID_DEVCLASS_COMPUTER; SelectObject(hMemImageDC, hbmMiniImage); hOldBrush = SelectObject(hMemImageDC, GetSysColorBrush(COLOR_WINDOW)); PatBlt(hMemImageDC, 0, 0, cxMiniIcon, cyMiniIcon, PATCOPY); SelectObject(hMemImageDC, hOldBrush); SetupDiGetClassBitmapIndex((LPGUID)pci->ClassGuid, &MiniIconIndex); SetupDiDrawMiniIcon(hMemImageDC, rc, MiniIconIndex, DMI_USERECT); SelectObject(hMemImageDC, hbmMiniMask); SetupDiDrawMiniIcon(hMemImageDC, rc, MiniIconIndex, DMI_MASK | DMI_USERECT); // // Deselect the bitmap from our DC BEFORE calling ImageList // functions. // SelectObject(hMemImageDC, hbmOldImage); pci->MiniBitmapIndex = ImageList_Add(ImageList, hbmMiniImage, hbmMiniMask); if(pci->MiniBitmapIndex == (UINT)-1) { Err = ERROR_NOT_ENOUGH_MEMORY; MyFree(pci); pci = NULL; goto clean0; } // // Link it in. // pci->Next = ImageData->ClassIconList; ImageData->ClassIconList = pci; // // Reset pci to NULL so we won't try to free it later. // pci = NULL; } // // Add the Overlay ICONs. // for (iIcon = IDI_CLASSICON_OVERLAYFIRST; iIcon <= IDI_CLASSICON_OVERLAYLAST; ++iIcon) { if(!(hIcon = LoadIcon(MyDllModuleHandle, MAKEINTRESOURCE(iIcon)))) { Err = GetLastError(); goto clean0; } iIndex = ImageList_AddIcon(ImageList, hIcon); if(iIndex == -1) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } if(!ImageList_SetOverlayImage(ImageList, iIndex, iIcon - IDI_CLASSICON_OVERLAYFIRST + 1)) { Err = ERROR_INVALID_DATA; goto clean0; } } // // If we get to this point, then we've successfully constructed the entire // image list, and associated CLASSICON nodes. Now, store this information // in the caller's SP_CLASSIMAGELIST_DATA buffer. // ClassImageListData->Reserved = (ULONG_PTR)ImageData; ClassImageListData->ImageList = ImageList; clean0: ; // nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_USER_BUFFER; if(hDC) { ReleaseDC(HWND_DESKTOP, hDC); } if(pci) { MyFree(pci); } // // Reference the following variables so the compiler will respect statement // ordering w.r.t. assignment. // ImageData = ImageData; DestroyLock = DestroyLock; ImageList = ImageList; } if (hbmMiniImage) { DeleteObject(hbmMiniImage); } if (hbmMiniMask) { DeleteObject(hbmMiniMask); } if (hMemImageDC) { DeleteDC(hMemImageDC); } if(Err != NO_ERROR) { if(ImageData) { if(DestroyLock) { DestroySynchronizedAccess(&ImageData->Lock); } if(ImageData->ClassGuidList) { MyFree(ImageData->ClassGuidList); } while(ImageData->ClassIconList) { pci = ImageData->ClassIconList; ImageData->ClassIconList = pci->Next; MyFree(pci); } MyFree(ImageData); } if(ImageList) { ImageList_Destroy(ImageList); } } SetLastError(Err); return (Err == NO_ERROR); } BOOL WINAPI SetupDiDestroyClassImageList( IN PSP_CLASSIMAGELIST_DATA ClassImageListData ) /*++ Routine Description: This routine destroys a class image list built by a call to SetupDiGetClassImageList. Arguments: ClassImageListData - Supplies the address of a SP_CLASSIMAGELIST_DATA structure containing the class image list to be destroyed. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. --*/ { DWORD Err = NO_ERROR; PCLASS_IMAGE_LIST ImageData = NULL; PCLASSICON pci; try { if(ClassImageListData->cbSize != sizeof(SP_CLASSIMAGELIST_DATA)) { Err = ERROR_INVALID_USER_BUFFER; goto clean0; } if (ClassImageListData->Reserved == 0x0) { Err = ERROR_INVALID_USER_BUFFER; goto clean0; } ImageData = (PCLASS_IMAGE_LIST)ClassImageListData->Reserved; if (!LockImageList(ImageData)) { Err = ERROR_CANT_LOAD_CLASS_ICON; goto clean0; } if (ClassImageListData->ImageList) { ImageList_Destroy(ClassImageListData->ImageList); } if (ImageData->ClassGuidList) { MyFree(ImageData->ClassGuidList); } while(ImageData->ClassIconList) { pci = ImageData->ClassIconList; ImageData->ClassIconList = pci->Next; MyFree(pci); } DestroySynchronizedAccess(&ImageData->Lock); MyFree(ImageData); ClassImageListData->Reserved = 0; clean0: ; // nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_USER_BUFFER; } SetLastError(Err); return (Err == NO_ERROR); } BOOL WINAPI SetupDiGetClassImageIndex( IN PSP_CLASSIMAGELIST_DATA ClassImageListData, IN CONST GUID *ClassGuid, OUT PINT ImageIndex ) /*++ Routine Description: This routine retrieves the index within the class image list of a specified class. Arguments: ClassImageListData - Supplies the address of a SP_CLASSIMAGELIST_DATA structure containing the specified class's image. ClassGuid - Supplies the address of the GUID for the class whose index is to be retrieved. ImageIndex - Supplies the address of a variable that receives the index of the specified class's image within the class image list. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. --*/ { DWORD Err = NO_ERROR; BOOL bFound = FALSE, bLocked = FALSE; PCLASS_IMAGE_LIST ImageData = NULL; PCLASSICON pci; try { if(ClassImageListData->cbSize != sizeof(SP_CLASSIMAGELIST_DATA)) { Err = ERROR_INVALID_USER_BUFFER; goto clean0; } if (ClassImageListData->Reserved == 0x0) { Err = ERROR_INVALID_USER_BUFFER; goto clean0; } ImageData = (PCLASS_IMAGE_LIST)ClassImageListData->Reserved; if (!LockImageList(ImageData)) { Err = ERROR_CANT_LOAD_CLASS_ICON; goto clean0; } bLocked = TRUE; if (ClassGuid) { // // check the "new stuff" list to see if it's there // for (pci = ImageData->ClassIconList; !bFound && pci; pci = pci->Next) { if(IsEqualGUID(pci->ClassGuid, ClassGuid)) { *ImageIndex = pci->MiniBitmapIndex; bFound = TRUE; } } } // // if no match was found, snag the "unknown" class // if (!bFound) { *ImageIndex = ImageData->UnknownImageIndex; } clean0: ; // nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_USER_BUFFER; } if (bLocked && ImageData) { UnlockImageList(ImageData); } SetLastError(Err); return (Err == NO_ERROR); }