Skip to content

Commit a0be632

Browse files
committed
Dynamic support of IPersistOption
1 parent b346dc2 commit a0be632

3 files changed

Lines changed: 112 additions & 26 deletions

File tree

SmartCmdArgs/SmartCmdArgs.Shared/Helper/CpsProjectConfigService.cs

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
using System.Collections.Immutable;
99
using System.Linq;
1010
using System.Threading.Tasks.Dataflow;
11+
#if DYNAMIC_VSProjectManaged
12+
using System.Reflection;
13+
using System.Reflection.Emit;
14+
using Expression = System.Linq.Expressions.Expression;
15+
#endif
1116

1217
// This isolation of Microsoft.VisualStudio.ProjectSystem dependencies into one file ensures compatibility
1318
// across various Visual Studio installations. This is crucial because not all Visual Studio workloads
@@ -151,7 +156,7 @@ public void SetConfig(EnvDTE.Project project, string arguments, IDictionary<stri
151156
if (baseLaunchProfile == null)
152157
return;
153158

154-
WritableLaunchProfile writableLaunchProfile = new WritableLaunchProfile(baseLaunchProfile);
159+
var writableLaunchProfile = WritableLaunchProfile.GetWritableLaunchProfile(baseLaunchProfile);
155160

156161
if (arguments != null)
157162
writableLaunchProfile.CommandLineArgs = arguments;
@@ -226,9 +231,8 @@ public void GetItemsFromConfig(EnvDTE.Project project, List<CmdItemJson> allArgs
226231
}
227232
}
228233
}
229-
230-
class WritableLaunchProfile : ILaunchProfile
231-
#if VS17
234+
public class WritableLaunchProfile : ILaunchProfile //must be public to avoid having to declare our dynamic assembly a friend
235+
#if VS17 && ! DYNAMIC_VSProjectManaged
232236
, IPersistOption
233237
#endif
234238
{
@@ -245,7 +249,10 @@ class WritableLaunchProfile : ILaunchProfile
245249

246250
// IPersistOption
247251
public bool DoNotPersist { get; set; }
248-
252+
#if DYNAMIC_VSProjectManaged
253+
private static Func<ILaunchProfile, bool> LaunchProfileIsDoNotPersistFunc;
254+
#endif
255+
private static Lazy<Type> IPersistOptionType = new Lazy<Type>(() => typeof(ILaunchProfile).Assembly.GetType("Microsoft.VisualStudio.ProjectSystem.Debug.IPersistOption"));
249256
public WritableLaunchProfile(ILaunchProfile launchProfile)
250257
{
251258
// ILaunchProfile
@@ -259,12 +266,78 @@ public WritableLaunchProfile(ILaunchProfile launchProfile)
259266
EnvironmentVariables = launchProfile.EnvironmentVariables;
260267
OtherSettings = launchProfile.OtherSettings;
261268
#if VS17
269+
#if DYNAMIC_VSProjectManaged
270+
if (LaunchProfileIsDoNotPersistFunc == null)
271+
{
272+
if (IPersistOptionType.Value == null)
273+
LaunchProfileIsDoNotPersistFunc = (_) => false;
274+
else
275+
{
276+
var instanceParam = Expression.Parameter(typeof(ILaunchProfile));
277+
var asIPersist = Expression.TypeAs(instanceParam, IPersistOptionType.Value);
278+
var expr = Expression.Condition(Expression.Equal(asIPersist, Expression.Constant(null)), Expression.Constant(false), Expression.Property(asIPersist, nameof(DoNotPersist)));
279+
LaunchProfileIsDoNotPersistFunc = Expression.Lambda<Func<ILaunchProfile, bool>>(expr, instanceParam).Compile();
280+
}
281+
}
282+
DoNotPersist = LaunchProfileIsDoNotPersistFunc(launchProfile);
283+
284+
#else
262285
if (launchProfile is IPersistOption persistOptionLaunchProfile)
263286
{
264287
// IPersistOption
265288
DoNotPersist = persistOptionLaunchProfile.DoNotPersist;
266289
}
290+
#endif
267291
#endif
268292
}
293+
294+
private static Func<ILaunchProfile, WritableLaunchProfile> getWritableProfileFunc;
295+
internal static WritableLaunchProfile GetWritableLaunchProfile(ILaunchProfile profile)
296+
{
297+
#if DYNAMIC_VSProjectManaged
298+
if (getWritableProfileFunc == null && IPersistOptionType.Value != null)
299+
{
300+
var ourType = typeof(WritableLaunchProfile);
301+
var asmName = new AssemblyName() { Name = "SmartCLIArgsDynamicAsm" };
302+
asmName.SetPublicKey(ourType.Assembly.GetName().GetPublicKey());
303+
var assemBuilder = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
304+
305+
var classBuilder = assemBuilder.DefineDynamicModule("SmartCLIArgsDynamicMod").DefineType("DynamicWritableLaunchProfile", TypeAttributes.NotPublic | TypeAttributes.Class, ourType);
306+
classBuilder.AddInterfaceImplementation(IPersistOptionType.Value);
307+
// not sure why AssemblyBuilder is a baby true IL code doesn't define interface impelmentations that are just inherited
308+
var persist_get = classBuilder.DefineMethod("get_" + nameof(DoNotPersist), MethodAttributes.Virtual | MethodAttributes.Public, typeof(bool), Type.EmptyTypes);
309+
var il = persist_get.GetILGenerator();
310+
il.Emit(OpCodes.Ldarg_0);
311+
il.EmitCall(OpCodes.Callvirt, ourType.GetMethod(persist_get.Name), null);
312+
il.Emit(OpCodes.Ret);
313+
314+
315+
classBuilder.DefineMethodOverride(persist_get, IPersistOptionType.Value.GetMethod(persist_get.Name));
316+
317+
var constructorArgTypes = new[] { typeof(ILaunchProfile) };
318+
var constructor = classBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, constructorArgTypes);
319+
var baseConstructor = ourType.GetConstructor(constructorArgTypes);
320+
il = constructor.GetILGenerator();
321+
il.Emit(OpCodes.Ldarg_0);
322+
il.Emit(OpCodes.Ldarg_1);
323+
il.Emit(OpCodes.Call, baseConstructor);
324+
il.Emit(OpCodes.Nop);
325+
il.Emit(OpCodes.Nop);
326+
il.Emit(OpCodes.Ret);
327+
var DynamicWritableLaunchProfileType = classBuilder.CreateType();
328+
var constructorInfo = DynamicWritableLaunchProfileType.GetConstructor(constructorArgTypes);
329+
var instanceParam = Expression.Parameter(typeof(ILaunchProfile));
330+
var expr = Expression.TypeAs(Expression.New(constructorInfo, instanceParam), ourType);
331+
getWritableProfileFunc = Expression.Lambda<Func<ILaunchProfile, WritableLaunchProfile>>(expr, instanceParam).Compile();
332+
333+
}
334+
if (IPersistOptionType.Value != null)
335+
return getWritableProfileFunc(profile);
336+
#endif
337+
return new WritableLaunchProfile(profile);
338+
339+
340+
}
341+
269342
}
270343
}

SmartCmdArgs/SmartCmdArgs17/SmartCmdArgs17.csproj

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<DebugType>full</DebugType>
3333
<Optimize>false</Optimize>
3434
<OutputPath>bin\Debug\</OutputPath>
35-
<DefineConstants>TRACE;DEBUG;VS17</DefineConstants>
35+
<DefineConstants>TRACE;DEBUG;VS17;DYNAMIC_VSProjectManaged</DefineConstants>
3636
<ErrorReport>prompt</ErrorReport>
3737
<WarningLevel>4</WarningLevel>
3838
</PropertyGroup>
@@ -99,15 +99,28 @@
9999
<PackageReference Include="Microsoft.Extensions.DependencyInjection">
100100
<Version>7.0.0</Version>
101101
</PackageReference>
102-
<PackageReference Include="CI.Microsoft.VisualStudio.ProjectSystem.Managed">
103-
<Version>17.11.1.102</Version>
104-
</PackageReference>
105102
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.0.31902.203" ExcludeAssets="runtime" />
106103
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.5.4065">
107104
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
108105
<PrivateAssets>all</PrivateAssets>
109106
</PackageReference>
110107
</ItemGroup>
108+
<Choose>
109+
<When Condition="$(DefineConstants.Contains(DYNAMIC_VSProjectManaged))">
110+
<ItemGroup>
111+
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem.Managed">
112+
<Version>2.0.6142705</Version>
113+
</PackageReference>
114+
</ItemGroup>
115+
</When>
116+
<Otherwise>
117+
<ItemGroup>
118+
<PackageReference Include="CI.Microsoft.VisualStudio.ProjectSystem.Managed">
119+
<Version>17.11.1.102</Version>
120+
</PackageReference>
121+
</ItemGroup>
122+
</Otherwise>
123+
</Choose>
111124
<ItemGroup>
112125
<VSCTCompile Include="..\SharedResources\CmdArgsPackage.vsct">
113126
<Link>CmdArgsPackage.vsct</Link>
Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
// ------------------------------------------------------------------------------
2-
// <auto-generated>
3-
// This file was generated by VSIX Synchronizer
4-
// </auto-generated>
5-
// ------------------------------------------------------------------------------
6-
namespace SmartCmdArgs
7-
{
8-
internal sealed partial class Vsix
9-
{
10-
public const string Id = "SmartCmdArgs17.6edd462d-d4d6-40b9-a7b4-451a6ce6c527";
11-
public const string Name = "Smart Command Line Arguments VS2022";
12-
public const string Description = @"A Visual Studio 2022 Extension which aims to provide a better UI to manage your command line arguments.";
13-
public const string Language = "en-US";
1+
// ------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This file was generated by VSIX Synchronizer
4+
// </auto-generated>
5+
// ------------------------------------------------------------------------------
6+
namespace SmartCmdArgs
7+
{
8+
internal sealed partial class Vsix
9+
{
10+
public const string Id = "SmartCmdArgs17.6edd462d-d4d6-40b9-a7b4-451a6ce6c527";
11+
public const string Name = "Smart Command Line Arguments VS2022";
12+
public const string Description = @"A Visual Studio 2022 Extension which aims to provide a better UI to manage your command line arguments.";
13+
public const string Language = "en-US";
1414
public const string Version = "3.2.0.1";
15-
public const string Author = "Irame & MBulli";
16-
public const string Tags = "Commandline Command Line Arguments cmd project";
17-
}
18-
}
15+
public const string Author = "Irame & MBulli";
16+
public const string Tags = "Commandline Command Line Arguments cmd project";
17+
}
18+
}

0 commit comments

Comments
 (0)