We discussed about how to do C++/CLI Addin development work for 64 bit of Revit 2011 using the Visual Studio 2008 previously. Since the Visual Studio 2010 has come out for a while and it’s becoming more and more popular, let’s see how to do the same thing with the latest VS IDE in this post.
Most things are pretty the same but there do be some particularities with the Visual Studio 2010 in this regard. To make things clear and complete, we will still demonstrate step by step.
At first hand, we need to create a Visual C++ CLR Class Library project and name it as VCAddin64BitRevit2011 for example:
A word about the New Project dialog: please choose the .NET Framework 3.5 as the target framework explicitly as the Visual Studio 2010 defaults the .NET Framework 4.0 for us which is not good for Revit 2011. Otherwise, Revit 2011 will not load the resultant assembly, the Visual Studio debugger does not allow us to debug it, commands, applications or any code behave not as expected, or some other weirdness may just occur.
Then we create a new Solution Platform for the x64 environment. As can be checked from the following Configuration Manager, only a Win32 Solution Platform is created by default. We drop down the Active solution platform combo box and click the <New…> item:
In the following New Solution Platform dialog, we create a new project platform through choosing the x64 platform and copying settings from the existing default Win32 platform:
Next, we double check some important properties of the configuration and platform combination of our concern, Debug|x64 here, and set them as expected if they are not.
Firstly, let’s look at the Framework and References property page:
The .NET CLR Class Library wizard only adds a few very basic .NET assembly references for us such as the System and the System.Core. We may want to add some more .NET assemblies such as PresentationCore, System.Data.DataSetExtensions, System.Data.Linq, System.Drawing, System.Windows.Forms, System.Xml, System.Xml.Linq, WindowBase and any others you like. More importantly, please do not forget to reference the two Revit API assemblies in, the RevitAPI and the RevitAPIUI. Please do not forget to make the Copy Local as False for the two Revit API assembly references.
Secondly, we need to make sure the Target Machine Linker setting is MachineX64 or /MACHINE:X64 instead of the default MachineX86. If the x64 platform is added by the Configuration Manager automatically as introduced above, the Target Machine will be automatically switched over from the /MACHINE:X86 to the expected /MACHINE:X64 but still it’s worth of a bit effort to double check the very important setting.
Thirdly, please make sure the /Zi Program Database is used as the Debug Information Format:
Fourthly, please double check the WIN64 preprocessor definition has successfully replaced the old default WIN32:
Another big difference happens here. As can be seen, the WIN32 preprocessor definition is still there. And in the Visual Studio 2010, it also invents a %(PreprocessorDefinitions). Let’s put aside the new thing and change the WIN32 to the WIN64:
In fact, it’s experienced that even if we leave the WIN32 preprocessor there the Revit 2011 still can recognize and load the assembly and the Visual Studio 2010 debugger can still debug into code successfully. It seems the new stuff %(PreprocessorDefinitions) plays some extra roles. But anyway, let’s correct the discrepancy here.
Last but not least, if we’d like to debug the C++ CLR project, please copy the full path of the Revit.exe to the Debugging Command setting:
Now it’s time to look after coding stuffs since the project configurations, platforms, and settings are ready.
Add one more header file, Namespaces.h, and copy and paste the following code in:
#pragma once
using namespace Autodesk::Revit::ApplicationServices;
using namespace Autodesk::Revit::Attributes;
using namespace Autodesk::Revit::DB;
using namespace Autodesk::Revit::DB::Events;
using namespace Autodesk::Revit::DB::Architecture;
using namespace Autodesk::Revit::DB::Structure;
using namespace Autodesk::Revit::DB::Mechanical;
using namespace Autodesk::Revit::DB::Electrical;
using namespace Autodesk::Revit::DB::Plumbing;
using namespace Autodesk::Revit::UI;
using namespace Autodesk::Revit::UI::Selection;
using namespace Autodesk::Revit::UI::Events;
using namespace Autodesk::Revit::Collections;
using namespace Autodesk::Revit::Exceptions;
using namespace Autodesk::Revit::Utility;
namespace RvtAppSrv = Autodesk::Revit::ApplicationServices;
namespace RvtDB = Autodesk::Revit::DB;
Add the following content to the header, Stdafx.h, which was created by the C++ CLR Class Library:
#pragma once
#using <mscorlib.dll>
using namespace System;
using namespace System::Collections;
using namespace System::Collections::Generic;
using namespace System::Text;
using namespace System::Linq;
using namespace System::Xml;
using namespace System::Reflection;
using namespace System::ComponentModel;
using namespace System::Globalization;
using namespace System::Resources;
using namespace System::Threading;
using namespace System::IO;
using namespace System::Windows;
using namespace System::Windows::Media::Imaging;
using namespace System::Windows::Forms;
using namespace System::Windows::Interop;
using namespace System::Drawing;
using namespace System::Drawing::Imaging;
Add one more source, ExtApp.cpp, and copy/paste the following code in it:
#include "StdAfx.h"
#include "Namespaces.h"
namespace VCAddin64BitRevit2011
{
[Transaction(TransactionMode::Manual)]
[Regeneration(RegenerationOption::Manual)]
public ref class ExtApp : IExternalApplication
{
#pragma region Cached Variables
public:
static UIControlledApplication^ _cachedUiCtrApp;
#pragma endregion
#pragma region IExternalApplication Members
virtual Result OnStartup(UIControlledApplication^ uiApp)
{
_cachedUiCtrApp = uiApp;
try
{
RibbonPanel^ ribbonPanel = CreateRibbonPanel();
//TODO: add you code below->
return Result::Succeeded;
}
catch (Exception^ ex)
{
MessageBox::Show( ex->ToString() );
return Result::Failed;
}
}
virtual Result OnShutdown(UIControlledApplication^ uiApp)
{
try
{
//TODO: add you code below->
return Result::Succeeded;
}
catch (Exception^ ex)
{
MessageBox::Show(ex->ToString());
return Result::Failed;
}
}
#pragma endregion
#pragma region Local Methods
private:
RibbonPanel^ CreateRibbonPanel()
{
RibbonPanel^ panel = _cachedUiCtrApp->CreateRibbonPanel("VCAddin64BitRevit2011");
////Default button:
PushButtonData^ pbDataExtCmd = gcnew PushButtonData("ExtCmd", "ExtCmd", Assembly::GetExecutingAssembly()->Location, "VCAddin64BitRevit2011.ExtCmd");
PushButton^ pbExtCmd = static_cast<PushButton^>(panel->AddItem(pbDataExtCmd));
pbExtCmd->ToolTip = "ExtCmd";
pbExtCmd->LargeImage = BmpImageSource("ExtCmd32x32");
pbExtCmd->Image = BmpImageSource("ExtCmd16x16");
////More buttons:
return panel;
}
System::Windows::Media::ImageSource^ BmpImageSource(String^ embeddedPath)
{
ResourceManager^ resources = gcnew ResourceManager("VCAddin64BitRevit2011.Resources", Assembly::GetExecutingAssembly());
System::Drawing::Bitmap^ img = (cli::safe_cast<System::Drawing::Bitmap^>(resources->GetObject(embeddedPath)));
IntPtr hBitmap = img->GetHbitmap();
return System::Windows::Interop::Imaging::CreateBitmapSourceFromHBitmap(hBitmap, IntPtr::Zero, Int32Rect::Empty, BitmapSizeOptions::FromEmptyOptions());
}
#pragma endregion
};
}
Add another source, ExtCmd.cpp, and copy/paste the following code in:
#include "stdafx.h"
#include "Namespaces.h"
namespace VCAddin64BitRevit2011
{
[Transaction(TransactionMode::Manual)]
[Regeneration(RegenerationOption::Manual)]
public ref class ExtCmd : IExternalCommand
{
#pragma region Cached Variables
private:
static ExternalCommandData^ _cachedCmdData;
public:
static property UIApplication^ CachedUiApp
{
UIApplication^ get()
{
return _cachedCmdData->Application;
}
}
static property RvtAppSrv::Application^ CachedApp
{
RvtAppSrv::Application^ get()
{
return CachedUiApp->Application;
}
}
static property RvtDB::Document^ CachedDoc
{
RvtDB::Document^ get()
{
return CachedUiApp->ActiveUIDocument->Document;
}
}
#pragma endregion
#pragma region IExternalCommand Members
public:
virtual Result Execute(ExternalCommandData^ cmdData, String^% msg, ElementSet^ elemSet)
{
_cachedCmdData = cmdData;
try
{
//TODO: add your code below.
return Result::Succeeded;
}
catch(System::Exception^ ex)
{
msg = ex->ToString();
return Result::Failed;
}
return Result::Succeeded;
}
#pragma endregion
};
}
Add an Assembly Resource File (.resx) and name it as Resources.resx:
After the Resources.resx is created, import the two images into it:
Create a Revit addin manifest file and name it VCAddin64BitRevit2011.Addin for example and copy/paste the following external application registration nodes in:
<?xml version="1.0" encoding="utf-8"?>
<RevitAddIns>
<AddIn Type="Application">
<Assembly>C:\Temp\VCAddin64BitRevit2011\x64\Debug\VCAddin64BitRevit2011.dll
</Assembly>
<FullClassName>VCAddin64BitRevit2011.ExtApp</FullClassName>
<ClientId>aeb8f380-2f91-4fd4-8f55-80a7b5742010</ClientId>
<Name>VCAddin64BitRevit2011</Name>
</AddIn>
</RevitAddIns>
Put the manifest file into the user roaming folder of the Revit 2011 Addins (e.g. C:\Users\<YOU>\AppData\Roaming\Autodesk\Revit\Addins\2011\ VCAddin64BitRevit2011.Addin on Windows 7), and then you are all set.
Ok, now set a breakpoint in the C++ project, and press the F5 button. The Revit 2011 x64 will be launched, the C++ Revit addin assembly will be registered and loaded automatically, and the ribbon panel and button will be created and added to the Revit ribbon Addins tab automatically, when the button is clicked, the external command will be called and if you set a breakpoint somewhere there it will be reached.
Enjoy!
RevitAddinWizard will take care of all these automatically and make everything ready for you in a moment for both 32bit and 64bit Revit and both 2008 and 2010 Visual Studio.
Recent Comments