2010-02-18 42 views
9

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

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

请帮助谢谢

回答

8

您可以保存HICON s的的​​方法下面是一个使用一个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; 
} 
+3

使用此代码,ICO文件始终以16种颜色(即4位颜色深度)保存。有什么方法可以保留原始图标(由HICON句柄引用的那个图标)的颜色深度? – 2012-08-22 14:49:36

3

我有同样的问题,所以我wr请注意将图标从HICON句柄保存到ICO文件的功能。下面显示的SaveIcon()函数支持4,8,24和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; 
} 
+0

当包含windows.h时,这不起作用 – SMUsamaShah

5

此代码正确地处理的透明度。

#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; 
}