Author Topic: EXPORTLAYOUTS command example  (Read 15039 times)

0 Members and 1 Guest are viewing this topic.

TheMaster

  • Guest
EXPORTLAYOUTS command example
« on: April 25, 2013, 12:18:50 PM »
It appears that Autodesk's discussion group server is mangling code (removing angle braces :roll:), so I'm posting this here in case anyone needs the actual code.

Code - C#: [Select]
  1.  
  2. /// ExportLayouts.cs  (c) 2013  Tony Tanzillo
  3. ///
  4. /// AutoCAD.NET API sample that automates
  5. /// the EXPORTLAYOUT command to export all
  6. /// layouts in the current document.
  7. ///
  8. /// Two versions of the command are included.
  9. /// The second version (EXPORTLAYOUTS2) requires
  10. /// a reference to AcExportLayout.dll.
  11.  
  12. using System;
  13. using System.Collections;
  14. using System.Collections.Generic;
  15. using System.IO;
  16. using System.Linq;
  17. using System.Reflection;
  18. using System.Linq.Expressions;
  19. using Autodesk.AutoCAD.ApplicationServices;
  20. using Autodesk.AutoCAD.DatabaseServices;
  21. using Autodesk.AutoCAD.EditorInput;
  22. using Autodesk.AutoCAD.Runtime;
  23.  
  24. /// Needed with EXPORTLAYOUTS2 command only, and
  25. /// requires a reference to AcExportLayout.dll:
  26.  
  27. using AcExportLayout = Autodesk.AutoCAD.ExportLayout;
  28.  
  29. namespace ExportLayoutsExample
  30. {
  31.    public static class ExportLayoutsCommands
  32.    {
  33.       /// <summary>
  34.       /// Automates the EXPORTLAYOUT command to export
  35.       /// all paper space layouts to .DWG files.
  36.       ///
  37.       /// In this example, we export each layout to
  38.       /// a drawing file in the same location as the
  39.       /// current drawing, wherein each file has the
  40.       /// name "<dwgname>_<layoutname>.dwg".
  41.       ///
  42.       /// This is not a functionally-complete example:
  43.       ///
  44.       /// No checking is done to see if any of the
  45.       /// files already exist, and existing files
  46.       /// are overwritten without warning or error.
  47.       ///
  48.       /// No checking is done to detect if an existing
  49.       /// file exists and is in-use by another user, or
  50.       /// cannot be overwritten for any other reason.
  51.       ///
  52.       /// No checking is done to ensure that the user
  53.       /// has sufficient rights to write files in the
  54.       /// target location.
  55.       ///
  56.       /// You can and should deal with any or all of
  57.       /// the above as per your own requirements.
  58.       ///
  59.       /// </summary>
  60.  
  61.       [CommandMethod( "EXPORTLAYOUTS" )]
  62.       public static void ExportLayoutsCommand()
  63.       {
  64.          var doc = Application.DocumentManager.MdiActiveDocument;
  65.          var db = doc.Database;
  66.          var editor = doc.Editor;
  67.          try
  68.          {
  69.             if( (short) Application.GetSystemVariable( "DWGTITLED" ) == 0 )
  70.             {
  71.                editor.WriteMessage(
  72.                   "\nCommand cannot be used on an unnamed drawing"
  73.                );
  74.                return;
  75.             }
  76.             string format =
  77.                Path.Combine(
  78.                   Path.GetDirectoryName( doc.Name ),
  79.                   Path.GetFileNameWithoutExtension( doc.Name ) )
  80.                + "_{0}.dwg";
  81.  
  82.             string[] names = null;
  83.  
  84.             using( Transaction tr = doc.TransactionManager.StartTransaction() )
  85.             {
  86.                // Get the localized name of the model tab:
  87.                BlockTableRecord btr = (BlockTableRecord)
  88.                   SymbolUtilityServices.GetBlockModelSpaceId( db )
  89.                      .GetObject( OpenMode.ForRead );
  90.                Layout layout = (Layout)
  91.                   btr.LayoutId.GetObject( OpenMode.ForRead );
  92.                string model = layout.LayoutName;
  93.                // Open the Layout dictionary:
  94.                IDictionary layouts = (IDictionary)
  95.                   db.LayoutDictionaryId.GetObject( OpenMode.ForRead );
  96.                // Get the names and ids of all paper space layouts into a list:
  97.                names = layouts.Keys.Cast<string>()
  98.                   .Where( name => name != model ).ToArray();
  99.  
  100.                tr.Commit();
  101.             }
  102.  
  103.             int cmdecho = 0;
  104. #if DEBUG
  105.             cmdecho = 1;
  106. #endif
  107.             using( new ManagedSystemVariable( "CMDECHO", cmdecho ) )
  108.             using( new ManagedSystemVariable( "CMDDIA", 0 ) )
  109.             using( new ManagedSystemVariable( "FILEDIA", 0 ) )
  110.             using( new ManagedSystemVariable( "CTAB" ) )
  111.             {
  112.                foreach( string name in names )
  113.                {
  114.                   string filename = string.Format( format, name );
  115.                   editor.WriteMessage( "\nExporting {0}\n", filename );
  116.                   Application.SetSystemVariable( "CTAB", name );
  117.                   editor.Command( "._EXPORTLAYOUT", filename );
  118.                }
  119.             }
  120.          }
  121.          catch( System.Exception ex )
  122.          {
  123. #if DEBUG
  124.             editor.WriteMessage( ex.ToString() );
  125. #else
  126.             throw ex;
  127. #endif
  128.          }
  129.       }
  130.  
  131.       /// <summary>
  132.       ///
  133.       /// Doesn't use the command line, requires AutoCAD R12
  134.       /// or later and a reference to AcExportLayout.dll:
  135.       ///
  136.       /// This version can be used from the application context,
  137.       /// which can make it easier to use in a batch process that
  138.       /// exports layouts of many files.
  139.       ///
  140.       /// The example also shows how to use the AcExportLayout
  141.       /// component to export a layout to an in-memory Database
  142.       /// without creating a drawing file.
  143.       ///
  144.       /// </summary>
  145.      
  146.       [CommandMethod( "EXPORTLAYOUTS2", CommandFlags.Session )]
  147.       public static void ExportLayouts2()
  148.       {
  149.          var doc = Application.DocumentManager.MdiActiveDocument;
  150.          var db = doc.Database;
  151.          var editor = doc.Editor;
  152.          try
  153.          {
  154.             if( (short) Application.GetSystemVariable( "DWGTITLED" ) == 0 )
  155.             {
  156.                editor.WriteMessage(
  157.                   "\nCommand cannot be used on an unnamed drawing"
  158.                );
  159.                return;
  160.             }
  161.             string format =
  162.                Path.Combine(
  163.                   Path.GetDirectoryName( doc.Name ),
  164.                   Path.GetFileNameWithoutExtension( doc.Name ) )
  165.                + "_{0}.dwg";
  166.  
  167.             Dictionary<string, ObjectId> layouts = null;
  168.  
  169.             using( doc.LockDocument() )
  170.             {
  171.                using( Transaction tr = doc.TransactionManager.StartTransaction() )
  172.                {
  173.                   // Get the localized name of the model tab:
  174.                   BlockTableRecord btr = (BlockTableRecord)
  175.                      SymbolUtilityServices.GetBlockModelSpaceId( db )
  176.                         .GetObject( OpenMode.ForRead );
  177.                   Layout layout = (Layout)
  178.                      btr.LayoutId.GetObject( OpenMode.ForRead );
  179.                   string model = layout.LayoutName;
  180.                   // Open the Layout dictionary:
  181.                   IDictionary layoutDictionary = (IDictionary)
  182.                      db.LayoutDictionaryId.GetObject( OpenMode.ForRead );
  183.                   // Get the names and ids of all paper space layouts
  184.                   // into a Dictionary<string,ObjectId>:
  185.                   layouts = layoutDictionary.Cast<DictionaryEntry>()
  186.                      .Where( e => ( (string) e.Key ) != model )
  187.                      .ToDictionary(
  188.                         e => (string) e.Key,
  189.                         e => (ObjectId) e.Value );
  190.  
  191.                   tr.Commit();
  192.                }
  193.  
  194.                /// Get the export layout 'engine':
  195.                Autodesk.AutoCAD.ExportLayout.Engine engine =
  196.                   Autodesk.AutoCAD.ExportLayout.Engine.Instance();
  197.  
  198.                using( new ManagedSystemVariable( "CTAB" ) )
  199.                {
  200.                   foreach( var entry in layouts )
  201.                   {
  202.                      string filename = string.Format( format, entry.Key );
  203.                      editor.WriteMessage( "\nExporting {0} => {1}\n", entry.Key, filename );
  204.                      Application.SetSystemVariable( "CTAB", entry.Key );
  205.                      using( Database database = engine.ExportLayout( entry.Value ) )
  206.                      {
  207.                         if( engine.EngineStatus == AcExportLayout.ErrorStatus.Succeeded )
  208.                         {
  209.                            database.SaveAs( filename, DwgVersion.Newest );
  210.                         }
  211.                         else
  212.                         {
  213.                            editor.WriteMessage( "\nExportLayout failed: ",
  214.                               engine.EngineStatus.ToString() );
  215.                            break;
  216.                         }
  217.                      }
  218.                   }
  219.                }
  220.             }
  221.          }
  222.          catch( System.Exception ex )
  223.          {
  224. #if DEBUG
  225.             editor.WriteMessage( ex.ToString() );
  226. #else
  227.             throw ex;
  228. #endif
  229.          }
  230.       }
  231.    }
  232.  
  233.    public static class EditorInputExtensionMethods
  234.    {
  235.       public static PromptStatus Command( this Editor editor, params object[] args )
  236.       {
  237.          if( editor == null )
  238.             throw new ArgumentNullException( "editor" );
  239.          return runCommand( editor, args );
  240.       }
  241.  
  242.       static Func<Editor, object[], PromptStatus> runCommand = GenerateRunCommand();
  243.  
  244.       static Func<Editor, object[], PromptStatus> GenerateRunCommand()
  245.       {
  246.          MethodInfo method = typeof( Editor ).GetMethod( "RunCommand",
  247.             BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
  248.          var instance = Expression.Parameter( typeof( Editor ), "instance" );
  249.          var args = Expression.Parameter( typeof( object[] ), "args" );
  250.          return Expression.Lambda<Func<Editor, object[], PromptStatus>>(
  251.             Expression.Call( instance, method, args ), instance, args )
  252.                .Compile();
  253.       }
  254.    }
  255.  
  256.  
  257.    /// <summary>
  258.    /// Automates saving/changing/restoring system variables
  259.    /// </summary>
  260.    
  261.    public class ManagedSystemVariable : IDisposable
  262.    {
  263.       string name = null;
  264.       object oldval = null;
  265.  
  266.       public ManagedSystemVariable( string name, object value )
  267.          : this( name )
  268.       {
  269.          Application.SetSystemVariable( name, value );
  270.       }
  271.  
  272.       public ManagedSystemVariable( string name )
  273.       {
  274.          if( string.IsNullOrWhiteSpace( name ) )
  275.             throw new ArgumentException( "name" );
  276.          this.name = name;
  277.          this.oldval = Application.GetSystemVariable( name );
  278.       }
  279.  
  280.       public void Dispose()
  281.       {
  282.          if( oldval != null )
  283.          {
  284.             object temp = oldval;
  285.             oldval = null;
  286.             Application.SetSystemVariable( name, temp );
  287.          }
  288.       }
  289.    }
  290.  
  291. }
  292.  
  293.  
« Last Edit: April 25, 2013, 02:30:44 PM by TT »

fixo

  • Guest
Re: EXPORTLAYOUTS command example
« Reply #1 on: April 25, 2013, 01:29:18 PM »
Thanks so much
Regards,

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: EXPORTLAYOUTS command example
« Reply #2 on: December 06, 2013, 11:31:32 PM »
Hello, Tonny. Thank you for your code. You wrote:
Code - C#: [Select]
  1.       public void Dispose()
  2.      {
  3.         if( oldval != null )
  4.         {
  5.            object temp = oldval;
  6.            oldval = null;
  7.            Application.SetSystemVariable( name, temp );
  8.         }
  9.      }

Why you don't write the more simple code?

Code - C#: [Select]
  1.       public void Dispose()
  2.      {
  3.         if( oldval != null )
  4.         {          
  5.            Application.SetSystemVariable( name, oldval );
  6.         }
  7.      }

Thank you.

owenwengerd

  • Bull Frog
  • Posts: 451
Re: EXPORTLAYOUTS command example
« Reply #3 on: December 07, 2013, 08:19:59 AM »
I'm not familiar with Tony's code, but using a local temp variable is the only way to ensure that oldvar is null while the system variable is changing. This can be important to prevent it from being changed multiple times if the code is reentrant.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: EXPORTLAYOUTS command example
« Reply #4 on: December 10, 2013, 07:37:04 AM »
I'm not familiar with Tony's code, but using a local temp variable is the only way to ensure that oldvar is null while the system variable is changing. This can be important to prevent it from being changed multiple times if the code is reentrant.
I don't understand you. How it can be changed multiple times? The oldval is a private field of an instance level. It was initialized once (for each instance) in the constructor and never changed.

owenwengerd

  • Bull Frog
  • Posts: 451
Re: EXPORTLAYOUTS command example
« Reply #5 on: December 10, 2013, 10:23:44 AM »
I don't understand you. How it can be changed multiple times? The oldval is a private field of an instance level. It was initialized once (for each instance) in the constructor and never changed.

Maybe this is more clear:
Quote
This can be important to prevent the system variable from being changed multiple times if the code is reentrant.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: EXPORTLAYOUTS command example
« Reply #6 on: December 10, 2013, 10:45:55 AM »
Maybe this is more clear:
Quote
This can be important to prevent the system variable from being changed multiple times if the code is reentrant.
I read it. The oldval is not pointer, as I see. I still don't understand you. :(
« Last Edit: December 10, 2013, 01:01:48 PM by Andrey Bushman »

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: EXPORTLAYOUTS command example
« Reply #7 on: December 10, 2013, 03:18:36 PM »
Here is a link to a wikipedia article that should explain it.  http://en.wikipedia.org/wiki/Reentrancy_(computing)
 
"A subroutine that is directly or indirectly recursive should be reentrant. This policy is partially enforced by structured programming languages.[citation needed] However a subroutine can fail to be reentrant if it relies on a global variable to remain unchanged but that variable is modified when the subroutine is recursively invoked."
 
The above is copied from the linked article.
 
« Last Edit: December 10, 2013, 05:23:19 PM by Keith Brown »
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

kaefer

  • Guest
Re: EXPORTLAYOUTS command example
« Reply #8 on: December 10, 2013, 04:52:31 PM »
Here is a link to a wikipedia article that should explain it.  http://en.wikipedia.org/wiki/Reentrancy_(computing)
 
Quote
A subroutine that is directly or indirectly recursive should be reentrant. ...

I suspect over-engineering. Dispose() shouldn't be called more than once, and even if it is, re-entrancy doesn't come into it except when the second Dispose() happens to get called while the first is still in progress. This kind of vulnerability doesn't seem to be eliminated completely even in TT's version:

Code - C#: [Select]
  1.            object temp = oldval;
  2.            // what about now? oldval isn't yet set
  3.            oldval = null;

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: EXPORTLAYOUTS command example
« Reply #9 on: December 10, 2013, 05:04:19 PM »
Code - C#: [Select]
  1.            object temp = oldval;
  2.            // what about now? oldval isn't yet set
  3.            oldval = null;

I am not for sure because I just really read up on it for this thread but the value of oldval at that point is no longer relevant.  We have saved it to the temporary variable and using the temporary variable to reset the original system variable value.  Since the temp variable is local and not global it should retain its original value of oldval.
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

owenwengerd

  • Bull Frog
  • Posts: 451
Re: EXPORTLAYOUTS command example
« Reply #10 on: December 10, 2013, 06:18:30 PM »
I think the point I wanted to make is simply that the pattern used in Tony's code does have a purpose. Whether or not it is necessary in this specific case may be debatable, but it might be necessary, and it's not simple to determine with certainty that it's never necessary.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: EXPORTLAYOUTS command example
« Reply #11 on: December 11, 2013, 12:13:14 AM »
However a subroutine can fail to be reentrant if it relies on a global variable
I understand it, but the oldval is not global. It is local private and not accessible for the external changing.
Quote from: kaefer
I suspect over-engineering. Dispose() shouldn't be called more than once
Yes, it is one of the reasons about which I asked my question.

Quote from: owenwengerd
I think the point I wanted to make is simply that the pattern used in Tony's code does have a purpose. Whether or not it is necessary in this specific case may be debatable, but it might be necessary, and it's not simple to determine with certainty that it's never necessary.
Now I understand, all thanks. :)

Regards, Andrey.
« Last Edit: December 11, 2013, 12:16:30 AM by Andrey Bushman »

TheMaster

  • Guest
Re: EXPORTLAYOUTS command example
« Reply #12 on: January 07, 2014, 03:52:30 AM »
Hello, Tonny. Thank you for your code. You wrote:
Code - C#: [Select]
  1.       public void Dispose()
  2.      {
  3.         if( oldval != null )
  4.         {
  5.            object temp = oldval;
  6.            oldval = null;
  7.            Application.SetSystemVariable( name, temp );
  8.         }
  9.      }

Why you don't write the more simple code?

Code - C#: [Select]
  1.       public void Dispose()
  2.      {
  3.         if( oldval != null )
  4.         {          
  5.            Application.SetSystemVariable( name, oldval );
  6.         }
  7.      }

Thank you.

Reading the framework design guidelines for IDisposable should clear that up.

The guidelines state that an IDisposable should be able to handle multiple calls to Dispose() without unintended/adverse side-effects.  The code is designed to ensure that if there were multiple erroneous calls to Dispose(), all but the first will do nothing at all.

Of course, we can argue that subsequent calls to Dispose() should throw an exception, and I would
probably not dispute that, but I have had cases where trying to keep track of whether Dispose() was called was far more complicated than taking the singleton approach. And, this approach also allows you to explicitly call Dispose() on an instance that is being managed by using(), for whatever reason, without it becoming a problem or being treated as an error.
« Last Edit: January 07, 2014, 03:59:24 AM by TT »

kdub_nz

  • Mesozoic keyThumper
  • SuperMod
  • Water Moccasin
  • Posts: 2124
  • class keyThumper<T>:ILazy<T>
Re: EXPORTLAYOUTS command example
« Reply #13 on: January 26, 2023, 04:15:50 PM »
It appears that Autodesk's discussion group server is mangling code (removing angle braces :roll:), so I'm posting this here in case anyone needs the actual code.

Code - C#: [Select]
  1.  
  2. < ... trim ... >
  3.  
  4.    /// <summary>
  5.    /// Automates saving/changing/restoring system variables
  6.    /// </summary>
  7.    
  8.    public class ManagedSystemVariable : IDisposable
  9.    {
  10.       string name = null;
  11.       object oldval = null;
  12.  
  13.       public ManagedSystemVariable( string name, object value )
  14.          : this( name )
  15.       {
  16.          Application.SetSystemVariable( name, value );
  17.       }
  18.  
  19.       public ManagedSystemVariable( string name )
  20.       {
  21.          if( string.IsNullOrWhiteSpace( name ) )
  22.             throw new ArgumentException( "name" );
  23.          this.name = name;
  24.          this.oldval = Application.GetSystemVariable( name );
  25.       }
  26.  
  27.       public void Dispose()
  28.       {
  29.          if( oldval != null )
  30.          {
  31.             object temp = oldval;
  32.             oldval = null;
  33.             Application.SetSystemVariable( name, temp );
  34.          }
  35.       }
  36.    }
  37.  
  38. }
  39.  
  40.  


belated thanks Tony.
For this , and all the other gems you're contributed.
:)  ( 10 years after the event )
Called Kerry in my other life
Retired; but they dragged me back in !

I live at UTC + 13.00

---
some people complain about loading the dishwasher.
Sometimes the question is more important than the answer.