January 13, 2012

Using Log4Net with Visual Lisp

Filed under: AutoCAD,Development — Tags: , , , — Darrin Maidlow @ 1:07 pm

If you’ve worked with me or talked technical with me in the past there is a good chance you area already very aware that I love .  There is also a really good chance that you know I still have a special place in my heart for .  Not only did I spend what may have been the “best years of my life” buried in VLIDE (or hey, maybe all those 30+ hour days and passing out under my desk were the best years of my life? =)) , I still firmly believe that Lisp is one of the most effective way to bang out even a relatively complex operation in AutoCAD when it comes to data manipulation.  ObjectARX (both original and .NET) is great – but the time and effort overhead is pretty high when you just need to bang out a quick routine.

<3 Visual Lisp

Even the quick routines need a little error handling and logging can make a huge difference in documenting the results or diagnosing problems.  Yes, Visual Lisp comes with file I/O calls and I’m sure most of you reading this have rolled your own logging code.  We had some at Kanotech that is still in use for DraftLogic.  Not for long though :=)  Today I’m here to show you how you could bring your Visual Lisp logging into the 21st century.  Not only will it give you – it will let even complex systems that mix technologies (ie. Visual Lisp, ObjectARX .NET) to bring the logging together into a common set of log files.

Overview

There are two sides to this.  First, in .NET we setup Log4Net and expose it to Visual Lisp.  Over on the Lisp side, we’ll load the vlx, setup an error handler, and issue some logging calls.  As usual I’ve attached a Visual Studio solution with sample code.  I’ve also included a compiled dll that you can just go ahead and use.  The attached project is built using .NET 3.5 as this machine only has AutoCAD 2010 installed.  Log4Net itself is built targeting .NET 1.0 so you can modify the .NET runtimes and ObjectARX references used in the attached project and use this code for pretty much any .NET enabled version of AutoCAD/ObjectARX.

In .NET – Setup Log4Net and Lisp Bindings

The Lisp bindings are pretty straight forward.  Expose your call(s) to AutoCAD as a LispFunction.  I’ve built two exposed calls.  One takes two parameters, and uses the value of the first parameter to determine the log type.  The second parameter is the message to log.  The second call is a LogError call that could be used to just log error type messages.  This call could be duplicated for each type of logging state if desired.

 
   1: /// <summary>
   2: /// Lisp exported Log call.  Expects two parameters in lisp.  First the log type which may be either INFO, WARNING, DEBUG, or ERROR.  The second is a string containing the error / message.
   3: /// </summary>
   4: /// <param name="lispArgs"></param>
   5: /// <returns></returns>
   6: /// <remarks>Lisp syntax (Loggit </remarks>
   7: [LispFunction("Loggit")]
   8: public TypedValue Log(ResultBuffer lispArgs)
   9: {
  10:     //Check that we have the correct number of parameters
  11:     Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
  12:     if (lispArgs == null)
  13:     {
  14:         ed.WriteMessage("\nLoggit - Incorrect number of arguments.  Expected 2 parameters, received none\n");
  15:         return new TypedValue((int)LispDataType.Nil);
  16:     }
  17:     if (lispArgs.AsArray().Length != 2)
  18:     {
  19:         ed.WriteMessage("\nLoggit - Incorrect number of arguments.  Exepected 2 parameters, received " + Convert.ToString(lispArgs.AsArray().Length));
  20:         return new TypedValue((int)LispDataType.Nil);
  21:     }
  22:     
  23:     //params are good.  We expect two strings so rather than test type just convert to string
  24:     String logType = Convert.ToString(lispArgs.AsArray()[0].Value).ToUpper();
  25:     String logMessage = Convert.ToString(lispArgs.AsArray()[1].Value);
  26:  
  27:     switch(logType)
  28:     {
  29:         case "INFO":
  30:             Logger.Current.Info(logMessage);
  31:             break;
  32:         case "WARNING":
  33:         Logger.Current.Warning(logMessage);
  34:             break;
  35:         case "DEBUG":
  36:         Logger.Current.Debug(logMessage);
  37:             break;
  38:         case "ERROR":
  39:             Logger.Current.Error(logMessage);
  40:             break;
  41:     }
  42:     return new TypedValue((int)LispDataType.T_atom);
  43: }
  44:  
  45: //Alternately you could expose individual calls like LogError here.
  46:  
  47: /// <summary>
  48: /// Exposed Lisp function that logs an error directly. Expects a single lisp parameter of type string which is the message to log.
  49: /// </summary>
  50: /// <param name="lispArgs"></param>
  51: /// <returns></returns>
  52: [LispFunction("LogError")]
  53: public TypedValue LogError(ResultBuffer lispArgs)
  54: {
  55:     //Check that we have the correct number of parameters
  56:     Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
  57:     if (lispArgs == null)
  58:     {
  59:         ed.WriteMessage("\nLoggit - Incorrect number of arguments.  Expected 2 parameters, received none\n");
  60:         return new TypedValue((int)LispDataType.Nil);
  61:     }
  62:  
  63:     String logMessage = Convert.ToString(lispArgs.AsArray()[0].Value);
  64:     Logger.Current.Error(logMessage);
  65:  
  66:     return new TypedValue((int)LispDataType.T_atom);
  67: }

 

One awesome little bit of magic exists in the Logger.cs class.  When configuring the logger it loads the assemblies in question using System.Reflection.  Then it checks in the folder of each loaded dll for the log4net.config file.  This means that logging will only actually log if you have a log4net.config file exists in the same folder as the loaded ObjectARX.NET assembly.  With AutoCAD based extensions this is especially important.  I’ve never liked placing my assemblies (vlx, arx, dll) in the AutoCAD folders.  I’ve always been a huge proponent of extending the AutoCAD search path if needed and using my own directory structures.  This configuration searching technique also eliminates the need for hard coded “here is my log config file” stored in the registry or some other config location. 

Compile that dll and you should be able to run with that in Visual Lisp.

Configure the Logging

As mentioned previously we need to create the configuration file to place with our dll.  There are a couple of lines of code that are quite relevant to get the logging working.  First we have a hard coded configuration file name defined in Logger.cs.  You can customize this if you want.

   1: /// <summary>
   2: /// Name of the file name to search for when trying to configure logging.
   3: /// </summary>
   4: public const string ConfigFileName = "log4net.config";

Next is the name of the logger.  This is quite relevant when configuring your logging.  If this value does not match the value in the config file you will be very frustrated trying to get the logging to fire! If desired change the VisualLisp value to whatever you would like.

   1: /// <summary>
   2: /// This logger name is referenced in the log4net configuration
   3: /// </summary>
   4: private ILog _logImplementation = LogManager.GetLogger("VisualLisp");

Next we have a little bit of XML to slough through.

   1: <?xml version="1.0" encoding="UTF-8"?>
   2: <log4net xmlns="urn:log">
   3:   <appender name="GeneralLog" type="log4net.Appender.FileAppender">
   4:     <file value="VisualLisp.log" />
   5:     <appendToFile value="true" />
   6:     <layout type="log4net.Layout.PatternLayout">
   7:         <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
   8:     </layout>
   9: </appender>
  10:   <logger name="VisualLisp">
  11:     <level value="ALL" />
  12:     <appender-ref ref="GeneralLog" />
  13:   </logger>
  14: </log4net>

This is where things can get crazy!  First the appender section.  This is where you define all the different settings for the actual logging.  I’m running with a standard log file appender, but there are so many choices.  Log to file, log to rolling file, log to database, log to e-mail, log to eventlog, log to almost whatever you want.  You can even write your own custom appenders to log to your toaster oven if you really wanted to.  This is one of the many reasons I <3 Log4Net soooo much.   The basics here are straight forward.  Set the file value=”” to the name and optionally path (either full or relative).  One important note., if you do not specify a full path or you use a relative path the logfile the base point used will be the AutoCAD program files folder.   If you are not running with administrative rights the Vista/Windows 7 file system virtualization will kick in and the log file location will be quite confusing.  It’s probably a good idea to specify a full path here.  More details on some Log4Net configurations can be found on the .

Next take a look at the logger element.  You can define multiple loggers, and each can log to multiple appenders.  Or a single logger can log to multiple appenders.   Note the important value of “Visual Lisp”.  This must match the GetLogger value defined in the private ILog above.  You can also specify which “level” of notice to log.  Though our code may log INFO, WARNING, DEBUG and ERROR calls – if your level is set to ERROR – only the actual error level messages will be logged.

In Visual Lisp – Setup an Error Handler and Issue Logging

So now we have an ObjectARX.NET assembly to load.  We have a handle on configuring our logging.  Here is the contents of the included Lisp file.

   1: ;only load the assembly if it has not been loaded once
   2: (if (not (car (atoms-family 1 '("loggit"))))
   3:     (command "netload" "C:/Users/dmaidlow/Documents/Visual Studio 2010/Projects/Log4Net.VisualLisp/bin/debug/Log4Net.VisualLisp.dll")
   4: )
   5:  
   6: (defun new_ErrorHandler (err)
   7:     (if (or (= (substr err 1 4) "quit")
   8:             (= err "Function cancelled")
   9:         )
  10:         (progn
  11:             ;put any function cancelled logic here
  12:             (princ)
  13:         )
  14:         (progn
  15:             ;an actual error happened here..
  16:             ;you could spew some stuff out to the console, or issue an alert..
  17:             ;(prompt (strcat "\nLisp Returned the following error: \"" st "\"\n"))
  18:             ;(alert (strcat "*ERROR*\nLisp returned the following error: \n\t" st))
  19:  
  20:             ;but we're here to log!
  21:             (Loggit "ERROR" (strcat "An error occurred\n" err))
  22:             
  23:         )
  24:     )
  25:     (gc)
  26:     (princ)
  27: )
  28:  
  29: (setq *error* new_ErrorHandler)
  30: (LogError "Oh No an Error!")
  31: (Loggit "INFO" "This is an informational message")

Not much to it.  To prevent some command line spewage if you load the assembly twice we check first to see if the function has already been exported to the environment.  Here we’ve setup a new error handler that differentiates between a function cancellation and an actual error.  At the bottom you can see a couple of examples of the logging.  If you’ve defined your log4net.config file in the same folder as the assembly if you load this lisp file you will see a log file get created.

Summary

So in review – we’ve exposed Log4Net to the Visual Lisp and command line environment inside of AutoCAD.  This will allow you to unify your logging (if you are already using Log4Net) and may even make you a Log4Net enthusiast as well.  As always I’m always interested in your feedback and constructive criticism!

November 8, 2011

OracleCommand and parameterized update statements

Filed under: Development,Oracle,RADE — Tags: , , , , — Darrin Maidlow @ 4:07 pm

This week I encountered an irritating situation with Oracle while working on code in the application logic.  In a nutshell I am building dynamically created parameterized insert and update statements based on the RADE metadata and the values entered by the user.  The first call InsertNewRecord works perfectly.  The next call UpdateExistingRecord however was not.  The parameterized SQL was being created.  The parameters were being created and assigned to the .  The ExecuteNonQuery() call was executing without returning an error.  Oracle just would not update.   Even more frustrating – this “just worked” in SQL server.

What were the differences?

The basic logic for insert was this (parts omitted because you probably just don’t care):

  1. Get table metadata
  2. Loop through fields in table
  3. for each field retrieve the value from the UI
  4. Add field to parameterized SQL statement with placeholder
  5. Create new parameter with appropriate name and value.   Add parameter to collection
  6. Loop through parameters in the collection and add to the DbCommand
  7. Finally execute the parameterized SQL statement

As I mentioned this worked great.  Fields were inserted and there was much rejoicing.

The logic for an update was similar but there was one big difference:

  1. Get Table
  2. Loop through fields in table
  3. for each field retrieve the value from the UI
  4. If the field is a key add the placeholder to the where condition, otherwise add the field name and value to the update fields part of the SQL
  5. Create new parameter with appropriate name and value.  Add parameter to collection
  6. Loop through parameters in the collection and add to the DbCommand
  7. Finally execute the parameterized SQL statement.

The branch in step 4 and the if statement ended up causing the problem.

The Problem

defaults to “bind by order” – making the order in which the parameters exist in the SQL statement match the order in which the parameters are added to the OracleCommand. This was happening during the insert because of the structure of an insert statement being so linear. However in the update statement I was building the SQL in a more dynamic way. I was maintaining a list field=value conditions and a separate where condition. In the ended up merging them :

   1: String parameterizedSQL = "UPDATE " + table.Name + " SET " + updateStatement + " WHERE " + whereStatement;

So unless my key field( s) all lined up at the end of the table metadata definitions,  appending that where condition at the end my parameter order got all out of whack in the DbCommand.  So my where condition was actually being set to the wrong value – which could have resulted in the wrong records being updated. Nasty.   Fortunately this can be resolved.

The Fix – BindByName=true

To correct this I had to set the Oracle specific BindByName property to true.  (btw this being the default behavior is just silly.  All the other big data providers default to bind by name and Oracle should too.  That’s a rant for another day though.)   My initial solution was to check if the command is an OracleCommand and if found do a little casting to set the BindByName property then recast it back to DbCommand before executing the query.  Constructive feedback is always welcome!

   1: /// <summary>
   2: /// Execute the parameterized query
   3: /// </summary>
   4: /// <param name="conn">open and active DbConnection</param>
   5: /// <param name="trans">Active DbTransaction</param>
   6: /// <param name="parameterizedSQL">the parameterized SQL</param>
   7: /// <param name="paramList">List of OledDbParameter</param>
   8: /// <returns>DataTable containing the results</returns>
   9: public static void RunParameterizedInsertUpdate(DbConnection conn, DbTransaction trans, String parameterizedSQL, List<DbParameter> paramList)
  10: {
  11:     //create the db command and set the parameterized SQL as a property
  12:     DbCommand command = conn.CreateCommand();
  13:     if(trans != null)
  14:     {
  15:         command.Transaction = trans;
  16:     }
  17:     //hack attack!  By default, Oracle requires its parameters to be placed into the command
  18:     //in the order the parameters appear in the parameterized SQL.  Little hackery here
  19:     //to set the Oracle Command to bind by name
  20:     if (command is Oracle.DataAccess.Client.OracleCommand)
  21:     {
  22:         OracleCommand oraCmd = (OracleCommand) command;
  23:         oraCmd.BindByName = true;
  24:         command = oraCmd;
  25:     }
  26:     command.CommandText = parameterizedSQL;
  27:     command.CommandType = CommandType.Text;
  28:  
  29:     //loop through the params and add them to the command
  30:     foreach (DbParameter parameter in paramList)
  31:     {
  32:         command.Parameters.Add(parameter);
  33:     }
  34:     try
  35:     {
  36:         command.Prepare();
  37:         command.ExecuteNonQuery();
  38:     }
  39:     catch (Exception ex)
  40:     {
  41:         command.Dispose();
  42:         throw;
  43:     }
  44: }

November 4, 2011

Introducing Question of the Week for ObjectARX.NET and Mapguide

Filed under: AutoCAD,Development,Mapguide — Tags: , , , — Darrin Maidlow @ 11:32 pm

Every good developer is constantly evolving and learning new technologies.  This can be a challenge when you’re hard at work in maintenance mode on familiar technology or when your every day needs don’t go as deeply into topics as you would like. 

If you take a step back and look at AutoCAD and – its a huge body of work and it is a challenge to get experience in all the various parts of it.  The recent request I received on how to or the command line and the subsequent research, code and blog post I wrote made me think,  what if I solicited small problems from the AutoCAD community that could make fun little 4 hour research projects.  These would allow me to dig into various portions of ObjectARX.NET once a week, write some fun code, and give me things to write about.  Everybody wins!

I’m looking to spend about 4-6 hours on each question.  I’m hoping to do one question per week, assuming I get enough requests.  I’ll provide the full source and project with each post.  All code will be written in c#.  Some examples of valid requests would be:

  • Submit a Visual Lisp routine to convert to ObjectARX.NET.
  • Request a particular routine that does some sort of drawing clean up or modifies something.
  • Identify some functionality that is missing in AutoCAD.
  • Request a piece of sample code explaining how to use API x of ObjectARX.NET or Mapguide Enterprise/Open Source.

The Do’s

  • Please keep your request realistic.   I’ve only got 4 to 6 hours and you’re not paying me =)
  • Please submit specific functionality along with sample data.
  • Please be available to answer any questions  I might have via e-mail, phone/skype or possibly gotomeeting.
  • Please keep the underlying technology somewhat current.  Don’ t request AutoCAD/Mapguide 2007.
  • Please review my code and comment if you see something wrong or that I could do more efficiently.  This is about me learning and I’m gonna make mistakes.

The Don’ts

  • Don’t expect days worth of work, I just don’t have that much spare time.
  • Don’t ask me to work on your project (unless of course you want to hire my team via ).
  • Please don’t get upset if I don’t pick your question.
  • I did my ten odd years in the trenches writing Visual Lisp – I don’t really want to write any more, so please don’t ask for lisp code.
  • Don’t be a ass.  I don’t have time for asses.

Let’s See What Happens

So I have no idea how this will play out.  I don’t know if 4-6 hours is a reasonable amount of time to allocate each week to do this right.   I don’t know if I will be able to make the time every week.  Maybe I’ll change it from “Question of the Week” to “Question of the Every Second Week”.  I’m going to put this out there and see what happens.   I reserve the right to change anything and everything! s  E-mail your requests to contact ~@~ geospecialling dot com.

November 2, 2011

Remotesoft Protector Runtime Error – yet to handle multiple .NET framework runtime

Filed under: Development — Tags: , , — Darrin Maidlow @ 9:07 pm

Remotesoft protector is one of the out there.  In fact, its pretty much the only one that really works.  Since I moved to .NET 4.0 I’ve been getting an annoying error when attempting to protect my binaries using the 4.0 suite of tools (with the ).  Everything still runs – it just pops up nasty alert boxes on load which is no good :)

Remotesoft Protector Runtime Error – yet to handle multiple .NET framework runtime

One other symptom of this was that .exe files would just blow chunks.  I’ve finally figured out what was causing this!  Most of our products are protected during the nightly builds.  This error however is specific to assemblies protected using the the Remotesoft .NET Explorer UI.  When using this application for the protect or obfuscate functionality it is really just a front end around the protector.exe/obfuscator.exe.  The UI is building the following command line string and tonight it finally dawned on me:Remotesoft .NET Explorer

Command: C:\Program Files (x86)\Remotesoft\Protector\bin\protector.exe -neutral -resource -string -cctor -clrversion v2.0.50727 "C:\temp\SmartInk for Kahua\SmartInk.UI.exe"

The UI was forcing the clr version to .NET 2.0 – when my assemblies are all built against .NET 4.0.  Oops…

After a bit of digging I found that you can set the CLR version in .NET Explorer from the Action / CLR Version menu item.  Unfortunately it has not been updated to support .NET 4.0 – and so has been rendered pretty much useless as a front end for protector.

The Fix

The only solution is to use the command line to execute your protection.   The updated command line ended up looking pretty similar:

"C:\Program Files (x86)\Remotesoft\Protector\bin\protector.exe" -neutral -resource -string  -clrversion v4.0.30319 "C:\temp\SmartInk for Kahua\SmartInk.UI.exe"

There you have it – I can’t believe I missed that…

October 28, 2011

Control Active AutoCAD Ribbon Tab from Visual Lisp – RibbonNinja!

Filed under: AutoCAD,Development — Tags: , , , — Darrin Maidlow @ 5:18 pm

A friend asked me recently if there was some way to control the active ribbon tab from a macro.  After a little bit of digging I could not find any lisp or command line command that would do this.  ObjectARX however does provide the functionality I needed so I decided to whip something together in c#.  I’ve named it RibbonNinja – because lets face it, ninjas are awesome and should play a much larger part of everyday life.

How’s it work?

Attached is a zip file that contains the dll.  To use this simply netload the dll into your AutoCAD session and issue the all powerful RibbonNinja lisp call.  It takes a single string parameter that is the name of the tab you want to activate.  If it finds and activates the tab it will return T, if not it returns nil.  For example given the following AutoCAD ribbon:

mapribbon

Issuing the following lisp statement will change the active tab from “Map Setup” to “Analyze”.  Then it would switch to “Home"

   1: (RibbonNinja “Analyze”)
   2: (RibbonNinja “Home”)

This code is not case sensitive.  If its even possible to have multiple menus within AutoCAD with the same spelling but in different cases you’re probably S.O.L. and it will always choose the last one.  I’ve assumed unique tab names.

This dll has been compiled to work against AutoCAD 2012.  It will support both x86 and x64 versions.  It should also work on any AutoCAD vertical (i.e AutoCAD Map, Civil, ADT etc etc).  If you get the following  error when you try to netload the dll we have one last change to make:

Cannot load assembly. Error details: System.IO.FileLoadException: Could not load file or assembly ‘file:///C:\Program Files\Autodesk\AutoCAD 2012 – English\RibbonNinja.dll’ or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0×80131515) File name: ‘file:/// C:\Program Files\Autodesk\AutoCAD 2012 – English\RibbonNinjadll ‘ —> System.NotSupportedException: An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch.

Please read and try the following posts.  .NET 4.0 is “protecting” us so we need to fix that.  First you can try and “unblock” the dll following the steps outlined on .  If that doesn’t work for you again shut down AutoCAD and edit your acad.exe.config file as explained by Kean Walmsley on .  Remember to run notepad or whatever editor you’re using as an administrator – or you will get permission errors when you try to save the config file.

Turn up the nerd

For those of you more technically inclined the source is pretty simple once you figure out the mess of  references that are needed:

   1: [LispFunction("RibbonNinja")]
   2: public TypedValue GoNinjaGoNinjaGo(ResultBuffer lispArgs)
   3: {
   4:     //check the params
   5:     Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
   6:     if (lispArgs == null)
   7:     {
   8:         ed.WriteMessage("\nIncorrect number of arguments.  Expected 1 parameter, received none\n");
   9:         return new TypedValue((int)LispDataType.Nil);
  10:     }
  11:  
  12:     if(lispArgs.AsArray().Length != 1)
  13:     {
  14:         ed.WriteMessage("\nRibbonNinja - Incorrect number of arguments.  Expected String - tab keyboard shortcut\n");
  15:         return new TypedValue((int)LispDataType.Nil);
  16:     }
  17:     //get the tab name
  18:     String tabKey = Convert.ToString(lispArgs.AsArray()[0].Value);
  19:  
  20:     //get the ribbon
  21:     Autodesk.Windows.RibbonControl ribCntrl = Autodesk.AutoCAD.Ribbon.RibbonServices.RibbonPaletteSet.RibbonControl;
  22:     
  23:     //find the custom tab using the Id
  24:     foreach (RibbonTab tab in ribCntrl.Tabs)
  25:     {
  26:         //is this the one we're looking for?
  27:         if(tab.AutomationName.ToUpper() == tabKey.ToUpper())
  28:         {
  29:             //yup - get it active and bail. weeeeee
  30:             tab.IsActive = true;
  31:             return new TypedValue((int)LispDataType.T_atom);
  32:         }
  33:     }
  34:     return new TypedValue((int)LispDataType.Nil);
  35: }

Thanks for the challenge Shawn!  Here is your shiny new

.  Feel free to use this commercially – I always appreciate links to my posts in exchange  =)

p.s. Two posts in two days.  that’s crazy talk I tell ya!

August 9, 2011

Hacking Visual Lisp IDE to be a little more awesome!

Filed under: AutoCAD,Development — Tags: , — Darrin Maidlow @ 11:12 am

Even though it’s the year 2011 and is available in all its glory for .NET I still spend a lot of time in the IDE (VLIDE).   While ObjectARX is powerful and full of awesome, it comes at a cost of a lot of overhead to setup a project, add all the needed references, heck even compiling / debugging is a huge pain in the ass in comparison to Lisp.  Lisp is still hands down the most efficient way to do most batch modifications within an AutoCAD session for those who have made the time to learn it.lispide

There are a couple of problems with Visual Lisp right now though.  One of my biggest complaints about the VLIDE is the default colors.  The high contrast white background is an eye killer.   The IDE is configurable though and we’re given full a 16 bit color palette – with which I was able to configure a color scheme that was more to my liking.   If you start Visual Lisp, go to the Tools menu and choose Window Attributes you are presented with the dialog that should let you configure your colors.   Before opening this configuration dialog select a code window in Visual Lisp to modify. Set the properties accordingly. If you are happy with the colors when you press Ok on the dialog you will be prompted to “Use current as EDITOR prototype?”. Clicking yes here will update you editor prototype, resulting in future windows using these defaults.

One key limitation here is that on x64 editions of AutoCAD this dialog is broken.  You may also see the following error when you attempt to close the dialog:

; warning: unwind skipped on exception
; error: Exception occurred: 0xC0000094

It seems that Autodesk has let this code go to some extent. I’ve seen a number of of  people mentioning the unwind error.  This is a shame and partly why I’m writing this post!  First off – if you have an x86 release of AutoCAD you are in luck.  You can configure your IDE no problem using the UI.  For you x64 users, all is not lost.  Lucky for us Visual Lisp saves all this configuration information to a text file named VLIDE.dsk.  On my Vista x64 machine its found in the following locations:

AutoCAD 2006 – C:\Users\dmaidlow\AppData\Roaming\Autodesk\AutoCAD 2006\R16.2\enu

AutoCAD 2009 – C:\Users\dmaidlow\AppData\Roaming\Autodesk\AutoCAD 2009\R17.2\enu

AutoCAD 2010 – C:\Users\dmaidlow\AppData\Roaming\Autodesk\AutoCAD 2010\R18.0\enu

AutoCAD 2011 – C:\Users\dmaidlow\AppData\Roaming\Autodesk\AutoCAD 2011\R18.1\enu

AutoCAD 2012 – C:\Users\dmaidlow\AppData\Roaming\Autodesk\AutoCAD 2012 – English\R18.2\enu

These paths should be the same or similar on Windows 7.

If you open up this file you’ll see a large list of dotted pair lists full of settings.  The one I’m most interested in here is the *editor-sample-window property.  This is the “default” configuration to use when opening a lisp file.

   1: (*editor-sample-window* (:AUTOLISP :FGC 16777215 :BGC 0 :LXC T :CLV #40(nil nil 16711935 nil nil nil 16776960 nil 32768 nil 32768 nil 8421376 nil 8388736 12632256 8388736 12632256 8388736 12632256 255 nil 128 nil 8388608 nil 128 nil 12632256 nil 16777215 0 16777215 16750899 16777215 255 nil 8388608 nil 16776960) :TW 4 :LM 10))

Two values of importance are FGC (foreground color) and BGC (background color).  I was having a hard time finding a color wheel that did integer based color codes – but you can convert your favorite hex color codes to decimal ().  The next bit is a little ugly.  Within the :CLV property we have a list.  This list is basically a list of 16 bit integers containing a color code of the foreground and background color of each window property.  If the color is set to nil – it is transparent.  This list appears to be in the order in which the properties are displayed within the UI:

  • :LEX-SPACE
  • :LEX-STR
  • :LEX-SYM
  • :LEX-NUM
  • :LEX-INT
  • :LEX-REAL
  • :LEX-COMM
  • :LEX-COMM1
  • :LEX-COMM2
  • :LEX-PAREN
  • :LEX-SPEC
  • :LEX-SPEC1
  • :LEX-UNKN
  • :WINDOW-TEXT
  • :WINDOW-SELECTION
  • :ERROR-HIGHLIGHT
  • :INPUT-ZONE
  • :CONSOLE-MESSAGE

TW and LM contain the tab width and left margin respectively.  So go wild editing these values and configure your new IDE!  Keep in mind when editing the Visual Lisp settings using the UI, the vlide.dsk file is not saved  until AutoCAD is shutdown cleanly.  If AutoCAD crashes, you will lose your configuration.  If you would like to try out my IDE settings – I’ve attached a zip file containing  a copy of my .DSK file.  I also recommend you make a backup of the .dsk file before you start tinkering.

Now, if only I could figure out some way to build a vlx file from the command line so as to integrate it into my automated build system..

Update!

For those of you who don’t read the comments – Dennis Hill was cool enough to share his digging into the DSK file and also his IDE colors which I’ve been running for a week or so now.  Take a minute to read the comments and check out his color scheme.  Thanks Dennis!

Technorati Tags: ,,

March 22, 2011

Debugging COM DLL files in AutoCAD x64 and Visual Studio

Filed under: AutoCAD,Development — Tags: , — Darrin Maidlow @ 12:19 pm

While attempting to work out some x64 specific kinks in some of the COM code that is being migrated to .NET I hit a pretty annoying wall.  Well, it was actually a series of small walls (probably around knee high)  that kept tripping me…

In this post, I’m working on a problem that is pertaining to a 64 bit version of AutoCAD loading a COM enabled .NET assembly – however this information should apply to any x64 executable calling any x64 COM assembly.   Replace AutoCAD with <YourApp.exe> and you should be good to go =)

The first thing I did was to ensure each of the projects had been set to build for  “ANY CPU”.   This got the ball of fun rolling.   We have a VLX which instantiates the COM class using vlax-get-or-create-object.  When called this would constantly return nil.   Of course this all works just peachy when running an x86 build of AutoCAD.   At this point my breakpoints would appear in Visual Studio as disabled and would happily inform me that “The breakpoint will not currently be hit”.

This usually results when the incorrect DLL is being loaded, or when the PDB file is missing.   Once Visual Studio is running in debug mode you can bring up the Modules window from the Debug/Windows/Modules menu.  This should show you all the assemblies involved in the current session as well a bunch of other information including their paths.  The assembly in question was not listed here – so at this point I’m assuming that Windows cannot find the dll.

Junk in the Registry

I am now thinking that I have orphaned and duplicated types and CLSIDs in the registry, likely pointing to other builds of the dlls.   If the GUIDs for the classes or types changed you end up with a lot of junk in the registry.   I don’t trust those registry “cleaner” apps and in the interest of keeping my OS working – I chose to manually search out and nuke all the registry references manually.  This can be done by searching both for your assembly’s type name, and COM exposed class names in regedit.  A lot of this was done in the HKEY_CLASSES_ROOT root.  After all of that the problem still exists..

Wow6432Node Registry Keys

Registry keys were a two part problem. After dealing with the junk in the registry, I found I also had to deal with the x86 .   DraftLogic has been until this point limited to x86 architecture as a result of  the VB6 code not being x64 friendly.   As a result of this, all of the needed keys had been placed in the WOW6432Node of the appropriate software registry sections.   This was the easiest wall to find and hop over.  Unfortunately, this also didn’t solve the problem.

Improperly Registered Assemblies-thanks for nothingVisual Studio

So the next and fortunately final problem turns out that Visual Studio 2005/2008 (unsure about 2010)   Turns out the last piece of the solution is pretty simple – couple changes to the project.  What’s happening is that Visual Studio is running the x86 build of regasm.exe to register the assemblies on run, probably because Visual Studio is an x86 app itself.  This results in all kinds of x64 hating… To solve this we have to change two things in the COM enabled projects.

First though, I created a new build configuration called x64 so these changes would not affect in my ANY CPU configuration – which will be used to build my shipping assemblies.  It is also important to note that this problem is MOSTLY only a problem when running code from the IDE.  Assemblies installed and registered using an installer should not have this problem if the installer takes into consideration the “bitness” of AutoCAD (or any application calling your COM enabled dll).  ie.  Installshield asks the user on install what “bitness” of AutoCAD they are using, and the assemblies are registered as needed.  It may also be possible to register an assembly using both the 32 and 64 bit versions of regasm – but I have not tried this.

So with the new x64 configuration created I edited the properties of each COM enabled project  I unchecked “Register for COM interop” and added a post-build event command line of:

%Windir%\Microsoft.NET\Framework64\v2.0.50727\regasm $(TargetPath) /register /codebase /tlb

This would ensure that my COM enabled assemblies were properly registered for use by 64 bit  applications.

After all that, my breakpoints were finally hit, and I could actually start working out the x64 specific bugs. Too bad we couldn’t have moved this to ObjectARX.NET instead – then this would have been a non-issue =)  In retrospect, each of these problems were a contributing factor and each needed to be resolved. 

Hopefully if you are facing a similar issue this post helps you get over at least a couple of the walls!

March 9, 2011

ReSharper 6 is going to be rad!

Filed under: Development — Tags: — Darrin Maidlow @ 2:38 pm

Finally had a little time to catch up on the internets today and came across this bundling a decompiler!   My curiosity had been piqued by a posted by JetBrains shortlt after the  about reflector and the subsequent firestorm in the .NET community that ensued that announcement.

The timing is perfect.  We’ve got a growing need to better understand how some of our code is interacting with some vendor provided 3rd party libraries and this is making a huge difference.   In true ReSharper form – this new functionality looks solid, and easy to use.  The ReSharper navigation tools we’ve all grown love (well maybe not just love, but also  to depend on) are available.   I’m not going to miss trying to navigate code only to be greeted with an Object Browser window. ..

Looks like a standalone decompiler/browser is also coming too…

And by saying “free”, we actually mean “free”.

Love it!  Keep up the great works JetBrains – looking forward to the release!

Technorati Tags:

February 24, 2011

AutoCAD VBA/VB6 to .NET Similar Function Comparison

Filed under: AutoCAD,Development — Tags: , — Darrin Maidlow @ 11:53 am

I’ve been working on supporting the team doing the VB6 code migration to .NET the past few weeks.   This code is many years worth of COM based AutoCAD development.   Came across this that will give you a good head start in the right direction on which functions are the new equivalents in .NET of the old VBA/COM  calls.

Technorati Tags: ,,

February 14, 2011

Debugging Visual Lisp and ObjectARX.NET Together

Filed under: AutoCAD,Development — Tags: , , — Darrin Maidlow @ 12:22 pm

I encountered the following error when trying to debug a new ObjectARX .NET assembly using loaderlockwasdetected

Attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain or image initialization function since doing so can cause the application to hang.

This error would occur 95% of the time and pretty much took out AutoCAD each time it happened.  Fortunately this can be disabled manageddebuggingeceptionspretty easily within Visual Studio (I’ve tried both VS 2008 and VS 2010).   In Visual Studio bring up the exceptions dialog from the Debug / Exceptions menu (or via the keyboard shortcut Cntrl D+E). 

Expand the “Managed Debugging Exceptions” section and uncheck “LoaderLock” in the “Thrown” column.  Next time you start AutoCAD from Visual Studio and load Visual Lisp you should no longer get this exception and you should be merrily debugging away.

Older Posts »

Powered by WordPress

Switch to our mobile site