Skip to content

Commit 313f86b

Browse files
committed
Replace DllImport & flags with CSWin32
1 parent ca01b25 commit 313f86b

File tree

4 files changed

+82
-133
lines changed

4 files changed

+82
-133
lines changed

Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@
5252
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
5353
</Content>
5454
</ItemGroup>
55+
56+
<ItemGroup>
57+
<AdditionalFiles Include="NativeMethods.txt" />
58+
</ItemGroup>
5559

5660
<ItemGroup>
5761
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
@@ -61,6 +65,10 @@
6165
<ItemGroup>
6266
<PackageReference Include="ini-parser" Version="2.5.2" />
6367
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0" />
68+
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
69+
<PrivateAssets>all</PrivateAssets>
70+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
71+
</PackageReference>
6472
</ItemGroup>
6573

6674
</Project>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
SHGetLocalizedName
2+
LoadString
3+
LoadLibraryEx
4+
FreeLibrary
5+
ExpandEnvironmentStrings
6+
S_OK
7+
SLGP_FLAGS
8+
WIN32_FIND_DATAW
9+
SLR_FLAGS
10+
IShellLinkW

Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs

Lines changed: 33 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,17 @@
11
using System;
2-
using System.Text;
32
using System.Runtime.InteropServices;
4-
using Accessibility;
53
using System.Runtime.InteropServices.ComTypes;
64
using Flow.Launcher.Plugin.Program.Logger;
5+
using Windows.Win32.Foundation;
6+
using Windows.Win32.UI.Shell;
7+
using Windows.Win32.Storage.FileSystem;
78

89
namespace Flow.Launcher.Plugin.Program.Programs
910
{
1011
class ShellLinkHelper
1112
{
12-
[Flags()]
13-
public enum SLGP_FLAGS
14-
{
15-
SLGP_SHORTPATH = 0x1,
16-
SLGP_UNCPRIORITY = 0x2,
17-
SLGP_RAWPATH = 0x4
18-
}
19-
20-
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
21-
public struct WIN32_FIND_DATAW
22-
{
23-
public uint dwFileAttributes;
24-
public long ftCreationTime;
25-
public long ftLastAccessTime;
26-
public long ftLastWriteTime;
27-
public uint nFileSizeHigh;
28-
public uint nFileSizeLow;
29-
public uint dwReserved0;
30-
public uint dwReserved1;
31-
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
32-
public string cFileName;
33-
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
34-
public string cAlternateFileName;
35-
}
36-
37-
[Flags()]
38-
public enum SLR_FLAGS
39-
{
40-
SLR_NO_UI = 0x1,
41-
SLR_ANY_MATCH = 0x2,
42-
SLR_UPDATE = 0x4,
43-
SLR_NOUPDATE = 0x8,
44-
SLR_NOSEARCH = 0x10,
45-
SLR_NOTRACK = 0x20,
46-
SLR_NOLINKINFO = 0x40,
47-
SLR_INVOKE_MSI = 0x80
48-
}
49-
50-
13+
5114
// Reference : http://www.pinvoke.net/default.aspx/Interfaces.IShellLinkW
52-
/// The IShellLink interface allows Shell links to be created, modified, and resolved
53-
[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
54-
interface IShellLinkW
55-
{
56-
/// <summary>Retrieves the path and file name of a Shell link object</summary>
57-
void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags);
58-
/// <summary>Retrieves the list of item identifiers for a Shell link object</summary>
59-
void GetIDList(out IntPtr ppidl);
60-
/// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
61-
void SetIDList(IntPtr pidl);
62-
/// <summary>Retrieves the description string for a Shell link object</summary>
63-
void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
64-
/// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
65-
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
66-
/// <summary>Retrieves the name of the working directory for a Shell link object</summary>
67-
void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
68-
/// <summary>Sets the name of the working directory for a Shell link object</summary>
69-
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
70-
/// <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
71-
void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
72-
/// <summary>Sets the command-line arguments for a Shell link object</summary>
73-
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
74-
/// <summary>Retrieves the hot key for a Shell link object</summary>
75-
void GetHotkey(out short pwHotkey);
76-
/// <summary>Sets a hot key for a Shell link object</summary>
77-
void SetHotkey(short wHotkey);
78-
/// <summary>Retrieves the show command for a Shell link object</summary>
79-
void GetShowCmd(out int piShowCmd);
80-
/// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
81-
void SetShowCmd(int iShowCmd);
82-
/// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
83-
void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
84-
int cchIconPath, out int piIcon);
85-
/// <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
86-
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
87-
/// <summary>Sets the relative path to the Shell link object</summary>
88-
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
89-
/// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
90-
void Resolve(ref Accessibility._RemotableHandle hwnd, SLR_FLAGS fFlags);
91-
/// <summary>Sets the path and file name of a Shell link object</summary>
92-
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
93-
}
94-
9515
[ComImport(), Guid("00021401-0000-0000-C000-000000000046")]
9616
public class ShellLink
9717
{
@@ -102,29 +22,40 @@ public class ShellLink
10222
public string arguments = string.Empty;
10323

10424
// Retrieve the target path using Shell Link
105-
public string retrieveTargetPath(string path)
25+
public unsafe string retrieveTargetPath(string path)
10626
{
10727
var link = new ShellLink();
10828
const int STGM_READ = 0;
10929
((IPersistFile)link).Load(path, STGM_READ);
110-
var hwnd = new _RemotableHandle();
111-
((IShellLinkW)link).Resolve(ref hwnd, 0);
30+
var hwnd = new HWND(IntPtr.Zero);
31+
((IShellLinkW)link).Resolve(hwnd, 0);
11232

11333
const int MAX_PATH = 260;
114-
StringBuilder buffer = new StringBuilder(MAX_PATH);
34+
char[] buffer = new char[MAX_PATH];
11535

11636
var data = new WIN32_FIND_DATAW();
117-
((IShellLinkW)link).GetPath(buffer, buffer.Capacity, ref data, SLGP_FLAGS.SLGP_SHORTPATH);
118-
var target = buffer.ToString();
37+
var target = string.Empty;
38+
fixed (char* bufferChar = buffer)
39+
{
40+
((IShellLinkW)link).GetPath((PWSTR)bufferChar, MAX_PATH, &data, (uint)SLGP_FLAGS.SLGP_SHORTPATH);
41+
int validLength = Array.IndexOf(buffer, '\0');
42+
if (validLength < 0) validLength = MAX_PATH;
43+
target = new string(buffer, 0, validLength);
44+
}
11945

12046
// To set the app description
121-
if (!String.IsNullOrEmpty(target))
47+
if (!string.IsNullOrEmpty(target))
12248
{
12349
try
12450
{
125-
buffer = new StringBuilder(MAX_PATH);
126-
((IShellLinkW)link).GetDescription(buffer, MAX_PATH);
127-
description = buffer.ToString();
51+
char[] buffer1 = new char[MAX_PATH];
52+
fixed (char* buffer1Char = buffer1)
53+
{
54+
((IShellLinkW)link).GetDescription((PWSTR)buffer1Char, MAX_PATH);
55+
int validLength = Array.IndexOf(buffer1, '\0');
56+
if (validLength < 0) validLength = MAX_PATH;
57+
description = new string(buffer1, 0, validLength);
58+
}
12859
}
12960
catch (COMException e)
13061
{
@@ -134,9 +65,14 @@ public string retrieveTargetPath(string path)
13465
e);
13566
}
13667

137-
buffer.Clear();
138-
((IShellLinkW)link).GetArguments(buffer, MAX_PATH);
139-
arguments = buffer.ToString();
68+
char[] buffer2 = new char[MAX_PATH];
69+
fixed (char* buffer2Char = buffer2)
70+
{
71+
((IShellLinkW)link).GetArguments((PWSTR)buffer2Char, MAX_PATH);
72+
int validLength = Array.IndexOf(buffer2, '\0');
73+
if (validLength < 0) validLength = MAX_PATH;
74+
arguments = new string(buffer2, 0, validLength);
75+
}
14076
}
14177

14278
// To release unmanaged memory

Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using System;
22
using System.IO;
3-
using System.Runtime.InteropServices;
4-
using System.Text;
5-
3+
using Windows.Win32;
4+
using Windows.Win32.Foundation;
5+
using Windows.Win32.System.LibraryLoader;
66

77
namespace Flow.Launcher.Plugin.Program.Programs
88
{
@@ -13,51 +13,46 @@ namespace Flow.Launcher.Plugin.Program.Programs
1313
/// </summary>
1414
public static class ShellLocalization
1515
{
16-
internal const uint DONTRESOLVEDLLREFERENCES = 0x00000001;
17-
internal const uint LOADLIBRARYASDATAFILE = 0x00000002;
18-
19-
[DllImport("shell32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
20-
internal static extern int SHGetLocalizedName(string pszPath, StringBuilder pszResModule, ref int cch, out int pidsRes);
21-
22-
[DllImport("user32.dll", EntryPoint = "LoadStringW", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
23-
internal static extern int LoadString(IntPtr hModule, int resourceID, StringBuilder resourceValue, int len);
24-
25-
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadLibraryExW")]
26-
internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
27-
28-
[DllImport("kernel32.dll", ExactSpelling = true)]
29-
internal static extern int FreeLibrary(IntPtr hModule);
30-
31-
[DllImport("kernel32.dll", EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, ExactSpelling = true)]
32-
internal static extern uint ExpandEnvironmentStrings(string lpSrc, StringBuilder lpDst, int nSize);
33-
3416
/// <summary>
3517
/// Returns the localized name of a shell item.
3618
/// </summary>
3719
/// <param name="path">Path to the shell item (e. g. shortcut 'File Explorer.lnk').</param>
3820
/// <returns>The localized name as string or <see cref="string.Empty"/>.</returns>
39-
public static string GetLocalizedName(string path)
21+
public static unsafe string GetLocalizedName(string path)
4022
{
41-
StringBuilder resourcePath = new StringBuilder(1024);
42-
StringBuilder localizedName = new StringBuilder(1024);
43-
int len, id;
44-
len = resourcePath.Capacity;
23+
int capacity = 1024;
24+
char[] resourcePathBuffer = new char[capacity];
4525

4626
// If there is no resource to localize a file name the method returns a non zero value.
47-
if (SHGetLocalizedName(path, resourcePath, ref len, out id) == 0)
27+
fixed (char* resourcePath = resourcePathBuffer)
4828
{
49-
_ = ExpandEnvironmentStrings(resourcePath.ToString(), resourcePath, resourcePath.Capacity);
50-
IntPtr hMod = LoadLibraryEx(resourcePath.ToString(), IntPtr.Zero, DONTRESOLVEDLLREFERENCES | LOADLIBRARYASDATAFILE);
51-
if (hMod != IntPtr.Zero)
29+
var result = PInvoke.SHGetLocalizedName(path, (PWSTR)resourcePath, (uint)capacity, out var id);
30+
if (result == HRESULT.S_OK)
5231
{
53-
if (LoadString(hMod, id, localizedName, localizedName.Capacity) != 0)
32+
int validLength = Array.IndexOf(resourcePathBuffer, '\0');
33+
if (validLength < 0) validLength = capacity;
34+
var resourcePathStr = new string(resourcePathBuffer, 0, validLength);
35+
_ = PInvoke.ExpandEnvironmentStrings(resourcePathStr, resourcePath, (uint)capacity);
36+
var handle = PInvoke.LoadLibraryEx(resourcePathStr,
37+
LOAD_LIBRARY_FLAGS.DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_DATAFILE);
38+
IntPtr safeHandle = handle.DangerousGetHandle();
39+
if (safeHandle != IntPtr.Zero)
5440
{
55-
string lString = localizedName.ToString();
56-
_ = FreeLibrary(hMod);
57-
return lString;
58-
}
41+
char[] localizedNameBuffer = new char[capacity];
42+
fixed (char* localizedName = localizedNameBuffer)
43+
{
44+
if (PInvoke.LoadString(handle, (uint)id, (PWSTR)localizedName, capacity) != 0)
45+
{
46+
validLength = Array.IndexOf(localizedNameBuffer, '\0');
47+
if (validLength < 0) validLength = capacity;
48+
var lString = new string(localizedNameBuffer, 0, validLength);
49+
PInvoke.FreeLibrary(new(safeHandle));
50+
return lString;
51+
}
52+
}
5953

60-
_ = FreeLibrary(hMod);
54+
PInvoke.FreeLibrary(new(safeHandle));
55+
}
6156
}
6257
}
6358

0 commit comments

Comments
 (0)