Author Topic: COM and Dynamic  (Read 2234 times)

0 Members and 1 Guest are viewing this topic.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8662
  • AKA Daniel
COM and Dynamic
« on: April 02, 2020, 08:21:31 PM »
I found this the other day, probably already been mentioned..
But I found you can interact with Cad’s COM interface without added those dreaded hard references.
So lets say there’s a function that’s in COM, that’s not in the wrappers, you can call it using the dynamic keyword
Example

Code - C#: [Select]
  1.  public class DynApplication
  2.     {
  3.         public dynamic DynObj {get; protected set;}
  4.         public DynApplication()
  5.         {
  6.             DynObj = _AcAp.Application.AcadApplication;
  7.         }
  8.         public List<string> ListArx
  9.         {
  10.             get
  11.             {
  12.                 List<string> apps = new List<string>();
  13.                 foreach(string app in DynObj.ListArx)
  14.                     apps.Add(app);
  15.                 return apps;
  16.             }
  17.         }
  18.     }
  19.  

Code - C#: [Select]
  1.         [CommandMethod("dyn")]
  2.         static public void dyn()
  3.         {
  4.             DynApplication dynApp = new DynApplication();
  5.  
  6.             foreach(string item in dynApp.ListArx)
  7.             {
  8.                 _AcAp.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n{0}", item);
  9.             }
  10.         }
  11.  

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8662
  • AKA Daniel
Re: COM and Dynamic
« Reply #1 on: April 02, 2020, 08:30:46 PM »
This only issues, I ran across are:
-if the function has an out parameter that outs a com class as a parameter. Dynamic complains it can’t convert an object to a com_object.
-Function signature errors are found at runtime instead of compile time.

Performance should be identical as interactions with COM are runtime anyway, early binding only compiles with the interface

Jeff H

  • Needs a day job
  • Posts: 6144
Re: COM and Dynamic
« Reply #2 on: April 03, 2020, 10:47:23 PM »
Here is an example of using dynamic with Preferences in AutoCAD

First snippet is example of commands interacting with it
Code - C#: [Select]
  1.     List<string> pathsAddedToEnd = new List<string>()
  2.         {
  3.             @"C:\Testing\AutoCAD\Fonts",
  4.             @"C:\Testing\AutoCAD\Shapes",
  5.             @"C:\Testing\AutoCAD\Blocks"
  6.         };
  7.         List<string> pathsAddedToBeginning = new List<string>()
  8.         {
  9.             @"C:\Testing\AutoCAD\LineTypes",
  10.             @"C:\Testing\AutoCAD\Pat"
  11.         };
  12.         /// <summary>
  13.         /// Adds list to search paths to end & to beginning
  14.         /// </summary>
  15.         [CommandMethod("AddSearchPaths")]
  16.         public void AddSearchPaths()
  17.         {
  18.             SupportPath sp = Preferences.SupportPaths;
  19.             foreach (var path in pathsAddedToEnd)
  20.             {
  21.                 if (!sp.Contains(path))
  22.                 {
  23.                     sp.Add(path);
  24.                     Ed.WriteLine("\nAdded Path {0}", path);
  25.                 }
  26.             }
  27.             foreach (var path in pathsAddedToBeginning)
  28.             {
  29.                 if (!sp.Contains(path))
  30.                 {
  31.                     sp.Insert(0, path);
  32.                     Ed.WriteLine("\nInserted Path {0}", path);
  33.                 }
  34.             }
  35.  
  36.             sp.SaveChanges();
  37.         }
  38.  
  39.  
  40.         /// <summary>
  41.         /// Clears current printer configuration paths & adds new path & Defalt path usng enviroment variables.
  42.         /// </summary>
  43.         [CommandMethod("AddPrinterConfigPaths")]
  44.         public void AddPrinterConfigPaths()
  45.         {
  46.             PrinterConfigPath config = Preferences.PrinterConfigPath;
  47.             config.Clear();
  48.             var newConfig = @"C:\Testing\AutoCAD\Plotting\Plotters";
  49.             config.Add(newConfig);
  50.             var defaultConfig = @"%appdata%\Autodesk\AutoCAD 2020\R23.1\enu\Plotters";
  51.             config.Add(defaultConfig);
  52.             Ed.WriteLine("\nAdded Path {0}", newConfig);
  53.             Ed.WriteLine("\nAdded Path {0}", defaultConfig);
  54.             config.SaveChanges();
  55.         }
  56.         /// <summary>
  57.         /// Clears current printer description paths & adds new path.
  58.         /// </summary>
  59.         [CommandMethod("AddPrinterDescPaths")]
  60.         public void AddPrinterDescPaths()
  61.         {
  62.             PrinterDescPath desc = Preferences.PrinterDescPath;
  63.             desc.Clear();
  64.             var newDesc = @"C:\Testing\AutoCAD\Plotting\Plotters\PMP Files";
  65.             desc.Add(newDesc);
  66.             Ed.WriteLine("\nAdded Path {0}", newDesc);
  67.             desc.SaveChanges();
  68.         }
  69.  
  70.         /// <summary>
  71.         /// Clears current printer style paths & adds new path.
  72.         /// </summary>
  73.         [CommandMethod("AddPrinterStyleSheetPaths")]
  74.         public void AddPrinterStyleSheetPaths()
  75.         {
  76.             PrinterStyleSheetPath styles = Preferences.PrinterStyleSheetPath;
  77.             styles.Clear();
  78.             var style = @"C:\Testing\AutoCAD\Plotting\PlotStyles";
  79.             styles.Add(style);
  80.             Ed.WriteLine("\nAdded Path {0}", style);
  81.             styles.SaveChanges();
  82.         }
  83.  

Class that returns different SupportPaths feeding dynamic object into constructors
Code - C#: [Select]
  1.  public static class Preferences
  2.     {
  3.         /// <summary>
  4.         /// Gets the acad preferences.
  5.         /// </summary>
  6.         /// <value>
  7.         /// The acad preferences.
  8.         /// </value>
  9.         private static dynamic AcadPreferences
  10.         { get { return Application.Preferences; } }
  11.  
  12.         /// <summary>
  13.         /// Gets the support paths.
  14.         /// </summary>
  15.         /// <value>
  16.         /// The support paths.
  17.         /// </value>
  18.         public static SupportPath SupportPaths { get { return new SupportPath(AcadPreferences); } }
  19.  
  20.         /// <summary>
  21.         /// Gets the tool palette paths.
  22.         /// </summary>
  23.         /// <value>
  24.         /// The tool palette paths.
  25.         /// </value>
  26.         public static ToolPalettePath ToolPalettePaths { get { return new ToolPalettePath(AcadPreferences); } }
  27.  
  28.         /// <summary>
  29.         /// Gets the printer configuration path.
  30.         /// </summary>
  31.         /// <value>
  32.         /// The printer configuration path.
  33.         /// </value>
  34.         public static PrinterConfigPath PrinterConfigPath { get { return new PrinterConfigPath(AcadPreferences); } }
  35.  
  36.         /// <summary>
  37.         /// Gets the printer desc path.
  38.         /// </summary>
  39.         /// <value>
  40.         /// The printer desc path.
  41.         /// </value>
  42.         public static PrinterDescPath PrinterDescPath { get { return new PrinterDescPath(AcadPreferences); } }
  43.  
  44.         /// <summary>
  45.         /// Gets the printer style sheet path.
  46.         /// </summary>
  47.         /// <value>
  48.         /// The printer style sheet path.
  49.         /// </value>
  50.         public static PrinterStyleSheetPath PrinterStyleSheetPath { get { return new PrinterStyleSheetPath(AcadPreferences); } }
  51.  
  52.         /// <summary>
  53.         /// Gets the template DWG path.
  54.         /// </summary>
  55.         /// <value>
  56.         /// The template DWG path.
  57.         /// </value>
  58.         public static TemplateDWGPath TemplateDWGPath { get { return new TemplateDWGPath(AcadPreferences); } }
  59.  
  60.         /// <summary>
  61.         /// Gets the enterprise menu file.
  62.         /// </summary>
  63.         /// <value>
  64.         /// The enterprise menu file.
  65.         /// </value>
  66.         public static EnterpriseMenuFile EnterpriseMenuFile { get { return new EnterpriseMenuFile(AcadPreferences); } }
  67.  
  68.         /// <summary>
  69.         /// Gets the menu file.
  70.         /// </summary>
  71.         /// <value>
  72.         /// The menu file.
  73.         /// </value>
  74.         public static MenuFile MenuFile { get { return new MenuFile(AcadPreferences); } }
  75.  
  76.         /// <summary>
  77.         /// Gets the display scroll bars.
  78.         /// </summary>
  79.         /// <value>
  80.         /// The display scroll bars.
  81.         /// </value>
  82.         public static DisplayScrollBars DisplayScrollBars { get { return new DisplayScrollBars(AcadPreferences); } }
  83.  
  84.         /// <summary>
  85.         /// Gets the SCM time value.
  86.         /// </summary>
  87.         /// <value>
  88.         /// The SCM time value.
  89.         /// </value>
  90.         public static SCMTimeValue SCMTimeValue { get { return new SCMTimeValue(AcadPreferences); } }
  91.  
  92.         /// <summary>
  93.         /// Gets the page setup overrides template file.
  94.         /// </summary>
  95.         /// <value>
  96.         /// The page setup overrides template file.
  97.         /// </value>
  98.         public static PageSetupOverridesTemplateFile PageSetupOverridesTemplateFile { get { return new PageSetupOverridesTemplateFile(AcadPreferences); } }
  99.  
  100.         /// <summary>
  101.         /// Gets the q new template file.
  102.         /// </summary>
  103.         /// <value>
  104.         /// The q new template file.
  105.         /// </value>
  106.         public static QNewTemplateFile QNewTemplateFile { get { return new QNewTemplateFile(AcadPreferences); } }
  107.     }
  108.  

SupportPath class handles enabling using environment variables, joining & splitting list of paths with';', functions to add, remove, clear, insert at indexes, etc.......
Code - C#: [Select]
  1.  
  2.  public class SupportPath : IAcadPathRepository
  3.     {
  4.         /// <summary>
  5.         /// The seperator
  6.         /// </summary>
  7.         private static char[] seperator = new char[] { ';' };
  8.  
  9.  
  10.         /// <summary>
  11.         /// The paths
  12.         /// </summary>
  13.         private List<string> paths = new List<string>();
  14.  
  15.  
  16.         /// <summary>
  17.         /// Gets or sets the preferences.
  18.         /// </summary>
  19.         /// <value>
  20.         /// The preferences.
  21.         /// </value>
  22.         protected dynamic Preferences { get; set; }
  23.  
  24.  
  25.         /// <summary>
  26.         /// Gets or sets the preferences file.
  27.         /// </summary>
  28.         /// <value>
  29.         /// The preferences file.
  30.         /// </value>
  31.         protected virtual dynamic PreferencesFile
  32.         {
  33.             get { return Preferences.Files.SupportPath; }
  34.             set { Preferences.Files.SupportPath = value; }
  35.         }
  36.  
  37.  
  38.         /// <summary>
  39.         /// Initializes a new instance of the <see cref="SupportPath"/> class.
  40.         /// </summary>
  41.         /// <param name="acadPreferences">The acad preferences.</param>
  42.         public SupportPath(object acadPreferences)
  43.         {
  44.             Preferences = acadPreferences;
  45.             paths = CreatepathList(PreferencesFile);
  46.         }
  47.  
  48.  
  49.         /// <summary>
  50.         /// Gets the paths.
  51.         /// </summary>
  52.         /// <returns></returns>
  53.         public IEnumerable<string> GetPaths()
  54.         {
  55.             return paths.AsReadOnly();
  56.         }
  57.  
  58.  
  59.         /// <summary>
  60.         /// Adds the specified path.
  61.         /// </summary>
  62.         /// <param name="path">The path.</param>
  63.         public void Add(string path)
  64.         {
  65.             path = Expand(path);
  66.             paths.Add(path);
  67.         }
  68.  
  69.  
  70.         /// <summary>
  71.         /// Inserts the specified index.
  72.         /// </summary>
  73.         /// <param name="index">The index.</param>
  74.         /// <param name="path">The path.</param>
  75.         public void Insert(int index, string path)
  76.         {
  77.             path = Expand(path);
  78.             paths.Insert(index, path);
  79.         }
  80.  
  81.  
  82.         /// <summary>
  83.         /// Removes the specified path.
  84.         /// </summary>
  85.         /// <param name="path">The path.</param>
  86.         /// <returns></returns>
  87.         public bool Remove(string path)
  88.         {
  89.             path = Expand(path);
  90.             int index = paths.FindIndex(p => p.Equals(path, StringComparison.OrdinalIgnoreCase));
  91.             paths.RemoveAt(index);
  92.             return index > -1;
  93.         }
  94.  
  95.  
  96.         /// <summary>
  97.         /// Removes at.
  98.         /// </summary>
  99.         /// <param name="index">The index.</param>
  100.         public void RemoveAt(int index)
  101.         {
  102.             paths.RemoveAt(index);
  103.         }
  104.  
  105.  
  106.         /// <summary>
  107.         /// Removes all.
  108.         /// </summary>
  109.         /// <param name="match">The match.</param>
  110.         /// <returns></returns>
  111.         public int RemoveAll(Predicate<string> match)
  112.         {
  113.             return paths.RemoveAll(match);
  114.         }
  115.  
  116.  
  117.         /// <summary>
  118.         /// Removes the range.
  119.         /// </summary>
  120.         /// <param name="index">The index.</param>
  121.         /// <param name="count">The count.</param>
  122.         public void RemoveRange(int index, int count)
  123.         {
  124.             paths.RemoveRange(index, count);
  125.         }
  126.  
  127.  
  128.         /// <summary>
  129.         /// Clears this instance.
  130.         /// </summary>
  131.         public void Clear()
  132.         {
  133.             paths.Clear();
  134.         }
  135.  
  136.  
  137.         /// <summary>
  138.         /// Determines whether [contains] [the specified path].
  139.         /// </summary>
  140.         /// <param name="path">The path.</param>
  141.         /// <returns></returns>
  142.         public bool Contains(string path)
  143.         {
  144.             return paths.Contains(Expand(path), StringComparer.OrdinalIgnoreCase);
  145.         }
  146.  
  147.  
  148.         /// <summary>
  149.         /// Saves the changes.
  150.         /// </summary>
  151.         public void SaveChanges()
  152.         {
  153.             this.PreferencesFile = CreatePathsString(paths);
  154.             paths = CreatepathList(PreferencesFile);
  155.         }
  156.  
  157.  
  158.         /// <summary>
  159.         /// Createpathes the list.
  160.         /// </summary>
  161.         /// <param name="pathString">The path string.</param>
  162.         /// <returns></returns>
  163.         private static List<string> CreatepathList(string pathString)
  164.         {
  165.             return pathString.Split(seperator).ToList();
  166.         }
  167.  
  168.  
  169.         /// <summary>
  170.         /// Creates the paths string.
  171.         /// </summary>
  172.         /// <param name="pathList">The path list.</param>
  173.         /// <returns></returns>
  174.         private static string CreatePathsString(IEnumerable<string> pathList)
  175.         {
  176.             return String.Join(";", pathList);
  177.         }
  178.  
  179.  
  180.         /// <summary>
  181.         /// Expands the specified path.
  182.         /// </summary>
  183.         /// <param name="path">The path.</param>
  184.         /// <returns></returns>
  185.         private static string Expand(string path)
  186.         {
  187.             return path.StartsWith("%") ? Environment.ExpandEnvironmentVariables(path) : path;
  188.         }
  189.     }

These classes below just overwrite the SupportPath dynamic PreferencesFile function to feed in specific support paths.
Code - C#: [Select]
  1.  public class PrinterConfigPath : SupportPath
  2.     {
  3.         /// <summary>
  4.         /// Initializes a new instance of the <see cref="PrinterConfigPath"/> class.
  5.         /// </summary>
  6.         /// <param name="acadPreferences">The acad preferences.</param>
  7.         public PrinterConfigPath(object acadPreferences)
  8.             : base(acadPreferences)
  9.         {
  10.         }
  11.  
  12.         /// <summary>
  13.         /// Gets or sets the preferences file.
  14.         /// </summary>
  15.         /// <value>
  16.         /// The preferences file.
  17.         /// </value>
  18.         protected override dynamic PreferencesFile
  19.         {
  20.             get { return Preferences.Files.PrinterConfigPath; }
  21.             set { Preferences.Files.PrinterConfigPath = value; }
  22.         }
  23.     }
  24.  
  25.  public class PrinterDescPath : SupportPath
  26.     {
  27.         /// <summary>
  28.         /// Initializes a new instance of the <see cref="PrinterDescPath"/> class.
  29.         /// </summary>
  30.         /// <param name="acadPreferences">The acad preferences.</param>
  31.         public PrinterDescPath(object acadPreferences)
  32.             : base(acadPreferences)
  33.         {
  34.         }
  35.  
  36.         /// <summary>
  37.         /// Gets or sets the preferences file.
  38.         /// </summary>
  39.         /// <value>
  40.         /// The preferences file.
  41.         /// </value>
  42.         protected override dynamic PreferencesFile
  43.         {
  44.             get { return Preferences.Files.PrinterDescPath; }
  45.             set { Preferences.Files.PrinterDescPath = value; }
  46.         }
  47.     }
  48.  
  49.  public class PrinterStyleSheetPath : SupportPath
  50.     {
  51.         /// <summary>
  52.         /// Initializes a new instance of the <see cref="PrinterStyleSheetPath"/> class.
  53.         /// </summary>
  54.         /// <param name="acadPreferences">The acad preferences.</param>
  55.         public PrinterStyleSheetPath(object acadPreferences)
  56.             : base(acadPreferences)
  57.         {
  58.         }
  59.  
  60.         /// <summary>
  61.         /// Gets or sets the preferences file.
  62.         /// </summary>
  63.         /// <value>
  64.         /// The preferences file.
  65.         /// </value>
  66.         protected override dynamic PreferencesFile
  67.         {
  68.             get { return Preferences.Files.PrinterStyleSheetPath; }
  69.             set { Preferences.Files.PrinterStyleSheetPath = value; }
  70.         }
  71.     }
  72.  

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8662
  • AKA Daniel
Re: COM and Dynamic
« Reply #3 on: April 04, 2020, 04:03:10 AM »
Very cool!

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: COM and Dynamic
« Reply #4 on: April 04, 2020, 04:45:14 AM »
Nice code Jeff (as usual).

The dynamic type can also be used with plain .NET and the Dynamic Language Runtime.

Code - C#: [Select]
  1.         public static void AddCircle(Point3d center, double radius)
  2.         {
  3.             dynamic btr = HostApplicationServices.WorkingDatabase.CurrentSpaceId;
  4.             btr.AppendEntity(new Circle(center, Vector3d.ZAxis, radius));
  5.         }
  6.  
  7.         public static void ToLayer0()
  8.         {
  9.             dynamic bt = HostApplicationServices.WorkingDatabase.BlockTableId;
  10.             foreach (var btr in bt)
  11.                 if (!btr.IsLayout)
  12.                     foreach (var ent in btr)
  13.                         ent.Layer = "0";
  14.         }
  15.  
  16.         public static List<string> LayoutList()
  17.         {
  18.             var layouts = new List<string>();
  19.             dynamic dict = HostApplicationServices.WorkingDatabase.LayoutDictionaryId;
  20.             foreach (var item in dict)
  21.                 layouts.Add(item.Key);
  22.             return layouts;
  23.         }
Speaking English as a French Frog