如何从shell32.dll中获取图标?

67

我想获取一棵树的图标,用于自制应用程序。有人知道如何提取出图像作为 .icon 文件吗?我需要 16x16 和 32x32 两种尺寸,或者我可以使用屏幕截图。


12
请注意,这样做是违反许可证的规定。“当软件正在运行时,您可以使用但不可分享其图标、图片、声音和媒体。” - Raymond Chen
13个回答

79
如果有人正在寻找一种简单的方法,只需使用7zip来解压shell32.dll并查找文件夹.src/ICON/。

1
将其重命名为shell32.7z,然后尝试使用7-zip、Peazip或其他喜欢的压缩工具打开它。 - ronaldwidha

58
在Visual Studio中,选择“文件打开...”,然后选择“文件...”。接着选择Shell32.dll。将会打开一个文件夹树,您可以在“Icon”文件夹中找到图标。
要保存图标,您可以右键单击文件夹树中的图标,然后选择“导出”。

2
当我使用它时,我只有这个文件夹树和所有列出的图标,但没有预览,而且标题也不是很有用(1、10、1001等)。是否有一种方法可以获得预览,或者我真的必须打开所有图标? - Dominik
2
@MickyD 无法确认 - 在我的VS Community 2017(15.8.3)中,导出图标的操作非常顺利 - 就像这个答案中所写的一样。菜单可能有些变化 - 现在是“文件”-->“打开”-->“文件...”。(我将shell32.dll复制到桌面上进行测试) - RicoBrassers
1
@RicoBrassers 嗯,这有点尴尬,现在可以了。我一定是做了什么傻事。谢谢伙计 :) - user585968
重要的是从Windows\System32文件夹打开shell32.dll文件,否则就没有“Icon”文件夹。 - ViH
1
截至2023年,此解决方案在Windows 10或11中不再适用。TheManInOz的答案是目前唯一正确的答案。 - trejder

22

我不能确定自己是否百分百正确,但是根据我的测试,在Windows 10 / 11版本的imageres.dll或shell32.dll上使用7Zip或VS的上述选项似乎不起作用。 这是我看到的内容:

[shell32.dll]
.rsrc\MANIFEST\124
.rsrc\MUI\1
.rsrc\TYPELIB\1
.rsrc\version.txt
.data
.didat
.pdata
.rdata
.reloc
.text
CERTIFICATE

更新:我想我找到原因了。链接到我发现的一篇文章。(抱歉超出领域)。您可以在这些位置的文件中找到资源:

"C:\Windows\SystemResources\shell32.dll.mun"
"C:\Windows\SystemResources\imageres.dll.mun"

Windows 10 1903中imageres.dll中的图标不再存在 - 4kb文件 (来自superuser.com)


哇,我浪费了很多时间试图在shell32.dll中找到UIFILES和图标!为什么他们把它们隐藏在.mun文件中??? - ScienceDiscoverer
3
我复制了 shell32.dll.mun 文件,然后使用 7-Zip 的 Extract Here 功能,找到了我需要的图标。 - Mark Davich
非常感谢!我自己解析了"shell32.dll"的资源表,但只找到了4个资源类型的条目,这让我非常困惑,因为VS2005可以列出很多种资源,比如GROUP_ICON、BITMAP等等。 - undefined

22

另一个选择是使用类似ResourceHacker的工具。它不仅可以处理图标,还有更多功能。干杯!


1
我尝试使用资源编辑器,但每个图标都有12个副本(具有不同的大小)。 - HighTechProgramming15
1
@HighTechProgramming15 这不是你想要的吗? - endolith
截至2023年,此解决方案在Windows 10或11中不再适用。TheManInOz的答案是目前唯一正确的答案。 - trejder

14

我需要从shell32.dll中提取图标#238,但不想下载Visual Studio或Resourcehacker,因此我在Technet上找到了一些PowerShell脚本(感谢John Grenfell和这里),这些脚本可以做类似的事情。然后我创建了一个新脚本(如下)以满足我的需求。

我输入的参数是(源DLL路径、目标图标文件名和DLL文件中的图标索引):

C:\Windows\System32\shell32.dll

C:\Temp\Restart.ico

238

我通过临时创建一个新快捷方式(右键单击桌面并选择“新建”-->“快捷方式”,然后键入calc并按两次Enter)进行试错来发现我需要的图标索引是#238。然后右键单击新快捷方式并选择“属性”,然后在“快捷方式”选项卡中单击“更改图标”按钮。粘贴路径C:\Windows\System32\shell32.dll并单击确定。找到你想要使用的图标并计算其索引。注意:索引#2在索引#1下方而不是右侧。我的Windows 7 x64机器上,图标索引#5位于第二列的顶部。

如果有人有类似但获得更高质量图标的更好方法,我会很感兴趣。谢谢,Shaun。

#Windows PowerShell Code###########################################################################
# http://gallery.technet.microsoft.com/scriptcenter/Icon-Exporter-e372fe70
#
# AUTHOR: John Grenfell
#
###########################################################################

<#
.SYNOPSIS
   Exports an ico and bmp file from a given source to a given destination
.Description
   You need to set the Source and Destination locations. First version of a script, I found other examples but all I wanted to do as grab and ico file from an exe but found getting a bmp useful. Others might find useful
   No error checking I'm afraid so make sure your source and destination locations exist!
.EXAMPLE
    .\Icon_Exporter.ps1
.Notes
        Version HISTORY:
        1.1 2012.03.8
#>
Param ( [parameter(Mandatory = $true)][string] $SourceEXEFilePath,
        [parameter(Mandatory = $true)][string] $TargetIconFilePath
)
CLS
#"shell32.dll" 238
If ($SourceEXEFilePath.ToLower().Contains(".dll")) {
    $IconIndexNo = Read-Host "Enter the icon index: "
    $Icon = [System.IconExtractor]::Extract($SourceEXEFilePath, $IconIndexNo, $true)    
} Else {
    [void][Reflection.Assembly]::LoadWithPartialName("System.Drawing")
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    $image = [System.Drawing.Icon]::ExtractAssociatedIcon("$($SourceEXEFilePath)").ToBitmap()
    $bitmap = new-object System.Drawing.Bitmap $image
    $bitmap.SetResolution(72,72)
    $icon = [System.Drawing.Icon]::FromHandle($bitmap.GetHicon())
}
$stream = [System.IO.File]::OpenWrite("$($TargetIconFilePath)")
$icon.save($stream)
$stream.close()
Write-Host "Icon file can be found at $TargetIconFilePath"

很棒的方法,但对我不起作用。我使用的是Windows 7x64,脚本似乎可以正常工作。但是我遇到了“TypeNotFound”错误,涉及到“System.Drawing.Icon”和“System.Drawing.Bitmap”。这是我第一次尝试运行PS脚本,也许我做错了什么? - Danny Lo
1
好的,我只需要先执行 Add-Type -AssemblyName System.Drawing - Danny Lo
无需添加任何汇编代码即可完美运行。 - Jan

7

只需使用IrfanView打开DLL文件,然后将结果保存为.gif或.jpg格式。

我知道这个问题已经过时了,但它是从“从dll中提取图标”搜索的第二个谷歌结果,我想避免在我的工作站上安装任何东西,并且我记得我使用IrfanView。


我已经在使用IrfanView了,因此这对于快速浏览图标来说是一个简单的选择。按F10以查看所有存储图像的缩略图。我没有找到识别图像编号的方法。有办法吗? - Ben

6

资源提取 是另一款工具,可以递归地从许多 DLL 文件中找到图标,非常方便。


3
虽然那个工具可以很好地提取资源,但他们还有一个工具IconsExtract https://www.nirsoft.net/utils/iconsext.html,在大多数情况下我会发现它更加方便,因为我可以浏览图标并在需要将图标嵌入/链接到办公文档时简单地链接到系统文件。 - undrline - Reinstate Monica
截至2023年,此解决方案在Windows 10或11中不再适用。TheManInOz的答案是目前唯一正确的答案。 - trejder

3
这是一个上面解决方案的更新版本。 我添加了一个在链接中被埋藏的缺失组件。 初学者将无法理解。 这个示例将不需要进行修改即可运行。
    <#
.SYNOPSIS
    Exports an ico and bmp file from a given source to a given destination
.Description
    You need to set the Source and Destination locations. First version of a script, I found other examples 
    but all I wanted to do as grab and ico file from an exe but found getting a bmp useful. Others might find useful
.EXAMPLE
    This will run but will nag you for input
    .\Icon_Exporter.ps1
.EXAMPLE
    this will default to shell32.dll automatically for -SourceEXEFilePath
    .\Icon_Exporter.ps1 -TargetIconFilePath 'C:\temp\Myicon.ico' -IconIndexNo 238
.EXAMPLE
    This will give you a green tree icon (press F5 for windows to refresh Windows explorer)
    .\Icon_Exporter.ps1 -SourceEXEFilePath 'C:/Windows/system32/shell32.dll' -TargetIconFilePath 'C:\temp\Myicon.ico' -IconIndexNo 41

.Notes
    Based on https://dev59.com/j3VD5IYBdhLWcg3wWKPc Version 1.1 2012.03.8
    New version: Version 1.2 2015.11.20 (Added missing custom assembly and some error checking for novices)
#>
Param ( 
    [parameter(Mandatory = $true)]
    [string] $SourceEXEFilePath = 'C:/Windows/system32/shell32.dll',
    [parameter(Mandatory = $true)]
    [string] $TargetIconFilePath,
    [parameter(Mandatory = $False)]
    [Int32]$IconIndexNo = 0
)

#https://social.technet.microsoft.com/Forums/windowsserver/en-US/16444c7a-ad61-44a7-8c6f-b8d619381a27/using-icons-in-powershell-scripts?forum=winserverpowershell
$code = @"
using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace System
{
    public class IconExtractor
    {

     public static Icon Extract(string file, int number, bool largeIcon)
     {
      IntPtr large;
      IntPtr small;
      ExtractIconEx(file, number, out large, out small, 1);
      try
      {
       return Icon.FromHandle(largeIcon ? large : small);
      }
      catch
      {
       return null;
      }

     }
     [DllImport("Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
     private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);

    }
}
"@

If  (-not (Test-path -Path $SourceEXEFilePath -ErrorAction SilentlyContinue ) ) {
    Throw "Source file [$SourceEXEFilePath] does not exist!"
}

[String]$TargetIconFilefolder = [System.IO.Path]::GetDirectoryName($TargetIconFilePath) 
If  (-not (Test-path -Path $TargetIconFilefolder -ErrorAction SilentlyContinue ) ) {
    Throw "Target folder [$TargetIconFilefolder] does not exist!"
}

Try {
    If ($SourceEXEFilePath.ToLower().Contains(".dll")) {
        Add-Type -TypeDefinition $code -ReferencedAssemblies System.Drawing
        $form = New-Object System.Windows.Forms.Form
        $Icon = [System.IconExtractor]::Extract($SourceEXEFilePath, $IconIndexNo, $true)    
    } Else {
        [void][Reflection.Assembly]::LoadWithPartialName("System.Drawing")
        [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
        $image = [System.Drawing.Icon]::ExtractAssociatedIcon("$($SourceEXEFilePath)").ToBitmap()
        $bitmap = new-object System.Drawing.Bitmap $image
        $bitmap.SetResolution(72,72)
        $icon = [System.Drawing.Icon]::FromHandle($bitmap.GetHicon())
    }
} Catch {
    Throw "Error extracting ICO file"
}

Try {
    $stream = [System.IO.File]::OpenWrite("$($TargetIconFilePath)")
    $icon.save($stream)
    $stream.close()
} Catch {
    Throw "Error saving ICO file [$TargetIconFilePath]"
}
Write-Host "Icon file can be found at [$TargetIconFilePath]"

我不得不删除这行代码才能让它工作: $form = New-Object System.Windows.Forms.Form而且输出的图标质量非常差,只有256种颜色(甚至只有16种)。也许需要对脚本进行一些调整?我使用的是Windows 10。 - luiggig
抱歉,但是你的代码出现了多个错误,从“路径中存在非法字符”到“提取ico文件时出错”。你尝试改进的代码在我这里立即运行成功。 - Jan

2

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接