aboutsummaryrefslogtreecommitdiffstats
path: root/examples/winextras/iconextractor
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2016-08-01 11:14:37 +0200
committerLiang Qi <liang.qi@qt.io>2016-08-01 11:14:41 +0200
commit93a998f5ef31f7d9f8dd72909c4342db84cc1b59 (patch)
treebd3cf0cd679166978eaf7c7b5e9e99740cd8bda9 /examples/winextras/iconextractor
parentdf31e3295eb114452bf4176ebdc1aa303fbe2b42 (diff)
parent4d09bf3dd58b1ccf67a8f6e9623964684b6f231a (diff)
Merge remote-tracking branch 'origin/5.6' into 5.7
Diffstat (limited to 'examples/winextras/iconextractor')
-rw-r--r--examples/winextras/iconextractor/iconextractor.pro2
-rw-r--r--examples/winextras/iconextractor/main.cpp277
2 files changed, 233 insertions, 46 deletions
diff --git a/examples/winextras/iconextractor/iconextractor.pro b/examples/winextras/iconextractor/iconextractor.pro
index a8a967c..66c6dad 100644
--- a/examples/winextras/iconextractor/iconextractor.pro
+++ b/examples/winextras/iconextractor/iconextractor.pro
@@ -2,7 +2,7 @@ TEMPLATE = app
TARGET = iconextractor
CONFIG += console
QT = core gui winextras
-LIBS += -lshell32
+LIBS += -lshell32 -luser32
SOURCES += main.cpp
target.path = $$[QT_INSTALL_EXAMPLES]/winextras/iconextractor
diff --git a/examples/winextras/iconextractor/main.cpp b/examples/winextras/iconextractor/main.cpp
index 69fec10..eb39f9f 100644
--- a/examples/winextras/iconextractor/main.cpp
+++ b/examples/winextras/iconextractor/main.cpp
@@ -50,75 +50,262 @@
#include <QtWin>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include <QDir>
+#include <QFileInfo>
#include <QGuiApplication>
+#include <QImage>
+#include <QPixmap>
#include <QScopedArrayPointer>
#include <QStringList>
-#include <QPixmap>
-#include <QImage>
-#include <QFileInfo>
-#include <QDir>
+#include <QSysInfo>
+
#include <iostream>
+#include <shellapi.h>
+#include <comdef.h>
+#include <commctrl.h>
+#include <objbase.h>
+#include <commoncontrols.h>
+
/* This example demonstrates the Windows-specific image conversion
* functions. */
-int main(int argc, char *argv[])
+struct PixmapEntry {
+ QString name;
+ QPixmap pixmap;
+};
+
+typedef QList<PixmapEntry> PixmapEntryList;
+
+static std::wostream &operator<<(std::wostream &str, const QString &s)
{
- QGuiApplication a(argc, argv);
-
- QStringList arguments = QCoreApplication::arguments();
- arguments.pop_front();
- const bool large = !arguments.isEmpty() && arguments.front() == "-l";
- if (large)
- arguments.pop_front();
- if (arguments.size() < 1) {
- std::cout << "Usage: iconextractor [OPTIONS] FILE [IMAGE_FILE_FOLDER]\n\n"
- "Extracts Windows icons from executables, DLL or icon files\n"
- "and writes them out as numbered .png-files.\n\n"
- "Options: -l Extract large icons.\n\n"
- "Based on Qt " << QT_VERSION_STR << "\n";
- return 1;
- }
- const QString sourceFile = arguments.at(0);
- QString imageFileRoot = arguments.size() > 1 ? arguments.at(1) : QDir::currentPath();
- const QFileInfo imageFileRootInfo(imageFileRoot);
- if (!imageFileRootInfo.isDir()) {
- std::cerr << imageFileRoot.toStdString() << " is not a directory.\n";
- return 1;
- }
+#ifdef Q_OS_WIN
+ str << reinterpret_cast<const wchar_t *>(s.utf16());
+#else
+ str << s.toStdWString();
+#endif
+ return str;
+}
- const UINT iconCount = ExtractIconEx((wchar_t *)sourceFile.utf16(), -1, 0, 0, 0);
+static QString formatSize(const QSize &size)
+{
+ return QString::number(size.width()) + QLatin1Char('x') + QString::number(size.height());
+}
+
+// Extract icons contained in executable or DLL using the Win32 API ExtractIconEx()
+static PixmapEntryList extractIcons(const QString &sourceFile, bool large)
+{
+ const QString nativeName = QDir::toNativeSeparators(sourceFile);
+ const wchar_t *sourceFileC = reinterpret_cast<const wchar_t *>(nativeName.utf16());
+ const UINT iconCount = ExtractIconEx(sourceFileC, -1, 0, 0, 0);
if (!iconCount) {
- std::cerr << sourceFile.toStdString() << " does not appear to contain icons.\n";
- return 1;
+ std::wcerr << sourceFile << " does not appear to contain icons.\n";
+ return PixmapEntryList();
}
QScopedArrayPointer<HICON> icons(new HICON[iconCount]);
const UINT extractedIconCount = large ?
- ExtractIconEx((wchar_t *)sourceFile.utf16(), 0, icons.data(), 0, iconCount) :
- ExtractIconEx((wchar_t *)sourceFile.utf16(), 0, 0, icons.data(), iconCount);
+ ExtractIconEx(sourceFileC, 0, icons.data(), 0, iconCount) :
+ ExtractIconEx(sourceFileC, 0, 0, icons.data(), iconCount);
if (!extractedIconCount) {
qErrnoWarning("Failed to extract icons from %s", qPrintable(sourceFile));
- return 1;
+ return PixmapEntryList();
}
- std::cout << sourceFile.toStdString() << " contains " << extractedIconCount << " icon(s).\n";
+ PixmapEntryList result;
+ result.reserve(int(extractedIconCount));
+
+ std::wcout << sourceFile << " contains " << extractedIconCount << " icon(s).\n";
- imageFileRoot = imageFileRootInfo.absoluteFilePath() + QLatin1Char('/') + QFileInfo(sourceFile).baseName();
for (UINT i = 0; i < extractedIconCount; ++i) {
- const QPixmap pixmap = QtWin::fromHICON(icons[i]);
- if (pixmap.isNull()) {
- std::cerr << "Error converting icons.\n";
- return 1;
+ PixmapEntry entry;
+ entry.pixmap = QtWin::fromHICON(icons[i]);
+ if (entry.pixmap.isNull()) {
+ std::wcerr << "Error converting icons.\n";
+ return PixmapEntryList();
+ }
+ entry.name = QString::fromLatin1("%1_%2x%3").arg(i, 3, 10, QLatin1Char('0'))
+ .arg(entry.pixmap.width()).arg(entry.pixmap.height());
+ result.append(entry);
+ }
+ return result;
+}
+
+// Helper for extracting large/jumbo icons available from Windows Vista onwards
+// via SHGetImageList().
+static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info)
+{
+ QPixmap result;
+ // For MinGW:
+ static const IID iID_IImageList = {0x46eb5926, 0x582e, 0x4017, {0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x9, 0x50}};
+
+ IImageList *imageList = Q_NULLPTR;
+ if (FAILED(SHGetImageList(iImageList, iID_IImageList, reinterpret_cast<void **>(&imageList))))
+ return result;
+
+ HICON hIcon = 0;
+ if (SUCCEEDED(imageList->GetIcon(info.iIcon, ILD_TRANSPARENT, &hIcon))) {
+ result = QtWin::fromHICON(hIcon);
+ DestroyIcon(hIcon);
+ }
+ return result;
+}
+
+// Extract icons that would be displayed by the Explorer (shell)
+static PixmapEntryList extractShellIcons(const QString &sourceFile, bool addOverlays)
+{
+ enum { // Shell image list ids
+ sHIL_EXTRALARGE = 0x2, // 48x48 or user-defined
+ sHIL_JUMBO = 0x4 // 256x256 (Vista or later)
+ };
+
+ struct FlagEntry {
+ const char *name;
+ unsigned flags;
+ };
+
+ const FlagEntry modeEntries[] =
+ {
+ {"", 0},
+ {"open", SHGFI_OPENICON},
+ {"sel", SHGFI_SELECTED},
+ };
+ const FlagEntry standardSizeEntries[] =
+ {
+ {"s", SHGFI_SMALLICON},
+ {"l", SHGFI_LARGEICON},
+ {"sh", SHGFI_SHELLICONSIZE},
+ };
+
+ const QString nativeName = QDir::toNativeSeparators(sourceFile);
+ const wchar_t *sourceFileC = reinterpret_cast<const wchar_t *>(nativeName.utf16());
+
+ SHFILEINFO info;
+ unsigned int baseFlags = SHGFI_ICON | SHGFI_SYSICONINDEX | SHGFI_ICONLOCATION;
+ if (addOverlays)
+ baseFlags |= SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX;
+ if (!QFileInfo(sourceFile).isDir())
+ baseFlags |= SHGFI_USEFILEATTRIBUTES;
+
+ const size_t modeEntryCount = sizeof(modeEntries) / sizeof(modeEntries[0]);
+ const size_t standardSizeEntryCount = sizeof(standardSizeEntries) / sizeof(standardSizeEntries[0]);
+ PixmapEntryList result;
+ for (size_t m = 0; m < modeEntryCount; ++m) {
+ const unsigned modeFlags = baseFlags | modeEntries[m].flags;
+ QString modePrefix = QLatin1String("_shell_");
+ if (modeEntries[m].name[0])
+ modePrefix += QLatin1String(modeEntries[m].name) + QLatin1Char('_');
+ for (size_t s = 0; s < standardSizeEntryCount; ++s) {
+ const unsigned flags = modeFlags | standardSizeEntries[s].flags;
+ const QString prefix = modePrefix + QLatin1String(standardSizeEntries[s].name)
+ + QLatin1Char('_');
+ ZeroMemory(&info, sizeof(SHFILEINFO));
+ const HRESULT hr = SHGetFileInfo(sourceFileC, 0, &info, sizeof(SHFILEINFO), flags);
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ std::wcerr << "SHGetFileInfo() failed for \"" << nativeName << "\", "
+ << std::hex << std::showbase << flags << std::dec << std::noshowbase
+ << " (" << prefix << "): " << error.ErrorMessage() << '\n';
+ continue;
+ }
+
+ if (info.hIcon) {
+ PixmapEntry entry;
+ entry.pixmap = QtWin::fromHICON(info.hIcon);
+ DestroyIcon(info.hIcon);
+ if (entry.pixmap.isNull()) {
+ std::wcerr << "Error converting icons.\n";
+ return PixmapEntryList();
+ }
+ entry.name = prefix + formatSize(entry.pixmap.size());
+
+ const int iconIndex = info.iIcon & 0xFFFFFF;
+ const int overlayIconIndex = info.iIcon >> 24;
+
+ std::wcout << "Obtained icon #" << iconIndex;
+ if (addOverlays)
+ std::wcout << " (overlay #" << overlayIconIndex << ')';
+ if (info.szDisplayName[0])
+ std::wcout << " from " << QString::fromWCharArray(info.szDisplayName);
+ std::wcout << " (" << entry.pixmap.width() << 'x'
+ << entry.pixmap.height() << ") for " << std::hex << std::showbase << flags
+ << std::dec << std::noshowbase << '\n';
+
+ result.append(entry);
+ }
+ } // for standardSizeEntryCount
+ // Windows Vista onwards: extract large/jumbo icons
+ if (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA && info.hIcon) {
+ const QPixmap extraLarge = pixmapFromShellImageList(sHIL_EXTRALARGE, info);
+ if (!extraLarge.isNull()) {
+ PixmapEntry entry;
+ entry.pixmap = extraLarge;
+ entry.name = modePrefix + QLatin1String("xl_") + formatSize(extraLarge.size());
+ result.append(entry);
+ }
+ const QPixmap jumbo = pixmapFromShellImageList(sHIL_JUMBO, info);
+ if (!jumbo.isNull()) {
+ PixmapEntry entry;
+ entry.pixmap = jumbo;
+ entry.name = modePrefix + QLatin1String("jumbo_") + formatSize(extraLarge.size());
+ result.append(entry);
+ }
}
- const QString fileName = QString::fromLatin1("%1%2.png").arg(imageFileRoot)
- .arg(i, 3, 10, QLatin1Char('0'));
- if (!pixmap.save(fileName)) {
- std::cerr << "Error writing image file " << fileName.toStdString() << ".\n";
+ } // for modes
+ return result;
+}
+
+static const char description[] =
+ "\nExtracts Windows icons from executables, DLL or icon files and writes them\n"
+ "out as numbered .png-files.\n"
+ "When passing the --shell option, the icons displayed by Explorer are extracted.\n";
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+ QCoreApplication::setApplicationName("Icon Extractor");
+ QCoreApplication::setOrganizationName("QtProject");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCommandLineParser parser;
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions);
+ parser.setApplicationDescription(QLatin1String(description));
+ parser.addHelpOption();
+ parser.addVersionOption();
+ const QCommandLineOption largeIconOption("large", "Extract large icons");
+ parser.addOption(largeIconOption);
+ const QCommandLineOption shellIconOption("shell", "Extract shell icons using SHGetFileInfo()");
+ parser.addOption(shellIconOption);
+ const QCommandLineOption shellOverlayOption("overlay", "Extract shell overlay icons");
+ parser.addOption(shellOverlayOption);
+ parser.addPositionalArgument("file", "The file to open.");
+ parser.addPositionalArgument("image file folder", "The folder to store the images.");
+ parser.process(app);
+ const QStringList &positionalArguments = parser.positionalArguments();
+ if (positionalArguments.isEmpty())
+ parser.showHelp(0);
+
+ QString imageFileRoot = positionalArguments.size() > 1 ? positionalArguments.at(1) : QDir::currentPath();
+ const QFileInfo imageFileRootInfo(imageFileRoot);
+ if (!imageFileRootInfo.isDir()) {
+ std::wcerr << imageFileRoot << " is not a directory.\n";
+ return 1;
+ }
+ const QString &sourceFile = positionalArguments.constFirst();
+ imageFileRoot = imageFileRootInfo.absoluteFilePath() + QLatin1Char('/') + QFileInfo(sourceFile).baseName();
+
+ const PixmapEntryList pixmaps = parser.isSet(shellIconOption)
+ ? extractShellIcons(sourceFile, parser.isSet(shellOverlayOption))
+ : extractIcons(sourceFile, parser.isSet(largeIconOption));
+
+ for (int i = 0, count = pixmaps.size(); i < count; ++i) {
+ const QString fileName = imageFileRoot + pixmaps.at(i).name + QLatin1String(".png");
+ if (!pixmaps.at(i).pixmap.save(fileName)) {
+ std::wcerr << "Error writing image file " << fileName << ".\n";
return 1;
}
- std::cout << "Wrote image file "
- << QDir::toNativeSeparators(fileName).toStdString() << ".\n";
+ std::wcout << "Wrote " << QDir::toNativeSeparators(fileName) << ".\n";
}
return 0;
}