-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathExternalCommandAssemblyBuilder.cs
More file actions
66 lines (60 loc) · 3.72 KB
/
ExternalCommandAssemblyBuilder.cs
File metadata and controls
66 lines (60 loc) · 3.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Xml.Linq;
namespace RevitPythonShell.RpsRuntime
{
/// <summary>
/// The ExternalCommandAssemblyBuilder creates an assembly (.net dll) for
/// a list of python scripts that can be used as IExternalCommand implementations
/// in the Revit user interface (PushButtonData).
/// </summary>
public class ExternalCommandAssemblyBuilder
{
/// <summary>
/// Build a new assembly and save it to disk as "pathToDll". Create a type (implementing IExternalCommand) for
/// each class name in classNamesToScriptPaths that, when "Execute()" is called on it, will load the corresponding python script
/// from disk and execute it.
/// </summary>
public void BuildExternalCommandAssembly(string pathToDll, IDictionary<string, string> classNamesToScriptPaths)
{
var dllName = Path.GetFileNameWithoutExtension(pathToDll);
var dllFolder = Path.GetDirectoryName(pathToDll);
var assemblyName = new AssemblyName { Name = dllName + ".dll", Version = new Version(1, 0, 0, 0) };
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave, dllFolder);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(dllName + "Module", dllName + ".dll");
foreach (var className in classNamesToScriptPaths.Keys)
{
var typebuilder = moduleBuilder.DefineType(className,
TypeAttributes.Class | TypeAttributes.Public,
typeof(RpsExternalCommandScriptBase));
// add RegenerationAttribute to type
// var regenerationConstrutorInfo = typeof(RegenerationAttribute).GetConstructor(new Type[] { typeof(RegenerationOption) });
// var regenerationAttributeBuilder = new CustomAttributeBuilder(regenerationConstrutorInfo, new object[] { RegenerationOption.Manual });
// typebuilder.SetCustomAttribute(regenerationAttributeBuilder);
// add TransactionAttribute to type
// var transactionConstructorInfo = typeof(TransactionAttribute).GetConstructor(new Type[] { typeof(TransactionMode) });
// var transactionAttributeBuilder = new CustomAttributeBuilder(transactionConstructorInfo, new object[] { TransactionMode.Manual });
// typebuilder.SetCustomAttribute(transactionAttributeBuilder);
// call base constructor with script path
var ci = typeof(RpsExternalCommandScriptBase).GetConstructor(new[] { typeof(string) });
var constructorBuilder = typebuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[0]);
var gen = constructorBuilder.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0); // Load "this" onto eval stack
gen.Emit(OpCodes.Ldstr, classNamesToScriptPaths[className]); // Load the path to the command as a string onto stack
gen.Emit(OpCodes.Call, ci); // call base constructor (consumes "this" and the string)
gen.Emit(OpCodes.Nop); // Fill some space - this is how it is generated for equivalent C# code
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ret); // return from constructor
typebuilder.CreateType();
}
assemblyBuilder.Save(dllName + ".dll");
}
}
}