Use COM Interfaces with COM Interop for AutoCAD OEM

There are some usefull COM Interfaces predating the introduction of the .NET software development platform for the customization of the AutoCAD ( COM Interoperability (.NET)).

When developing for AutoCAD OEM, you have to be aware that the approach to work with the COM Interfaces is different than when working with a vanilla AutoCAD (see here for more details)

Here is some sample code where an existing drawing is openend and all layouts are cycled to zoom the paperspace to the maximum extents.

using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;

using AcApp2 = Autodesk.AutoCAD.ApplicationServices.Application;
using AcApp = Autodesk.AutoCAD.ApplicationServices;
using AcRt = Autodesk.AutoCAD.Runtime;
using AcDb = Autodesk.AutoCAD.DatabaseServices;
using AcIop = Autodesk.AutoCAD.Interop;
[assembly: AcRt.ExtensionApplication(null)]
[assembly: AcRt.CommandClass(typeof(layout.ZoomLayoutsToExtents))]
namespace Zoom
{
    public class Layouts
    {
        [AcRt.CommandMethod("ZoomLayoutsToExtents", AcRt.CommandFlags.Session)]
        public void ZoomLayoutsToExtents()
        {
            try
            {
                /* get path to executing assembly */
                string dwgPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

                /* Full file path to a sample drawing that is located in the same directory as the executing assembly */
                dwgPath += "test.dwg";

                /* close document without saving */
                bool bSaveDocumentBeforeClosing = false;

                /* open document from filepath */
                AcApp.DocumentCollection docCol = AcApp2.DocumentManager;
                AcApp.Document doc = AcApp.DocumentCollectionExtension.Open(docCol, dwgPath, false);
                docCol.MdiActiveDocument = doc;
                AcDb.Database db = doc.Database;

                /* ObjectId List to store the objectIds for all layouts */
                List<AcDb.ObjectId> oIdList = new List<AcDb.ObjectId>();

                /* Here we get the objectId's of all layouts */
                using (AcApp.DocumentLock docLoc = AcApp2.DocumentManager.MdiActiveDocument.LockDocument())
                {                   
                    using (AcDb.Transaction trans = db.TransactionManager.StartTransaction())
                    {
                        /* switch to modelspace */
                        AcDb.LayoutManager.Current.CurrentLayout = @"Model";
                        /* ALTERNATIVE >> */
                        /* AcApp.Application.SetSystemVariable("TILEMODE", 1); */

                        /* get layout dictionary */
                        AcDb.DBDictionary layoutDict = trans.GetObject(db.LayoutDictionaryId, AcDb.OpenMode.ForRead) as AcDb.DBDictionary;

                        /* get objectId's from dictionary */
                        foreach (AcDb.DBDictionaryEntry entry in layoutDict)
                            oIdList.Add(entry.Value);
                       
                        trans.Commit();

                    } // using trans

                } // using trans

                
                /* this works with vanilla AutoCAD */
                #if !OEM
                dynamic thisApp = AcApp.Application.AcadApplication;

                /* dynamic does NOT work for AutoCAD OEM */
                /* Use AcIop.AcadApplication instead */
                #elif OEM
                AcIop.AcadApplication thisApp = AcApp.Application.AcadApplication as AcIop.AcadApplication;
                #endif


                /* lock the document for editing */
                using (AcApp.DocumentLock docLoc = doc.LockDocument())
                {
                    /* cycle objectId's */
                    foreach (AcDb.ObjectId oId in oIdList)
                    {
                        AcDb.LayoutManager.Current.SetCurrentLayoutId(oId);

                        if (AcDb.LayoutManager.Current.CurrentLayout != "Model")
                        {
                            using (var tr = doc.Database.TransactionManager.StartTransaction())
                            {
                                /* https://forums.autodesk.com/t5/net/how-to-activate-viewport-in-paperspace/td-p/12740814 */
                                doc.Editor.SwitchToPaperSpace();
                                tr.Commit();
                            }
                        }

                        /* zoom paperspace to extents */
                        thisApp.GetType().InvokeMember("ZoomExtents", BindingFlags.InvokeMethod, null, thisApp, null);                                
                    }
                }

                /* The command MUST be run in AcRt.CommandFlags.Session */
                /* otherwise, there is a System.Runtime.InteropServices.COMException */
                if (bSaveDocumentBeforeClosing)
                    AcApp.DocumentExtension.CloseAndSave(doc, dwgPath);
                else
                    AcApp.DocumentExtension.CloseAndDiscard(doc);
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(
                    ex.Message.ToString(), 
                    @"Error Message", 
                    System.Windows.Forms.MessageBoxButtons.OK, 
                    System.Windows.Forms.MessageBoxIcon.Error
                    );    
            }

        } // END

    } // class

} // namespace

The zoom is done with this COM Interop:

thisApp.GetType().InvokeMember("ZoomExtents", BindingFlags.InvokeMethod, null, thisApp, null);

For vanilla AutoCAD you can use

dynamic thisApp = AcApp.Application.AcadApplication;

to get a variable for the AutoCAD application.

This will not work for AutoCAD OEM.
For AutoCAD OEM you have to add the using to the interop:

using AcIop = Autodesk.AutoCAD.Interop;

and an explicit variable assignment with

AcIop.AcadApplication thisApp = AcApp.Application.AcadApplication as AcIop.AcadApplication;

You can do this to target vanilla AutoCAD and AutoCAD OEM:

/* this works with vanilla AutoCAD */
#if !OEM
dynamic thisApp = AcApp.Application.AcadApplication;

/* dynamic does NOT work for AutoCAD OEM */
/* Use AcIop.AcadApplication instead */
#elif OEM
AcIop.AcadApplication thisApp = AcApp.Application.AcadApplication as AcIop.AcadApplication;
#endif

When you want to use the variable as a parameter for a subroutine, you have to avoid some implicit casting. This will not work with AutoCAD OEM:

        private void SomeSubRoutine(
            dynamic thisApp);

Instead, you have to do this with explicit variable parameters:

        private void SomeSubRoutine(
            #if !OEM
            dynamic thisApp
            #elif OEM
            AcIop.AcadApplication thisApp
            #endif
            )
2 Likes

Thanks for this @jm1