Replace DllImport & flags with CSWin32

This commit is contained in:
Jack251970 2024-12-10 15:09:06 +08:00
parent ca01b25a39
commit 313f86b648
4 changed files with 82 additions and 133 deletions

View file

@ -52,6 +52,10 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="NativeMethods.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
@ -61,6 +65,10 @@
<ItemGroup>
<PackageReference Include="ini-parser" Version="2.5.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View file

@ -0,0 +1,10 @@
SHGetLocalizedName
LoadString
LoadLibraryEx
FreeLibrary
ExpandEnvironmentStrings
S_OK
SLGP_FLAGS
WIN32_FIND_DATAW
SLR_FLAGS
IShellLinkW

View file

@ -1,97 +1,17 @@
using System;
using System.Text;
using System.Runtime.InteropServices;
using Accessibility;
using System.Runtime.InteropServices.ComTypes;
using Flow.Launcher.Plugin.Program.Logger;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;
using Windows.Win32.Storage.FileSystem;
namespace Flow.Launcher.Plugin.Program.Programs
{
class ShellLinkHelper
{
[Flags()]
public enum SLGP_FLAGS
{
SLGP_SHORTPATH = 0x1,
SLGP_UNCPRIORITY = 0x2,
SLGP_RAWPATH = 0x4
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WIN32_FIND_DATAW
{
public uint dwFileAttributes;
public long ftCreationTime;
public long ftLastAccessTime;
public long ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
[Flags()]
public enum SLR_FLAGS
{
SLR_NO_UI = 0x1,
SLR_ANY_MATCH = 0x2,
SLR_UPDATE = 0x4,
SLR_NOUPDATE = 0x8,
SLR_NOSEARCH = 0x10,
SLR_NOTRACK = 0x20,
SLR_NOLINKINFO = 0x40,
SLR_INVOKE_MSI = 0x80
}
// Reference : http://www.pinvoke.net/default.aspx/Interfaces.IShellLinkW
/// The IShellLink interface allows Shell links to be created, modified, and resolved
[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
interface IShellLinkW
{
/// <summary>Retrieves the path and file name of a Shell link object</summary>
void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags);
/// <summary>Retrieves the list of item identifiers for a Shell link object</summary>
void GetIDList(out IntPtr ppidl);
/// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
void SetIDList(IntPtr pidl);
/// <summary>Retrieves the description string for a Shell link object</summary>
void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
/// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
/// <summary>Retrieves the name of the working directory for a Shell link object</summary>
void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
/// <summary>Sets the name of the working directory for a Shell link object</summary>
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
/// <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
/// <summary>Sets the command-line arguments for a Shell link object</summary>
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
/// <summary>Retrieves the hot key for a Shell link object</summary>
void GetHotkey(out short pwHotkey);
/// <summary>Sets a hot key for a Shell link object</summary>
void SetHotkey(short wHotkey);
/// <summary>Retrieves the show command for a Shell link object</summary>
void GetShowCmd(out int piShowCmd);
/// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
void SetShowCmd(int iShowCmd);
/// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
int cchIconPath, out int piIcon);
/// <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
/// <summary>Sets the relative path to the Shell link object</summary>
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
/// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
void Resolve(ref Accessibility._RemotableHandle hwnd, SLR_FLAGS fFlags);
/// <summary>Sets the path and file name of a Shell link object</summary>
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
[ComImport(), Guid("00021401-0000-0000-C000-000000000046")]
public class ShellLink
{
@ -102,29 +22,40 @@ namespace Flow.Launcher.Plugin.Program.Programs
public string arguments = string.Empty;
// Retrieve the target path using Shell Link
public string retrieveTargetPath(string path)
public unsafe string retrieveTargetPath(string path)
{
var link = new ShellLink();
const int STGM_READ = 0;
((IPersistFile)link).Load(path, STGM_READ);
var hwnd = new _RemotableHandle();
((IShellLinkW)link).Resolve(ref hwnd, 0);
var hwnd = new HWND(IntPtr.Zero);
((IShellLinkW)link).Resolve(hwnd, 0);
const int MAX_PATH = 260;
StringBuilder buffer = new StringBuilder(MAX_PATH);
char[] buffer = new char[MAX_PATH];
var data = new WIN32_FIND_DATAW();
((IShellLinkW)link).GetPath(buffer, buffer.Capacity, ref data, SLGP_FLAGS.SLGP_SHORTPATH);
var target = buffer.ToString();
var target = string.Empty;
fixed (char* bufferChar = buffer)
{
((IShellLinkW)link).GetPath((PWSTR)bufferChar, MAX_PATH, &data, (uint)SLGP_FLAGS.SLGP_SHORTPATH);
int validLength = Array.IndexOf(buffer, '\0');
if (validLength < 0) validLength = MAX_PATH;
target = new string(buffer, 0, validLength);
}
// To set the app description
if (!String.IsNullOrEmpty(target))
if (!string.IsNullOrEmpty(target))
{
try
{
buffer = new StringBuilder(MAX_PATH);
((IShellLinkW)link).GetDescription(buffer, MAX_PATH);
description = buffer.ToString();
char[] buffer1 = new char[MAX_PATH];
fixed (char* buffer1Char = buffer1)
{
((IShellLinkW)link).GetDescription((PWSTR)buffer1Char, MAX_PATH);
int validLength = Array.IndexOf(buffer1, '\0');
if (validLength < 0) validLength = MAX_PATH;
description = new string(buffer1, 0, validLength);
}
}
catch (COMException e)
{
@ -134,9 +65,14 @@ namespace Flow.Launcher.Plugin.Program.Programs
e);
}
buffer.Clear();
((IShellLinkW)link).GetArguments(buffer, MAX_PATH);
arguments = buffer.ToString();
char[] buffer2 = new char[MAX_PATH];
fixed (char* buffer2Char = buffer2)
{
((IShellLinkW)link).GetArguments((PWSTR)buffer2Char, MAX_PATH);
int validLength = Array.IndexOf(buffer2, '\0');
if (validLength < 0) validLength = MAX_PATH;
arguments = new string(buffer2, 0, validLength);
}
}
// To release unmanaged memory

View file

@ -1,8 +1,8 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.LibraryLoader;
namespace Flow.Launcher.Plugin.Program.Programs
{
@ -13,51 +13,46 @@ namespace Flow.Launcher.Plugin.Program.Programs
/// </summary>
public static class ShellLocalization
{
internal const uint DONTRESOLVEDLLREFERENCES = 0x00000001;
internal const uint LOADLIBRARYASDATAFILE = 0x00000002;
[DllImport("shell32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
internal static extern int SHGetLocalizedName(string pszPath, StringBuilder pszResModule, ref int cch, out int pidsRes);
[DllImport("user32.dll", EntryPoint = "LoadStringW", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
internal static extern int LoadString(IntPtr hModule, int resourceID, StringBuilder resourceValue, int len);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadLibraryExW")]
internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
[DllImport("kernel32.dll", ExactSpelling = true)]
internal static extern int FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, ExactSpelling = true)]
internal static extern uint ExpandEnvironmentStrings(string lpSrc, StringBuilder lpDst, int nSize);
/// <summary>
/// Returns the localized name of a shell item.
/// </summary>
/// <param name="path">Path to the shell item (e. g. shortcut 'File Explorer.lnk').</param>
/// <returns>The localized name as string or <see cref="string.Empty"/>.</returns>
public static string GetLocalizedName(string path)
public static unsafe string GetLocalizedName(string path)
{
StringBuilder resourcePath = new StringBuilder(1024);
StringBuilder localizedName = new StringBuilder(1024);
int len, id;
len = resourcePath.Capacity;
int capacity = 1024;
char[] resourcePathBuffer = new char[capacity];
// If there is no resource to localize a file name the method returns a non zero value.
if (SHGetLocalizedName(path, resourcePath, ref len, out id) == 0)
fixed (char* resourcePath = resourcePathBuffer)
{
_ = ExpandEnvironmentStrings(resourcePath.ToString(), resourcePath, resourcePath.Capacity);
IntPtr hMod = LoadLibraryEx(resourcePath.ToString(), IntPtr.Zero, DONTRESOLVEDLLREFERENCES | LOADLIBRARYASDATAFILE);
if (hMod != IntPtr.Zero)
var result = PInvoke.SHGetLocalizedName(path, (PWSTR)resourcePath, (uint)capacity, out var id);
if (result == HRESULT.S_OK)
{
if (LoadString(hMod, id, localizedName, localizedName.Capacity) != 0)
int validLength = Array.IndexOf(resourcePathBuffer, '\0');
if (validLength < 0) validLength = capacity;
var resourcePathStr = new string(resourcePathBuffer, 0, validLength);
_ = PInvoke.ExpandEnvironmentStrings(resourcePathStr, resourcePath, (uint)capacity);
var handle = PInvoke.LoadLibraryEx(resourcePathStr,
LOAD_LIBRARY_FLAGS.DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_DATAFILE);
IntPtr safeHandle = handle.DangerousGetHandle();
if (safeHandle != IntPtr.Zero)
{
string lString = localizedName.ToString();
_ = FreeLibrary(hMod);
return lString;
}
char[] localizedNameBuffer = new char[capacity];
fixed (char* localizedName = localizedNameBuffer)
{
if (PInvoke.LoadString(handle, (uint)id, (PWSTR)localizedName, capacity) != 0)
{
validLength = Array.IndexOf(localizedNameBuffer, '\0');
if (validLength < 0) validLength = capacity;
var lString = new string(localizedNameBuffer, 0, validLength);
PInvoke.FreeLibrary(new(safeHandle));
return lString;
}
}
_ = FreeLibrary(hMod);
PInvoke.FreeLibrary(new(safeHandle));
}
}
}