Through the Interface: Creating demand-loading entries automatically for your AutoCAD application using C#, F# or VB.NET

Kean Walmsley

May 2015

Sun Mon Tue Wed Thu Fri Sat
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30


« Using a jig from F# to create Spirograph patterns in AutoCAD | Main | Plugin of the Month featured in the March/April edition of AUGIWorld »

March 08, 2010

Creating demand-loading entries automatically for your AutoCAD application using C#, F# or VB.NET

This week I’m going to posting a few topics related to F#, as it feels as though I’ve been neglecting it, of late. And as this technology is going to hit the mainstream very soon – when Visual Studio 2010 ships – it seems all the more important to keep one’s F# skills honed.

We’re going to start the week with an F# equivalent to the code shown in this previous post, where we go through and reflect on the commands exposed by an assembly in order to create corresponding demand-loading Registry keys automatically.

We’ve shipped VB.NET and C# versions of this code as part of our Plugins of the Month, but I wanted to make sure the latest versions were posted here, all in one place:

I’ve made a few general changes to the previous approach:

  • More use of “using” to make sure that Registry keys get closed properly
    • Not that this caused any particular issues of which I’m aware, but it’s certainly cleaner
  • Use of CreateSubKey() to open the Applications key for write, or to create it if it doesn’t exist
    • This can apparently be a problem on some vertical versions of AutoCAD such as AutoCAD Electrical

Let’s take a look at the F# code, in particular:

module DemandLoading.RegistryUpdate


open System.Reflection

open System.Resources

open Microsoft.Win32

open Autodesk.AutoCAD.DatabaseServices

open Autodesk.AutoCAD.Runtime


// Get the information from a custom attribute, if of the right type


let commandInfo (rm : ResourceManager) (attb : obj) =

  let cma = attb :?> CommandMethodAttribute

  if cma <> null then


    // Harvest the information about each command


    match cma.LocalizedNameId with

    | null -> (cma.GlobalName, cma.GlobalName, cma.GroupName)

    | _ ->





      with _ ->

        (cma.GlobalName, cma.GlobalName, cma.GroupName)


    (null, null, null)


let commandsFromMethod rm (meth : MethodInfo) = (commandInfo rm)

    (meth.GetCustomAttributes (typeof<CommandMethodAttribute>, true))


let commandsFromType assem (t : System.Type) =

  let rm = new ResourceManager(t.FullName, assem)

  rm.IgnoreCase <- true (commandsFromMethod rm) (t.GetMethods()) |> Array.concat


let commandsFromModule assem (m : Module) = (commandsFromType assem) (m.GetTypes()) |> Array.concat


let commandsFromAssembly assem = (commandsFromModule assem) (assem.GetModules(true))

    |> Array.concat


let createDemandLoadingEntries name path currentUser cmds =


  // Choose the Registry hive according to the inputs


  let hive =

    if currentUser then





  // Check whether any valid commands exist (the command-names are

  // the first two entries in the tuple contained in the command

  // information array)


  let hasCmds =

    Array.exists (fun (a,b,c) -> a <> null && b <> null) cmds


  // And the same for groups, which is the third entry


  let hasGrps = Array.exists (fun (a,b,c) -> c <> null) cmds


  // Define whether to load the module on startup (if no commands)

  // or on command invocation (if any are defined)


  let flags = if hasCmds then 12 else 2


  // Open the main AutoCAD (or vertical) and "Applications" keys


  use ack =




  use appk = ack.CreateSubKey("Applications")


  // Already registered? Just return


  if not

    (Array.exists (fun x -> x = name) (appk.GetSubKeyNames())) then


    // Create the our application's root key and its values


    use rk = appk.CreateSubKey(name)

    rk.SetValue("DESCRIPTION", name, RegistryValueKind.String)

    rk.SetValue("LOADCTRLS", flags, RegistryValueKind.DWord)

    rk.SetValue("LOADER", path, RegistryValueKind.String)

    rk.SetValue("MANAGED", 1, RegistryValueKind.DWord)


    // Create a subkey if there are any commands...


    if hasCmds then

      use ck = rk.CreateSubKey("Commands")

      let createCommand (key : RegistryKey) info =

        match info with

        | (null, _, _) -> () // Ignore any null global commands

        | (_, null, _) -> () // Ignore any null local commands

        | (glob, loc, _) ->



      Array.iter (createCommand ck) cmds |> ignore


    // And the command groups, if there are any


    if hasGrps then

      use gk = rk.CreateSubKey("Groups")

      let createGroup (key : RegistryKey) info =

        match info with

        | (_, _, null) -> ()

        | (_, _, group) ->

          key.SetValue(group, group,RegistryValueKind.String)


      Array.iter (createGroup gk) cmds


let removeDemandLoadingEntries name currentUser =


  // Choose the Registry hive according to the input


  let hive =

    if currentUser then





  // Open the main AutoCAD (or vertical) and "Applications" keys


  use ack =



  use appk = ack.OpenSubKey("Applications", true)


  // Delete the key with the same name as this assembly




let RegisterForDemandLoading() =


  // Get the current assembly


  let assem = Assembly.GetExecutingAssembly()


  // Get the command information and create Registry

  // entries from it


  commandsFromAssembly assem

    |> createDemandLoadingEntries

        (assem.GetName().Name) assem.Location true


let UnregisterForDemandLoading() =


    (Assembly.GetExecutingAssembly().GetName().Name) true

I’ve tried to structure the F# code somewhat differently to the prior, more imperative versions of the code: it has more emphasis on the application of functions and the flow of data between them. It also makes use of higher order functions such as and Array.iter to apply functions to the contents of data structures (in this case arrays), and uses pattern-matching where sequences of if-then-else statements would have proven cumbersome.

Later in the week we’ll see this added to the Spirograph application, to create its demand-loading information automatically on startup, but first we’re going to need to see how to implement the IExtensionApplication interface from an F# application…

blog comments powered by Disqus


10 Random Posts