Skip to content

Commit 184d49a

Browse files
authored
[Hidden files] - Show thumbnail (#2388)
1 parent 409f5b4 commit 184d49a

File tree

6 files changed

+167
-195
lines changed

6 files changed

+167
-195
lines changed

Files.Launcher/Program.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,12 @@ private static async Task ParseArgumentsAsync(AppServiceRequestReceivedEventArgs
300300

301301
case "GetIconOverlay":
302302
var fileIconPath = (string)args.Request.Message["filePath"];
303-
var iconOverlay = Win32API.GetFileOverlayIcon(fileIconPath);
303+
var thumbnailSize = (int)args.Request.Message["thumbnailSize"];
304+
var iconOverlay = Win32API.GetFileIconAndOverlay(fileIconPath, thumbnailSize);
304305
await args.Request.SendResponseAsync(new ValueSet()
305306
{
306-
{ "IconOverlay", iconOverlay.icon },
307+
{ "Icon", iconOverlay.icon },
308+
{ "Overlay", iconOverlay.overlay },
307309
{ "HasCustomIcon", iconOverlay.isCustom }
308310
});
309311
break;

Files.Launcher/Win32API.cs

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.ComponentModel;
33
using System.Diagnostics;
44
using System.Drawing;
5+
using System.Drawing.Imaging;
56
using System.IO;
67
using System.Linq;
78
using System.Runtime.InteropServices;
@@ -93,13 +94,13 @@ public static string[] CommandLineToArgs(string commandLine)
9394
}
9495
}
9596

96-
public static (string icon, bool isCustom) GetFileOverlayIcon(string path)
97+
public static (string icon, string overlay, bool isCustom) GetFileIconAndOverlay(string path, int thumbnailSize)
9798
{
9899
var shfi = new Shell32.SHFILEINFO();
99100
var ret = Shell32.SHGetFileInfo(path, 0, ref shfi, Shell32.SHFILEINFO.Size, Shell32.SHGFI.SHGFI_OVERLAYINDEX | Shell32.SHGFI.SHGFI_ICON | Shell32.SHGFI.SHGFI_SYSICONINDEX | Shell32.SHGFI.SHGFI_ICONLOCATION);
100101
if (ret == IntPtr.Zero)
101102
{
102-
return (null, false);
103+
return (null, null, false);
103104
}
104105

105106
bool isCustom = !shfi.szDisplayName.StartsWith(Environment.GetFolderPath(Environment.SpecialFolder.Windows));
@@ -108,26 +109,43 @@ public static (string icon, bool isCustom) GetFileOverlayIcon(string path)
108109
using var imageList = ComCtl32.SafeHIMAGELIST.FromIImageList(tmp);
109110
if (imageList.IsNull || imageList.IsInvalid)
110111
{
111-
return (null, isCustom);
112+
return (null, null, isCustom);
112113
}
113114

115+
string iconStr = null, overlayStr = null;
114116
var overlay_idx = shfi.iIcon >> 24;
115-
//var icon_idx = shfi.iIcon & 0xFFFFFF;
116-
if (overlay_idx == 0)
117+
if (overlay_idx != 0)
117118
{
118-
return (null, isCustom);
119+
var overlay_image = imageList.Interface.GetOverlayImage(overlay_idx);
120+
using var hOverlay = imageList.Interface.GetIcon(overlay_image, ComCtl32.IMAGELISTDRAWFLAGS.ILD_TRANSPARENT);
121+
if (!hOverlay.IsNull && !hOverlay.IsInvalid)
122+
{
123+
using var image = hOverlay.ToIcon().ToBitmap();
124+
byte[] bitmapData = (byte[])new ImageConverter().ConvertTo(image, typeof(byte[]));
125+
overlayStr = Convert.ToBase64String(bitmapData, 0, bitmapData.Length);
126+
}
119127
}
120128

121-
var overlay_image = imageList.Interface.GetOverlayImage(overlay_idx);
122-
using var hIcon = imageList.Interface.GetIcon(overlay_image, ComCtl32.IMAGELISTDRAWFLAGS.ILD_TRANSPARENT);
123-
if (hIcon.IsNull || hIcon.IsInvalid)
129+
// The following only returns the icon (not thumbnail) and it's difficult to get a high-res icon
130+
//var icon_idx = shfi.iIcon & 0xFFFFFF;
131+
//using var hIcon = imageList.Interface.GetIcon(icon_idx, ComCtl32.IMAGELISTDRAWFLAGS.ILD_TRANSPARENT);
132+
133+
using var shellItem = new Vanara.Windows.Shell.ShellItem(path);
134+
if (shellItem.IShellItem is Shell32.IShellItemImageFactory fctry)
124135
{
125-
return (null, isCustom);
136+
var flags = Shell32.SIIGBF.SIIGBF_BIGGERSIZEOK;
137+
if (thumbnailSize < 80) flags |= Shell32.SIIGBF.SIIGBF_ICONONLY;
138+
var hres = fctry.GetImage(new SIZE(thumbnailSize, thumbnailSize), flags, out var hbitmap);
139+
if (hres == HRESULT.S_OK)
140+
{
141+
using var image = GetBitmapFromHBitmap(hbitmap);
142+
byte[] bitmapData = (byte[])new ImageConverter().ConvertTo(image, typeof(byte[]));
143+
iconStr = Convert.ToBase64String(bitmapData, 0, bitmapData.Length);
144+
}
145+
//Marshal.ReleaseComObject(fctry);
126146
}
127147

128-
using var image = hIcon.ToIcon().ToBitmap();
129-
byte[] bitmapData = (byte[])new ImageConverter().ConvertTo(image, typeof(byte[]));
130-
return (Convert.ToBase64String(bitmapData, 0, bitmapData.Length), isCustom);
148+
return (iconStr, overlayStr, isCustom);
131149
}
132150

133151
private static void RunPowershellCommand(string command, bool runAsAdmin)
@@ -171,6 +189,63 @@ public static void SetVolumeLabel(string driveName, string newLabel)
171189
RunPowershellCommand($"-command \"$Signature = '[DllImport(\\\"kernel32.dll\\\", SetLastError = false)]public static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);'; $SetVolumeLabel = Add-Type -MemberDefinition $Signature -Name \"Win32SetVolumeLabel\" -Namespace Win32Functions -PassThru; $SetVolumeLabel::SetVolumeLabel('{driveName}', '{newLabel}')\"", true);
172190
}
173191

192+
private static Bitmap GetBitmapFromHBitmap(HBITMAP hBitmap)
193+
{
194+
Bitmap bmp = hBitmap.ToBitmap();
195+
196+
if (Bitmap.GetPixelFormatSize(bmp.PixelFormat) < 32)
197+
{
198+
return bmp;
199+
}
200+
201+
if (IsAlphaBitmap(bmp, out var bmpData))
202+
{
203+
return GetAlphaBitmapFromBitmapData(bmpData);
204+
}
205+
206+
return bmp;
207+
}
208+
209+
private static Bitmap GetAlphaBitmapFromBitmapData(BitmapData bmpData)
210+
{
211+
return new Bitmap(
212+
bmpData.Width,
213+
bmpData.Height,
214+
bmpData.Stride,
215+
PixelFormat.Format32bppArgb,
216+
bmpData.Scan0);
217+
}
218+
219+
private static bool IsAlphaBitmap(Bitmap bmp, out BitmapData bmpData)
220+
{
221+
Rectangle bmBounds = new Rectangle(0, 0, bmp.Width, bmp.Height);
222+
223+
bmpData = bmp.LockBits(bmBounds, ImageLockMode.ReadOnly, bmp.PixelFormat);
224+
225+
try
226+
{
227+
for (int y = 0; y <= bmpData.Height - 1; y++)
228+
{
229+
for (int x = 0; x <= bmpData.Width - 1; x++)
230+
{
231+
Color pixelColor = Color.FromArgb(
232+
Marshal.ReadInt32(bmpData.Scan0, (bmpData.Stride * y) + (4 * x)));
233+
234+
if (pixelColor.A > 0 & pixelColor.A < 255)
235+
{
236+
return true;
237+
}
238+
}
239+
}
240+
}
241+
finally
242+
{
243+
bmp.UnlockBits(bmpData);
244+
}
245+
246+
return false;
247+
}
248+
174249
// There is usually no need to define Win32 COM interfaces/P-Invoke methods here.
175250
// The Vanara library contains the definitions for all members of Shell32.dll, User32.dll and more
176251
// The ones below are due to bugs in the current version of the library and can be removed once fixed

Files.Launcher/Win32API_ContextMenu.cs

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Collections.Generic;
66
using System.Diagnostics;
77
using System.Drawing;
8-
using System.Drawing.Imaging;
98
using System.Linq;
109
using System.Runtime.InteropServices;
1110
using System.Threading;
@@ -340,63 +339,6 @@ private static string GetCommandString(Shell32.IContextMenu cMenu, uint offset,
340339
}
341340
}
342341

343-
private static Bitmap GetBitmapFromHBitmap(HBITMAP hBitmap)
344-
{
345-
Bitmap bmp = hBitmap.ToBitmap();
346-
347-
if (Bitmap.GetPixelFormatSize(bmp.PixelFormat) < 32)
348-
{
349-
return bmp;
350-
}
351-
352-
if (IsAlphaBitmap(bmp, out var bmpData))
353-
{
354-
return GetAlphaBitmapFromBitmapData(bmpData);
355-
}
356-
357-
return bmp;
358-
}
359-
360-
private static Bitmap GetAlphaBitmapFromBitmapData(BitmapData bmpData)
361-
{
362-
return new Bitmap(
363-
bmpData.Width,
364-
bmpData.Height,
365-
bmpData.Stride,
366-
PixelFormat.Format32bppArgb,
367-
bmpData.Scan0);
368-
}
369-
370-
private static bool IsAlphaBitmap(Bitmap bmp, out BitmapData bmpData)
371-
{
372-
Rectangle bmBounds = new Rectangle(0, 0, bmp.Width, bmp.Height);
373-
374-
bmpData = bmp.LockBits(bmBounds, ImageLockMode.ReadOnly, bmp.PixelFormat);
375-
376-
try
377-
{
378-
for (int y = 0; y <= bmpData.Height - 1; y++)
379-
{
380-
for (int x = 0; x <= bmpData.Width - 1; x++)
381-
{
382-
Color pixelColor = Color.FromArgb(
383-
Marshal.ReadInt32(bmpData.Scan0, (bmpData.Stride * y) + (4 * x)));
384-
385-
if (pixelColor.A > 0 & pixelColor.A < 255)
386-
{
387-
return true;
388-
}
389-
}
390-
}
391-
}
392-
finally
393-
{
394-
bmp.UnlockBits(bmpData);
395-
}
396-
397-
return false;
398-
}
399-
400342
#region IDisposable Support
401343

402344
private bool disposedValue = false; // To detect redundant calls

0 commit comments

Comments
 (0)