Friday, May 27, 2011

Which of the parameters on the ProjectInfo are built in?

Yesterday I needed to know which of the ProjectInfo parameters are actually built in and can be relied on to be found in any Autodesk Revit Architecture document. Of course I fired up RPS and had a go at finding a solution. Here is what I came up with:

import clr
from Autodesk.Revit.DB import *
from System import Enum

doc = __revit__.ActiveUIDocument.Document
pi = doc.ProjectInformation
params = list(pi.Parameters)
builtInParameterIds = set([p.Id.IntegerValue for p in params if p.Id.IntegerValue < 0])
for id in builtInParameterIds:
    parameter = pi.get_Parameter(Enum.ToObject(BuiltInParameter, id))
    name = Enum.GetName(BuiltInParameter, id)
    if parameter:
        print name, ': ', parameter.AsString()
        print name, ': <NULL>'

There are some aspects of this script I'd like to point out, since they might help you writing your own scripts:

This gives you access to the System.Enum type. The BuiltInParameter enumeration has to be marshalled back and forth to IronPython integers using Enum:

  • Enum.ToObject(BuiltInParameter, id) returns the enum constant that has the integer value id
  • Enum.GetName(BuiltInParameter, id) returns the name of the enum constant (handy for printing to the user)

Also note that all the predefined parameters in Revit have negative ids - that is how we find them:

The output on a test project on my computer is:

PROJECT_STATUS :  Projekt  Status
CLIENT_NAME :  Bauherr
PROJECT_ADDRESS :  Projektadresse
PROJECT_NAME :  Projekt Name

Thursday, May 19, 2011

Adding both 32 and 64 bit versions of DLLs to a Visual Studio 2010 Setup project

I ran into a snag, when trying to create an installer using a Visual Studio 2010 Setup project that would install for 32 and 64 bit versions of Autodesk Revit Architecture. Since my program depends on a DLL (in this case System.Data.SQLite.dll) that ships in two different versions, I wanted to include both in the MSI and then copy the correct one to the installation folder with a CustomAction project. The snag consists of this pesky error message whenever you add a 64 bit dll to a Setup project that is targeting x86:

File 'System.Data.SQLite.dll' of project output 'Content Files from MYPROJECT (Active)' targeting 'AMD64' is not compatible with the project's target platform 'x86'

That was easy to fix! All I had to do was include the file with a different extension, right? So... I renamed System.Data.SQLite.dll to System.Data.SQLite.txt and added it to MYPROJECT as a new content file. Same error when building the setup project:

File 'System.Data.SQLite.txt' of project output 'Content Files from MYPROJECT (Active)' targeting 'AMD64' is not compatible with the project's target platform 'x86'

It seems, the setup project figures out that you have a DLL here, even if the extension doesn't is something totally different. I tried various extensions to no avail.

So, the next logical step would be to use the old email zip file trick, right? It is rather easy to zip a DLL. Any number of tools will do that for you. It turns out to be rather tricky to unzip a DLL in C# inside your CustomAction.

It is possible to use a 3rd party library for unzipping, but I didn't really want to add a dependency just for the installer. Instead, I opted to use the DEFLATE algorithm provided in .NET here: System.IO.Compression.DeflateStream.

Given a file compressed with this algorithm, the code to inflate it back to normal size is trivial:

using (var srcFile = File.OpenRead(@"C:\MYPATH\System.Data.SQLite.x64"))
    using (var dstFile = File.OpenWrite(@"C:\MYPATH\System.Data.SQLite.dll"))
        using (var deflator = new DeflateStream(srcFile, CompressionMode.Decompress))
            CopyStream(deflator, dstFile);

Just... how to get the original file into the deflated version? I couldn't find a tool that reliably did this for me, so I created my own:

To quote from the projects page:

This project provides two simple programs:

deflate.exe inflate.exe They are implented using the System.IO.Compression.DeflateStream provided in .NET.


C:\> type MYFILE | deflate.exe > MYFILE.deflated
C:\> type MYFILE.deflated | inflate.exe > MYFILE

I now have the honour to present the steps to create a DLL targeted at a different architecture as your setup:

  1. deflate the file using deflate.exe, naming it with a different extension (e.g. .x64)
  2. add it to your main project as a content file
  3. add a custom action project to your solution
  4. add the custom action to the setup projects "Install" custom actions
  5. inflate the file inside the custom actions Install method using System.IO.Compression.DeflateStream (see code above)
  6. do a little dance around your desk, down the hall, and past as many coworkers as you care to annoy :)