فهرست منبع

Issue 2339 – Find WinSCP.exe in process path if assembly path is unknown

https://winscp.net/tracker/2339

Switching to the latest (12 atm) C# version (was using 9 so far).
In this commit for [] collection initialization particularly, but wanted to do it for a long time already

Source commit: c49962a35cb7f3f4b8c4f5e0718f51749fd5855c
Martin Prikryl 9 ماه پیش
والد
کامیت
d46422070d
3فایلهای تغییر یافته به همراه41 افزوده شده و 23 حذف شده
  1. 1 0
      dotnet/WinSCPnet.csproj
  2. 33 23
      dotnet/internal/ExeSessionProcess.cs
  3. 7 0
      dotnet/internal/Logger.cs

+ 1 - 0
dotnet/WinSCPnet.csproj

@@ -7,6 +7,7 @@
     <OutputPath>bin\$(Configuration)</OutputPath>
     <SignAssembly>false</SignAssembly>
     <AssemblyOriginatorKeyFile></AssemblyOriginatorKeyFile>
+    <LangVersion>latest</LangVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
     <DefineConstants>NETSTANDARD</DefineConstants>

+ 33 - 23
dotnet/internal/ExeSessionProcess.cs

@@ -16,6 +16,7 @@ using System.Security.AccessControl;
 #endif
 using System.ComponentModel;
 using System.Security.Cryptography;
+using System.Linq;
 
 namespace WinSCP
 {
@@ -914,20 +915,21 @@ namespace WinSCP
         {
             using (_logger.CreateCallstack())
             {
-                string executablePath;
-                if (!string.IsNullOrEmpty(_session.ExecutablePath))
+                string executablePath = _session.ExecutablePath;
+                string result;
+                if (!string.IsNullOrEmpty(executablePath))
                 {
-                    executablePath = _session.ExecutablePath;
                     if (!File.Exists(executablePath))
                     {
-                        throw _logger.WriteException(new SessionLocalException(_session, string.Format(CultureInfo.CurrentCulture, "{0} does not exists.", executablePath)));
+                        throw _logger.WriteException(new SessionLocalException(_session, $"{executablePath} does not exists."));
                     }
+                    result = executablePath;
                 }
                 else
                 {
-                    executablePath = FindExecutable(_session);
+                    result = FindExecutable(_session);
                 }
-                return executablePath;
+                return result;
             }
         }
 
@@ -936,25 +938,27 @@ namespace WinSCP
         {
             Logger logger = session.Logger;
             string executablePath;
-            if (!TryFindExecutableInPath(logger, GetAssemblyPath(logger), out executablePath) &&
-                !TryFindExecutableInPath(logger, GetEntryAssemblyPath(logger), out executablePath) &&
+            List<string> paths = [];
+            string assemblyPath = GetAssemblyPath(logger);
+            // If the assembly is not loaded from a file, look to the path of the process execuable
+            // (particularly useful for single-file bundles)
+            // (also limited this way not to for example look into powershell.exe folder)
+            string processPath = !string.IsNullOrEmpty(assemblyPath) ? null : Path.GetDirectoryName(Logger.GetProcessPath());
+            if (!TryFindExecutableInPath(logger, paths, assemblyPath, out executablePath) &&
+                !TryFindExecutableInPath(logger, paths, GetEntryAssemblyPath(logger), out executablePath) &&
+                !TryFindExecutableInPath(logger, paths, processPath, out executablePath) &&
 #if !NETSTANDARD
-                !TryFindExecutableInPath(logger, GetInstallationPath(RegistryHive.CurrentUser), out executablePath) &&
-                !TryFindExecutableInPath(logger, GetInstallationPath(RegistryHive.LocalMachine), out executablePath) &&
+                !TryFindExecutableInPath(logger, paths, GetInstallationPath(RegistryHive.CurrentUser), out executablePath) &&
+                !TryFindExecutableInPath(logger, paths, GetInstallationPath(RegistryHive.LocalMachine), out executablePath) &&
 #endif
-                !TryFindExecutableInPath(logger, GetDefaultInstallationPath(), out executablePath))
+                !TryFindExecutableInPath(logger, paths, GetDefaultInstallationPath(), out executablePath))
             {
-                string entryAssemblyDesc = string.Empty;
-                Assembly entryAssembly = Assembly.GetEntryAssembly();
-                if (entryAssembly != null)
-                {
-                    entryAssemblyDesc = $", nor the entry assembly {entryAssembly.GetName().Name} ({GetEntryAssemblyPath(logger)})";
-                }
-                throw logger.WriteException(
-                    new SessionLocalException(session,
-                        string.Format(CultureInfo.CurrentCulture,
-                            "The {0} executable was not found at location of the assembly {1} ({2}){3}, nor in an installation path. You may use Session.ExecutablePath property to explicitly set path to {0}.",
-                            ExeExecutableFileName, Assembly.GetExecutingAssembly().GetName().Name, GetAssemblyPath(logger), entryAssemblyDesc)));
+                string pathsStr = string.Join(", ", paths);
+                string filename = ExeExecutableFileName;
+                string message =
+                    $"The {filename} executable was not found at any of the inspected locations ({pathsStr}). " +
+                    $"You may use Session.ExecutablePath property to explicitly set path to {filename}.";
+                throw logger.WriteException(new SessionLocalException(session, message));
             }
 
             return executablePath;
@@ -984,14 +988,20 @@ namespace WinSCP
         }
 #endif
 
-        private static bool TryFindExecutableInPath(Logger logger, string path, out string result)
+        private static bool TryFindExecutableInPath(Logger logger, List<string> paths, string path, out string result)
         {
             if (string.IsNullOrEmpty(path))
             {
                 result = null;
             }
+            else if (paths.Contains(path, StringComparer.CurrentCultureIgnoreCase))
+            {
+                logger.WriteLine($"Already searched {path}");
+                result = null;
+            }
             else
             {
+                paths.Add(path);
                 string executablePath = Path.Combine(path, ExeExecutableFileName);
                 if (File.Exists(executablePath))
                 {

+ 7 - 0
dotnet/internal/Logger.cs

@@ -414,6 +414,13 @@ namespace WinSCP
             {
                 WriteLine("Entry assembly path: {0}", GetEntryAssemblyFilePath());
             }
+            WriteLine($"Process path: {GetProcessPath()}");
+        }
+
+        public static string GetProcessPath()
+        {
+            // Can be replaced with Environment.ProcessPath in .NET 6 and newer
+            return Process.GetCurrentProcess().MainModule?.FileName;
         }
 
         public static string LastWin32ErrorMessage()