Skip to content

Commit

Permalink
Stop using shell execute (#310)
Browse files Browse the repository at this point in the history
UseShellExecute causes issues when launching process.

 * MSBuild.exe waits for devenv.exe if UseShellExecute is used
* The Visual Studio Version Selector is launched which isn't always correct

Instead, use the instance of Visual Studio for the MSBuild.exe that was found on the PATH which is more precise

Fixes #203
  • Loading branch information
jeffkl authored Nov 18, 2021
1 parent 01bc203 commit a224cf8
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 92 deletions.
6 changes: 0 additions & 6 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ Options:
--ignoreMainProject None of the projects receive special treatment.
--launch <true|false> Launch Visual Studio after generating the Solution file. Default: true on Windows
--loadprojects <false> When launching Visual Studio, opens the specified solution without loading any projects. Default: true
You must disable shell execute when using this command-line option.
  --useshellexecute:false
--logger Use this logger to log events from SlnGen. To specify multiple loggers, specify each logger separately.
The <logger> syntax is:
  [<class>,]<assembly>[;<parameters>]
Expand All @@ -90,7 +88,6 @@ Options:
-p|--property <name=value[;]> Set or override these project-level properties. <name> is the property name, and <value> is the property value. Use a semicolon or a comma to separate multiple properties, or specify each property separately.
  Example:
    --property:WarningLevel=2;MyProperty=true
-u|--useshellexecute <false> Indicates whether or not the Visual Studio solution file should be opened by the registered file extension handler. Default: true
-d|--solutiondir <path> An optional path to the directory in which the solution file will be generated. Defaults to the same directory as the project. --solutionfile will take precedence over this switch.
-o|--solutionfile <path> An optional path to the solution file to generate. Defaults to the same directory as the project.
-v|--verbosity Display this amount of information in the event log. The available verbosity levels are:
Expand Down Expand Up @@ -125,7 +122,6 @@ The following properties only apply when using SlnGen as an MSBuild target.
|--------------------------|------------------------------------------------------------------------------------------------------------|--------------------|---------|
| `SlnGenLaunchVisualStudio` | Indicates whether or not Visual Studio should be launched to open the solution file after it is generated. | `true` or `false` | `true` |
| `SlnGenSolutionFileFullPath` | Specifies the full path to the Visual Studio solution file to generate. By default, the path is the same as the project. | | ProjectPath.sln|
| `SlnGenUseShellExecute` | Indicates whether or not the Visual Studio solution file should be opened by the registered file extension handler. You can disable this setting to use whatever `devenv.exe` is on your `PATH` or you can specify a full path to `devenve.exe` with the `SlnGenDevEnvFullPath` property. | `true` or `false` | `true` |
| `SlnGenDevEnvFullPath` | Specifies a full path to Visual Studio's `devenv.exe` to use when opening the solution file. By default, SlnGen will launch the program associated with the `.sln` file extension. However, in some cases you may want to specify a custom path to Visual Studio. | | |
| `SlnGenGlobalProperties` | Specifies MSBuild properties to set when loading projects and project references. | | `DesignTimeBuild=true;BuildingProject=false` |
| `SlnGenInheritGlobalProperties` | Indicates whether or not all global variables specified when loading the initial project should be passed around when loading project references. | `true` or `false` | `true` |
Expand All @@ -136,7 +132,6 @@ The following properties only apply when using SlnGen as an MSBuild target.
Command-line argument
```cmd
MSBuild.exe /Target:SlnGen /Property:"SlnGenLaunchVisualStudio=false"
/Property:"SlnGenUseShellExecute=false"
/Property:"SlnGenDevEnvFullPath=%VSINSTALLDIR%Common7\IDE\devenv.exe"
```

Expand All @@ -145,7 +140,6 @@ MSBuild properties
<PropertyGroup>
<SlnGenLaunchVisualStudio>false</SlnGenLaunchVisualStudio>
<SlnGenSolutionFileFullPath>$(MSBuildProjectDirectory)\$(MSBuildProjectName).sln</SlnGenSolutionFileFullPath>
<SlnGenUseShellExecute>false</SlnGenUseShellExecute>
<SlnGenGlobalProperties>DoNotDoSomethingWhenLoadingProjects=true;TodayIsYesterday=false</SlnGenGlobalProperties>
<SlnGenInheritGlobalProperties>false</SlnGenInheritGlobalProperties>
<SlnGenGlobalPropertiesToRemove>Property1;Property2</SlnGenGlobalPropertiesToRemove>
Expand Down
5 changes: 0 additions & 5 deletions src/Microsoft.VisualStudio.SlnGen/MSBuildPropertyNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,6 @@ public static class MSBuildPropertyNames
/// </summary>
public const string SlnGenSolutionFolder = nameof(SlnGenSolutionFolder);

/// <summary>
/// Represents the SlnGenUseShellExecute property.
/// </summary>
public const string SlnGenUseShellExecute = nameof(SlnGenUseShellExecute);

/// <summary>
/// Represents the UsingMicrosoftNETSdk property.
/// </summary>
Expand Down
1 change: 0 additions & 1 deletion src/Microsoft.VisualStudio.SlnGen/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,6 @@ private static void LogTelemetry(ProgramArguments arguments, TimeSpan evaluation
#endif
["UseBinaryLogger"] = arguments.BinaryLogger.HasValue.ToString(),
["UseFileLogger"] = arguments.FileLoggerParameters.HasValue.ToString(),
["UseShellExecute"] = arguments.EnableShellExecute().ToString(),
["CustomProjectTypeGuidCount"] = customProjectTypeGuidCount,
["ProjectCount"] = evaluationCount,
["ProjectEvaluationMilliseconds"] = evaluationTime.TotalMilliseconds,
Expand Down
21 changes: 1 addition & 20 deletions src/Microsoft.VisualStudio.SlnGen/ProgramArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace Microsoft.VisualStudio.SlnGen
{
Expand Down Expand Up @@ -147,9 +146,7 @@ public sealed class ProgramArguments
"--loadprojects",
CommandOptionType.MultipleValue,
ValueName = "false",
Description = @"When launching Visual Studio, opens the specified solution without loading any projects. Default: true
You must disable shell execute when using this command-line option.
  --useshellexecute:false")]
Description = @"When launching Visual Studio, opens the specified solution without loading any projects. Default: true")]
public string[] LoadProjectsInVisualStudio { get; set; }

/// <summary>
Expand Down Expand Up @@ -220,16 +217,6 @@ public sealed class ProgramArguments
    --property:WarningLevel=2;MyProperty=true")]
public string[] Property { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the shell should be used when starting the process.
/// </summary>
[Option(
"-u|--useshellexecute",
CommandOptionType.MultipleValue,
ValueName = "false",
Description = "Indicates whether or not the Visual Studio solution file should be opened by the registered file extension handler. Default: true")]
public string[] ShellExecute { get; set; }

/// <summary>
/// Gets or sets the full path to the solution file to generate.
/// </summary>
Expand Down Expand Up @@ -284,12 +271,6 @@ public sealed class ProgramArguments
/// <returns>true if folders should be used, otherwise false.</returns>
public bool EnableFolders() => GetBoolean(Folders);

/// <summary>
/// Gets a value indicating whether or not shell execute should be used when launching the solution.
/// </summary>
/// <returns>true if shell executed should be used, otherwise false.</returns>
public bool EnableShellExecute() => GetBoolean(ShellExecute, defaultValue: true);

/// <summary>
/// Gets the Configuration values based on what was specified as command-line arguments.
/// </summary>
Expand Down
1 change: 0 additions & 1 deletion src/Microsoft.VisualStudio.SlnGen/Tasks/SlnGenToolTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ protected override string GenerateCommandLineCommands()
commandLineBuilder.AppendSwitchIfNotNull("--launch:", GetPropertyValue(MSBuildPropertyNames.SlnGenLaunchVisualStudio));
commandLineBuilder.AppendSwitchIfNotNull("--loadprojects:", GetPropertyValue(MSBuildPropertyNames.SlnGenLoadProjects));
commandLineBuilder.AppendSwitchIfNotNull("--solutionfile:", GetPropertyValue(MSBuildPropertyNames.SlnGenSolutionFileFullPath));
commandLineBuilder.AppendSwitchIfNotNull("--useshellexecute:", GetPropertyValue(MSBuildPropertyNames.SlnGenUseShellExecute));
commandLineBuilder.AppendSwitchIfNotNull("--property:", globalProperties.Count == 0 ? null : string.Join(";", globalProperties.Select(i => $"{i.Key}={i.Value}")));

if (string.Equals(GetPropertyValue(MSBuildPropertyNames.SlnGenDebug), bool.TrueString, StringComparison.OrdinalIgnoreCase))
Expand Down
89 changes: 31 additions & 58 deletions src/Microsoft.VisualStudio.SlnGen/VisualStudioLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,90 +40,63 @@ public static bool TryLaunch(ProgramArguments arguments, VisualStudioInstance vi
return true;
}

bool loadProjectsInVisualStudio = arguments.ShouldLoadProjectsInVisualStudio();
bool enableShellExecute = arguments.EnableShellExecute();

string devEnvFullPath = arguments.DevEnvFullPath?.LastOrDefault();

if (!enableShellExecute || !loadProjectsInVisualStudio || Program.CurrentDevelopmentEnvironment.IsCorext)
if (!devEnvFullPath.IsNullOrWhiteSpace())
{
if (devEnvFullPath.IsNullOrWhiteSpace())
{
if (visualStudioInstance == null)
{
logger.LogError(
Program.CurrentDevelopmentEnvironment.IsCorext
? $"Could not find a Visual Studio {Environment.GetEnvironmentVariable("VisualStudioVersion")} installation. Please do one of the following:\n a) Specify a full path to devenv.exe via the -vs command-line argument\n b) Update your corext.config to specify a version of MSBuild.Corext that matches a Visual Studio version you have installed\n c) Install a version of Visual Studio that matches the version of MSBuild.Corext in your corext.config"
: "Could not find a Visual Studio installation. Please specify the full path to devenv.exe via the -vs command-line argument");

return false;
}

if (visualStudioInstance.IsBuildTools)
{
logger.LogError("Cannot use a BuildTools instance of Visual Studio.");
visualStudioInstance = VisualStudioConfiguration.GetInstanceForPath(devEnvFullPath);
}

return false;
}
if (visualStudioInstance == null)
{
logger.LogError(
Program.CurrentDevelopmentEnvironment.IsCorext
? $"Could not find a Visual Studio {Environment.GetEnvironmentVariable("VisualStudioVersion")} installation. Please do one of the following:\n a) Specify a full path to devenv.exe via the -vs command-line argument\n b) Update your corext.config to specify a version of MSBuild.Corext that matches a Visual Studio version you have installed\n c) Install a version of Visual Studio that matches the version of MSBuild.Corext in your corext.config"
: "Could not find a Visual Studio installation. Please run from a command window that has MSBuild.exe on the PATH or specify the full path to devenv.exe via the -vs command-line argument");

devEnvFullPath = Path.Combine(visualStudioInstance.InstallationPath, "Common7", "IDE", "devenv.exe");
}
return false;
}

if (solutionFileFullPath.IsNullOrWhiteSpace())
if (visualStudioInstance.IsBuildTools)
{
throw new ArgumentNullException(nameof(solutionFileFullPath));
}
logger.LogError("Cannot use a BuildTools instance of Visual Studio.");

CommandLineBuilder commandLineBuilder = new CommandLineBuilder();
return false;
}

ProcessStartInfo processStartInfo;
devEnvFullPath = Path.Combine(visualStudioInstance.InstallationPath, "Common7", "IDE", "devenv.exe");

if (!devEnvFullPath.IsNullOrWhiteSpace())
if (!File.Exists(devEnvFullPath))
{
if (!File.Exists(devEnvFullPath))
{
logger.LogError($"The specified path to Visual Studio ({devEnvFullPath}) does not exist or is inaccessible.");
logger.LogError($"The specified path to Visual Studio ({devEnvFullPath}) does not exist or is inaccessible.");

return false;
}
return false;
}

processStartInfo = new ProcessStartInfo
{
FileName = devEnvFullPath!,
UseShellExecute = false,
};
CommandLineBuilder commandLineBuilder = new CommandLineBuilder();

commandLineBuilder.AppendFileNameIfNotNull(solutionFileFullPath);
commandLineBuilder.AppendFileNameIfNotNull(solutionFileFullPath);

if (!arguments.ShouldLoadProjectsInVisualStudio())
{
commandLineBuilder.AppendSwitch(DoNotLoadProjectsCommandLineArgument);
}
}
else
if (!arguments.ShouldLoadProjectsInVisualStudio())
{
processStartInfo = new ProcessStartInfo
{
FileName = solutionFileFullPath,
UseShellExecute = true,
};
commandLineBuilder.AppendSwitch(DoNotLoadProjectsCommandLineArgument);
}

try
{
processStartInfo.Arguments = commandLineBuilder.ToString();

Process process = new Process
{
StartInfo = processStartInfo,
StartInfo = new ProcessStartInfo
{
FileName = devEnvFullPath,
Arguments = commandLineBuilder.ToString(),
UseShellExecute = false,
},
};

logger.LogMessageHigh("Launching Visual Studio...");
logger.LogMessageLow(" FileName = {0}", processStartInfo.FileName);
logger.LogMessageLow(" Arguments = {0}", processStartInfo.Arguments);
logger.LogMessageLow(" UseShellExecute = {0}", processStartInfo.UseShellExecute);
logger.LogMessageLow(" WindowStyle = {0}", processStartInfo.WindowStyle);
logger.LogMessageLow(" FileName = {0}", process.StartInfo.FileName);
logger.LogMessageLow(" Arguments = {0}", process.StartInfo.Arguments);

if (!process.Start())
{
Expand Down
2 changes: 1 addition & 1 deletion version.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "7.2",
"version": "8.0",
"assemblyVersion": "3.0",
"buildNumberOffset": -1,
"publicReleaseRefSpec": [
Expand Down

0 comments on commit a224cf8

Please sign in to comment.