Blocking AutoCAD commands from .NET

Thanks to Viru Aithal from the DevTech team in India team for this idea (he reminded me of this technique in a recent ADN support request he handled).

A quick disclaimer: the technique shown in this entry could really confuse your users, if implemented with inadequate care. Please use it for good, not evil.

I talked at some length in previous posts about MDI in AutoCAD, and how various commands lock documents when they need to work on them. When commands try to lock (or unlock) a document, an event gets fired. You can respond to this event in your AutoCAD .NET module, asking AutoCAD to veto the operation requesting the lock.

For some general background on events in .NET, I'd suggest checking out this information on MSDN, both from the Developer's Guide and from MSDN Magazine. To see how they work in AutoCAD, take a look at the EventsWatcher sample on the ObjectARX SDK, under samples/dotNet/EventsWatcher. This is a great way to see what information is provided via AutoCAD events.

It seems that almost every command causes the DocumentLockModeChanged event to be fired โ€“ I would say every command, but I can imagine there being commands that don't trigger it, even if the technique appears to work even for commands that have no reason to lock the current document, such as HELP.

As you can imagine, having something block commands that a user expects to use could result in a serious drop in productivity, but there are genuine cases where you might have a legitimate need to disable certain product functionality, such as to drive your users through a more automated approach to performing the same task. This is not supposed to act as a fully-fledged security layer โ€“ an advanced user could easily prevent a module from being loaded, which invalidates this technique โ€“ but it could still be used to help stop the majority of users from doing something that might (for instance) break the drawing standards implemented in their department.

So, let's take a look at what needs to happen to define and register our event handler.

Firstly, we must declare a callback function somewhere in our code that we will register as an event handler. In this case we're going to respond to the DocumentLockModeChanged event, and so will take as one of our arguments an object of type DocumentLockModeChangedEventArgs:

VB.NET:

Private Sub vetoLineCommand (ByVal sender As Object, _

  ByVal e As DocumentLockModeChangedEventArgs)

  If (e.GlobalCommandName = "LINE") Then

    e.Veto()

  End If

End Sub

C#:

private void vetoLineCommand(

  object sender,

  DocumentLockModeChangedEventArgs e)

{

  if (e.GlobalCommandName == "LINE")

  {

    e.Veto();

  }

}

This callback function, named vetoLineCommand(), simply checks whether the command that is requesting the change in document lock mode is the LINE command, and, if so, it then vetoes it. In our more complete sample, later on, we'll maintain a list of commands to veto, which can be manipulated by the user using come commands we define (you will not want to do this in your application โ€“ this is for your own testing during development).

Next we need to register the command. This event belongs to the DocumentManager object, but the technique for registering events is different between VB.NET and C#. In VB.NET you use the AddHandler() keyword, referring to the DocumentLockModeChanged event from the DocumentManager object โ€“ in C# you add it directly to the DocumentLockModeChanged property. This could be done from a command or from the Initialize() function, as I've done in the complete sample further below.

VB.NET:

Dim dm As DocumentCollection = Application.DocumentManager()

AddHandler dm.DocumentLockModeChanged, AddressOf vetoLineCommand

C#:

DocumentCollection dm = Application.DocumentManager;

dm.DocumentLockModeChanged += new DocumentLockModeChangedEventHandler(vetoLineCommand);

That's really all there is to it... as mentioned, the complete sample maintains a list of commands that are currently being vetoed. I've made this shared/static, and so it will be shared across documents (so if you try to modify this in concurrent drawings, you might get some interesting results).

Here's the complete sample (just to reiterate - use this technique both at your own risk and with considerable caution).

VB.NET:

Imports Autodesk.AutoCAD.Runtime

Imports Autodesk.AutoCAD.ApplicationServices

Imports Autodesk.AutoCAD.EditorInput

Imports System.Collections.Specialized

Namespace VetoTest

  Public Class VetoCmds

    Implements IExtensionApplication

    Shared cmdList As StringCollection _

      = New StringCollection

    Public Sub Initialize() _

      Implements IExtensionApplication.Initialize

      Dim dm As DocumentCollection

      dm = Application.DocumentManager()

      AddHandler dm.DocumentLockModeChanged, _

        AddressOf vetoCommandIfInList

    End Sub

    Public Sub Terminate() _

      Implements IExtensionApplication.Terminate

      Dim dm As DocumentCollection

      dm = Application.DocumentManager()

      RemoveHandler dm.DocumentLockModeChanged, _

        AddressOf vetoCommandIfInList

    End Sub

    <CommandMethod("ADDVETO")> _

    Public Sub AddVeto()

      Dim ed As Editor _

        = Application.DocumentManager.MdiActiveDocument.Editor

      Dim pr As PromptResult

      pr = ed.GetString(vbLf + _

        "Enter command to veto: ")

      If (pr.Status = PromptStatus.OK) Then

        If (cmdList.Contains(pr.StringResult.ToUpper())) Then

          ed.WriteMessage(vbLf + _

            "Command already in veto list.")

        Else

          cmdList.Add(pr.StringResult.ToUpper())

          ed.WriteMessage( _

            vbLf + "Command " + _

            pr.StringResult.ToUpper() + _

            " added to veto list.")

        End If

      End If

    End Sub

    <CommandMethod("LISTVETOES")> _

    Public Sub ListVetoes()

      Dim ed As Editor _

        = Application.DocumentManager.MdiActiveDocument.Editor

      ed.WriteMessage( _

        "Commands currently on veto list: " + vbLf)

      For Each str As String In cmdList

        ed.WriteMessage(str + vbLf)

      Next

    End Sub

    <CommandMethod("REMVETO")> _

    Public Sub RemoveVeto()

      ListVetoes()

      Dim ed As Editor _

        = Application.DocumentManager.MdiActiveDocument.Editor

      Dim pr As PromptResult

      pr = ed.GetString( _

        "Enter command to remove from veto list: ")

      If (pr.Status = PromptStatus.OK) Then

        If (cmdList.Contains(pr.StringResult.ToUpper())) Then

          cmdList.Remove(pr.StringResult.ToUpper())

        Else

          ed.WriteMessage(vbLf + _

            "Command not found in veto list.")

        End If

      End If

    End Sub

    Private Sub vetoCommandIfInList(ByVal sender As Object, _

      ByVal e As DocumentLockModeChangedEventArgs)

      If (cmdList.Contains(e.GlobalCommandName.ToUpper())) Then

        e.Veto()

      End If

    End Sub

  End Class

End Namespace

C#:

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using System;

using System.Collections.Specialized;

namespace VetoTest

{

  public class VetoCmds : IExtensionApplication

  {

    static StringCollection cmdList =

      new StringCollection();

    public void Initialize()

    {

      DocumentCollection dm =

        Application.DocumentManager;

      dm.DocumentLockModeChanged += new

        DocumentLockModeChangedEventHandler(

          vetoCommandIfInList

        );

    }

    public void Terminate()

    {

      DocumentCollection dm;

      dm = Application.DocumentManager;

      dm.DocumentLockModeChanged -= new

        DocumentLockModeChangedEventHandler(

          vetoCommandIfInList

        );

    }

    [CommandMethod("ADDVETO")]

    public void AddVeto()

    {

      Editor ed =

        Application.DocumentManager.MdiActiveDocument.Editor;

      PromptResult pr =

        ed.GetString("\nEnter command to veto: ");

      if (pr.Status == PromptStatus.OK)

      {

        if (cmdList.Contains(pr.StringResult.ToUpper()))

        {

          ed.WriteMessage(

            "\nCommand already in veto list."

          );

        }

        else

        {

          cmdList.Add(pr.StringResult.ToUpper());

          ed.WriteMessage(

            "\nCommand " +

            pr.StringResult.ToUpper() +

            " added to veto list.");

        }

      }

    }

    [CommandMethod("LISTVETOES")]

    public void ListVetoes()

    {

      Editor ed =

        Application.DocumentManager.MdiActiveDocument.Editor;

      ed.WriteMessage("Commands currently on veto list:\n");

      foreach (string str in cmdList)

      {

        ed.WriteMessage(str + "\n");

      }

    }

    [CommandMethod("REMVETO")]

    public void RemoveVeto()

    {

      ListVetoes();

      Editor ed =

        Application.DocumentManager.MdiActiveDocument.Editor;

      PromptResult pr;

      pr =

        ed.GetString(

          "Enter command to remove from veto list: "

        );

      if (pr.Status == PromptStatus.OK)

      {

        if (cmdList.Contains(pr.StringResult.ToUpper()))

        {

          cmdList.Remove(pr.StringResult.ToUpper());

        }

        else

        {

          ed.WriteMessage(

            "\nCommand not found in veto list."

          );

        }

      }

    }

    private void vetoCommandIfInList(

      object sender,

      DocumentLockModeChangedEventArgs e)

    {

      if (cmdList.Contains(e.GlobalCommandName.ToUpper()))

      {

        e.Veto();

      }

    }

  }

}

In terms of usage, it should be simple enough to work out while playing with it:

  • ADDVETO adds commands to the veto list
  • LISTVETOES lists the commands on the veto list
  • REMVETO removes commands from the veto list

These commands are really only intended for you to try out different commands on the veto list, to see what happens. If you need to control use of command(s) from your application, you should not allow your users to manipulate the list of blocked commands themselves.

17 responses to “Blocking AutoCAD commands from .NET”

  1. Veto has never worked from .NET in 2005 or 2006, however it does finally work in 2007.

  2. Thanks, gr.

    Once again I forgot to state that I wrote this code for AutoCAD 2007, and it will not necessarily work with previous versions.

    Regards,

    Kean

  3. I always get help from your post,thank you very much!

  4. Benjamin Weir Avatar

    Is there a way to modify a command rather then blocking it all together? I ask as I'm looking at using a scale value when the user enters values for things such as the diameter of a circle. I'd like to trap when ever the user enters a distance for OFFSET or other such values and behind the scenes multiply the value they entered.

  5. You can certainly respond to events to manipulate geometry etc., although not as they pass through the user-input system. You'd be best looking at Database events, rather than DocumentCollection or Editor events.

    Some of the events used in this series of posts may be of help.

    Regards,

    Kean

  6. hi, i want to link/catch the autocad AcadDocument.SelectionChanged event with C#, but I have searched in the internet for some days, and still can't work out how to do that , would you please tell me how i can do that or even sample code ? I use autocad2004+ VS2003,C# (VS2005 is ok) my skype is iamwheat, yahoo msg is wheatinthesky@yahoo.com.cn , thank you in advance! Sincerely. Richard

  7. Hi Richard,

    Please submit your question to the AutoCAD .NET Discussion Group.

    I should be able to get to this, at some point, but there are no guarantees it'll be soon.

    Regards,

    Kean

  8. What recommendations could you give to vetoing a command? I would like to veto DDEDIT to open a custom form depending on the contents of a text entity. It would be vetoed when a text entity is double clicked thus providing something in Editor.SelectImplied method.

  9. The classic way to solve this is to undefine and redefine DDEDIT, passing through to the original command where appropriate.

    Kean

  10. Dear Mr. Walmsley, is there a way to intercept the savefiledialog and change the default save filename?
    thank you in advance, you are my beacon

  11. Kean Walmsley Avatar

    Dear qu,

    You might try undefining the SAVEAS command and redefining it with your custom behaviour.

    Or you might try to hook into the command-level events and then use some Windows API tricks to pump the default you want into the dialog's edit box.

    Regards,

    Kean

  12. How can you tell the difference between "line" and ".line"?

    1. Not directly (as far as I'm aware). You could undefine/redefine the LINE command, if you needed to, though.

      Kean

  13. I'm trying to make a simpler user interface for my work where I wouldnt have to enter full command in the prompt+Confirm... but for "Line+Enter" just press letter "L" (without enter). - this should disable all default inputs for autocad, including icons and caddys (except numbers... and alt+, ctrl+ commands),

    I have narrowed my approach to "veto" and "PreFilterMessage"/"KeyPressEventHandler" concepts, but Im not sure if there's a simple way to aproaching this.

    one of my ideas is to force press "enter" after every key pressed(through PreFilterMessage):

    if (any key pressed)
    do enter

    but that approach seems like "ducktaping" the code ...with many sideeffects I might not be aware of (4 example enter will be pressed every time I move the mouse(I can disable it, but its an example)... and god knows on what other events I might not think of).

    sorry if I went OT. (tried posting this at .net but no replies ๐Ÿ™ )

    1. Kean Walmsley Avatar

      How would users enter other commands starting with L? Might be worth looking at the CUI-defined accelerators/shortcuts (you'd need to use Ctrl-L, etc., though).

      Kean

      1. I was thinking of having a "special mode ON/OFF" button, so you can switch between the default, and minimized shortcut mode. just like you can do with "Cleanscreen" mode.
        that way I'd have few important tools at my disposal with least typing effort ๐Ÿ™‚
        as for CTRL... I still want to disable autocad com. prompt or create an alternative command prompt that does not trigger standard commands.

        1. You might also just have a separate, invisible app that steals focus when enabled and pumps commands into AutoCAD when it wants to.

          Anyway - we're getting way OT... good luck with your project!

          Kean

Leave a Reply to Kean Walmsley Cancel reply

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