Skip to content

Commit

Permalink
Merge pull request #13030 from iterate-ch/feature/GH-12802
Browse files Browse the repository at this point in the history
Edit capability for Store-apps
  • Loading branch information
dkocher committed May 4, 2022
2 parents bec408a + 2e4e2c5 commit 167c89c
Show file tree
Hide file tree
Showing 23 changed files with 803 additions and 778 deletions.
Expand Up @@ -76,7 +76,7 @@ protected override void setFactories()
typeof(AssemblyApplicationResourcesFinder).AssemblyQualifiedName);
this.setDefault("factory.editorfactory.class", typeof(SystemWatchEditorFactory).AssemblyQualifiedName);
this.setDefault("factory.applicationlauncher.class", typeof(WindowsApplicationLauncher).AssemblyQualifiedName);
this.setDefault("factory.applicationfinder.class", typeof(RegistryApplicationFinder).AssemblyQualifiedName);
this.setDefault("factory.applicationfinder.class", typeof(ShellApplicationFinder).AssemblyQualifiedName);
this.setDefault("factory.local.class", typeof(SystemLocal).AssemblyQualifiedName);
this.setDefault("factory.passwordstore.class", typeof(PasswordStoreFacade).AssemblyQualifiedName);
this.setDefault("factory.proxycredentialsstore.class",
Expand Down
47 changes: 43 additions & 4 deletions core/native/refresh/src/main/csharp/Services/IconProvider.cs
@@ -1,15 +1,19 @@
using ch.cyberduck.core;
using ch.cyberduck.core.local;
using Ch.Cyberduck.Core.Local;
using System;
using System.IO;
using Windows.Win32;
using Windows.Win32.Storage.FileSystem;
using Windows.Win32.UI.Shell;
using Windows.Win32.UI.WindowsAndMessaging;
using static Windows.Win32.CorePInvoke;
using static Windows.Win32.Storage.FileSystem.FILE_FLAGS_AND_ATTRIBUTES;
using static Windows.Win32.UI.Shell.SHGFI_FLAGS;
using Path = System.IO.Path;

namespace Ch.Cyberduck.Core.Refresh.Services
{
using System.IO;

public abstract class IconProvider
{
public IconProvider(IconCache iconCache, IIconProviderImageSource imageSource)
Expand Down Expand Up @@ -42,9 +46,42 @@ protected IconProvider(IconCache iconCache, IIconProviderImageSource imageSource

public delegate bool GetCacheIconCallback(IconCache cache, int size);

public abstract T GetDisk(Protocol protocol, int size);
public T GetApplication(Application application, int size)
{
string key = "app:" + application.getIdentifier();
if (!IconCache.TryGetIcon(key, size, out T image))
{
string iconPath;
int iconIndex;
switch (application)
{
case ShellApplicationFinder.ShellApplication shell:
iconPath = shell.IconPath;
iconIndex = shell.IconIndex;
break;

case ShellApplicationFinder.ProgIdApplication progId:
iconPath = progId.IconPath;
iconIndex = progId.IconIndex;
break;

default:
return default;
}
iconPath = SHLoadIndirectString(iconPath);

SHCreateFileExtractIcon(iconPath, 0, out IExtractIconW icon);
using HICON_Handle largeIcon = new();
using HICON_Handle smallIcon = new();
icon.Extract(iconPath, (uint)iconIndex, largeIcon.Ref, smallIcon.Ref, 0);
Get(largeIcon.Value, (c, s, i) => c.CacheIcon(key, s, i));
Get(smallIcon.Value, (c, s, i) => c.CacheIcon(key, s, i));
image = Get(key, size);
}
return image;
}

public abstract T GetIcon(Protocol protocol, int size);
public abstract T GetDisk(Protocol protocol, int size);

public T GetFileIcon(string filename, bool isFolder, bool large, bool isExecutable)
{
Expand Down Expand Up @@ -87,6 +124,8 @@ public T GetFileIcon(string filename, bool isFolder, bool large, bool isExecutab
}
}

public abstract T GetIcon(Protocol protocol, int size);

public T GetResource(string name, int? requestSize = default) => requestSize switch
{
int size => Get(name, size),
Expand Down
@@ -1,9 +1,11 @@
using ch.cyberduck.core;
using Ch.Cyberduck.Core.Local;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using static Windows.Win32.CorePInvoke;

namespace Ch.Cyberduck.Core.Refresh.Services
{
Expand Down Expand Up @@ -34,7 +36,25 @@ public Image AliasFolder()
return overlayed;
}

public Image DefaultBrowser() => GetFileIcon(Utils.GetSystemDefaultBrowser(), false, true, true);
public Image DefaultBrowser()
{
if (IconCache.TryGetIcon("app:defaultbrowser", out Image image))
{
return image;
}

if (Utils.GetSystemDefaultBrowser() is not ShellApplicationFinder.ProgIdApplication app)
{
return default;
}

uint result = ExtractIconEx(app.IconPath, app.IconIndex, out var largeIcon, out var smallIcon, 1);
using (smallIcon)
using (largeIcon)
{
return Get(largeIcon.DangerousGetHandle(), (c, s, i) => c.CacheIcon("app:defaultbrowser", s, i));
}
}

public override Image GetDisk(Protocol protocol, int size)
=> IconCache.TryGetIcon(protocol, size, out Image image, "Disk")
Expand Down
28 changes: 22 additions & 6 deletions core/src/main/csharp/NativeMethods.txt
@@ -1,9 +1,6 @@
AssocCreate
ASSOCDATA
ASSOCF_INIT_DEFAULTTOSTAR
ASSOCF_INIT_DEFAULTTOSTAR
ASSOCF_NOTRUNCATE
ASSOCKEY
ASSOCSTR
BHID_DataObject
CLSID_QueryAssociations
CoTaskMemFree
CRED_FLAGS
Expand All @@ -14,27 +11,46 @@ CredFree
CredRead
CredWrite
DefWindowProc
ExtractIconEx
GetCurrentPackageFullName
GetTokenInformation
GetWindowLong
GetWindowLongPtr
HWND_TOPMOST
IApplicationAssociationRegistration
IAssocHandler
IAssocHandlerInvoker
IDataObject
IExtractIconW
ILCreateFromPath
IQueryAssociations
IShellItem
MESSAGEBOX_RESULT
PathParseIconLocation
PBST_ERROR
PBST_NORMAL
PBST_PAUSED
S_FALSE
S_OK
SEE_MASK_CLASSNAME
SEE_MASK_NOASYNC
SendMessage
SetWindowLong
SetWindowPos
SetWindowText
SHAssocEnumHandlers
SHCreateAssociationRegistration
SHCreateFileExtractIcon
SHCreateItemFromIDList
Shell_GetCachedImageIndex
ShellExecuteEx
SHGetFileInfo
SHGFI_FLAGS
SHLoadIndirectString
SHOpenFolderAndSelectItems
SHOpenWithDialog
SHParseDisplayName
STRSAFE_E_INSUFFICIENT_BUFFER
TASKDIALOG_COMMON_BUTTON_FLAGS
TASKDIALOG_ELEMENTS
TASKDIALOG_FLAGS
Expand All @@ -47,4 +63,4 @@ WINDOW_EX_STYLE
WINDOW_STYLE
WM_APP
WM_SETICON
WM_USER
WM_USER
99 changes: 99 additions & 0 deletions core/src/main/csharp/Windows/Win32/FriendlyOverloadExtensions.cs
@@ -0,0 +1,99 @@
#pragma warning disable IDE0001,IDE0002,IDE0005,CS1591,CS1573,CS0465,CS0649,CS8019,CS1570,CS1584,CS1658,CS0436

namespace Windows.Win32
{
using global::System;
using global::System.Diagnostics;
using global::System.Runtime.CompilerServices;
using global::System.Runtime.InteropServices;
using winmdroot = global::Windows.Win32;

public static partial class FriendlyOverloadExtensions
{
/// <inheritdoc cref="winmdroot.UI.Shell.IShellItem.BindToHandler(winmdroot.System.Com.IBindCtx, global::System.Guid*, global::System.Guid*, out object)"/>
public static unsafe void BindToHandler<T>(this winmdroot.UI.Shell.IShellItem @this, winmdroot.System.Com.IBindCtx pbc, in global::System.Guid bhid, out T ppv)
{
global::System.Guid riid = typeof(T).GUID;
fixed (global::System.Guid* bhidLocal = &bhid)
{
@this.BindToHandler(pbc, bhidLocal, &riid, out object ppvLocal);
ppv = (T)ppvLocal;
}
}

/// <inheritdoc cref="winmdroot.UI.Shell.IExtractIconW.Extract(winmdroot.Foundation.PCWSTR, uint, winmdroot.UI.WindowsAndMessaging.HICON*, winmdroot.UI.WindowsAndMessaging.HICON*, uint)"/>
public static unsafe void Extract(this winmdroot.UI.Shell.IExtractIconW @this, string pszFile, uint nIconIndex, in winmdroot.UI.WindowsAndMessaging.HICON phiconLarge, in winmdroot.UI.WindowsAndMessaging.HICON phiconSmall, uint nIconSize)
{
fixed (char* pszFileLocal = pszFile)
{
@this.Extract(pszFileLocal, nIconIndex, (winmdroot.UI.WindowsAndMessaging.HICON*)phiconLarge.Value, (winmdroot.UI.WindowsAndMessaging.HICON*)phiconSmall.Value, nIconSize);
}
}

/// <inheritdoc cref="winmdroot.UI.Shell.IAssocHandler.GetIconLocation(Foundation.PWSTR*, int*)"/>
public static unsafe string GetIconLocation(this winmdroot.UI.Shell.IAssocHandler @this, out int pIndex)
{
fixed (int* pIndexLocal = &pIndex)
{
winmdroot.Foundation.PWSTR ppszLocal = new();
@this.GetIconLocation(&ppszLocal, pIndexLocal);
return ppszLocal.ToString();
}
}

/// <inheritdoc cref="winmdroot.UI.Shell.IAssocHandler.GetName(winmdroot.Foundation.PWSTR*)"/>
public static unsafe string GetName(this winmdroot.UI.Shell.IAssocHandler @this)
{
winmdroot.Foundation.PWSTR ppszLocal = new();
@this.GetName(&ppszLocal);
return ppszLocal.ToString();
}

/// <inheritdoc cref="winmdroot.UI.Shell.IQueryAssociations.GetString(uint, UI.Shell.ASSOCSTR, Foundation.PCWSTR, Foundation.PWSTR, uint*)"
public static unsafe bool GetString(this winmdroot.UI.Shell.IQueryAssociations @this, winmdroot.UI.Shell.ASSOCSTR str, string pszExtra, out string pszOut)
{
var pool = global::System.Buffers.ArrayPool<char>.Shared;
uint length = 0;
try
{
@this.GetString(winmdroot.CorePInvoke.ASSOCF_NOTRUNCATE, str, pszExtra, default, ref length);
char[] buffer = null;
try
{
buffer = pool.Rent((int)length);
length = (uint)buffer.Length;
fixed (char* bufferLocal = buffer)
{
@this.GetString(winmdroot.CorePInvoke.ASSOCF_NOTRUNCATE, str, pszExtra, bufferLocal, ref length);

pszOut = ((winmdroot.Foundation.PCWSTR)bufferLocal).ToString();
return true;
}
}
finally
{
pool.Return(buffer);
}
}
catch { }
pszOut = default;
return false;
}

/// <inheritdoc cref="winmdroot.UI.Shell.IAssocHandler.GetUIName(winmdroot.Foundation.PWSTR*)"/>
public static unsafe string GetUIName(this winmdroot.UI.Shell.IAssocHandler @this)
{
winmdroot.Foundation.PWSTR ppszLocal = new();
@this.GetUIName(&ppszLocal);
return ppszLocal.ToString();
}

/// <inheritdoc cref="winmdroot.UI.Shell.IEnumAssocHandlers.Next(uint, winmdroot.UI.Shell.IAssocHandler[], uint*)"/>
public static unsafe int Next(this winmdroot.UI.Shell.IEnumAssocHandlers @this, winmdroot.UI.Shell.IAssocHandler[] rgelt)
{
uint pceltFetched = 0;
@this.Next((uint)rgelt.Length, rgelt, &pceltFetched);
return (int)pceltFetched;
}
}
}
51 changes: 51 additions & 0 deletions core/src/main/csharp/Windows/Win32/HICON_Handle.cs
@@ -0,0 +1,51 @@
using static InlineIL.FieldRef;
using static InlineIL.IL;
using static InlineIL.IL.Emit;
using static Windows.Win32.CorePInvoke;

namespace Windows.Win32.UI.WindowsAndMessaging
{
public unsafe ref struct HICON_Handle
{
private HICON ptr;

public HICON Ref
{
get
{
// C# doesn't allow return ref ptr.
// IL doesn't have a problem with it.
Ldarg_0();
Ldflda(Field(typeof(HICON_Handle), nameof(ptr)));
Ret();
throw Unreachable();
}
}

public ref HICON Value
{
get
{
// C# doesn't allow return ref ptr.
// IL doesn't have a problem with it.
Ldarg_0();
Ldflda(Field(typeof(HICON_Handle), nameof(ptr)));
Ret(); // return ref *(HICON*)&ptr;
throw Unreachable();
}
}

public static implicit operator bool(in HICON_Handle @this) => @this.ptr != null;

public static implicit operator HICON_Handle(in HICON hicon) => new() { ptr = hicon };

public void Dispose()
{
if (ptr != null)
{
DestroyIcon(ptr);
ptr = default;
}
}
}
}

0 comments on commit 167c89c

Please sign in to comment.