如何获取驱动器、设备或文件的默认系统图标

6
我正在创建一个远程文件管理器。服务器端应用程序获取计算机中驱动器、目录和文件列表,将其保存为流并将其发送到我所在的当前计算机。一切正常运作。
在当前计算机上,我首先使用"OnCreate"事件来填充TListImage与shell图标(来自同一台当前计算机),并将其与文件列表将显示的ListView联系起来。因此,一旦收到列表,我使用下面的函数在ListView中显示通用文件图标:
function GetGenericFileIconIndex(Filename: string): Integer;
var
  FInfo: TSHFileInfo;
begin
  Result := -1;
  if (SHGetFileInfo(PChar(Filename), FILE_ATTRIBUTE_NORMAL, FInfo, SizeOf(FInfo),
    SHGFI_SYSICONINDEX or SHGFI_USEFILEATTRIBUTES) <> 0) then
    Result := FInfo.iIcon;
end;

for I := 0 to Pred(List.Count) do
begin
  Item := ListView.Items.Add;
  Item.ImageIndex := GetGenericFileIconIndex(List[I]);
  Item.Caption := List[I];
end;

通过文件名/扩展名,我可以获取正确的外壳图标,即使在不同的计算机上。但是我还需要显示正确的驱动器类型图标。例如,在远程计算机上,“D:\”是本地固定驱动器,但在当前计算机上,“D:\”是CD / DVD驱动器,因此我不能使用驱动器字母来获取此图标。我需要一种从远程计算机(固定驱动器)获取“通用”驱动器类型图标索引并将其发送到当前计算机的方法。
例如,我的目标是获取本地硬盘、远程磁盘、CD / DVD设备等的默认图标...
抱歉,我的英语不是母语。我尽力尝试解释。
也许我需要的是不可能的,请告诉我...
谢谢!

我不太明白你所说的“通用”的意思。而且我也不明白在另一台电脑上使用这些信息的意义所在。 - David Heffernan
你尝试过驱动器根目录吗? - TLama
1
你得到的(系统图标)索引是一个动态构建结构(系统图像列表)中所有 shell 图标的索引,这在每台计算机上可能都不同。因此,你想要的可能是不可能的。 - Rudy Velthuis
就此而言,通用驱动器类型图标会是什么样子?在我的电脑上,启动驱动器(C:)的图标与第二个分区(D:)的图标不同,两者都与BD-ROM的图标不同。您实际上想要实现什么? - Rudy Velthuis
@RudyVelthuis,我在Windows 7中的整个shell图标列表大约有1MB大小(1000个图标):(...但我进行了一些新的测试,并意识到各个计算机和Windows版本中主驱动器图标具有相同的索引。例如,DRIVE_REMOVABLE = 7,DRIVE_FIXED = 8,DRIVE_CDROM = 11...我在Windows XP、7、8和10上进行了测试... - Guybrush
显示剩余9条评论
1个回答

9
你一定要使用 SHGetStockIconInfo 函数
HRESULT SHGetStockIconInfo(
          SHSTOCKICONID   siid,
          UINT            uFlags,
  _Inout_ SHSTOCKICONINFO *psii
);

siid参数是一个SHSTOCKICONID类型的参数,用于标识要检索哪个系统库存图标

function GetDefaultSystemIcon(ASiid: Integer): Integer;
var
  sInfo: TSHStockIconInfo;
begin
  sInfo.cbSize := SizeOf(TSHStockIconInfo);
  if S_OK = SHGetStockIconInfo(ASiid, SHGSI_SYSICONINDEX, sInfo) then
    Result := sInfo.iSysImageIndex
  else
    Result := -1;
end;

您可以像这样调用上述内容GetDefaultSystemIcon(SIID_DRIVECD)来获取默认的CDROM驱动器图标索引。
来自Microsoft文档:

最低支持的客户端      Windows Vista [仅限桌面应用程序]
最低支持的服务器     Windows Server 2008 [仅限桌面应用程序]

旧版本的ShellAPI单元可能没有SHGetStockIconInfo声明:在这种情况下,必须向Delphi项目添加以下unit
注意,下面的单元使用受限于shell32.dll系统库中SHGetStockIconInfo函数的可用性——即需要库导出该函数。
unit MyShellAPI;

interface

uses
  Windows;

type
  SHSTOCKICONID = Integer;

  _SHSTOCKICONINFO = record
    cbSize: Cardinal;
    hIcon: HICON;
    iSysImageIndex,
    iIcon: Integer;
    szPath: packed array [0..MAX_PATH-1] of Char;
  end;
  SHStockIconInfo = _SHSTOCKICONINFO;
  TSHStockIconInfo = SHSTOCKICONINFO;
  PSHStockIconInfo = ^TSHStockIconInfo;

const
  //https://msdn.microsoft.com/en-us/library/windows/desktop/bb762179%28v=vs.85%29.aspx
  SHGFI_ADDOVERLAYS       = $000000020;
  SHGFI_ATTR_SPECIFIED    = $000020000;
  SHGFI_ATTRIBUTES        = $000000800;
  SHGFI_DISPLAYNAME       = $000000200;
  SHGFI_EXETYPE           = $000002000;
  SHGFI_ICON              = $000000100;
  SHGFI_ICONLOCATION      = $000001000;
  SHGFI_LARGEICON         = $000000000;
  SHGFI_LINKOVERLAY       = $000008000;
  SHGFI_OPENICON          = $000000002;
  SHGFI_OVERLAYINDEX      = $000000040;
  SHGFI_PIDL              = $000000008;
  SHGFI_SELECTED          = $000010000;
  SHGFI_SHELLICONSIZE     = $000000004;
  SHGFI_SMALLICON         = $000000001;
  SHGFI_SYSICONINDEX      = $000004000;
  SHGFI_TYPENAME          = $000000400;
  SHGFI_USEFILEATTRIBUTES = $000000010;

  //https://msdn.microsoft.com/en-us/library/windows/desktop/bb762205(v=vs.85).aspx
  SHGSI_ICONLOCATION = 0;
  SHGSI_ICON = SHGFI_ICON;
  SHGSI_SYSICONINDEX = SHGFI_SYSICONINDEX;
  SHGSI_LINKOVERLAY = SHGFI_LINKOVERLAY;
  SHGSI_SELECTED = SHGFI_SELECTED;
  SHGSI_LARGEICON = SHGFI_LARGEICON;
  SHGSI_SMALLICON = SHGFI_SMALLICON;
  SHGSI_SHELLICONSIZE = SHGFI_SHELLICONSIZE;

  //https://msdn.microsoft.com/en-us/library/windows/desktop/bb762542%28v=vs.85%29.aspx
  SIID_DOCNOASSOC         = 0;
  SIID_DOCASSOC           = 1;
  SIID_APPLICATION        = 2;
  SIID_FOLDER             = 3;
  SIID_FOLDEROPEN         = 4;
  SIID_DRIVE525           = 5;
  SIID_DRIVE35            = 6;
  SIID_DRIVEREMOVE        = 7;
  SIID_DRIVEFIXED         = 8;
  SIID_DRIVENET           = 9;
  SIID_DRIVENETDISABLED   = 10;
  SIID_DRIVECD            = 11;
  SIID_DRIVERAM           = 12;
  SIID_WORLD              = 13;
  SIID_SERVER             = 15;
  SIID_PRINTER            = 16;
  SIID_MYNETWORK          = 17;
  SIID_FIND               = 22;
  SIID_HELP               = 23;
  SIID_SHARE              = 28;
  SIID_LINK               = 29;
  SIID_SLOWFILE           = 30;
  SIID_RECYCLER           = 31;
  SIID_RECYCLERFULL       = 32;
  SIID_MEDIACDAUDIO       = 40;
  SIID_LOCK               = 47;
  SIID_AUTOLIST           = 49;
  SIID_PRINTERNET         = 50;
  SIID_SERVERSHARE        = 51;
  SIID_PRINTERFAX         = 52;
  SIID_PRINTERFAXNET      = 53;
  SIID_PRINTERFILE        = 54;
  SIID_STACK              = 55;
  SIID_MEDIASVCD          = 56;
  SIID_STUFFEDFOLDER      = 57;
  SIID_DRIVEUNKNOWN       = 58;
  SIID_DRIVEDVD           = 59;
  SIID_MEDIADVD           = 60;
  SIID_MEDIADVDRAM        = 61;
  SIID_MEDIADVDRW         = 62;
  SIID_MEDIADVDR          = 63;
  SIID_MEDIADVDROM        = 64;
  SIID_MEDIACDAUDIOPLUS   = 65;
  SIID_MEDIACDRW          = 66;
  SIID_MEDIACDR           = 67;
  SIID_MEDIACDBURN        = 68;
  SIID_MEDIABLANKCD       = 69;
  SIID_MEDIACDROM         = 70;
  SIID_AUDIOFILES         = 71;
  SIID_IMAGEFILES         = 72;
  SIID_VIDEOFILES         = 73;
  SIID_MIXEDFILES         = 74;
  SIID_FOLDERBACK         = 75;
  SIID_FOLDERFRONT        = 76;
  SIID_SHIELD             = 77;
  SIID_WARNING            = 78;
  SIID_INFO               = 79;
  SIID_ERROR              = 80;
  SIID_KEY                = 81;
  SIID_SOFTWARE           = 82;
  SIID_RENAME             = 83;
  SIID_DELETE             = 84;
  SIID_MEDIAAUDIODVD      = 85;
  SIID_MEDIAMOVIEDVD      = 86;
  SIID_MEDIAENHANCEDCD    = 87;
  SIID_MEDIAENHANCEDDVD   = 88;
  SIID_MEDIAHDDVD         = 89;
  SIID_MEDIABLURAY        = 90;
  SIID_MEDIAVCD           = 91;
  SIID_MEDIADVDPLUSR      = 92;
  SIID_MEDIADVDPLUSRW     = 93;
  SIID_DESKTOPPC          = 94;
  SIID_MOBILEPC           = 95;
  SIID_USERS              = 96;
  SIID_MEDIASMARTMEDIA    = 97;
  SIID_MEDIACOMPACTFLASH  = 98;
  SIID_DEVICECELLPHONE    = 99;
  SIID_DEVICECAMERA       = 100;
  SIID_DEVICEVIDEOCAMERA  = 101;
  SIID_DEVICEAUDIOPLAYER  = 102;
  SIID_NETWORKCONNECT     = 103;
  SIID_INTERNET           = 104;
  SIID_ZIPFILE            = 105;
  SIID_SETTINGS           = 106;
  SIID_DRIVEHDDVD         = 132;
  SIID_DRIVEBD            = 133;
  SIID_MEDIAHDDVDROM      = 134;
  SIID_MEDIAHDDVDR        = 135;
  SIID_MEDIAHDDVDRAM      = 136;
  SIID_MEDIABDROM         = 137;
  SIID_MEDIABDR           = 138;
  SIID_MEDIABDRE          = 139;
  SIID_CLUSTEREDDRIVE     = 140;
  SIID_MAX_ICONS          = 175;

function SHGetStockIconInfo(siid: SHSTOCKICONID; uFlags: UINT; var psii: TSHStockIconInfo): HRESULT; stdcall;

implementation

const
  SHELL32 = 'shell32.dll';

function SHGetStockIconInfo; external SHELL32 name 'SHGetStockIconInfo';

end.

谢谢fantaghirocco!我已经研究了一段时间关于SHGetStockIconInfo的问题...似乎不能像你在答案中展示的那样简单地使用(如果我有错请纠正)。我正在使用Delphi 7。我找到了这个链接http://embarcadero.newsgroups.archived.at/public.delphi.language.delphi.win32/200902/0902022547.html,试图将代码综合成一个简单的函数... - Guybrush
我按照您的建议编辑了问题。请问您能否再帮我一次?谢谢! - Guybrush
@Paruba,虽然 Delphi 7 发布于 Windows Vista 之前,但您可能没有在 ShellAPI 单元中可用的 SHGetStockIconInfo。根据 MS 文档:*最低支持客户端 Windows Vista [仅限桌面应用程序]*。我将在我的答案中补充您需要添加的 unit。以上对我在 XE4 中有效。 - fantaghirocco
我会动态加载函数,因为在XP上,使用静态链接会导致应用程序崩溃。 - kobik
@kobik 谢谢,我已经注意到了。我正在搜索原因,以便扩展我的答案。 - fantaghirocco
显示剩余5条评论

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