Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/shell icons path extension #65

Merged
merged 3 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ public class EnvironmentPathService : IEnvironmentPathService
public string GetFileName(string path) => Path.GetFileName(path);

public string GetExtension(string path) => Path.GetExtension(path);

public char GetDirectorySeparator() => Path.DirectorySeparatorChar;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface IEnvironmentPathService
string GetFileName(string path);

string GetExtension(string path);

char GetDirectorySeparator();
}
6 changes: 6 additions & 0 deletions src/Camelot.Services/PathService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ public string GetFileName(string path)
return string.IsNullOrEmpty(fileName) ? path : fileName;
}

/// <summary>
/// Returns the extension of the given path, without the prefix of dot.
/// This is intentionally returns different result than <see cref="System.IO.Path.GetExtension"/
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public string GetExtension(string path)
{
if (path.StartsWith("."))
Expand Down
8 changes: 5 additions & 3 deletions src/Camelot.ViewModels.Windows/ShellIcons/WindowsIconTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ public static bool IsIconThatRequiresExtract(string fileName, string extension)
{
throw new ArgumentNullException(nameof(fileName));
}

if (string.IsNullOrEmpty(extension))
{
throw new ArgumentNullException(nameof(extension));
}
if (fileName.Contains('?') || fileName.Contains(','))
{
return true; // icon in indexed resource
}

var extensionsOfIcoFiles = new List<string> { ".ico", ".exe", ".dll" , ".cpl", ".appref-ms", ".msc" };
var extensionsOfIcoFiles = new List<string> { "ico", "exe", "dll" , "cpl", "appref-ms", "msc" };

return extensionsOfIcoFiles.Contains(extension);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,12 @@
{
if (string.IsNullOrEmpty(extension))
{
throw new ArgumentNullException(nameof(extension));
throw new ArgumentException(extension, nameof(extension));
}

if (!extension.StartsWith("."))
if (extension.ToLower() == "lnk")
{
throw new ArgumentException(nameof(extension));
}

if (extension.ToLower() == ".lnk")
{
throw new ArgumentException("Need to resolve .lnk first");
throw new ArgumentException("Need to resolve 'lnk' first");
}

var iconFilename = ShellIcon.GetIconForExtension(extension);
Expand Down Expand Up @@ -65,11 +60,10 @@
{
throw new ArgumentOutOfRangeException(nameof(path));
}

var ext = GetExtension(path);
if (ext == ".lnk")
var ext = GetExtensionAsLowerCase(path);
if (ext == "lnk")
{
throw new ArgumentException("Need to resolve .lnk first");
throw new ArgumentException("Need to resolve 'lnk' first");
}

return LoadIcon(path);
Expand All @@ -84,13 +78,13 @@

ImageModel result;

var needsExtract = WindowsIconTypes.IsIconThatRequiresExtract(path, GetExtension(path));
var needsExtract = WindowsIconTypes.IsIconThatRequiresExtract(path, GetExtensionAsLowerCase(path));
if (needsExtract)
{
var icon = IconExtractor.ExtractIcon(path);
// TODO: check if lossy and/or try other options, see url below. (iksi4prs).
// https://learn.microsoft.com/en-us/dotnet/api/system.drawing.imageconverter.canconvertfrom?view=dotnet-plat-ext-7.0
var systemBitmap = icon.ToBitmap();

Check warning on line 87 in src/Camelot.ViewModels.Windows/ShellIcons/WindowsShellIconsService.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

This call site is reachable on all platforms. 'Icon.ToBitmap()' is only supported on: 'windows'.

Check warning on line 87 in src/Camelot.ViewModels.Windows/ShellIcons/WindowsShellIconsService.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

This call site is reachable on all platforms. 'Icon.ToBitmap()' is only supported on: 'windows'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

Check warning on line 87 in src/Camelot.ViewModels.Windows/ShellIcons/WindowsShellIconsService.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

This call site is reachable on all platforms. 'Icon.ToBitmap()' is only supported on: 'windows'.
var avaloniaBitmap = SystemImageToAvaloniaBitmapConverter.Convert(systemBitmap);
result = new ImageModel(avaloniaBitmap);
}
Expand Down Expand Up @@ -118,18 +112,18 @@
throw new ArgumentNullException(nameof(filename));
}

var ext = GetExtension(filename);

// next extensions require that the icon will be resolved by full path,
var ext = GetExtensionAsLowerCase(filename);
// Next extensions require that the icon will be resolved by full path,
// and not just the extension itself.
var extensionForFullPaths = new[] { ".exe", ".cpl", ".appref-ms", ".msc" };
// As per stanards of this project, extension don't have dot prefix.
var extensionForFullPaths = new[] { "exe", "cpl", "appref-ms", "msc" };

return extensionForFullPaths.Contains(ext)
? ShellIconType.FullPath
: ShellIconType.Extension;
}

private string GetExtension(string filename)
private string GetExtensionAsLowerCase(string filename)
{
return _pathService.GetExtension(filename).ToLower();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ public bool IsShellLink(string path)
}

var ext = _pathService.GetExtension(path).ToLower();

return ext == ".lnk";
return ext == "lnk";
}

public string ResolveLink(string path)
Expand Down
33 changes: 26 additions & 7 deletions src/Camelot.ViewModels.Windows/WinApi/ShellIcon.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Camelot.Services.Windows.WinApi;
using System.Diagnostics;

namespace Camelot.ViewModels.Windows.WinApi;

Expand All @@ -11,17 +12,35 @@ public static string GetIconForExtension(string extension)
throw new ArgumentNullException(nameof(extension));
}

if (!extension.StartsWith("."))
{
throw new ArgumentException(nameof(extension));
}
// But to work with Windows API, we still need to add dot prefix...
extension = "." + extension;

const Win32.AssocF assocFlag = Win32.AssocF.None;
var currentAppIcon = Win32.AssocQueryString(assocFlag, Win32.AssocStr.AppIconRreference, extension);

return string.IsNullOrEmpty(currentAppIcon)
? Win32.AssocQueryString(assocFlag, Win32.AssocStr.DefaultIcon, extension)
: currentAppIcon;
string result;
if (IsValid(currentAppIcon))
{
result = currentAppIcon;
}
else
{
// fallback to use default
result = Win32.AssocQueryString(assocFlag, Win32.AssocStr.DefaultIcon, extension);
}
return result;
}

private static bool IsValid(string icon)
{
if (string.IsNullOrEmpty(icon))
return false;
if (icon.TrimStart().StartsWith("%"))
{
// looks like because of invalid values in registry
return false;
}
return true;
}
}

2 changes: 1 addition & 1 deletion src/Camelot.ViewModels.Windows/WinApi/ShellLinkResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public string ResolveLink(string path)
}

var ext = _pathService.GetExtension(path).ToLower();
if (ext != ".lnk")
if (ext != "lnk")
{
throw new ArgumentNullException(nameof(path));
}
Expand Down
Loading