Last year, I postponed the porting of The Building Coder samples to Revit 2010 until pretty late, because I wanted to avoid forcing anyone to update.
This time around, I am doing it pretty early, and have a good reason for that as well. The migration from the Revit 2010 to the 2011 API is not quite as painless as in previous updates, because the whole Revit API has been significantly restructured and future-proofed. For details, please refer to the Revit 2011 beta SDK documentation, which includes ample explanations of all aspects.
Therefore, it is important to get some information and examples of the modifications required out there pretty early on, so that others can make use of the new samples for their own migration efforts.
I spent several days migrating The Building Coder samples. This may seem like a huge amount of time, and compared to the previous cycle it is, too.
On the other hand, I did have over sixty commands to migrate, and a lot of the migration effort has to do with the command registration and setup. Once you are past the command definition itself, the Execute method entry point, and the initial element selection or user interaction, there are much less changes in the real meat of an application. And I would assume that no other application out there defines sixty separate commands.
So without further ado, first of all let me present the new Revit 2011 version 2011.0.0.62 of The Building Coder sample code. To make it easier to compare with the 2010 version, here is an updated last Revit 2010 version 1.1.0.65.
I hope you are aware of the Unix diff, Visual Studio Windiff, and the numerous other tools for comparing files and directory structures.
Note that every single source file differs. Many of the differences are systematic, however, and are the same for all, such as the transaction mode and regeneration option settings. I did go through and try to identify commands which do not modify the database at all, so I could explicitly set their transaction mode to read-only.
I also introduced a new version numbering system. Previously the major revision number was always 1. The minor revision number 0 corresponded to Revit 2009, and 1 to Revit 2010. Now I bumped the major revision to 2011, so that the correspondence to Revit 2011 is more obvious. I may use the minor revision to indicate intermediate updates of the Revit API, if any appear. The last number is currently set to 62 and indicates the number of commands defined, at the moment, at least.
I dropped one or two commands during the migration process, notably the filter performance one. It was specific to the Revit 2009 and 2010 filtering mechanism, which has been totally revamped in 2011. Obviously, a new filter performance test for 2011 would be very interesting to implement and evaluate.
I am still using RvtSamples to load and launch all The Building Coder samples, as well as all the other ADN samples that I work with. The BcSamples.txt include file read by RvtSamples is included in the zip file and provides all the details required. That data can also be used to define alternative loading mechanisms, e.g. using the old Revit.ini or the new add-in manifest approach.
More about the issues I encountered by and by. Much more!
Good luck in your own porting efforts!
Jeremy,
Any guidelines about how to port your code from 2010 to 2011 but keep it compiling on both versions?
How to use the new 2011-only features and make them be ignored by the 2010 compilation?
Are there any #if preprocessor directive which allow our code to guess it is being compiled along 2010 or 2011 SDK?
Most of times we cannot just trash our current version in favor of a smooth and definitive migration to the new SDK.
Cheers.
Posted by: Fernando Malard | March 29, 2010 at 15:00
Dear Fernando,
Thank you for these very pertinent questions. Here are some quick and dirty spontaneous answers to them:
1. Nope.
2. Use an #if preprocessor directive.
3. Nothing predefined, but you can set them up yourself, of course. I added some #if _2010 directives to The Building Coder samples, but they are more decorative than functional, since I have no intention of compiling the ported version for 2010. I simply added them to preserve some 2010 version code here and there for comparison purposes, and intend to possibly use that to explain some of the main differences at some point.
4. Yes, I definitely understand and agree with that, and I would suggest that you do exactly what you are sort of suggesting, use a preprocessor directive to switch between 2010 and 2011 version code, or even other versions as well, if needed. You can of course also use .NET reflection to determine at runtime what version of Revit has loaded your add-in, and switch dynamically between differenc versions of your functions appropriately, cf. the end of
http://thebuildingcoder.typepad.com/blog/2009/10/revit-2010-subscription-pack.html#2
If you like, we can work together to set up a little sample demonstrating the technique for publishing here on the blog to explain to others how this works.
Cheers, Jeremy.
Posted by: Jeremy Tammik | March 29, 2010 at 17:57
Jeremy,
I think this issue is pretty much related to the project configuration than the sample features itself.
I think if you get a sample which contains code that have changed significantly from 2010 to 2011 this would be a very nice example of the problems users may face when they are trying to migrate their codes.
You could show them how to create the #if preprocessor definition and how to address the namespaces changes, changed signatures, removed API methods, etc.
How does it sound for you?
Cheers.
Posted by: Fernando Malard | March 30, 2010 at 08:15
Dear Fernando,
Yes, I absolutely agree. Please go ahead and send a minimal but not completey uninteresting sample along :-)
Please send me some free time as well. ;-)
Cheers, Jeremy.
Posted by: Jeremy Tammik | March 30, 2010 at 08:29
Ok, I will think about something simple and representative.
If I can find some free time I will send you but it will cost some money...hehehe
Cheers.
Posted by: Fernando Malard | March 30, 2010 at 08:39
Hi there,
we did the migration to 2011 keeping the 2010 even 2009 running.
Finally we create a seperate DLLs for 2011/2010/2009, but we use the same source code.
We addded special using statments for the major objects changed like:
#if Revit2011
using Autodesk.Revit.DB;
using RvtGeoElement = Autodesk.Revit.DB.GeometryElement;
using RvtGeoInstance = Autodesk.Revit.DB.GeometryInstance;
using Symbol = Autodesk.Revit.DB.ElementType;
...
#else
using RvtGeoElement = Autodesk.Revit.Geometry.Element;
using RvtGeoInstance = Autodesk.Revit.Geometry.Instance;
using Symbol = Autodesk.Revit.Symbol;
...
#endif
And it works fine for us.
Regards,
Steffen
Posted by: Steffen Rabe | March 31, 2010 at 06:19
Dear Steffen,
Thank you for the information.
That is exactly the kind of approach I had in mind.
Congratulations on handling all three versions so effectively and elegantly.
Do you have any bits of source code or other functionality that also need conditional compilation?
If so, which are they, please?
If you like, I will happily publish some small non-confidential parts of your code as an multi-version example.
Cheers, Jeremy.
Posted by: Jeremy Tammik | March 31, 2010 at 06:53
Jeremy,
I still don't understand why Autodesk does not provide a some native SDK preprocessor symbols for each AutoCAD/Revit release. It is very annoying to keep creating custom preprocessor definitions for every single new product release.
Steffen,
Did you create different VStudio ouput configurations for each Revit release/platform?
Something like:
Release Revit 2009
Debug Revit 2009
Release x32 Revit 2010
Release x64 Revit 2010
Debug x32 Revit 2010
Debug x64 Revit 2010
Release x32 Revit 2011
Release x64 Revit 2011
Debug x32 Revit 2011
Debug x64 Revit 2011
It is a nightmare for us.
Cheers.
Posted by: Fernando Malard | March 31, 2010 at 08:34
Dear Fernando,
The reason is simply the KISS principle, as described in
http://en.wikipedia.org/wiki/KISS_principle
There is no need for it, it would only complicate things unnecessarily for those who do not require that kind of support, and unnecessarily limit those who do.
Just imagine if we at Autodesk had designed support for all those platforms in our own fashion.
I promise you it would not fulfil your needs, AND it would make it MUCH harder for you to do so yourself, because you would have to battle our principles.
I am absolutely convinced that there is nothing in the world stopping you from creating a completely comfortable and efficient system to manage the different versions with no pain at all, and certainly no nightmares.
If you like, you can drop me a mail when a new release comes out and I will invent a pre-processor symbol for you :-)
Cheers, Jeremy.
Posted by: Jeremy Tammik | March 31, 2010 at 10:15
Hi Fernando,
We only have different copies of the initial solution file from 2009 and added the define Revt2010 and now Revit2011.
Why different solutions? We need to add differnet files for the installaton packages.
Because we use .net we always compile with AnyCPU, so we only have two configurations for every sln. Debug/Release as usual.
Regards,
Steffen
Posted by: Steffen Rabe | March 31, 2010 at 17:13
Jeremy,
Sure we have a some more. The rest is very specific for our naming conventions.
Regards,
Steffen
1.)inside main class
#if Revit2011
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
#endif
public class Command : IExternalCommand
2.)inside Execute method
#if Revit2011
RevitCore.ActiveDocument = commandData.Application.ActiveUIDocument.Document;
RevitCore.Application = commandData.Application.Application;
RevitCore.Document = commandData.Application.ActiveUIDocument.Document;
RevitCore.RevitUIApplication = commandData.Application;
#else
RevitCore.ActiveDocument = commandData.Application.ActiveDocument;
RevitCore.Application = commandData.Application;
RevitCore.Document = commandData.Application.ActiveDocument;
#endif
3.)to create the ribbon
//create cpi pulldown menu
RibbonPanel rvtRibbonPanel = app.CreateRibbonPanel(NLS.Revit2010_TLB_PanelName);
#if Revit2011
PulldownButton cpiPullDownBtn = rvtRibbonPanel.AddItem(new PulldownButtonData(NLS.Revit2010_TLB_PullDownMenuName, NLS.Revit2010_TLB_PullDownMenuName)) as PulldownButton;
#else
PulldownButton cpiPullDownBtn = rvtRibbonPanel.AddPulldownButton(NLS.Revit2010_TLB_PullDownMenuName, NLS.Revit2010_TLB_PullDownMenuName);
#endif
#if Revit2011
PushButton cpiPushBtn = cpiPullDownBtn.AddPushButton(new PushButtonData(NLS.Revit2010_TLB_ButtonText, NLS.Revit2010_TLB_ButtonText, ExecutingAssemblyPath, "Commandclass"));
#else
PushButton cpiPushBtn = cpiPullDownBtn.AddItem(NLS.Revit2010_TLB_ButtonText, ExecutingAssemblyPath, "Commandclass");
#endif
4.)to get all elements
#if Revit2011
IEnumerator allElementsEnumerator = RevitCore.Document.get_Elements_Bridge();
#else
IEnumerator allElementsEnumerator = RevitCore.Document.Elements;
#endif
Posted by: Steffen Rabe | March 31, 2010 at 17:15
Dear Steffen,
Thank you very much for the useful examples, they are just like I would have imagined them.
I think the principle is totally clear.
Cheers, Jeremy.
Posted by: Jeremy Tammik | March 31, 2010 at 17:24