Adding a dynamic password property to an AutoCAD object using .NET

In this recent post we looked at adding custom editing capabilities for dynamic properties we've added via .NET. In the first example we looked at a "distance" property which provided a button allowing the user to select two points. In this post we'll look at implementing a masked string property, such as one you would use for a password.

I won't repeat too much of the background information from the last post in this series: you should refer to that to understand the fundamentals (the fact that we're basing this implementation on Cyrille Fauvel's OPM .NET sample, that we're not dealing with per-object persistence, and that we're using "stock" ActiveX editing controls we've discovered via the Registry). That said, the code we're providing here stands alone from the code in the last post, even if it's altogether possible to combine them.

Here is the C# code to implement a simple, masked string dynamic property:

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Windows.OPM;

using Autodesk.AutoCAD.Windows.ToolPalette;

using System;

using System.Reflection;

using System.Runtime.InteropServices;

 

namespace OPMNetSample

{

  #region Our Custom Password Property

  [

    Guid("B5D9010E-48A2-4bfb-8601-76A7B1007C68"),

    ProgId("OPMNetSample.PasswordProperty.1"),

    ClassInterface(ClassInterfaceType.None),

    ComDefaultInterface(typeof(IDynamicProperty2)),

    ComVisible(true)

  ]

  public class PasswordProp : IDynamicProperty2, IAcPiPropertyDisplay

  {

    private IDynamicPropertyNotify2 _pSink = null;

    private string _password = "";

 

    #region IDynamicProperty2 methods

 

    // Unique property ID

 

    public void GetGUID(out Guid propGUID)

    {

      propGUID =

        new Guid("B5D9010E-48A2-4bfb-8601-76A7B1007C68");

    }

 

    // Property display name

 

    public void GetDisplayName(out string szName)

    {

      szName = "Password";

    }

 

    // Show/Hide property in the OPM, for this object instance

 

    public void IsPropertyEnabled(object pUnk, out int bEnabled)

    {

      bEnabled = 1;

    }

 

    // Is property showing but disabled

 

    public void IsPropertyReadOnly(out int bReadonly)

    {

      bReadonly = 0;

    }

 

    // Get the property description string

 

    public void GetDescription(out string szName)

    {

      szName =

        "This masked property is a string";

    }

 

    // OPM will typically display these in an edit field

    // optional: meta data representing property type name,

    // ex. ACAD_ANGLE

 

    public void GetCurrentValueName(out string szName)

    {

      throw new System.NotImplementedException();

    }

 

    // What is the property type, ex. VT_R8

 

    public void GetCurrentValueType(out ushort varType)

    {

      // The Property Inspector supports the following data

      // types for dynamic properties:

      // VT_I2 (2), VT_I4 (3), VT_R4 (4), VT_R8 (5),

      // VT_BSTR (8), VT_BOOL (11), and VT_USERDEFINED (29).

 

      varType = 8; // VT_BSTR

    }

 

    // Get the property value, passes the specific object

    // we need the property value for.

 

    public void GetCurrentValueData(object pUnk, ref object pVarData)

    {

      pVarData = _password;

    }

 

    // Set the property value, passes the specific object we

    // want to set the property value for

 

    public void SetCurrentValueData(object pUnk, object varData)

    {

      _password = varData.ToString();

      System.Windows.Forms.MessageBox.Show(

        "String entered was \"" + _password + "\"."

      );

    }

 

    // OPM passes its implementation of IDynamicPropertyNotify, you

    // cache it and call it to inform OPM your property has changed

 

    public void Connect(object pSink)

    {

      _pSink = (IDynamicPropertyNotify2)pSink;

    }

 

    public void Disconnect() {

      _pSink = null;

    }

 

    #endregion

 

    #region IAcPiPropertyDisplay methods

 

    public void GetCustomPropertyCtrl(

      object id, uint lcid, out string progId

    )

    {

      progId = "AcPEXCtl.AcPePropertyEditorPwdText.16";

    }

 

    public void GetPropertyWeight(object id, out int propertyWeight)

    {

      propertyWeight = 0;

    }

 

    public void GetPropertyIcon(object id, out object icon)

    {

      icon =
null;

    }

 

    public void GetPropTextColor(object id, out uint textColor)

    {

      textColor = 0;

    }

 

    public void IsFullView(

      object id, out bool visible, out uint integralHeight

    )

    {

      visible = false;

      integralHeight = 1;

    }

    #endregion

  }

  #endregion

 

  #region Application Entry Point

  public class MyEntryPoint : IExtensionApplication

  {

    protected internal PasswordProp passProp = null;

 

    public void Initialize()

    {

      Assembly.LoadFrom("asdkOPMNetExt.dll");

 

      // Add the Dynamic Property

 

      Dictionary classDict = SystemObjects.ClassDictionary;

      RXClass lineDesc = (RXClass)classDict.At("AcDbLine");

      IPropertyManager2 pPropMan =

        (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(lineDesc);

 

      passProp = new PasswordProp();

      pPropMan.AddProperty((object)passProp);

    }

 

    public void Terminate()

    {

      // Remove the Dynamic Property

 

      Dictionary classDict = SystemObjects.ClassDictionary;

      RXClass lineDesc = (RXClass)classDict.At("AcDbLine");

      IPropertyManager2 pPropMan =

        (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(lineDesc);

 

      pPropMan.RemoveProperty((object)passProp);

      passProp = null;

    }

  }

  #endregion

}

Let's see what happens when we NETLOAD the asdkOPMNetExt.dll and our OPMNetSample.dll, and then select a standard line:

Our new dynamic password property on the line object

Once we start entering a string inside our dynamic password property, we see it displayed as a masked string:

Entering a string via our new dynamic password property

And once we finish entering our string, we can see that our code has the unmasked data available to it – here's a MessageBox we throw up to display the contents:

MessageBox showing the string entered via our new dynamic password property

If we really wanted to store this securely – to avoid any determined application from snooping the underlying data – then we would really need to encrypt it before assigning it to our object (whether in Xdata or an Xrecord stored in the extension dictionary of an object). Beyond that we might even want to take measures to make sure the variable – as stored in memory, not just persisted with the object – isn't readable by external applications. But again, this is beyond the scope of this post, which is really just to introduce another property editing control available inside AutoCAD.

9 responses to “Adding a dynamic password property to an AutoCAD object using .NET”

  1. Hi Kean,

    Is there way to fill some default values for the activex-control ? I'd like to use combobox-control (AcPEXCtl.AcPePropertyEditorEnum.16) but have no idea how to fill it.

  2. Hi Petri,

    I think I tried to get this working, myself, if I remember correctly, but didn't get there. There was probably an interface that needed exposing via .NET and implementing, but I didn't have the time to follow through on it. It's a bit of a blur, at this stage, I have to admit.

    Regards,

    Kean

  3. Hi Kean,

    We got it done by exposing IDynamicEnumProperty interface to .NET and implementing it to our Enum property.

    The functions we had to implement was:
    GetNumPropertyValues(LONG* numValues)
    GetPropValueName(LONG index, BSTR* valueName)
    GetPropValueData(LONG index, VARIANT* valueName)

    Again thanks for the guys from ADN 🙂

    1. Hi, I know, this is an old post, but I just got to the situation, I needed to add CustomProps to the OPM.

      Q1: are there any changes for ACAd2014 (e.g. "simplier" approach

      Q2: if not, could you pls explain (or share the code) how to get there?

      I have absolute no experience in ARX, just struggling around with this one (after netload even the initialization is not starting... ?).
      Any help is appreciated!
      Thanks,
      Daniel

      1. It's still completely valid. You should be able to NETLOAD the pre-built asdkOPMNetExt.dll for AutoCAD 2014, at which point you'll need some custom code to make the properties appear (there is also a test project somewhere which should help).

        Kean

  4. Great - thanks for letting me know, Petri.

    Kean

  5. Hı Kean we can give dwg files for example imagine our customers who doesnt paid our project price, with that dynamic password with setup dll and if password doesnt correct the dwg files must not be zoomextent or unplottable bla bla ets.İs it possible
    I think if so everybody like this mehod and someone doesnt like thank yu best regards

  6. Hi alyoşa,

    You could certainly implement this kind of mechanism - that's really up to you - but it would take work to make it hard for sophisticated users or programmers to circumvent. Implementing software security is a significant undertaking and way beyond the scope of this blog.

    Kean

  7. Thanks for information, Petri. I exposed IDynamicEnumProperty interface to .NET, but when I implement it, I have to override GetNumPropertyValues method:
    unsafe public void GetNumPropertyValues(int* numValues)

    Due to pointer in argument, I use unsafe keyword and "Allow unsafe code" in the project options. Is it possible to avoid unsafe code in this case?

Leave a Reply to dba Cancel reply

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