Wednesday, October 6, 2010

Obfuscating an Autodesk Revit Plugin

Jeremy Tammik mentions obfuscation for Revit plugins in his post Obfuscation Tools. I would like to mention two free obfusctators I have used:

Both of these can be hooked up to your build script so that they obfuscate automatically. Please read their documentation on how to do this.

I liked Eazfuscator a lot and used it for all dependant assemblies of my project, but sadly, it chokes on references to the assembly RevitAPI. So for developing Autodesk Revit Plugins, it will not work.

Babel .NET can obfuscate assemblies that reference RevitAPI.dll. I tied it into my build process in Visual Studio 2010 as a Post-build event command line:

  1. select your project in the Solution Explorer

  2. right-click and choose Properties from the context menu

  3. in the tab Build Events, enter the following in the field Post-build event command line:

    if /I "$(ConfigurationName)" NEQ "Debug" "C:\Program Files\Babel\Babel.exe" "$(TargetPath)" --stringencrypt --output "$(TargetPath)"
    

This assumes, of course, that you have installed Babel .NET to C:\Program Files\Babel. Also note, that the build configuration "Debug" is excluded from obfuscation.

Thursday, August 19, 2010

Deleting a bunch of Elements By ElementId in RevitPythonShell

I know I promised examples for RevitPythonShell and never delivered. Well, here is an example I came across this morning:

I had to delete a bunch of elements from a document. I new the ids of the elements and was about to manually delete them using the "Select by ID" tool in Revit and deleting them one by one. I know this works, because I did the exact same thing yesterday on the exact same document, but forgot to save the changes. I'd just have to do all that tedious work again.

But then I thought: Wait a minute... aren't we supposed to be scripting this stuff?

So, here is a session in the shell part of RevitPythonShell

>>>ids = [136116, 136119, 136120, 136121, 136122, 136131, 136132, 136133, 136134, 136135, 136136, 136137, 136138, 136139, 136140, 136141, 136142, 136143, 136144, 136145, 136155, 136165, 136206, 136306, 136309, 136419, 136422]

>>>import clr

>>>clr.AddReference("RevitAPI")

>>>from Autodesk.Revit.DB import *

>>>transaction = Transaction(doc, "find all the mutants!")

>>>transaction.Start()

>>>for id in ids:
...    doc.Delete(doc.get_Element(ElementId(id)))
...
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x000000000000002D [136116]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x000000000000002E [136119]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x000000000000002F [136120]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000030 [136121]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000031 [136122]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000032 [136131]>, <Autodesk.Revit.DB.ElementId object at 0x0000000000000033 [170627]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000034 [136132]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000035 [136133]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000036 [136134]>, <Autodesk.Revit.DB.ElementId object at 0x0000000000000037 [170628]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000038 [136135]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000039 [136136]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x000000000000003A [136137]>, <Autodesk.Revit.DB.ElementId object at 0x000000000000003B [170629]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x000000000000003C [136138]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x000000000000003D [136139]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x000000000000003E [136140]>, <Autodesk.Revit.DB.ElementId object at 0x000000000000003F [170630]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000040 [136141]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000041 [136142]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000042 [136143]>, <Autodesk.Revit.DB.ElementId object at 0x0000000000000043 [170631]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000044 [136144]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000045 [136145]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000046 [136155]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000047 [136165]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000048 [136206]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x0000000000000049 [136306]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x000000000000004A [136309]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x000000000000004B [136419]>])
List[ElementId]([<Autodesk.Revit.DB.ElementId object at 0x000000000000004C [136422]>])
>>>transaction.Commit()

<Autodesk.Revit.DB.TransactionStatus object at 0x000000000000004D [Committed]>
>>>

Here a few notes that might illuminate some parts of working with RevitPythonShell:

  • access to the Revit API is added by the following lines
import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
  • the document is provided via the __revit__ variable
doc = __revit__.ActiveUIDocument.Document
  • you need to start a transaction before changing the document
transaction = Transaction(doc, "call this whatever you like")
transaction.Start()
# do stuff
transaction.Commit()
  • to get an element given an integer, use this technique
element = doc.get_Element(ElementId(myinteger))
  • Document.Delete() returns a list of deleted elements on each call. That is what the clutter (List[ElementId]([Autodesk.Revit.DB.ElementID object at...) is all about in the session transcript above.

Thursday, July 15, 2010

New release of RevitPythonShell for x64 and Revit 2011 only

I upgraded my development machine a couple of weeks ago to Windows 7 64-bit. At the same time I migrated all my Revit Addin projects to Autodesk Revit 2011. This includes RevitPythonShell. I finally got round to building a new installer using the RevitAddInUtility. This was as good a project as any to try out building x64 applications and installers for Revit 2011.

Most of the port was done with the help of code I recieved from Jason Schaeffer (joespiff) via the RevitPythonShell google group

I ran into some snares:

  • Visual Studio 2010 has some restrictions with x64 managed applications
    • The Designer will not show your forms unless you set the build platform to "Any CPU" and rebuild.
    • The Setup projects bundle the wrong "InstallUtilLib.dll" shim when building x64 .msi packages
  • Windows 7 doesn't like it when programs modify stuff in the "Program Files" folder, so I moved the RevitPythonShell.xml file to the AppData folder.

Wednesday, June 9, 2010

My .vimrc

This is another post for my future self: At the time of writing, my .vimrc looks like this:

" --------------------------------------------------------------------------------
" call system vimrc
" --------------------------------------------------------------------------------
source $VIM/_vimrc

" --------------------------------------------------------------------------------
" startup
" --------------------------------------------------------------------------------
autocmd GUIEnter * simalt ~X    " maximize window

" --------------------------------------------------------------------------------
" opening files
" --------------------------------------------------------------------------------

" change working directory to path of file
autocmd BufEnter * lcd %:p:h    

" reformat xml files (pretty print)
autocmd FileType xml exe ":silent 1,$!xmllint --format --encode UTF-8 --recover - 2>/dev/null"

" --------------------------------------------------------------------------------
" saving files
" --------------------------------------------------------------------------------
set backupdir=./_Backup,$TEMP,.

" --------------------------------------------------------------------------------
" searching
" --------------------------------------------------------------------------------
set hlsearch        " highlight matches to search pattern
set incsearch        " incremental search
set smartcase       " case sensitive if search string contains uppercase letters
set ignorecase        " case insensitive searching

" --------------------------------------------------------------------------------
" configure editor with tabs and nice stuff...
" --------------------------------------------------------------------------------
set expandtab           " enter spaces when tab is pressed
set textwidth=0         " do not break lines when line length increases
set tabstop=4            " use 4 spaces to represent tab
set softtabstop=4
set shiftwidth=4        " number of spaces to use for auto indent
set autoindent          " copy indent from current line when starting a new line

" make backspaces more powerfull
set backspace=indent,eol,start 

set ruler               " show line and column number


" --------------------------------------------------------------------------------
" encoding
" --------------------------------------------------------------------------------
set encoding=utf-8
"set printencoding=utf-8

" --------------------------------------------------------------------------------
" keyboard mappings
" --------------------------------------------------------------------------------
set langmap=¨]


Future Daren, you can find the location of the .vimrc by editing the file $MYVIMRC in vim itself! Unless it hasn't been created yet ($MYVIMRC will then point to the system-wide version). In Windows, just plop a _vimrc file in your %userprofile%.

Wednesday, May 12, 2010

Google Charts: AnnotatedTimeline

Note to future self: The Google Charts visualization "AnnotatedTimeline" displays inside a div element. As per documentation, you need to provide height and width of the div element. If you don't (or specify a size too small), the script goes into an infinite loop - I guess it is trying to scale to fit inside the div, but I didn't feel like debugging squashed code...

Wednesday, April 28, 2010

CakePHP and Umlauts (utf-8)

I spent the last couple of weeks writing PHP. Not my favorite language, but it does get the job done. I won't bitch about PHP here, since that would be snobbery.

What eases the pain a lot is the CakePHP framework. Except, I couldn't get Umlauts to work. Umlauts are special characters, like a, o and u, but with two dots on them: ä, ö, ü.

I found the solution on this page: http://www.missingfeatures.com/2008/10/23/using-utf8-in-your-cakephp-app/

Not only must you tell your browser that you are using UTF-8, but you must also tell the database connection to use utf-8. Check the database.php file in your app/config directory and add 'encoding' => 'utf8' to the end of the database configuration.

Wednesday, April 14, 2010

Wiki syntax for UML diagrams

Yay! I have just implemented my very first Trac Hack: YumlPreprocessorMacro for integrating yuml.me diagrams into the trac wiki.

I have come to depend on using Trac as a bug tracking and documentation tool. I really like using wikis for documentation. I like being able to create links quickly. I like being able to search the documentation in a webby way. I absolutely hate using MS Word for technical documentation.

Often, you want to add little UML sketches to your docs. The workflow normally goes like this:

  1. Open your UML editor of choice
  2. Create UML diagram
  3. Export as image
  4. Include in documentation
Notice how that can be a little disruptive for just a sketch? Most UML tools want you to go BDUF. They want you to create a whole model of everything, when all you really want to do is a little use case. I like the Violet UML Editor precisely because it lets me sketch. But yuml.me really changes the whole game!