-
-
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 all 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 |
|---|---|---|
| @@ -1,8 +1,10 @@ | ||
| using System; | ||
| using System.IO; | ||
| using System.Reflection; | ||
| using System.Diagnostics; | ||
| using Autodesk.Revit.UI; | ||
| using Autodesk.Revit.Attributes; | ||
| using pyRevitExtensionParser; | ||
|
|
||
| /* Note: | ||
| * It is necessary that this code object do not have any references to IronPython. | ||
|
|
@@ -12,82 +14,164 @@ | |
| */ | ||
| namespace PyRevitLoader | ||
| { | ||
| [Regeneration(RegenerationOption.Manual)] | ||
| [Transaction(TransactionMode.Manual)] | ||
| class PyRevitLoaderApplication : IExternalApplication | ||
| { | ||
| public static string LoaderPath => Path.GetDirectoryName(typeof(PyRevitLoaderApplication).Assembly.Location); | ||
|
|
||
| // Hook into Revit to allow starting a command. | ||
| Result IExternalApplication.OnStartup(UIControlledApplication application) | ||
| { | ||
| LoadAssembliesInFolder(LoaderPath); | ||
| // We need to also looad dlls from two folders up | ||
| var commonFolder = Path.GetDirectoryName(Path.GetDirectoryName(LoaderPath)); | ||
| LoadAssembliesInFolder(commonFolder); | ||
|
|
||
| try | ||
| { | ||
| return ExecuteStartupScript(application); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| TaskDialog.Show("Error Loading Startup Script", ex.ToString()); | ||
| return Result.Failed; | ||
| } | ||
| } | ||
|
|
||
| private static void LoadAssembliesInFolder(string folder) | ||
| { | ||
| // load all engine assemblies | ||
| // this is to ensure pyRevit is loaded on its own assemblies | ||
| foreach (var engineDll in Directory.GetFiles(folder, "*.dll")) | ||
| { | ||
| try | ||
| { | ||
| Assembly.LoadFrom(engineDll); | ||
| } | ||
| catch | ||
| { | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static Result ExecuteStartupScript(UIControlledApplication uiControlledApplication) | ||
| { | ||
| // we need a UIApplication object to assign as `__revit__` in python... | ||
| 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); | ||
| // execute StartupScript | ||
| Result result = Result.Succeeded; | ||
| var startupScript = GetStartupScriptPath(); | ||
| if (startupScript != null) | ||
| { | ||
| var executor = new ScriptExecutor(uiApplication); // uiControlledApplication); | ||
| result = executor.ExecuteScript(startupScript); | ||
| if (result == Result.Failed) | ||
| { | ||
| TaskDialog.Show("Error Loading pyRevit", executor.Message); | ||
| } | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| private static string GetStartupScriptPath() | ||
| { | ||
| var loaderDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); | ||
| var dllDir = Path.GetDirectoryName(loaderDir); | ||
| return Path.Combine(dllDir, string.Format("{0}.py", Assembly.GetExecutingAssembly().GetName().Name)); | ||
| } | ||
|
|
||
| Result IExternalApplication.OnShutdown(UIControlledApplication application) | ||
| { | ||
| // FIXME: deallocate the python shell... | ||
| return Result.Succeeded; | ||
| } | ||
| } | ||
| } | ||
| [Regeneration(RegenerationOption.Manual)] | ||
| [Transaction(TransactionMode.Manual)] | ||
| class PyRevitLoaderApplication : IExternalApplication | ||
| { | ||
| public static string LoaderPath => Path.GetDirectoryName(typeof(PyRevitLoaderApplication).Assembly.Location); | ||
| private static UIControlledApplication _uiControlledApplication; | ||
|
|
||
| // Hook into Revit to allow starting a command. | ||
| Result IExternalApplication.OnStartup(UIControlledApplication application) | ||
| { | ||
| _uiControlledApplication = application; | ||
| LoadAssembliesInFolder(LoaderPath); | ||
| // We also need to load dlls from two folders up | ||
| var commonFolder = Path.GetDirectoryName(Path.GetDirectoryName(LoaderPath)); | ||
| LoadAssembliesInFolder(commonFolder); | ||
|
|
||
| try | ||
| { | ||
| // 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unused variable |
||
| var result = ExecuteStartupScript(application); | ||
| if (result == Result.Failed) | ||
| { | ||
| TaskDialog.Show("Error Loading pyRevit", executor.Message); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| TaskDialog.Show("Error Loading Startup Script", ex.ToString()); | ||
| return Result.Failed; | ||
| } | ||
| } | ||
|
|
||
| private static void LoadAssembliesInFolder(string folder) | ||
| { | ||
| // load all engine assemblies | ||
| // this is to ensure pyRevit is loaded on its own assemblies | ||
| foreach (var engineDll in Directory.GetFiles(folder, "*.dll")) | ||
| { | ||
| try | ||
| { | ||
| Assembly.LoadFrom(engineDll); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| // Log assembly load failures - some assemblies may fail to load and that's acceptable | ||
| Trace.WriteLine($"Failed to load assembly '{engineDll}': {ex.Message}"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static Result ExecuteStartupScript(UIControlledApplication uiControlledApplication) | ||
| { | ||
| // we need a UIApplication object to assign as `__revit__` in python... | ||
| 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); | ||
| // uiControlledApplication); | ||
| var uiApplication = (UIApplication)fi.GetValue(uiControlledApplication); | ||
|
Comment on lines
+79
to
+83
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. duplicate code |
||
| // execute StartupScript | ||
| Result result = Result.Succeeded; | ||
| var startupScript = GetStartupScriptPath(); | ||
| if (startupScript != null) | ||
| { | ||
| var executor = new ScriptExecutor(uiApplication); | ||
| result = executor.ExecuteScript(startupScript); | ||
| if (result == Result.Failed) | ||
| { | ||
| TaskDialog.Show("Error Loading pyRevit", executor.Message); | ||
| } | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| public static Result LoadSession(object pythonLogger = null, string buildStrategy = null) | ||
| { | ||
| try | ||
| { | ||
| // Use the stored UIControlledApplication | ||
| if (_uiControlledApplication == null) | ||
| { | ||
| throw new InvalidOperationException("UIControlledApplication not available. LoadSession can only be called after OnStartup."); | ||
| } | ||
|
|
||
| var uiControlledApplication = _uiControlledApplication; | ||
| // we need a UIApplication object to assign as `__revit__` in python... | ||
| 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); | ||
|
Comment on lines
+110
to
+115
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. duplicate code |
||
|
|
||
| // Get the current Revit version | ||
| var revitVersion = uiControlledApplication.ControlledApplication.VersionNumber; | ||
|
|
||
| // Determine build strategy: use provided parameter, or read from config, or default to ILPack | ||
| var assemblyBuildStrategyType = typeof(pyRevitAssemblyBuilder.AssemblyMaker.AssemblyBuildStrategy); | ||
| object strategyValue; | ||
|
|
||
| if (!string.IsNullOrEmpty(buildStrategy)) | ||
| { | ||
| // Use provided build strategy from Python | ||
| strategyValue = Enum.Parse(assemblyBuildStrategyType, buildStrategy); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why parse enum? |
||
| } | ||
| else | ||
| { | ||
| // Fallback: read from config | ||
| try | ||
| { | ||
| var config = PyRevitConfig.Load(); | ||
| var strategyName = config.NewLoaderRoslyn ? "Roslyn" : "ILPack"; | ||
| strategyValue = Enum.Parse(assemblyBuildStrategyType, strategyName); | ||
|
Comment on lines
+134
to
+136
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why parse enum? |
||
| } | ||
| catch | ||
| { | ||
| // Final fallback: default to ILPack | ||
| strategyValue = Enum.Parse(assemblyBuildStrategyType, "ILPack"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why parse enum? |
||
| } | ||
| } | ||
|
|
||
| // Create services using factory | ||
| var strategyEnum = (pyRevitAssemblyBuilder.AssemblyMaker.AssemblyBuildStrategy)strategyValue; | ||
| var sessionManager = pyRevitAssemblyBuilder.SessionManager.ServiceFactory.CreateSessionManagerService( | ||
| revitVersion, | ||
| strategyEnum, | ||
| uiApplication, | ||
| pythonLogger); | ||
|
|
||
| // Load the session using the C# SessionManagerService | ||
| sessionManager.LoadSession(); | ||
|
|
||
| return Result.Succeeded; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| TaskDialog.Show("Error Loading C# Session", | ||
| $"An error occurred while loading the C# session:\n\n{ex.Message}\n\nCheck the output window for details."); | ||
| return Result.Failed; | ||
| } | ||
| } | ||
| private static string GetStartupScriptPath() | ||
| { | ||
| var loaderDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); | ||
| var dllDir = Path.GetDirectoryName(loaderDir); | ||
| return Path.Combine(dllDir, string.Format("{0}.py", Assembly.GetExecutingAssembly().GetName().Name)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. possible NRE and string.Format to string interpolation |
||
| } | ||
| Result IExternalApplication.OnShutdown(UIControlledApplication application) | ||
| { | ||
| // FIXME: deallocate the python shell... | ||
| return Result.Succeeded; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
possible NRE (GetField can return null)