In this post I will go over some of the most useful things that you need to learn to be able to successfully create Revit plug-ins and some good coding practices (I hope they are good because I am not actually a computer scientist – just a lowly architect):
- using statements for external libraries
- IExternalCommand implementation
- Exception handling
- Pseudo code and commenting
- Addin files
We ended last post upon successfully adding RevitAPI and RevitAPIUI dlls to our project. Now let’s tell our plug-in that we are going to use them:
This three lines will allow us to use Classes and Methods defined in the two external libraries. They will basically give us access to everything that we will need from the Revit API. Now that we have them referenced in let’s look at IExternalCommand implementation.
- Hover over the IExternalCommand. Little yellow bulb will appear, click on the down arrow next to it to reveal a contextual menu. It should give us two options. Click on Implement interface.
This will “implement” the interface called IExternalCommand which is what Revit uses to recognize plug-ins. Our code should look like this after I deleted some redundant stuff that get’s put in automatically:
Now, really quickly I will do something that I call adding “pseudo code”. This is pretty good practice when coding. It’s basically a road map for what we want to do in our code and what order we want to achieve this. You probably also noticed that Execute has been underlined in red. That means that our class is missing something. In this case the message bubble states that “not all code paths return a value”. It means that we are missing “return” statements. For IExternalCommand to work properly it has to return a Result.Failed, Result.Canceled or Result.Succeeded. You don’t always need all three of them, but in case of our plug-in we will actually use all three. Let’s also add those in:
What you see above is called “pseudo code”. It’s just me adding text notes to my code to let myself know what I think I need to do in my code to successfully create my plug-in. Now that we have our road map, let’s create a little framework that we will be working within. What I mean by that is adding “try” statements to take care of possible errors (it’s always a good idea to enclose your code in try-catch), as well as return results so that our “Execute” method stops barking at us with that ugly red underline.
Please pay attention to couple of things that we did here:
- This is how you “catch” a very specific error. Since I know that this error is actually very common, I will list it out here. This error will happen if user hits ESC button during selection process. We will talk about this a little more later so stay tuned.
- This is how you “catch” unexpected errors. There might be things that happen, that you didn’t anticipate in your code, so this statement will catch them and then return them as “message”.
This part was pretty straight forward. There is only one more little thing that Revit requires for all its External Commands – Transaction Attributes. Transaction Attribute is telling Revit what method to use to manage Transactions for a given plug-in. Our options are either Automatic in which case Revit will try to automatically create a Transaction for us when it deems it necessary. Another option is Manual, in which case we will have to make sure to start a new transaction in our code for every process that modifies Revit’s Database. I personally prefer the Manual mode of transactions as it makes it clearer when we are making changes to Revit document. To define this attribute we need a single line of code like that:
So there it is. We have the structure of our plug-in in place. We defined the sequence of how we will create our plug-in using pseudo code, we implemented IExternalCommand with Transaction Attribute and prepared ourselves for potential errors and how to handle them. Now, we just need to add a *.addin file to our assembly and we can build our plug-in. For those of you not familiar with *.addin, its a file that tells Revit to load our plug-in DLL file when Revit starts. Addin and Dll files will always go together. Let’s do that really quickly.
- Right Click on the assembly name in Solution Explorer.
- Navigate to Add
- Click on New Item…
This will produce another window that we should be familiar with already:
- Select Text File from the list.
- Insert a name for the file with .addin extension.
- Click Add.
Now we just need to add a few things to our addin file that will tell Revit what to look for.
Next, we have to change the Build Action for our addin file, so that it actually get’s created with our DLL file (remember we will need both of them).
- Select our addin in Solution Explorer.
- Change its Build Action to Content and change its Copy to Output Directory property to Copy if newer.
Now if you navigate to Build and click on Build Solution:
This will create the two files that we need in the following location:
- This is the location that if you remember we specified our new Visual Studio project to be located.
- This is the location that Visual Studio creates for us. Every time we hit “Build” it will replace files in this location unless otherwise specified.
- These are our files that we need for the next step.
Now that we have our DLL and Addin files, we just need to add them to the Revit Addins folder so that Revit can load them up next time it starts. Just copy/paste these two files to the following location:
Keep in mind to change the “ksobon” to your own user name.
Now, if you fire up Revit, you should be able to find this on your Add-ins tab:
You can go ahead and click on it, but nothing will happen since our code, doesn’t yet do anything other then return Result.Succeeded. :-)
In the next part of this tutorial we will talk about ISelectionFilter and how to ask user to select things in Revit so that our plug-in can process them.
Thank you for the very useful tutorials, I take it that the addin code is available for this application from Github?
Should be good now. I must have missed it when I was typing.
Please, continue with this works…is extremely usefull.
I am working on a small graduate project in which I need to create custom button and upon clicking that button another application launches.
The tutorial looks like perfect for my project, but for some reason I dont see the buttons being created.
I am using Revit 2016 student version is that the reason?
And could you please explain the significance oof clientID and VendorID in the addin file.
Thanks and Regards,
Student version should have nothing to do with this although I have never used one so I wouldn’t know for sure. The ids are important if you are loading in multiple plug-ins as you want to avoid two plug-ins with the same id. It will usually throw an exception and prevent the plug-in from loading.
It was a prompt reply I appreciate.
I am just creating one plug-in so how can I generate a VendorID and ClientID, Or can I skip IDs (lines that use IDs) since I have only one plug in.
They are totally arbitrary. You can just make up your own. I think if you were a certified Autodesk Developer you can register your Vendor Id so that the same isn’t issue to someone else, but for in house development like this, you are totally fine just using whatever I have posted or making your own.
It actually works… I started the whole exercise again carefully and it works. Sorry to bother you…
Thank you so much for making it so easy by breaking it down to simple steps.
Hope you are doing great.
I need some help with Revit. I need to get the properties like current document name, filepath etc. For that I gues I need to get Revit application object from that document object etc. Could you please help me with that.
Thanks and Regards,
No you don’t, the Document object is good enough. You can get it by calling:
Document doc = uiApp.ActiveUIDocument.Document;
and then you can get file path like this:
string filePath = doc.PathName;
Bear in mind that PathName will return an empty string if document has been detached or was not saved yet. Otherwise it will give you a full path to the file including file name. You can then deconstruct the file name using methods like: Path.GetFileName() from System.IO
Have a look at this link: https://msdn.microsoft.com/en-us/library/system.io.path.getfilename(v=vs.110).aspx
Hey Konrad, I’m using Revit 2017 and Visual Studio 2017. When I load Revit for the first time after completing these steps, I receive an error saying the GUID is duplicated. (I currently have about 1/2 dozen plug-ins loaded.) It’s using the same GUID as Navisworks File Exporter 2017.
How do you modify the GUID of the “Builld”? I can’t find any information at all about this thru Googling.
Do you know of a way to have VS populate the info from the project to the *.addin file somehow? it seems like there should be a way to autopopulate the guid and assembly info from somewhere else.
Also, I’m working with another example from Troy Gates where his .addin has as .app
Generally, what should that relate to or identify?
So the .addin manifest file is just an XML schema to tell Revit what it is that you are loading and where it is located. If you want to programmatically generate that file, you can create a class with all of these properties and then have VS serialize it into an addin manifest file. My question would be, why do you want to do that. The thing with these manifest files is that they are not supposed to change. So basically you don’t want it to have a different id every time you build the solution. If the file is really meant to be static, then its easier to simply have it attached to the VS solution as a resource, and copy it out to proper location in the post-build. That’s what I usually do. Does that help?
the procedure you have shown has been a big relief for me. All this time I have been adding the file in C:program files instead of users.
Also in Autodesk first plugin tutorials, they are adding the address of .dll in addin file itself instead of copying it along with .addin. Does this make any difference..?
Not really, but I feel like making that DLL location relative is clearer and more flexible. It will work on other user’s computers as well, not just yours.
Thanks for this awesome resource.
I just ran through with VS2019, I had to add ‘Using System’ & update my target framework as here:
Hopefully that’s of interest.
Always! Thanks for sharing.
First of all, THANK YOU for all your contributions to the Dynamo community. You’re a beast!
In case you wish to update this page, the using Autodesk.Revit.UI.Selection; statement is missing here.
Thanks for all!
Thanks! I am not sure where. Can you post a full sample so that I can better asses?
Konrad, my bad, the code was correct. :)
In Revit 2020, the directory to save the .dll and the .addin files is C:\ProgramData\Autodesk\Revit\Addins\20XX
where XX will be replaced by your version of Revit