diff --git a/Dockerfile b/Dockerfile
index ab548b7..cb79bdd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@
# This docker can be used to test the linux implementation of FindExecutable
#
# On a Windows dev PC run:
-# docker build -t sgr/findexecutabletest .
+# docker build -t sgr/findexecutabletest .
# docker run --rm -v ".\bin\Debug\net10.0:/myapp" -t sgr/findexecutabletest
#
# Adjust path of the volume mount if you are to test other build flavors.
diff --git a/FindExecutable/ComponentSource.json b/FindExecutable/ComponentSource.json
deleted file mode 100644
index 6ee736b..0000000
--- a/FindExecutable/ComponentSource.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "_type": "ComponentSourceManifest",
- "_version": 1,
- "components": [
- {
- "name": "SGrottel FindExecutable",
- "source": [
- {
- "type": "git",
- "url": "https://www.github.com/sgrottel/FindExecutable",
- "hash": "7f50fbe3e3211719817d76f637ea01cb1d410447",
- "path": "FindExecutable"
- }
- ]
- }
- ]
-}
diff --git a/FindExecutable/FindExecutable.cs b/FindExecutable/FindExecutable.cs
index 3ecb700..0ff3b0d 100644
--- a/FindExecutable/FindExecutable.cs
+++ b/FindExecutable/FindExecutable.cs
@@ -25,6 +25,8 @@
//
// Version history:
//
+// v1.0.2 -- 2026-04-01
+// resolved further finding by code analyzer
// v1.0.1 -- 2026-02-22
// resolved findings by scanners
// v1.0 -- 2025-05-19
@@ -164,6 +166,7 @@ public static class FindExecutable
#region Test if File is Executable
#region Windows P/Invoke
+ [SupportedOSPlatform("Windows")]
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, IntPtr psfi, uint cbSizeFileInfo, uint uFlags);
private const uint SHGFI_EXETYPE = 0x000002000;
@@ -204,7 +207,8 @@ private static bool IsExecutableOnWindow(string path)
}
#region Linux P/Invoke
- [DllImport("libc", CharSet = CharSet.Ansi, SetLastError = true)]
+ [UnsupportedOSPlatform("Windows")]
+ [DllImport("libc", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int access(string pathname, int mode);
// https://codebrowser.dev/glibc/glibc/posix/unistd.h.html#283
private const int X_OK = 1;
diff --git a/Program.cs b/Program.cs
index a741aca..33ecb62 100644
--- a/Program.cs
+++ b/Program.cs
@@ -1,64 +1,122 @@
-//
-// Run Docker/Linux test with:
-//
-// docker build -t sgr/findexecutable .
-// docker run --rm sgr/findexecutable
-//
-
-using System;
-using System.IO;
-
-namespace FindExecutable
-{
- internal class Program
- {
- static int Main(string[] args)
- {
- int retval = 0;
-
- string[] executables = new string[] {
- "git.exe",
- "git"
- };
-
- foreach (string executable in executables)
- {
- try
- {
- Console.WriteLine($"Searching for: {executable}");
-
- string? fullPath = FindExecutable.FullPath(executable);
-
- if (fullPath != null)
- {
- if (Path.IsPathFullyQualified(fullPath))
- {
- Console.WriteLine($"FOUND: {fullPath}");
- }
- else
- {
- Console.WriteLine($"FOUND w/ WARNING: non-full path, {fullPath}");
- }
- }
- else
- {
- Console.Error.WriteLine("NOT FOUND");
- retval = 1;
- }
- }
- catch (Exception e)
- {
- Console.Error.WriteLine($"FAILED, Exception: {e}");
- retval = 2;
- }
- }
-
- Console.WriteLine("done.");
+//
+// Run Docker/Linux test with:
+//
+// docker build -t sgr/findexecutable .
+// docker run --rm sgr/findexecutable
+//
+
+using System;
+using System.IO;
+using System.Reflection;
+
+namespace FindExecutable
+{
+ internal class Program
+ {
+ static int Main(string[] args)
+ {
+ Console.OutputEncoding = System.Text.Encoding.UTF8;
+ int retval = 0;
+
+ retval = Math.Max(retval, TestSearchGit());
+
+ retval = Math.Max(retval, TestUnicodeFile());
+
+ Console.WriteLine("done.");
if (retval != 0)
{
Console.WriteLine($"exit code: {retval}");
- }
- return retval;
- }
- }
-}
+ }
+ return retval;
+ }
+
+ static int TestSearchGit()
+ {
+ string[] executables = [
+ "git.exe",
+ "git"
+ ];
+
+ foreach (string executable in executables)
+ {
+ try
+ {
+ Console.WriteLine($"Searching for: {executable}");
+
+ string? fullPath = FindExecutable.FullPath(executable);
+
+ if (fullPath != null)
+ {
+ if (Path.IsPathFullyQualified(fullPath))
+ {
+ Console.WriteLine($"FOUND: {fullPath}");
+ }
+ else
+ {
+ Console.WriteLine($"FOUND w/ WARNING: non-full path, {fullPath}");
+ }
+ }
+ else
+ {
+ Console.Error.WriteLine("NOT FOUND");
+ return 1;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine($"FAILED, Exception: {e}");
+ return 2;
+ }
+ }
+
+ return 0;
+ }
+
+ static int TestUnicodeFile()
+ {
+ Console.WriteLine("Searching for an (artificial) executable with a unicode name:");
+
+ const string gitExec = "git";
+ const string unicodeExec = "まじかよ。.exe";
+
+ if (File.Exists(unicodeExec))
+ {
+ File.Delete(unicodeExec);
+ }
+
+ string unicodeExecPath = Path.Join(AppContext.BaseDirectory, unicodeExec);
+ string? gitFullPath = FindExecutable.FullPath(gitExec);
+
+ if (gitFullPath == null)
+ {
+ Console.WriteLine("Failed to find git exec as test subject");
+ return 5;
+ }
+
+ File.Copy(gitFullPath, unicodeExecPath);
+
+ if (!OperatingSystem.IsWindows())
+ {
+ File.SetUnixFileMode(unicodeExecPath, File.GetUnixFileMode(gitFullPath));
+ }
+
+ if (!FindExecutable.IsExecutable(unicodeExecPath))
+ {
+ Console.WriteLine($"Failed to setup the unicode executable file name: {unicodeExecPath}");
+ return 3;
+ }
+
+ string? findIt = FindExecutable.FullPath(unicodeExec, includeCurrentDirectory: true);
+ if (string.IsNullOrEmpty(findIt))
+ {
+ Console.WriteLine($"Failed to find the unicode executable file name: {unicodeExec}");
+ return 4;
+ }
+
+ Console.WriteLine($"FOUND: {findIt}");
+
+ return 0;
+ }
+
+ }
+}
diff --git a/SGrottel.FindExecutable.nuspec b/SGrottel.FindExecutable.nuspec
index 205a909..1931ca0 100644
--- a/SGrottel.FindExecutable.nuspec
+++ b/SGrottel.FindExecutable.nuspec
@@ -6,7 +6,7 @@
SGrottel.FindExecutable
- 1.0.1
+ 1.0.2
SGrottel.FindExecutable
SGrottel
SGrottel
@@ -20,7 +20,6 @@
-
@@ -28,7 +27,6 @@
-