如何将HICON保存到.ico文件?

我从.exe / .dll中提取图标,并将其保存在.ico文件中。 做这个的最好方式是什么?

我试图使用::OleCreatePictureIndirect() ,然后IPicture->SaveAsFile() 。 它的作品,但图标的透明部分被漆成黑色(显然是不透明的:()。

我尝试手动parsing。 它工作正常,但是很麻烦,我怕与Vista的图标/ .icl文件/等的复杂性。

请帮忙。 谢谢。

Solutions Collecting From Web of "如何将HICON保存到.ico文件?"

您可以使用IPicture::SaveAsFile()方法保存HICON 下面是一个使用它的示例C ++程序:

 #include "stdafx.h" #include <windows.h> #include <olectl.h> #pragma comment(lib, "oleaut32.lib") HRESULT SaveIcon(HICON hIcon, const wchar_t* path) { // Create the IPicture intrface PICTDESC desc = { sizeof(PICTDESC) }; desc.picType = PICTYPE_ICON; desc.icon.hicon = hIcon; IPicture* pPicture = 0; HRESULT hr = OleCreatePictureIndirect(&desc, IID_IPicture, FALSE, (void**)&pPicture); if (FAILED(hr)) return hr; // Create a stream and save the image IStream* pStream = 0; CreateStreamOnHGlobal(0, TRUE, &pStream); LONG cbSize = 0; hr = pPicture->SaveAsFile(pStream, TRUE, &cbSize); // Write the stream content to the file if (!FAILED(hr)) { HGLOBAL hBuf = 0; GetHGlobalFromStream(pStream, &hBuf); void* buffer = GlobalLock(hBuf); HANDLE hFile = CreateFile(path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); if (!hFile) hr = HRESULT_FROM_WIN32(GetLastError()); else { DWORD written = 0; WriteFile(hFile, buffer, cbSize, &written, 0); CloseHandle(hFile); } GlobalUnlock(buffer); } // Cleanup pStream->Release(); pPicture->Release(); return hr; } int _tmain(int argc, _TCHAR* argv[]) { HICON hIcon = (HICON)LoadImage(0, L"c:\\windows\\system32\\perfcentercpl.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE); if (!hIcon) return GetLastError(); HRESULT hr = SaveIcon(hIcon, L"c:\\temp\\test.ico"); return hr; } 

此代码正确处理透明度。

 #include <windows.h> #include <stdio.h> #include <tchar.h> // // ICONS (.ICO type 1) are structured like this: // // ICONHEADER (just 1) // ICONDIR [1...n] (an array, 1 for each image) // [BITMAPINFOHEADER+COLOR_BITS+MASK_BITS] [1...n] (1 after the other, for each image) // // CURSORS (.ICO type 2) are identical in structure, but use // two monochrome bitmaps (real XOR and AND masks, this time). // typedef struct { WORD idReserved; // must be 0 WORD idType; // 1 = ICON, 2 = CURSOR WORD idCount; // number of images (and ICONDIRs) // ICONDIR [1...n] // ICONIMAGE [1...n] } ICONHEADER; // // An array of ICONDIRs immediately follow the ICONHEADER // typedef struct { BYTE bWidth; BYTE bHeight; BYTE bColorCount; BYTE bReserved; WORD wPlanes; // for cursors, this field = wXHotSpot WORD wBitCount; // for cursors, this field = wYHotSpot DWORD dwBytesInRes; DWORD dwImageOffset; // file-offset to the start of ICONIMAGE } ICONDIR; // // After the ICONDIRs follow the ICONIMAGE structures - // consisting of a BITMAPINFOHEADER, (optional) RGBQUAD array, then // the color and mask bitmap bits (all packed together // typedef struct { BITMAPINFOHEADER biHeader; // header for color bitmap (no mask header) //RGBQUAD rgbColors[1...n]; //BYTE bXOR[1]; // DIB bits for color bitmap //BYTE bAND[1]; // DIB bits for mask bitmap } ICONIMAGE; // // Write the ICO header to disk // static UINT WriteIconHeader(HANDLE hFile, int nImages) { ICONHEADER iconheader; DWORD nWritten; // Setup the icon header iconheader.idReserved = 0; // Must be 0 iconheader.idType = 1; // Type 1 = ICON (type 2 = CURSOR) iconheader.idCount = nImages; // number of ICONDIRs // Write the header to disk WriteFile( hFile, &iconheader, sizeof(iconheader), &nWritten, 0); // following ICONHEADER is a series of ICONDIR structures (idCount of them, in fact) return nWritten; } // // Return the number of BYTES the bitmap will take ON DISK // static UINT NumBitmapBytes(BITMAP *pBitmap) { int nWidthBytes = pBitmap->bmWidthBytes; // bitmap scanlines MUST be a multiple of 4 bytes when stored // inside a bitmap resource, so round up if necessary if(nWidthBytes & 3) nWidthBytes = (nWidthBytes + 4) & ~3; return nWidthBytes * pBitmap->bmHeight; } // // Return number of bytes written // static UINT WriteIconImageHeader(HANDLE hFile, BITMAP *pbmpColor, BITMAP *pbmpMask) { BITMAPINFOHEADER biHeader; DWORD nWritten; UINT nImageBytes; // calculate how much space the COLOR and MASK bitmaps take nImageBytes = NumBitmapBytes(pbmpColor) + NumBitmapBytes(pbmpMask); // write the ICONIMAGE to disk (first the BITMAPINFOHEADER) ZeroMemory(&biHeader, sizeof(biHeader)); // Fill in only those fields that are necessary biHeader.biSize = sizeof(biHeader); biHeader.biWidth = pbmpColor->bmWidth; biHeader.biHeight = pbmpColor->bmHeight * 2; // height of color+mono biHeader.biPlanes = pbmpColor->bmPlanes; biHeader.biBitCount = pbmpColor->bmBitsPixel; biHeader.biSizeImage = nImageBytes; // write the BITMAPINFOHEADER WriteFile(hFile, &biHeader, sizeof(biHeader), &nWritten, 0); // write the RGBQUAD color table (for 16 and 256 colour icons) if(pbmpColor->bmBitsPixel == 2 || pbmpColor->bmBitsPixel == 8) { } return nWritten; } // // Wrapper around GetIconInfo and GetObject(BITMAP) // static BOOL GetIconBitmapInfo(HICON hIcon, ICONINFO *pIconInfo, BITMAP *pbmpColor, BITMAP *pbmpMask) { if(!GetIconInfo(hIcon, pIconInfo)) return FALSE; if(!GetObject(pIconInfo->hbmColor, sizeof(BITMAP), pbmpColor)) return FALSE; if(!GetObject(pIconInfo->hbmMask, sizeof(BITMAP), pbmpMask)) return FALSE; return TRUE; } // // Write one icon directory entry - specify the index of the image // static UINT WriteIconDirectoryEntry(HANDLE hFile, int nIdx, HICON hIcon, UINT nImageOffset) { ICONINFO iconInfo; ICONDIR iconDir; BITMAP bmpColor; BITMAP bmpMask; DWORD nWritten; UINT nColorCount; UINT nImageBytes; GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask); nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask); if(bmpColor.bmBitsPixel >= 8) nColorCount = 0; else nColorCount = 1 << (bmpColor.bmBitsPixel * bmpColor.bmPlanes); // Create the ICONDIR structure iconDir.bWidth = (BYTE)bmpColor.bmWidth; iconDir.bHeight = (BYTE)bmpColor.bmHeight; iconDir.bColorCount = nColorCount; iconDir.bReserved = 0; iconDir.wPlanes = bmpColor.bmPlanes; iconDir.wBitCount = bmpColor.bmBitsPixel; iconDir.dwBytesInRes = sizeof(BITMAPINFOHEADER) + nImageBytes; iconDir.dwImageOffset = nImageOffset; // Write to disk WriteFile(hFile, &iconDir, sizeof(iconDir), &nWritten, 0); // Free resources DeleteObject(iconInfo.hbmColor); DeleteObject(iconInfo.hbmMask); return nWritten; } static UINT WriteIconData(HANDLE hFile, HBITMAP hBitmap) { BITMAP bmp; int i; BYTE * pIconData; UINT nBitmapBytes; DWORD nWritten; GetObject(hBitmap, sizeof(BITMAP), &bmp); nBitmapBytes = NumBitmapBytes(&bmp); pIconData = (BYTE *)malloc(nBitmapBytes); GetBitmapBits(hBitmap, nBitmapBytes, pIconData); // bitmaps are stored inverted (vertically) when on disk.. // so write out each line in turn, starting at the bottom + working // towards the top of the bitmap. Also, the bitmaps are stored in packed // in memory - scanlines are NOT 32bit aligned, just 1-after-the-other for(i = bmp.bmHeight - 1; i >= 0; i--) { // Write the bitmap scanline WriteFile( hFile, pIconData + (i * bmp.bmWidthBytes), // calculate offset to the line bmp.bmWidthBytes, // 1 line of BYTES &nWritten, 0); // extend to a 32bit boundary (in the file) if necessary if(bmp.bmWidthBytes & 3) { DWORD padding = 0; WriteFile(hFile, &padding, 4 - bmp.bmWidthBytes, &nWritten, 0); } } free(pIconData); return nBitmapBytes; } // // Create a .ICO file, using the specified array of HICON images // BOOL SaveIcon3(TCHAR *szIconFile, HICON hIcon[], int nNumIcons) { HANDLE hFile; int i; int * pImageOffset; if(hIcon == 0 || nNumIcons < 1) return FALSE; // Save icon to disk: hFile = CreateFile(szIconFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); if(hFile == INVALID_HANDLE_VALUE) return FALSE; // // Write the iconheader first of all // WriteIconHeader(hFile, nNumIcons); // // Leave space for the IconDir entries // SetFilePointer(hFile, sizeof(ICONDIR) * nNumIcons, 0, FILE_CURRENT); pImageOffset = (int *)malloc(nNumIcons * sizeof(int)); // // Now write the actual icon images! // for(i = 0; i < nNumIcons; i++) { ICONINFO iconInfo; BITMAP bmpColor, bmpMask; GetIconBitmapInfo(hIcon[i], &iconInfo, &bmpColor, &bmpMask); // record the file-offset of the icon image for when we write the icon directories pImageOffset[i] = SetFilePointer(hFile, 0, 0, FILE_CURRENT); // bitmapinfoheader + colortable WriteIconImageHeader(hFile, &bmpColor, &bmpMask); // color and mask bitmaps WriteIconData(hFile, iconInfo.hbmColor); WriteIconData(hFile, iconInfo.hbmMask); DeleteObject(iconInfo.hbmColor); DeleteObject(iconInfo.hbmMask); } // // Lastly, skip back and write the icon directories. // SetFilePointer(hFile, sizeof(ICONHEADER), 0, FILE_BEGIN); for(i = 0; i < nNumIcons; i++) { WriteIconDirectoryEntry(hFile, i, hIcon[i], pImageOffset[i]); } free(pImageOffset); // finished! CloseHandle(hFile); return TRUE; } int saveIcon(TCHAR* filename, TCHAR* iconFile) { HICON hIconLarge; HICON hIconSmall; BOOL ret; if ( ExtractIconEx(filename, 0, &hIconLarge, &hIconSmall, 1) == 0 ) { return 1; } ret = SaveIcon3(iconFile, &hIconSmall, 1); if ( ret ) { return 0; } return -1; } int _tmain(int argc, TCHAR* argv[]) { if ( argc < 3 ) { printf("Usage: <exe/dll file> <output ico file>"); return EXIT_FAILURE; } _tprintf(_T("src = %s\n"), argv[1]); _tprintf(_T("dest = %s\n"), argv[2]); saveIcon(argv[1], argv[2]); return 0; } 

我有同样的问题,因此我写了一个功能,以保存从一个HICON句柄图标到一个ICO文件。 下面显示的SaveIcon()函数支持SaveIcon()和32位的颜色深度。 PNG图标格式不受支持。

该功能通过直接构建ICO文件来工作。 幸运的是,这并不难,因为ICO格式与BMP文件格式几乎相同; 此外,BMP文件格式与GetDIBits()返回的内存中表示几乎相同。

这里是SaveIcon()函数和一个小测试函数( _tmain ):

 #include <afx.h> #include <afxwin.h> #include <atlbase.h> struct ICONDIRENTRY { UCHAR nWidth; UCHAR nHeight; UCHAR nNumColorsInPalette; // 0 if no palette UCHAR nReserved; // should be 0 WORD nNumColorPlanes; // 0 or 1 WORD nBitsPerPixel; ULONG nDataLength; // length in bytes ULONG nOffset; // offset of BMP or PNG data from beginning of file }; // Helper class to release GDI object handle when scope ends: class CGdiHandle { public: CGdiHandle(HGDIOBJ handle) : m_handle(handle) {}; ~CGdiHandle(){DeleteObject(m_handle);}; private: HGDIOBJ m_handle; }; // Save icon referenced by handle 'hIcon' as file with name 'szPath'. // The generated ICO file has the color depth specified in 'nColorBits'. // bool SaveIcon(HICON hIcon, int nColorBits, const TCHAR* szPath) { ASSERT(nColorBits == 4 || nColorBits == 8 || nColorBits == 24 || nColorBits == 32); if (offsetof(ICONDIRENTRY, nOffset) != 12) { return false; } CDC dc; dc.Attach(::GetDC(NULL)); // ensure that DC is released when function ends // Open file for writing: CFile file; if (!file.Open(szPath, CFile::modeWrite | CFile::modeCreate)) { return false; } // Write header: UCHAR icoHeader[6] = {0, 0, 1, 0, 1, 0}; // ICO file with 1 image file.Write(icoHeader, sizeof(icoHeader)); // Get information about icon: ICONINFO iconInfo; GetIconInfo(hIcon, &iconInfo); CGdiHandle handle1(iconInfo.hbmColor), handle2(iconInfo.hbmMask); // free bitmaps when function ends BITMAPINFO bmInfo = {0}; bmInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmInfo.bmiHeader.biBitCount = 0; // don't get the color table if (!GetDIBits(dc, iconInfo.hbmColor, 0, 0, NULL, &bmInfo, DIB_RGB_COLORS)) { return false; } // Allocate size of bitmap info header plus space for color table: int nBmInfoSize = sizeof(BITMAPINFOHEADER); if (nColorBits < 24) { nBmInfoSize += sizeof(RGBQUAD) * (int)(1 << nColorBits); } CAutoVectorPtr<UCHAR> bitmapInfo; bitmapInfo.Allocate(nBmInfoSize); BITMAPINFO* pBmInfo = (BITMAPINFO*)(UCHAR*)bitmapInfo; memcpy(pBmInfo, &bmInfo, sizeof(BITMAPINFOHEADER)); // Get bitmap data: ASSERT(bmInfo.bmiHeader.biSizeImage != 0); CAutoVectorPtr<UCHAR> bits; bits.Allocate(bmInfo.bmiHeader.biSizeImage); pBmInfo->bmiHeader.biBitCount = nColorBits; pBmInfo->bmiHeader.biCompression = BI_RGB; if (!GetDIBits(dc, iconInfo.hbmColor, 0, bmInfo.bmiHeader.biHeight, (UCHAR*)bits, pBmInfo, DIB_RGB_COLORS)) { return false; } // Get mask data: BITMAPINFO maskInfo = {0}; maskInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); maskInfo.bmiHeader.biBitCount = 0; // don't get the color table if (!GetDIBits(dc, iconInfo.hbmMask, 0, 0, NULL, &maskInfo, DIB_RGB_COLORS)) { return false; } ASSERT(maskInfo.bmiHeader.biBitCount == 1); CAutoVectorPtr<UCHAR> maskBits; maskBits.Allocate(maskInfo.bmiHeader.biSizeImage); CAutoVectorPtr<UCHAR> maskInfoBytes; maskInfoBytes.Allocate(sizeof(BITMAPINFO) + 2 * sizeof(RGBQUAD)); BITMAPINFO* pMaskInfo = (BITMAPINFO*)(UCHAR*)maskInfoBytes; memcpy(pMaskInfo, &maskInfo, sizeof(maskInfo)); if (!GetDIBits(dc, iconInfo.hbmMask, 0, maskInfo.bmiHeader.biHeight, (UCHAR*)maskBits, pMaskInfo, DIB_RGB_COLORS)) { return false; } // Write directory entry: ICONDIRENTRY dir; dir.nWidth = (UCHAR) pBmInfo->bmiHeader.biWidth; dir.nHeight = (UCHAR) pBmInfo->bmiHeader.biHeight; dir.nNumColorsInPalette = (nColorBits == 4 ? 16 : 0); dir.nReserved = 0; dir.nNumColorPlanes = 0; dir.nBitsPerPixel = pBmInfo->bmiHeader.biBitCount; dir.nDataLength = pBmInfo->bmiHeader.biSizeImage + pMaskInfo->bmiHeader.biSizeImage + nBmInfoSize; dir.nOffset = sizeof(dir) + sizeof(icoHeader); file.Write(&dir, sizeof(dir)); // Write DIB header (including color table): int nBitsSize = pBmInfo->bmiHeader.biSizeImage; pBmInfo->bmiHeader.biHeight *= 2; // because the header is for both image and mask pBmInfo->bmiHeader.biCompression = 0; pBmInfo->bmiHeader.biSizeImage += pMaskInfo->bmiHeader.biSizeImage; // because the header is for both image and mask file.Write(&pBmInfo->bmiHeader, nBmInfoSize); // Write image data: file.Write((UCHAR*)bits, nBitsSize); // Write mask data: file.Write((UCHAR*)maskBits, pMaskInfo->bmiHeader.biSizeImage); file.Close(); return true; } // Test program for SaveIcon() function. // // Usage: first argument is input ICO file (must be 32x32 pixels); second argument is output ICO file // int _tmain(int argc, _TCHAR* argv[]) { ASSERT(argc == 3); // Load a 32x32 icon: HICON hIcon = (HICON)LoadImage(0, argv[1], IMAGE_ICON, 32, 32, LR_LOADFROMFILE | LR_CREATEDIBSECTION); ASSERT(hIcon != NULL); // Save with 24-bits colors: if (!SaveIcon(hIcon, 24, argv[2])) { _ftprintf(stderr, _T("Error: saving icon to %s failed"), argv[2]); return EXIT_FAILURE; } return EXIT_SUCCESS; }