0

I am able to extract a file's icon and save it using the script below, but the script saves the icons in grey (seems to be 4 bit color depth).

How can I save icons while preserving their original color depth?

using namespace std;

#include <iostream>
#include <string>
#include <fstream>
#include <windows.h>

#include "commctrl.h"
#pragma comment(lib, "comctl32.lib")

#include <olectl.h>
#pragma comment(lib, "oleaut32.lib")

HRESULT SaveIcon(HICON hIcon, PCTSTR 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;

}

HICON GetIcon(PCTSTR pszFile)
{
    SHFILEINFO sfi;
    HIMAGELIST himl = reinterpret_cast<HIMAGELIST>(
        SHGetFileInfo(pszFile, 0, &sfi, sizeof(sfi),
            SHGFI_SYSICONINDEX));
    if (himl) {
        return ImageList_GetIcon(himl, sfi.iIcon, ILD_NORMAL);
    }
    else {
        return NULL;
    }
}

int main()
{
    string fileBaseName = "appName";
    wstring fileBaseNameWSTRING(fileBaseName.begin(), fileBaseName.end());

    HICON hIcon = GetIcon((fileBaseNameWSTRING + L".lnk").c_str());

    if (hIcon == NULL) {
        cout << "GetIcon failed" << endl;
        return 1;
    }
    else {
        HRESULT hr = SaveIcon(hIcon, (L"temp\\" + fileBaseNameWSTRING + L".ico").c_str());
        return hr;
    }
}

The SaveIcon function was taken from this page: How can I save HICON to an .ico file?

Naser AlOqab
  • 499
  • 4
  • 9

1 Answers1

0

Generally, icons contain multiple original bit depths, also multiple resolutions. APIs which return HICON won’t do, they pick a single image from the icon.

If you want the complete one, change SHGFI_SYSICONINDEX to SHGFI_ICONLOCATION, you’ll get path to a DLL with icon + index of the icon.

Load the DLL, ideally with LoadLibraryEx API and LOAD_LIBRARY_AS_DATAFILE option.

Then call resource APIs, FindResource / LoadResource / SizeofResource / LockResource, to get the source data for the icon.

Soonts
  • 20,079
  • 9
  • 57
  • 130