Skip to content

Commit faff816

Browse files
📝 Add docstrings to dev
Docstrings generation was requested by @sstream17. * #4006 (comment) The following files were modified: * `Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs`
1 parent 1e8819a commit faff816

File tree

1 file changed

+81
-4
lines changed

1 file changed

+81
-4
lines changed

Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
using System;
2-
using System.Runtime.InteropServices;
1+
using System;
32
using System.IO;
3+
using System.Runtime.InteropServices;
44
using System.Windows;
55
using System.Windows.Interop;
66
using System.Windows.Media.Imaging;
7+
using IniParser;
78
using Windows.Win32;
89
using Windows.Win32.Foundation;
9-
using Windows.Win32.UI.Shell;
1010
using Windows.Win32.Graphics.Gdi;
11+
using Windows.Win32.UI.Shell;
1112

1213
namespace Flow.Launcher.Infrastructure.Image
1314
{
@@ -35,9 +36,32 @@ public class WindowsThumbnailProvider
3536

3637
private static readonly HRESULT S_PATHNOTFOUND = (HRESULT)0x8004B205;
3738

39+
private const string UrlExtension = ".url";
40+
41+
/// <summary>
42+
/// Obtains a BitmapSource thumbnail for the specified file.
43+
/// </summary>
44+
/// <remarks>
45+
/// If the file is a Windows URL shortcut (".url"), the method attempts to resolve the shortcut's icon and use that for the thumbnail; otherwise it requests a thumbnail for the file path. The native HBITMAP used to create the BitmapSource is always released to avoid native memory leaks.
46+
/// </remarks>
47+
/// <param name="fileName">Path to the file (can be a regular file or a ".url" shortcut).</param>
48+
/// <param name="width">Requested thumbnail width in pixels.</param>
49+
/// <param name="height">Requested thumbnail height in pixels.</param>
50+
/// <param name="options">Thumbnail extraction options (flags) controlling fallback and caching behavior.</param>
51+
/// <returns>A BitmapSource representing the requested thumbnail.</returns>
3852
public static BitmapSource GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
3953
{
40-
HBITMAP hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
54+
HBITMAP hBitmap;
55+
56+
var extension = Path.GetExtension(fileName)?.ToLowerInvariant();
57+
if (extension is UrlExtension)
58+
{
59+
hBitmap = GetHBitmapForUrlFile(fileName, width, height, options);
60+
}
61+
else
62+
{
63+
hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
64+
}
4165

4266
try
4367
{
@@ -50,6 +74,21 @@ public static BitmapSource GetThumbnail(string fileName, int width, int height,
5074
}
5175
}
5276

77+
/// <summary>
78+
/// Obtains a native HBITMAP for the specified file at the requested size using the Windows Shell image factory.
79+
/// </summary>
80+
/// <remarks>
81+
/// If <paramref name="options"/> is <see cref="ThumbnailOptions.ThumbnailOnly"/> and thumbnail extraction fails
82+
/// due to extraction errors or a missing path, the method falls back to requesting an icon (<see cref="ThumbnailOptions.IconOnly"/>).
83+
/// The returned HBITMAP is a raw GDI handle; the caller is responsible for releasing it (e.g., via DeleteObject) to avoid native memory leaks.
84+
/// </remarks>
85+
/// <param name="fileName">Path to the file to thumbnail.</param>
86+
/// <param name="width">Requested thumbnail width in pixels.</param>
87+
/// <param name="height">Requested thumbnail height in pixels.</param>
88+
/// <param name="options">Thumbnail request flags that control behavior (e.g., ThumbnailOnly, IconOnly).</param>
89+
/// <returns>An HBITMAP handle containing the image. Caller must free the handle when finished.</returns>
90+
/// <exception cref="System.Runtime.InteropServices.COMException">If creating the shell item fails (HRESULT returned by SHCreateItemFromParsingName).</exception>
91+
/// <exception cref="InvalidOperationException">If the shell item does not expose IShellItemImageFactory or if an unexpected error occurs while obtaining the image.</exception>
5392
private static unsafe HBITMAP GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
5493
{
5594
var retCode = PInvoke.SHCreateItemFromParsingName(
@@ -108,5 +147,43 @@ private static unsafe HBITMAP GetHBitmap(string fileName, int width, int height,
108147

109148
return hBitmap;
110149
}
150+
151+
/// <summary>
152+
/// Obtains an HBITMAP for a Windows .url shortcut by resolving its IconFile entry and delegating to GetHBitmap.
153+
/// </summary>
154+
/// <remarks>
155+
/// The method parses the .url file as an INI, looks in the "InternetShortcut" section for the "IconFile" entry,
156+
/// and requests a bitmap for that icon path. If no IconFile is present or any error occurs while reading or
157+
/// resolving the icon, it falls back to requesting a thumbnail for the .url file itself.
158+
/// </remarks>
159+
/// <param name="fileName">Path to the .url shortcut file.</param>
160+
/// <param name="width">Requested thumbnail width (pixels).</param>
161+
/// <param name="height">Requested thumbnail height (pixels).</param>
162+
/// <param name="options">ThumbnailOptions flags controlling extraction behavior.</param>
163+
/// <returns>An HBITMAP containing the requested image; callers are responsible for freeing the native handle.</returns>
164+
private static unsafe HBITMAP GetHBitmapForUrlFile(string fileName, int width, int height, ThumbnailOptions options)
165+
{
166+
HBITMAP hBitmap;
167+
168+
try
169+
{
170+
var parser = new FileIniDataParser();
171+
var data = parser.ReadFile(fileName);
172+
var urlSection = data["InternetShortcut"];
173+
174+
var iconPath = urlSection?["IconFile"];
175+
if (string.IsNullOrEmpty(iconPath))
176+
{
177+
throw new FileNotFoundException();
178+
}
179+
hBitmap = GetHBitmap(Path.GetFullPath(iconPath), width, height, options);
180+
}
181+
catch
182+
{
183+
hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
184+
}
185+
186+
return hBitmap;
187+
}
111188
}
112189
}

0 commit comments

Comments
 (0)