【问题标题】:ExtractIconEx: works but occasionally crashesExtractIconEx:有效但偶尔会崩溃
【发布时间】:2018-10-28 12:23:29
【问题描述】:

我正在从文件中提取图标并在对话框中显示它们

const LPCWSTR path = L"c:\path\to\file";
const UINT nIconsCheck = ExtractIconEx(path, -1, nullptr, nullptr, 0);
if(nIconsCheck > 0)
{
    HICON *iconHandles=new HICON;
    const UINT nIcons = ExtractIconEx(path, 0, iconHandles, nullptr, nIconsCheck);

    if(nIcons == nIconsCheck && nIcons != unsigned(-1))
    {

        IconSelect iconSelect(this); //dialog
        for(UINT i=0; i<nIcons; i++)
        {
            qDebug() << i;
            iconSelect.addIcon(QtWin::fromHICON(iconHandles[i])); //fromHICON returns QPixmap
            DestroyIcon(iconHandles[i]);
        }

        iconSelect.exec();
    }
}

对话框中的图标已正确加载,但有时会意外导致应用程序崩溃。

知道发生了什么吗?

Documentation on ExtractIconEx

编辑:感谢您提供快速而有用的答案。以下是我使用 atm 的完整工作代码:

// In my case I have a QString `filePath`
// `QString::toWCharArray` retrieves a non-0-terminated string,
// so append a 0 to `path`
std::vector<WCHAR> path(unsigned(filePath.length())+1); 
filePath.toWCharArray(path.data());
path.at(path.size()-1) = 0;

// Get number of icons in selected file
UINT nIcons = ExtractIconEx(path.data(), -1, nullptr, nullptr, 0);

if(nIcons == 0)
{
    // Try to find associated file that contains icon(s)
    // If found, `path` is replaced with the new path
    WORD index=0;
    DestroyIcon(ExtractAssociatedIcon(GetModuleHandle(nullptr), path.data(), &index));
    // Get number of icons in associated file
    nIcons = ExtractIconEx(path.data(), -1, nullptr, nullptr, 0);
}

if(nIcons > 0)
{
    // Get array of HICONs
    std::vector<HICON> iconHandles(nIcons);
    nIcons = ExtractIconEx(path.data(), 0, iconHandles.data(), nullptr, nIcons);

    for(UINT i=0; i<nIcons; i++) // Using iconHandles.size() is possibly safer,
                                 // but AFAIK nIcons always carries the correct value
    {
        // Use iconHandles[i]
        // In Qt you can use QtWin::fromHICON(iconHandles[i]) to generate a QPixmap
        DestroyIcon(iconHandles[i]);
    }
}

【问题讨论】:

  • 您确定iconHandles 的大小足以容纳所有nIconsCheck 句柄吗? (我不是 C++ 程序员,也不经常使用 ExtractIconEx,但这似乎是您的代码的问题。)
  • HICON *iconHandles=new HICON; 为单个 HICON 句柄分配了足够的内存,而不是句柄数组。正如@AndreasRejbrand 指出的那样,您需要分配足够的内存来接收至少nIconsCheck 句柄。
  • 谢谢,就是这样!更改为HICON *iconHandles=new HICON[nIcons];(并在循环后添加delete[] iconHandles;
  • 我建议用std::vector&lt;HICON&gt; iconHandles(nIconsCheck); const UINT nIcons = ExtractIconEx(path, 0, iconHandles.data(), nullptr, iconHandles.size());代替手动分配
  • 如果它偶尔崩溃,它不是“工作正常”!

标签: c++ arrays winapi icons allocation


【解决方案1】:
HICON *iconHandles=new HICON;

在这里,您只分配了一个 HICON 对象。如果给定文件中有多个图标,则对ExtractIconEx() 的下一次调用会通过写入分配的内存来创建缓冲区溢出。你已经进入了行为不明的黑暗世界。

要解决此问题,您可以像这样使用std::vector

std::vector<HICON> iconHandles(nIconsCheck); 
const UINT nIcons = ExtractIconEx(path, 0, iconHandles.data(), nullptr, iconHandles.size());
iconHandles.resize(nIcons); // Resize to the actual number of icons.

// Instead of: if(nIcons == nIconsCheck && nIcons != unsigned(-1))
if(!iconHandles.empty())
{
    // Use icons
}

与手动分配相比,这具有优势,您不需要delete 分配的内存。 vector 析构函数将在作用域结束时自动执行此操作。尽管您仍然需要为每个图标句柄调用 DestroyIcon()

【讨论】:

  • 谢谢!我想补充一点,正确分配时不需要if(nIcons == nIconsCheck &amp;&amp; nIcons != unsigned(-1))检查。
  • @bur 我已经添加了如何更改条件。引用中没有任何内容表明ExtractIconEx() 的返回值可以是-1。虽然返回值可能小于nIcons 参数,如果不是所有图标都可以成功提取,所以我相应地调整了向量的大小。在第一次和第二次调用 ExtractIconEx 之间也可能存在 TOCTTOU 竞争条件。
【解决方案2】:

来自您链接到的文档:

指向图标句柄数组的指针,该数组接收从文件中提取的大图标的句柄。如果此参数为 NULL,则不会从文件中提取大图标。

你只给了它一个指向一个图标句柄的指针。

分配一个与函数期望一样大的数组;从外观上看,这意味着nIconsCheck 元素。正如 zett42 所说,向量对此很有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-23
    • 1970-01-01
    • 1970-01-01
    • 2015-03-14
    • 1970-01-01
    • 1970-01-01
    • 2012-10-10
    • 2013-09-28
    相关资源
    最近更新 更多