-
-
Notifications
You must be signed in to change notification settings - Fork 406
refactor: Rewrite Loader in C# #2647
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 5 commits
0ecc3de
723821c
355f92e
847c246
fc104aa
f659f49
c916a4a
aa4fc9d
aea0787
41a5955
2fc4643
81efe9a
3886911
038fd01
e93db34
ae30736
bb06cdc
987570d
4ead9b0
b6dda98
2972ffc
0ba9785
52b9424
7f5eadb
3fb7b20
db6510d
736cfed
6ccc8c1
8ae818e
4e98317
80f6ccf
1d6da91
fa8fc2b
ba7d564
6964633
46bdaa9
0546001
cffdb8a
b686515
33b3d75
18ecf76
e8e445a
4acf0a9
84ee64b
1c90cac
1b92a55
f408e57
869ed85
27ca06d
2f9b799
88f0223
b202aa1
525f2f6
3e8596d
3ca0f22
faaa8e6
f785799
aef99de
1f65b01
1a6ff56
b46c55b
b999f2f
ad13740
185b0e2
8e5e1f1
35a3633
8057962
d0168ce
283b4e6
3544d22
917d222
073ac77
511a8f5
f00d80e
f87944a
00a5d96
317d884
8ae47e2
c01af21
a5068ae
d0101c1
7abde0f
9968c49
f9416e3
30b0c75
b071d73
0559d77
708f0a7
de410a3
87f0f48
3668d5b
d970046
0882b38
10bf5cc
fbe8a93
4c7c940
ddae5b8
a28928a
fa401e4
d3de63d
9a66391
06aa085
1713883
79aa50c
42c498b
a4b45c9
d4c0cb6
3708082
94e97b8
6e52f48
e26fa8d
78b2916
8b5b8e6
2fe5ddc
555e427
13aa78c
ce6e846
aa56784
d469267
d18412c
a9ad8c7
8b0c5c2
8cc3695
6d2d33a
acf9ca6
e7a1652
3ac8e84
6a11c3c
cbb0dd1
9dcac91
84293a9
524760a
117d1e5
0db108d
32b6e59
80a3275
efecce3
2a29139
6811bdd
1afb551
6978597
b597854
6d01e12
3a23ee1
4e5c613
c181f00
16eadda
74064e9
fdc7639
4411154
380c6be
1d6b0b1
04f932a
9c45ccf
fb32407
8ee95b1
f4201ef
e88d970
21f390e
9d52ec3
817862f
85f2ca8
9e4e011
98aca03
4bb534d
e9f968e
ba3b87f
d53bb79
aa7760f
8052c17
1cf9ea8
356db9f
38d8931
8ef477e
76f8aaa
adaab3d
5f43a93
993d3c8
ab894a5
dc7b6ad
422499b
8733537
3e58f03
2cb20af
9512628
d75efb3
dc619dd
8ec1af5
1f356f5
2b08baa
4b470fc
d1c0c1f
2d8f2b0
2b928e4
93ca394
56c1e81
3bf321b
1f87056
aed046a
f9db957
fb68dc5
57903f6
e50cc76
3e2b4e2
df9c3a6
ca65de4
d31dab3
13a930a
a80f173
95f779c
4abd568
5ddb267
cbdb606
faa76de
ea52196
c612301
4a97a83
be7d2a1
e01bfdb
68788a4
ad8e74f
d4f3f32
f17dbe4
2d91df9
bc7c247
dd8a60d
ef80f74
7cbaf7e
09cfb09
8cec1de
a656e09
6a258de
4132e3c
149b849
b1a083f
8fefb23
0480d95
28acae3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,12 @@ | |
| using System.Reflection; | ||
| using Autodesk.Revit.UI; | ||
| using Autodesk.Revit.Attributes; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Logging; | ||
| using pyRevitAssemblyBuilder.AssemblyMaker; | ||
| using pyRevitAssemblyBuilder.SessionManager; | ||
| using pyRevitAssemblyBuilder.Shared; | ||
| using pyRevitAssemblyBuilder.Startup; | ||
|
|
||
| /* Note: | ||
| * It is necessary that this code object do not have any references to IronPython. | ||
|
|
@@ -28,7 +34,21 @@ Result IExternalApplication.OnStartup(UIControlledApplication application) | |
|
|
||
| try | ||
| { | ||
| return ExecuteStartupScript(application); | ||
| // we need a UIApplication object to assign as `__revit__` in python... | ||
| var versionNumber = application.ControlledApplication.VersionNumber; | ||
| var fieldName = int.Parse(versionNumber) >= 2017 ? "m_uiapplication" : "m_application"; | ||
| var fi = application.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); | ||
|
|
||
| var uiApplication = (UIApplication)fi.GetValue(application); | ||
|
|
||
| var executor = new ScriptExecutor(uiApplication); | ||
| var result = ExecuteStartupScript(application); | ||
| if (result == Result.Failed) | ||
| { | ||
| TaskDialog.Show("Error Loading pyRevit", executor.Message); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
|
|
@@ -54,6 +74,13 @@ private static void LoadAssembliesInFolder(string folder) | |
| } | ||
|
|
||
| private static Result ExecuteStartupScript(UIControlledApplication uiControlledApplication) | ||
| { | ||
| //TODO: Implement a switcher here to be able to switch between Python/C# loaders | ||
| //return ExecuteStartUpPython(uiControlledApplication); | ||
|
||
| return ExecuteStartUpCsharp(uiControlledApplication); | ||
| } | ||
|
|
||
| public static Result ExecuteStartUpPython(UIControlledApplication uiControlledApplication) | ||
| { | ||
| // we need a UIApplication object to assign as `__revit__` in python... | ||
| var versionNumber = uiControlledApplication.ControlledApplication.VersionNumber; | ||
|
|
@@ -76,7 +103,48 @@ private static Result ExecuteStartupScript(UIControlledApplication uiControlledA | |
|
|
||
| return result; | ||
| } | ||
| public static Result ExecuteStartUpCsharp(UIControlledApplication uiControlledApplication) | ||
| { | ||
| try | ||
| { | ||
| var versionNumber = uiControlledApplication.ControlledApplication.VersionNumber; | ||
| var fieldName = int.Parse(versionNumber) >= 2017 ? "m_uiapplication" : "m_application"; | ||
| var fi = uiControlledApplication.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); | ||
| var uiApplication = (UIApplication)fi.GetValue(uiControlledApplication); | ||
|
|
||
| var services = new ServiceCollection(); | ||
| services.AddLogging(cfg => cfg.AddDebug()); | ||
|
|
||
| // Add Revit UIApplication to services | ||
| services.AddSingleton(uiApplication); | ||
|
|
||
| // Add known services | ||
| services.AddSingleton<ICommandTypeGenerator, DefaultCommandTypeGenerator>(); | ||
| services.AddSingleton<IHookManager, DummyHookManager>(); | ||
| services.AddSingleton<IUIManager, UIManagerService>(); | ||
| services.AddSingleton<ISessionManager, SessionManagerService>(); | ||
| services.AddSingleton<IExtensionManager, ExtensionManagerService>(); | ||
|
|
||
| // Register AssemblyBuilderService with explicit string parameter | ||
| services.AddSingleton<AssemblyBuilderService>(sp => | ||
| new AssemblyBuilderService( | ||
| sp.GetRequiredService<ICommandTypeGenerator>(), | ||
| versionNumber | ||
| ) | ||
| ); | ||
|
|
||
| var serviceProvider = services.BuildServiceProvider(); | ||
| var sessionManager = serviceProvider.GetRequiredService<ISessionManager>(); | ||
| sessionManager.LoadSessionAsync().Wait(); | ||
|
|
||
| return Result.Succeeded; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| TaskDialog.Show("Error Starting pyRevit Session", ex.ToString()); | ||
| return Result.Failed; | ||
| } | ||
| } | ||
| private static string GetStartupScriptPath() | ||
| { | ||
| var loaderDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); | ||
|
|
@@ -90,4 +158,4 @@ Result IExternalApplication.OnShutdown(UIControlledApplication application) | |
| return Result.Succeeded; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| using System; | ||
| using System.IO; | ||
| using System.Reflection; | ||
| using System.Reflection.Emit; | ||
| using pyRevitAssemblyBuilder.Shared; | ||
|
|
||
| namespace pyRevitAssemblyBuilder.AssemblyMaker | ||
| { | ||
| public class AssemblyBuilderService | ||
| { | ||
| private readonly ICommandTypeGenerator _typeGenerator; | ||
| private readonly string _revitVersion; | ||
|
|
||
| public AssemblyBuilderService(ICommandTypeGenerator typeGenerator, string revitVersion) | ||
| { | ||
| _typeGenerator = typeGenerator; | ||
| _revitVersion = revitVersion ?? throw new ArgumentNullException(nameof(revitVersion)); | ||
| } | ||
|
|
||
| public ExtensionAssemblyInfo BuildExtensionAssembly(IExtension extension) | ||
| { | ||
| string extensionHash = GetStableHash(extension.GetHash() + _revitVersion).Substring(0, 16); | ||
| string fileName = $"pyRevit_{_revitVersion}_{extensionHash}_{extension.Name}.dll"; | ||
|
|
||
| string outputDir = Path.Combine( | ||
| Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), | ||
| "pyRevit", | ||
| _revitVersion | ||
| ); | ||
| Directory.CreateDirectory(outputDir); | ||
|
|
||
| string outputPath = Path.Combine(outputDir, fileName); | ||
|
|
||
| var asmName = new AssemblyName(extension.Name) | ||
| { | ||
| Version = new Version(1, 0, 0, 0) | ||
| }; | ||
|
|
||
| string fileNameWithoutExt = Path.GetFileNameWithoutExtension(outputPath); | ||
|
|
||
| #if NETFRAMEWORK | ||
| var domain = AppDomain.CurrentDomain; | ||
| var asmBuilder = domain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave, outputDir); | ||
| var moduleBuilder = asmBuilder.DefineDynamicModule(fileNameWithoutExt, fileName); | ||
| #else | ||
| var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run); | ||
| var moduleBuilder = asmBuilder.DefineDynamicModule(fileNameWithoutExt); | ||
| #endif | ||
|
|
||
| foreach (var cmd in extension.GetAllCommands()) | ||
| { | ||
| _typeGenerator.DefineCommandType(extension, cmd, moduleBuilder); | ||
| } | ||
|
|
||
| #if NETFRAMEWORK | ||
| asmBuilder.Save(fileName); | ||
| #else | ||
| var generator = new Lokad.ILPack.AssemblyGenerator(); | ||
| generator.GenerateAssembly(asmBuilder, outputPath); | ||
| #endif | ||
|
|
||
| return new ExtensionAssemblyInfo( | ||
| name: extension.Name, | ||
| location: outputPath, | ||
| isReloading: CheckIfExtensionAlreadyLoaded(extension.Name) | ||
| ); | ||
| } | ||
|
|
||
| private bool CheckIfExtensionAlreadyLoaded(string extensionName) | ||
| { | ||
| foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) | ||
| { | ||
| if (asm.GetName().Name == extensionName) | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| private static string GetStableHash(string input) | ||
| { | ||
| using (var sha1 = System.Security.Cryptography.SHA1.Create()) | ||
| { | ||
| var hash = sha1.ComputeHash(System.Text.Encoding.UTF8.GetBytes(input)); | ||
| return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| using System.Reflection.Emit; | ||
| using System.Reflection; | ||
| using pyRevitAssemblyBuilder.Shared; | ||
|
|
||
| namespace pyRevitAssemblyBuilder.AssemblyMaker | ||
| { | ||
| public class DefaultCommandTypeGenerator : ICommandTypeGenerator | ||
| { | ||
| public void DefineCommandType(IExtension extension, ICommandComponent command, ModuleBuilder moduleBuilder) | ||
| { | ||
| var typeBuilder = moduleBuilder.DefineType( | ||
| name: command.UniqueId, | ||
| attr: TypeAttributes.Public | TypeAttributes.Class | ||
| ); | ||
|
|
||
| var attrBuilder = new CustomAttributeBuilder( | ||
| typeof(System.ComponentModel.DescriptionAttribute).GetConstructor(new[] { typeof(string) }), | ||
| new object[] { command.Tooltip } | ||
| ); | ||
| typeBuilder.SetCustomAttribute(attrBuilder); | ||
|
|
||
| typeBuilder.CreateType(); | ||
jmcouffin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| namespace pyRevitAssemblyBuilder.AssemblyMaker | ||
| { | ||
| public class ExtensionAssemblyInfo | ||
| { | ||
| public string Name { get; } | ||
| public string Location { get; } | ||
| public bool IsReloading { get; } | ||
|
|
||
| public ExtensionAssemblyInfo(string name, string location, bool isReloading) | ||
| { | ||
| Name = name; | ||
| Location = location; | ||
| IsReloading = isReloading; | ||
| } | ||
|
|
||
| public override string ToString() | ||
| { | ||
| return $"{Name} ({(IsReloading ? "Reloaded" : "Fresh")}) -> {Location}"; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| using pyRevitAssemblyBuilder.Shared; | ||
| using System.Reflection.Emit; | ||
|
|
||
| namespace pyRevitAssemblyBuilder.AssemblyMaker | ||
| { | ||
| public interface ICommandTypeGenerator | ||
| { | ||
| void DefineCommandType(IExtension extension, ICommandComponent command, ModuleBuilder moduleBuilder); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using pyRevitAssemblyBuilder.AssemblyMaker; | ||
|
|
||
| namespace pyRevitAssemblyBuilder.Startup | ||
| { | ||
| public static class ServiceRegistration | ||
| { | ||
| public static IServiceCollection AddAssemblyBuilder(this IServiceCollection services) | ||
| { | ||
| services.AddSingleton<ICommandTypeGenerator, DefaultCommandTypeGenerator>(); | ||
| services.AddSingleton<AssemblyBuilderService>(); | ||
| return services; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| using System; | ||
| using System.Reflection; | ||
| using System.Reflection.Emit; | ||
| using pyRevitAssemblyBuilder.Shared; | ||
|
|
||
| namespace pyRevitAssemblyBuilder.AssemblyMaker | ||
| { | ||
| public class TypeMakerService : ICommandTypeGenerator | ||
| { | ||
| public void DefineCommandType(IExtension extension, ICommandComponent command, ModuleBuilder moduleBuilder) | ||
| { | ||
| var typeBuilder = moduleBuilder.DefineType( | ||
| name: command.UniqueId, | ||
| attr: TypeAttributes.Public | TypeAttributes.Class | ||
| ); | ||
|
|
||
| if (!string.IsNullOrEmpty(command.Tooltip)) | ||
| { | ||
| var attrBuilder = new CustomAttributeBuilder( | ||
| typeof(System.ComponentModel.DescriptionAttribute).GetConstructor(new[] { typeof(string) }), | ||
| new object[] { command.Tooltip } | ||
| ); | ||
| typeBuilder.SetCustomAttribute(attrBuilder); | ||
| } | ||
|
|
||
| // Emit a default constructor | ||
| typeBuilder.DefineDefaultConstructor(MethodAttributes.Public); | ||
|
|
||
| // Create the type (works in .NET Standard 2.0 via CreateTypeInfo) | ||
| _ = typeBuilder.CreateTypeInfo(); | ||
| } | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.