Getting the list of .NET assemblies loaded into AutoCAD from LISP

A big thank you to Jim Cameron from Dematic for providing the code for this post, and to Wayne Brill from DevTech Americas who helped him on his way via ADN support. I've made a few changes of my own to the code, but the concept is very much Jim's.

Jim recently had a frustrating problem with a VLX application he wrote that depends – via (vl-arx-import) – on a .NET module. It turns out the module was not being demand-loaded properly, but this took some time to diagnose. In the past Jim used (arx) from LISP to detect the loaded ObjectARX modules, but now that he's using .NET this is no longer possible.

Here's some C# code that can be used from LISP (once it's been NETLOADed, of course) to get a list of the .NET assemblies currently loaded into AutoCAD's AppDomain, helping with the decision on what to go ahead and import. The returned list contains dotted pairs of the module names along with their versions, which should also be handy when troubleshooting version-related problems.

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.DatabaseServices;

using System.Reflection;

using System;

 

namespace LoadedAssemblies

{

  public class Commands

  {

    [LispFunction("GetAssemblies")]

    public ResultBuffer GetLoadedAssemblies(ResultBuffer rb)

    {

      // Get the list of loaded assemblies

 

      Assembly[] assems = AppDomain.CurrentDomain.GetAssemblies();

 

      // A ResultBuffer to populate and return

 

      ResultBuffer res = new ResultBuffer();

 

      // List the assemblies in the current application domain.

 

      const string start = "Version=";

 

      foreach (Assembly assem in assems)

      {

        // We want the name and version of each assembly

 

        string dllname = assem.ManifestModule.Name;

 

        string version = assem.FullName;

 

        // If our assembly name includes version info...

 

        if (version.Contains(start))

        {

          // Get the string starting with the version number

 

          version =

            version.Substring(

              version.IndexOf(start) + start.Length

            );

 

          // Strip off anything after (and including) the comma

 

          version =

            version.Remove(version.IndexOf(','));

        }

        else

          version = "";

 

        // Add a dotted pair of the name with the version

 

        res.Add(new TypedValue((int)LispDataType.ListBegin));

        res.Add(new TypedValue((int)LispDataType.Text, dllname));

        res.Add(new TypedValue((int)LispDataType.Text, version));

        res.Add(new TypedValue((int)LispDataType.DottedPair));

      }

      return res;

    }

  }

}

Let's see what happens when we run this from the command-line (with the output reformatted to show the list contents better):

Command: (getassemblies)

(("mscorlib.dll" . "2.0.0.0")

("AcdbMgd.dll" . "18.0.0.0")

("msvcm90.dll" . "9.0.30729.4148")

("System.dll" . "2.0.0.0")

("System.Xml.dll" . "2.0.0.0")

("System.Drawing.dll" . "2.0.0.0")

("PresentationFramework.dll" . "3.0.0.0")

("WindowsBase.dll" . "3.0.0.0")

("PresentationCore.dll" . "3.0.0.0")

("AdWindowsInterop.dll" . "0.0.0.0")

("AdWindows.dll" . "2.1.0.0")

("acmgd.dll" . "18.0.0.0")

("System.Configuration.dll" . "2.0.0.0")

("AcWindows.dll" . "18.0.0.0")

("System.Core.dll" . "3.5.0.0")

("AcWindows.resources.dll" . "18.0.0.0")

("AcCui.dll" . "18.0.0.0")

("AliasUnderlayRibbionUI.dll&quo
t; . "1.0.0.0")

("ADNPlugin-OffsetInXref.dll" . "1.0.1.0")

("MyApplication.dll" . "1.0.3695.27974")

("<Unknown>" . "0.0.0.0")

("AliasRibbonUI.dll" . "18.0.55.1")

("PresentationFramework.Aero.dll" . "3.0.0.0")

("ManagedMC3.dll" . "2.20.0.0")

("System.Management.dll" . "2.0.0.0")

("AcLayer.dll" . "18.0.0.0")

("System.Windows.Forms.dll" . "2.0.0.0")

("AcLayer.resources.dll" . "18.0.0.0")

("System.Web.Services.dll" . "2.0.0.0")

("WindowsFormsIntegration.dll" . "3.0.0.0")

("<Unknown>" . "2.1.0.0"))

Thanks again, Jim, for this very useful little application. 🙂

  1. Hello Kean,
    for illumination,
    do you know what the ("<unknown>" .... entrys entrys represent ??

    Regards
    Kerry

  2. oops, post didn't like the lesser/morethan tags 🙂
    (" Unknown "

  3. I did wonder about that (but clearly not enough to actually investigate :-).

    I'll take a look tomorrow to see if I can work it out.

    Kean

  4. I added this after the dllname declaration/assignment to provide more information:

    if (dllname == "<unknown>")
    dllname = assem.ManifestModule.ScopeName;

    This uses a property that does contain something even when the Name property is "Unknown" (and presumably indicates some kind of logical assembly when there perhaps isn't a physical one, I can only guess).

    The first one that came up was "ContextualTabSelectorRules.dll", and appears to be related in some way to AcWindows.dll.

    The second was stranger: it has a randomly generated name (the 3 times I tried it it came up as 5hd_wdai.dll, k8gjwxlk.dll and xng6wql.dll) and appears to be related to System.dll, and so is a core framework assembly.

    Cheers,

    Kean

  5. Hi Kean,

    Is there any way to loop thorugh the points of a closed polyline ? For example if I have a regtangle and use the editor.GetEntity to prompt the use to select it..Can I somehow get the 4 points on each end (to show them in a table) ? Are there way to iterate through them ?
    Anything will help. Thanks very much.

    Cheers,
    Catalin

  6. Hi Catalin,

    Your question isn't related to this post: I really don't have time to provide personal support, so please post your future questions via ADN or via the appropriate online discussion group.

    Just this once, this post should be of help.

    Regards,

    Kean

  7. Thank you so much. Won't happen again. I'm a big fan of your post. Have a pleasant day.

  8. The 'UNKNOWN' entries are dynamic assemblies that are generated on the fly by the runtime. They usually house dynamically-generated types.

    You can also generate types dynamically and compile them into a dynamic assembly and load it on the fly.

    An example of when the runtime does that, is to house dynamically-generated XML serialization classes for your serializable types.

  9. Johnny Tarmeño Avatar
    Johnny Tarmeño

    Buenas tardes,

    Como puedo cargar desde una lista enviada por codigo LISP a un Listbox??

    example: (LoadListBox '("Item1" "Item2" "Item3"))

    Gracias.

  10. Sorry - this is not a forum for support. Please
    post your question to one of our discussion groups.

    Kean

  11. Herman Mayfarth Avatar

    Thanks for this.
    I wrote a small LISP command to pretty-print the association list.

    I'll post it at theswamp.org
    with reference to your C# source here,
    unless you object.

    Thanks again,

  12. Please go ahead!

    Kean

  13. Michael J. Smith Avatar

    Thanks for the very useful info!!!!

  14. If you define a LISP function in your assembly, you can check if the assembly is loaded as long as you use a unique LISP function name.

    <lispfunction("meloaded")> Public Function MeLoaded(ByVal rbArgs As ResultBuffer) As Object
    Return New ResultBuffer(New TypedValue(LispDataType.T_atom))
    End Function

    (MeLoaded) ;; returns T if assembly is loaded, error otherwise
    (= 'EXRXSUBR (type MeLoaded)) ;; returns T if assembly is loaded, nil otherwise

Leave a Reply

Your email address will not be published. Required fields are marked *