Hi
Having some external command issues, seems like my command executes just fine from the Revit UI, but when executed from my add-in there seems to be no exceptions - but the desired result is not achieved.
Thanks to Jeremy Tammik's post Programmatic Custom Add-In External Command Launch It seems like I am able to execute a custom external command using the UIApplication,.PostCommand by looking up the custom command's revit id using its description (manifest id)
Assemblies
RevitAPI RevitAPIUI
Namespaces
using Autodesk.Revit.DB; using Autodesk.Revit.UI;
Custom Addin for External Command...
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.ReadOnly)] public class DocumentSwop : IExternalCommand { public static string filePlaceholderPath = @"\\someserver\C$\somedirectory\PlaceHolder.rvt"; public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { Autodesk.Revit.UI.UIApplication uiapp = null; Autodesk.Revit.DB.Document doc = null; Autodesk.Revit.UI.UIDocument docPlaceHolder = null; string filePlaceholder = null; string fileRevitActive = null; int fileCompare = 0; message = "Velocity.Revit.AddIns.DocumentSwop..."; try { // Assign UIApplication uiapp = commandData.Application; message += Environment.NewLine + " UIApplication object assigned"; if (uiapp != null) { message += Environment.NewLine + " UIApplication not null"; // Assign curren tactive document doc = uiapp.ActiveUIDocument.Document; message += Environment.NewLine + " Current active document object assigned"; if (doc != null) { message += Environment.NewLine + " Document object not null"; // Continue processing if placeholder file exists if (File.Exists(filePlaceholderPath)) { message += Environment.NewLine + " Placeholder file found in file system for " + filePlaceholderPath; // Assign filename of placeholder file filePlaceholder = System.IO.Path.GetFileName(filePlaceholderPath); message += Environment.NewLine + " Placeholder File = " + filePlaceholder; // Assign filename of current active revit document fileRevitActive = doc.Title; message += Environment.NewLine + " Revit Active Document = " + fileRevitActive; // Compare files fileCompare = String.Compare(filePlaceholder, fileRevitActive); message += Environment.NewLine + " File Compare = " + fileCompare.ToString(); // Perform document swop out if files dont match message += Environment.NewLine + " Swop Active Document..."; if (fileCompare != 0) { docPlaceHolder = uiapp.OpenAndActivateDocument(filePlaceholderPath); doc.Close(true); } message += Environment.NewLine + " Swop Active Document completed"; } message += Environment.NewLine + "Velocity.Revit.AddIns.DocumentSwop completed"; return Autodesk.Revit.UI.Result.Succeeded; } else { message += Environment.NewLine + " Document object could not be set"; message += Environment.NewLine + "Velocity.Revit.AddIns.DocumentSwop completed"; return Autodesk.Revit.UI.Result.Failed; } } else { message += Environment.NewLine + " UIApplication object could not be set"; message += Environment.NewLine + "Velocity.Revit.AddIns.DocumentSwop completed"; return Autodesk.Revit.UI.Result.Failed; } } catch (Exception ex) { message += Environment.NewLine + " Velocity.Revit.AddIns.DocumentSwop exception: " + ex.Message; message += Environment.NewLine + "Velocity.Revit.AddIns.DocumentSwop completed"; return Autodesk.Revit.UI.Result.Failed; } finally { filePlaceholder = null; fileRevitActive = null; fileCompare = 0; uiapp = null; doc = null; docPlaceHolder = null; } } }
Custom Addin for External Command Manifest...
<?xml version="1.0" encoding="utf-8"?> <RevitAddIns> <AddIn Type="Command"> <Name>Document Swop</Name> <Description>Swop the active document</Description> <Text>Document Swop</Text> <Assembly>C:\My Addins\DocumentSwop.dll</Assembly> <AddInId>21A8C920-ED49-434A-AACD-176784316B93</AddInId> <FullClassName>AddIns.DocumentSwop</FullClassName> <VendorId>Vendor Name</VendorId> <VendorDescription>Vendor Desc</VendorDescription> </AddIn> </RevitAddIns>
Custom Addin calling External Command...
private static string comIdCommandDocumentSwop = "21A8C920-ED49-434A-AACD-176784316B93"; Autodesk.Revit.UI.RevitCommandId addinId = null; try { addinId = RevitCommandId.LookupCommandId(comIdCommandDocumentSwop); } catch (Exception ex) { Misc.WriteLog(ex.Message); } if (addinId != null) { try { UIApplication.PostCommand(addinId); } catch (Exception ex) { Misc.WriteLog(ex.Message); } }
Seems like the custom external command is executed, but the desired result is not obtained - thus the document swop does not occur.
Anyone have some ideas?
Regards,
Zach
Have added additional debugging by writing to a log, found that the RevitId or AddInId of my custom external command is null, and thus the cause
of my external command not being executed.
I have looked at the example again on Jermery's post, and listed both the external commands; manifest
Jeremy's Example Manifest...
<AddIn Type="Command"> <Text>PostAddinCommand Dummy Command</Text> <Description>Test command for PostAddinCommand.</Description> <Assembly>PostAddinCommand.dll</Assembly> <FullClassName>PostAddinCommand.CmdDummy</FullClassName> <ClientId>64b3d907-37cf-4cab-8bbc-3de9b66a3efa</ClientId> <VendorId>TBC_</VendorId> <VendorDescription>The Building Coder, http://thebuildingcoder.typepad.com</VendorDescription> </AddIn>
My External Commands's Manifest...
<?xml version="1.0" encoding="utf-8"?> <RevitAddIns> <AddIn Type="Command"> <Name>Document Swop</Name> <Description>Swop the active document</Description> <Text>Document Swop</Text> <Assembly>C:\VelocityBIM\Revit Addin\Velocity.Revit.AddIns.DocumentSwop.dll</Assembly> <AddInId>21A8C920-ED49-434A-AACD-176784316B93</AddInId> <FullClassName>Velocity.Revit.AddIns.DocumentSwop</FullClassName> <VendorId>Velocity-IT</VendorId> <VendorDescription>Velocity-IT: www.velocity-it.co.uk</VendorDescription> </AddIn> </RevitAddIns>
My external command contains a node for <AddInId> holding the value of the AddInId, while Jeremy's example store this value in the <ClientId> node.
Using the UIApplication.PostCommand method to execute the external command from my addin. but the lookup method RevitCommandId.LookupCommandId([manifestId]) brings back a null for my RevitCommandId.
Hi zach.bester,
there may be another approach for invoking commands, using Reflection.
using System; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using System.Reflection; namespace MyNamespace { [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.ReadOnly)] [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)] public class Command_InvokeThirdPartyCommand : IExternalCommand { public Autodesk.Revit.UI.Result Execute(ExternalCommandData revit, ref String message, ElementSet elements) { try { // dll path, can be read from addin manifest file Assembly asm = Assembly.LoadFrom(@"D:\Tools\2016\RevitLookup.dll"); // Namespace.ClassName, can be read from addin manifest file, too Type t = asm.GetType("RevitLookup.CmdSnoopModScope", true); // creating an instance of the CmdSnoopModScope object obj = Activator.CreateInstance(t); // we just hand over the parameters of our own Execute method object[] args = new object[3] { revit, message, elements }; // some modfications must be made because 'message' is a ref parameter // http://stackoverflow.com/questions/9881069/why-cant-i-retrieve-the-value-for-parameters-of-type-out-or-ref-using-type-invo ParameterModifier pMod = new ParameterModifier(3); pMod[1] = true; ParameterModifier[] mods = { pMod }; // here we run the instance's Execute method and get the result... Result res = (Result)(t.InvokeMember("Execute", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public| BindingFlags.Instance | BindingFlags.Static, null, obj, args, mods, null, null)); // ...for just returning it by our own Execute method return res; } catch (Exception ex) { // log Exception... return Autodesk.Revit.UI.Result.Failed; } } } }
I tested the code with two readonly commands, RevitLookup's 'CmdSnoopModScope' and 'HelloWorld'.
Don't know if the TransactionMode needs to be changed to 'Manual' if the invoked command needs to modify the database.
I suppose that the target method's own TransactionMode attribute will be used.
Best regards, have a nice weekend,
Revitalizer
Can't find what you're looking for? Ask the community or share your knowledge.