[使用Win32 API检索C#中的文件/文件夹图标是一个琐碎的过程,已经记录了无数次,一直追溯到.Net的开头。
每个记录在案的解决方案只是相同方法的一种变体:P /调用SHGetFileInfo(),并传递适当的属性/标志。
我正在Windows 10上进行开发,我假设使用Shell32.dll v6.1-Windows 7附带的相同版本,并且根据Microsoft's documentation发布了最后一个版本。在我的开发系统上,对于C:\ Windows \ system32 \ shell32.dll,资源管理器中“属性”窗口的“详细信息”选项卡中显示的版本是“ 10.0.18362.719”。
我的目标是使用这种方法来生成“通用”文件夹图标。我需要它小(相对于大)并且处于打开(相对于关闭)状态。
我注意到,如果有人从SHGetFileInfo()请求一个小的通用文件夹图标,则结果始终显示为“关闭”状态。
相反,如果从SHGetFileInfo()请求大的通用文件夹图标,则结果始终显示为“打开”状态。
下面的代码代表了对此的最简单的演示。对于大图标,请将SmallIcon标志更改为LargeIcon。对于关闭的图标,请删除OpenIcon标志。
public static Icon GetMyIcon()
{
var flags = (uint)(ShellAttribute.Icon | ShellAttribute.SmallIcon | ShellAttribute.OpenIcon);
var fileInfo = new ShellFileInfo();
var size = (uint)Marshal.SizeOf(fileInfo);
var result = Interop.SHGetFileInfo(string.Empty, (uint)FileAttribute.Directory, out fileInfo, size, flags);
if (result == IntPtr.Zero)
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
try
{
return (Icon)Icon.FromHandle(fileInfo.hIcon).Clone();
}
finally
{
Interop.DestroyIcon(fileInfo.hIcon);
}
}
使用Interop方法声明如下:
public static class Interop
{
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SHGetFileInfo(string path,
uint attributes,
out ShellFileInfo fileInfo,
uint size,
uint flags);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DestroyIcon(IntPtr pointer);
}
是否需要某种黑魔法来获得通用的文件夹图标,该图标既小又开,或者又大又闭?
我还可以在项目中使用Windows API代码包,因此,如果可以仅使用此库就可以绕过Win32 API并达到我想要的结果,这将令人满意。
更新:根据Ian H的建议,我尝试使用SHGetStockIconInfo API,但不幸的是结果是相同的:
private static Icon GetStockIcon()
{
SHSTOCKICONINFO sii = new SHSTOCKICONINFO();
sii.cbSize = (UInt32)Marshal.SizeOf(typeof(SHSTOCKICONINFO));
Marshal.ThrowExceptionForHR(SHGetStockIconInfo(SHSTOCKICONID.SIID_FOLDEROPEN,
SHGSI.SHGSI_ICON | SHGSI.SHGSI_SMALLICON,
ref sii));
return Icon.FromHandle(sii.hIcon);
}
系统打开图标的大小是32 * 32,您可以使用GetObject
获得。
我的要求是带有“打开”外观的小(16x16)图标。
以下是在C ++中使用IShellItemImageFactory
根据系统打开图标外观创建尺寸16 * 16 BITMAP的示例,您可以将其称为解决方法。
IShellItemImageFactory
小的打开图标将显示为:
SHFILEINFOW sfi = { 0 };
ICONINFO iconinfo;
HBITMAP hBitmap;
IShellItemImageFactory *pImageFactory;
SIZE size = { 16, 16 };
CoInitialize(NULL);
hr = SHGetFileInfo(L"D:\\Test",
-1,
&sfi,
sizeof(sfi),
SHGFI_SYSICONINDEX | SHGFI_OPENICON | SHGFI_ICON);
if (FAILED(hr))
{
OutputDebugString(L"SHGetFileInfo fails.\n");
}
GetIconInfo(sfi.hIcon, &iconinfo);
hBitmap = iconinfo.hbmColor;
BITMAP bmp;
ZeroMemory(&bmp, sizeof(bmp));
if (iconinfo.hbmColor)
{
// Get initial size is 32*32
int nWrittenBytes = GetObject(iconinfo.hbmColor, sizeof(bmp), &bmp);
hr = SHCreateItemFromParsingName(L"D:\\Test", NULL, IID_PPV_ARGS(&pImageFactory));
if (SUCCEEDED(hr))
{
// Get expected size 16*16
hr = pImageFactory->GetImage(size, SIIGBF_BIGGERSIZEOK, &hBitmap);
if (FAILED(hr))
{
OutputDebugString(L"IShellItemImageFactory::GetImage failed with error code %x");
}
pImageFactory->Release();
}
// Confirm if the size is 16*16
nWrittenBytes = GetObject(hBitmap, sizeof(bmp), &bmp);
}