TheSwamp

Code Red => .NET => Topic started by: Bryco on December 17, 2010, 07:06:06 PM

Title: Unsafe code and debug
Post by: Bryco on December 17, 2010, 07:06:06 PM
I have a vba routine  that uses the Flatshot routive via sendcommand that takes flatshot views for top front and side views. It also names the blocks and puts the items in the blocks on a layer named after the views.

Trying to do this in C# has proven difficult as sendcommand has worked nor has the p/invoke alternatives mentioned by Kean Walmsley. However Tony's method does work, but only  in debug. When I rebuild in Release and use it I get a crash with no error report.
Anyone have an idea as to why or how to fix it?

Hopefully this will run now
Code: [Select]
        class test
        {


        const int RTNONE = 5000; /* No result */
        const int RTREAL = 5001; /*Real number */
        const int RTPOINT = 5002; /* 2D point X and Y only */
        const int RTSHORT = 5003; /* Short integer */
        const int RTANG = 5004; /* Angle */
        const int RTSTR = 5005; /* String */
        const int RTENAME = 5006; /* Entity name */
        const int RTPICKS = 5007; /* Pick set */
        const int RTORINT = 5008; /* Orientation */
        const int RT3DPOINT = 5009; /* 3D point - X, Y, and Z */
        const int RTLONG = 5010; /* Long integer */
        const int RTVOID = 5014; /* Blank symbol */
        const int RTLB = 5016; /* list begin */
        const int RTLE = 5017; /* list end */
        const int RTDOTE = 5018; /* dotted pair */
        const int RTNIL = 5019; /* nil */
        const int RTDXF0 = 5020; /* DXF code 0 for ads_buildlist only */
        const int RTT = 5021; /* T atom */
        const int RTRESBUF = 5023; /* resbuf */
        const int RTMODELESS = 5027; /* interrupted by modeless dialog */


        //http://forums.autodesk.com/t5/NET/acedcommand-function-in-Net-managed-classes/m-p/1332900
        //TonyT
        //Requires Allow Unsafe code to be checked in Build

        [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl,
            EntryPoint = "acedCmd")]
        extern static int acedCmd(IntPtr pResbuf);

        unsafe static int Command(ResultBuffer args)
        {
            if (!acadApp.DocumentManager.IsApplicationContext)
                return acedCmd((IntPtr)args.UnmanagedObject.ToPointer());
            else
                return 0;
        }








             [CommandMethod("Flatshot3Views")]
        public static void Flatshot3Views()
        {
            Document doc = acadApp.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;
            acadApp.SetSystemVariable("TILEMODE", 1);
            string[] sName = new string[] { "Top", "Front", "Left" };

            ObjectIdCollection ViewIds = new ObjectIdCollection();
            string cLayer = (string)acadApp.GetSystemVariable("CLAYER");
            ViewTableRecord CurrentVtr = ed.GetCurrentView();

           
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                bool hasSolid = false;
                RXClass rxSolid = RXClass.GetClass(typeof(Solid3d));

                ViewTable vt = tr.GetObject(db.ViewTableId, OpenMode.ForWrite) as ViewTable;
                ViewTableRecord vtr;
                //Make the views
                for (int i = 0; i < 3; i++)
                {
                    string name = sName[i];
                    if (vt.Has(name))
                        ViewIds.Add(vt[name]);
                    else
                    {
                        OrthographicView ov;
                        vtr = new ViewTableRecord();
                        vtr.Name = name;

                        if (i == 0)
                            ov = OrthographicView.TopView;
                        else if (i == 1)
                            ov = OrthographicView.FrontView;
                        else
                            ov = OrthographicView.LeftView;

                        vtr.SetViewDirection(ov);
                        vtr.SetUcs(ov);
                        ViewIds.Add(vt.Add(vtr));
                        tr.AddNewlyCreatedDBObject(vtr, true);
                    }
                }
                tr.Commit();
            }

            //Get the insertion point
            PromptPointOptions ppo = new PromptPointOptions("\nPick the insertion point:");
            PromptPointResult per = ed.GetPoint(ppo);
            if (per.Status != PromptStatus.OK) return;
            Matrix3d ucs = ed.CurrentUserCoordinateSystem;
            Point3d p = per.Value.TransformBy(ucs);


            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                for (int i = 0; i < 3; i++)
                {
                    string name = sName[i];
                    ed.SetCurrentView((ViewTableRecord)tr.GetObject(ViewIds[i], OpenMode.ForRead));
                    ResultBuffer args = new ResultBuffer();
                    args.Add(new TypedValue(RTSTR, "Flatshot"));
                    args.Add(new TypedValue(RT3DPOINT, p));
                    args.Add(new TypedValue(5001, 1.0));
                    args.Add(new TypedValue(5001, 1.0));
                    args.Add(new TypedValue(5001, 0.0));

                    try
                    {
                        ed.WriteMessage(Environment.NewLine + "Command=" + Command(args).ToString());
                    }
                    catch (System.Exception ex)
                    {
                        //ed.WriteMessage(Environment.NewLine + ex);
                        throw;
                    }

                    //com doesn't work
                    //((com)acadApp.AcadApplication).ActiveDocument.SendCommand("Flatshot " + sPoint +"    " );

                    ObjectId brefId = SelectLastEnt(doc);
                    BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                    if (brefId != ObjectId.Null)
                    {
                        BlockReference br = tr.GetObject(brefId, OpenMode.ForWrite) as BlockReference;
                        if (br == null) continue;
                        BlockTableRecord btr = tr.GetObject(br.BlockTableRecord, OpenMode.ForWrite) as BlockTableRecord;
                        string bname = name;
                        if (!bt.Has(name))
                            btr.Name = name;
                        else
                        {
                            int j = 1;
                            while (true)
                            {
                                bname = name + j.ToString();
                                if (!bt.Has(bname))
                                {
                                    btr.Name = bname;
                                    break;
                                }
                                j++;
                            }
                        }
                    }
                }
                tr.Commit();
            }
            ed.SetCurrentView(CurrentVtr);
           
        } //end Flatshot3Views



             //Kerry
             static public ObjectId SelectLastEnt(Document doc)
             {
                 PromptSelectionResult LastEnt = doc.Editor.SelectLast();
                 if (LastEnt.Value != null && LastEnt.Value.Count == 1)
                 {
                     return LastEnt.Value[0].ObjectId;
                 }
                 return ObjectId.Null;
             }



        } //endclass test

Title: Re: Unsafe code and debug
Post by: Jeff H on December 20, 2010, 05:47:30 PM
Cannot get it to complie has Layers, ssSet and Util missing
Title: Re: Unsafe code and debug
Post by: Bryco on December 20, 2010, 08:50:16 PM
Jeff I modified it.
Title: Re: Unsafe code and debug
Post by: Kerry on December 20, 2010, 09:02:29 PM
Hi Bryce

In the time since SelectLastEnt(Document doc) was devised AutoDesk has added

public static ObjectId EntLast();
Declaring Type: Autodesk.AutoCAD.Internal.Utils
Assembly: acmgd, Version=18.1.0.0

.. not sure of the initial release version :)

time flies, heh  :wink:
Title: Re: Unsafe code and debug
Post by: Bryco on December 21, 2010, 12:05:51 PM
Thanks Kerry I didn't know that, works in 2010 but
Declaring Type:using Autodesk.AutoCAD.Internal;
then Utils.EntLast();
Title: Re: Unsafe code and debug
Post by: Jeff H on December 21, 2010, 09:36:49 PM
I have no idea how I got it working but this is what happened and steps

1. Checked Allow Unsafe code and built  in debug mode.
2. Checked Allow Unsafe code and built  in release mode.
3. Went through with no solids in drawing with debug bulid and nothing happened.
4. Tried in release mode with no solids and crashed after a couple of the dialog boxes
5. Tried with solids in release mode and crashed right after picked insertion point
6. Rebuilt it and for some reason 2 warnings went away about hasSolid is assinged and not used and another variable with same warning
7. Tried it and work

The only line of code I added

Code: [Select]
[assembly: CommandClass(typeof(ClassLibrary4.test))]
Title: Re: Unsafe code and debug
Post by: Bryco on December 22, 2010, 06:19:20 PM
Thanks for trying it out Jeff.
I can't figure it out either, but with the following code (very little changed) I crashed 1 machine and 4 others didn't.
On the machine that crashed, reopening cad and running it again numerous times there were no more crashes.
So perhaps something gets set after the first try. (Very Scientific explanation lol).
Code: [Select]
using System;

using System.Runtime.InteropServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Internal;
using Autodesk.AutoCAD.Colors;
using acadApp = Autodesk.AutoCAD.ApplicationServices.Application;


[assembly:CommandClass(typeof(Testing.test))]

    namespace Testing
    {
        class test
        {
        const int RTREAL = 5001; /*Real number */
        const int RTSTR = 5005; /* String */
        const int RT3DPOINT = 5009; /* 3D point - X, Y, and Z */

        //http://forums.autodesk.com/t5/NET/acedcommand-function-in-Net-managed-classes/m-p/1332900
        //TonyT
        //Requires Allow Unsafe code to be checked in Build

        [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl,
            EntryPoint = "acedCmd")]
        extern static int acedCmd(IntPtr pResbuf);

        unsafe static int Command(ResultBuffer args)
        {
            if (!acadApp.DocumentManager.IsApplicationContext)
                return acedCmd((IntPtr)args.UnmanagedObject.ToPointer());
            else
                return 0;
        }




        [CommandMethod("Flatshot3Views")]
        public static void Flatshot3Views()
        {
            Document doc = acadApp.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;
            acadApp.SetSystemVariable("TILEMODE", 1);
            string[] sName = new string[] { "Top", "Front", "Left" };

            ObjectIdCollection ViewIds = new ObjectIdCollection();
            string cLayer = (string)acadApp.GetSystemVariable("CLAYER");
            ViewTableRecord CurrentVtr = ed.GetCurrentView();

            try
            {
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                    bool hasSolid = false;
                    RXClass rxSolid = RXClass.GetClass(typeof(Solid3d));

                    //Check for no solids
                    foreach (ObjectId blockId in bt)
                    {
                        foreach (ObjectId id in (BlockTableRecord)tr.GetObject(blockId, OpenMode.ForRead))
                        {
                            if (id.ObjectClass.IsDerivedFrom(rxSolid))
                            {
                                hasSolid = true;
                                break;
                            }
                        }
                        if (!hasSolid)
                        {
                            System.Windows.Forms.MessageBox.Show("There are no solids.");
                            return;
                        }
                    }

                    ViewTable vt = tr.GetObject(db.ViewTableId, OpenMode.ForWrite) as ViewTable;
                    ViewTableRecord vtr;
                    //Make the views
                    for (int i = 0; i < 3; i++)
                    {
                        string name = sName[i];
                        if (vt.Has(name))
                            ViewIds.Add(vt[name]);
                        else
                        {
                            OrthographicView ov;
                            vtr = new ViewTableRecord();
                            vtr.Name = name;

                            if (i == 0)
                                ov = OrthographicView.TopView;
                            else if (i == 1)
                                ov = OrthographicView.FrontView;
                            else
                                ov = OrthographicView.LeftView;

                            vtr.SetViewDirection(ov);
                            vtr.SetUcs(ov);
                            ViewIds.Add(vt.Add(vtr));
                            tr.AddNewlyCreatedDBObject(vtr, true);
                        }
                    }
                    tr.Commit();
                }

                //Get the insertion point
                PromptPointOptions ppo = new PromptPointOptions("\nPick the insertion point:");
                PromptPointResult per = ed.GetPoint(ppo);
                if (per.Status != PromptStatus.OK) return;
                Matrix3d ucs = ed.CurrentUserCoordinateSystem;
                Point3d p = per.Value.TransformBy(ucs);
                ed.WriteMessage(Environment.NewLine + "Set the Foreground Lines color field to bylayer" + Environment.NewLine);
                //Add layers
                AddLayer("Top", 200, "Continuous");
                AddLayer("Front", 205, "Continuous");
                AddLayer("Left", 210, "Continuous");

                AddLayer("Top-Hidden", 5, "HIDDEN2");
                AddLayer("Front-Hidden", 5, "HIDDEN2");
                AddLayer("Left-Hidden", 5, "HIDDEN2");

                ResultBuffer args = new ResultBuffer();
                args.Add(new TypedValue(RTSTR, "Flatshot"));
                args.Add(new TypedValue(RT3DPOINT, p));
                args.Add(new TypedValue(5001, 1.0));
                args.Add(new TypedValue(5001, 1.0));
                args.Add(new TypedValue(5001, 0.0));


                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    for (int i = 0; i < 3; i++)
                    {
                        string name = sName[i];
                        acadApp.SetSystemVariable("CLAYER",name);
                        ed.SetCurrentView((ViewTableRecord)tr.GetObject(ViewIds[i], OpenMode.ForRead));
                       
                        try
                        {
                             Command(args);
                        }
                        catch (System.Exception ex)
                        {
                            ed.WriteMessage(Environment.NewLine + ex);
                            throw;
                        }


                        //((com)acadApp.AcadApplication).ActiveDocument.SendCommand("Flatshot " + sPoint +"    " );


                        ObjectId brefId = Utils.EntLast();
                        BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                        if (brefId != ObjectId.Null)
                        {
                            BlockReference br = tr.GetObject(brefId, OpenMode.ForWrite) as BlockReference;
                            if (br == null) continue;
                            BlockTableRecord btr = tr.GetObject(br.BlockTableRecord, OpenMode.ForWrite) as BlockTableRecord;
                            string bname = name;
                            if (!bt.Has(name))
                                btr.Name = name;
                            else
                            {
                                int j = 1;
                                while (true)
                                {
                                    bname = name + j.ToString();
                                    if (!bt.Has(bname))
                                    {
                                        btr.Name = bname;
                                        break;
                                    }
                                    j++;
                                }
                            }
                            foreach (ObjectId entId in btr)
                            {
                                Entity ent = tr.GetObject(entId, OpenMode.ForWrite) as Entity;
                                if (ent.ColorIndex == 0)
                                {
                                    ent.Layer = sName[i] + "-Hidden";
                                    ent.ColorIndex = 256;
                                    ent.Linetype = "ByLayer";
                                }
                            }
                        }
                    }
                    tr.Commit();
                }

                acadApp.SetSystemVariable("CLAYER",cLayer);
                ed.SetCurrentView(CurrentVtr);
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage(Environment.NewLine + ex);
                throw;
            }
        } //end Flatshot3Views



        static private bool AddLayer(string sLayer, short iColor, string sLtype)
        {

            Database db = HostApplicationServices.WorkingDatabase;     
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
                if (lt.Has(sLayer)) return true;
                lt.UpgradeOpen();
                LayerTableRecord ltr = new LayerTableRecord();
                ltr.Name = sLayer;
                ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, iColor);
                LinetypeTable linet = tr.GetObject(db.LinetypeTableId, OpenMode.ForRead, false) as LinetypeTable;
                if (linet.Has(sLtype))
                    ltr.LinetypeObjectId = linet[sLtype];
                else
                {
                    string Acadlinetypes ="acadiso.lin";           
                    AddLinetype(sLtype, Acadlinetypes,db);
                }
                lt.Add(ltr);
                tr.AddNewlyCreatedDBObject(ltr, true);
                tr.Commit();
                return true;
            }

        } //end AddLayerCode


        static public ObjectId AddLinetype(string sLType, string sFile, Database db)
        {
            ObjectId LTypeID = ObjectId.Null;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                LinetypeTable LTypeTable = tr.GetObject
                    (db.LinetypeTableId, OpenMode.ForRead, false) as LinetypeTable;
                if (LTypeTable.Has(sLType))
                    LTypeID = LTypeTable[sLType];
                else
                {
                    db.LoadLineTypeFile(sLType, sFile);
                    LTypeID = LTypeTable[sLType];
                }
                tr.Commit();
                return LTypeID;
            }
        } //end AddLinetype



        } //endclass test
     

}
Title: Re: Unsafe code and debug
Post by: Jeff H on December 22, 2010, 10:19:40 PM
Maybe saying it needed to warm up sounds more scientific
Title: Re: Unsafe code and debug
Post by: It's Alive! on December 22, 2010, 11:28:16 PM
Are you using attach to process?  If it's an access violation or similar, you may be able to  catch it there..
or it could be a gamma ray blasting a single bit in memory, those bugs are really hard to catch
Title: Re: Unsafe code and debug
Post by: Bryco on December 23, 2010, 10:24:33 AM
I don't know what attach to process is, can I do that with express?
Title: Re: Unsafe code and debug
Post by: It's Alive! on December 23, 2010, 11:09:47 AM
Ack! probably not with express, sorry   :|
Title: Re: Unsafe code and debug
Post by: Bryco on March 07, 2012, 01:26:52 PM
2010 Express and cad 2012  won't run this at all without crashing. Must be less leeway with threading or something
Title: Re: Unsafe code and debug
Post by: TheMaster on March 08, 2012, 04:07:56 AM
2010 Express and cad 2012  won't run this at all without crashing. Must be less leeway with threading or something

Look at the type of the parameter in the declaration for acedCmd(),
and look at the type of the ResultBuffer's UnmanagedObject
property.

Title: Re: Unsafe code and debug
Post by: exmachina on March 08, 2012, 05:29:03 PM
sorry, maybe I'm wrong but acedCmd expects an IntPtr, not a pointer.

just today i am playing with a similar code (not for autocad)
Code - C#: [Select]
  1. public unsafe int AnnotationGetStyles(int line, out byte[] styles) {
  2.   int lenght = SendScintilla(Constants.SCI_ANNOTATIONGETSTYLES, line);
  3.   if (lenght == 0) {
  4.     styles = null;
  5.     return 0;
  6.   }
  7.   styles = new byte[lenght];
  8.   fixed (byte* pB = &styles[0]) // THIS A POINTER
  9.     SendScintilla(Constants.SCI_ANNOTATIONGETSTYLES, (IntPtr)line, (IntPtr)pB);
  10.   return lenght;
  11. }
  12.  
  13. public IntPtr SendScintilla(uint msg, IntPtr wParam, IntPtr lParam) {
Title: Re: Unsafe code and debug
Post by: Bryco on March 08, 2012, 07:33:14 PM
(IntPtr)args.UnmanagedObject.ToPointer()  AND  args.UnmanagedObject  print out as system.intPtr so I am lost on this one.
Title: Re: Unsafe code and debug
Post by: exmachina on March 08, 2012, 08:31:54 PM
Ok. Look at 'public static class CommandLine'. This works well (at least in acad 2007) in the IDE, in 'debug' and also in 'release';  and without the IDE

Is based on an example provided by Tony Tanzillo.
Title: Re: Unsafe code and debug
Post by: Bryco on March 08, 2012, 09:12:21 PM
Thanks but I don't think that is any different than what I am using (It's Tony's as well).
I can't even figure out why it works sometimes, especially when I add a messagebox, but changing anything in the flatshot gui  nearly always causes a crash.
Title: Re: Unsafe code and debug
Post by: exmachina on March 08, 2012, 09:39:10 PM
Sorry. I am not a programmer and I'm not a regular user of Autocad.
Perhaps the failure is due to a limitation of acedcmd (or flatshot command) or maybe not using the correct arguments. I searched through the documentation of ObjectARX, but I have not found any explanation on this problem

I tried your code but I can not reproduce the problem. The code ask for a point. Then enters in a loop which calls the 'flatshot' command , but nothing is crashed???
Title: Re: Unsafe code and debug
Post by: TheMaster on March 09, 2012, 03:27:43 AM
(IntPtr)args.UnmanagedObject.ToPointer()  AND  args.UnmanagedObject  print out as system.intPtr so I am lost on this one.

They may both print out as IntPtr because *void has no managed representation.

The ToPointer() method returns the raw pointer value that can
only be used in unsafe code, but there's no need for it.

See http://msdn.microsoft.com/en-us/library/system.intptr.topointer.aspx

I think that may have been one of the first posts I made about
P/Invoke and acedCmd(), so it was probably wrong. There's no
need for unsafe code, you just do this:

      acedCmd( resbuf.UnmanagedObject );

Title: Re: Unsafe code and debug
Post by: kaefer on March 09, 2012, 04:30:57 AM
Perhaps the failure is due to a limitation of acedcmd (or flatshot command)

That's the most plausible explanation.

Did anyone try late bound SendCommand (demonstrated here (http://through-the-interface.typepad.com/through_the_interface/2011/11/generating-preview-images-for-all-blocks-in-an-autocad-drawing-using-net.html)) on this one?
Title: Re: Unsafe code and debug
Post by: Bryco on March 09, 2012, 05:38:22 PM
Kaeker, I tried it and it is not asynchronous.(If I got the right word) and the 3 commands execute after the code.

exmachina, thanks for trying but the code works fine up till acad 2008,  crashes one time only per computer in 2009 to 2011 then works fine. 2012  hardly ever. So I think it is a net version issue.

TheMaster- I tried    acedCmd( resbuf.UnmanagedObject ); but no difference.
I think it is the fact that the command has a gui that causes the problem.
I'm just experimenting w/ filedia=0;

Title: Re: Unsafe code and debug
Post by: TheMaster on March 10, 2012, 01:36:58 PM
Kaeker, I tried it and it is not asynchronous.(If I got the right word) and the 3 commands execute after the code.

exmachina, thanks for trying but the code works fine up till acad 2008,  crashes one time only per computer in 2009 to 2011 then works fine. 2012  hardly ever. So I think it is a net version issue.

TheMaster- I tried    acedCmd( resbuf.UnmanagedObject ); but no difference.
I think it is the fact that the command has a gui that causes the problem.
I'm just experimenting w/ filedia=0;

I've seen bad things happen when acedCmd() is used inside a transaction boundary.

Title: Re: Unsafe code and debug
Post by: kaefer on March 11, 2012, 12:01:04 PM
Kaeker, I tried it and it is not asynchronous.(If I got the right word) and the 3 commands execute after the code.

Disagree: the exection of your code is stayed while SendCommand waits for completion of the dialog (it is synchronous, just what Kean said). The problem at least with my AutoCAD is that there's no command line version of the FLATSHOT command.
Title: Re: Unsafe code and debug
Post by: TheMaster on March 11, 2012, 12:15:50 PM
Kaeker, I tried it and it is not asynchronous.(If I got the right word) and the 3 commands execute after the code.

Disagree: the exection of your code is stayed while SendCommand waits for completion of the dialog (it is synchronous, just what Kean said). The problem at least with my AutoCAD is that there's no command line version of the FLATSHOT command.

If I'm not mistaken, if a dialog temporarily hides itself to allow the user to supply graphical input (e.g, picking points), SendCommand() can not run synchronously. 

Title: Re: Unsafe code and debug
Post by: kaefer on March 11, 2012, 01:02:12 PM
If I'm not mistaken, if a dialog temporarily hides itself to allow the user to supply graphical input (e.g, picking points), SendCommand() can not run synchronously.

Can acedCmd, apart from an indication that something went wrong?

Take this command string:
Code: [Select]
string args = System.String.Format("_Flatshot {0},{1},{2} 1.0 1.0 0.0\n", p.X, p.Y, p.Z);

Picking an existing block won't work. But feeding it to SendCommand and just clicking "Create" kind of does, in a way, perhaps.
Title: Re: Unsafe code and debug
Post by: Bryco on March 11, 2012, 05:06:56 PM
well acedCmd worked fine in 2011. Part of the function requires changing the layer in the gui to  ByLayer.  So in 2011 express 2008  it worked.
I tried not having it in a transaction and no diference.
CommandFlags.Interruptible doesn't help
No problem when using CommandFlags.NoMultiple or was it Singlethread (Sorry I have express 2008 at home and not available) as the gui never comes up

kaefer  apologies for spelling your name wrong in previous post, was typo
Title: Re: Unsafe code and debug
Post by: TheMaster on March 11, 2012, 06:22:13 PM
If I'm not mistaken, if a dialog temporarily hides itself to allow the user to supply graphical input (e.g, picking points), SendCommand() can not run synchronously.

Can acedCmd, apart from an indication that something went wrong?

Take this command string:
Code: [Select]
string args = System.String.Format("_Flatshot {0},{1},{2} 1.0 1.0 0.0\n", p.X, p.Y, p.Z);

Picking an existing block won't work. But feeding it to SendCommand and just clicking "Create" kind of does, in a way, perhaps.

It actually has a lot to do with how Flatshot is implemented. When using
acedCmd() from an ObjectARX command, there is a risk of running out of
command-nesting levels (which I believe is 4 levels). So, if your command
executes an AutoCAD command that itself executes one or more other
AutoCAD commands internally, the number of nested command invocations
can be more than AutoCAD allows, and the whole thing goes up on smoke.

The way around that is to use [LispFunction("C:COMMANDNAME")] rather
than [CommandMethod("COMMANDNAME")], to define the command.

Many internal commands check to see if they're being scripted from LISP
(or acedCmd() more precisely) and do things differently to accommodate
that, so I suspect that because it is very much by-design that FLATSHOT
not be scriptable, it most-likely doesn't do much in support of scripting, and
could even be doing things to sabotage attempts to script it.

Another cause of many problems with scripting AutoCAD commands that
have GUIs, is the Action Recorder (The layer dialogs are an example). To
be recordable a GUI command does everything by sending commands to
the command line using an internal equivalent of SendStringToExecute().
That can really mess up attempts to script commands too.

Other possible causes of problems are running acedCmd() inside of a
transaction, and not ensuring that a command that is started by a call
to acedCmd() has ended before the command that calls it ends, which
usually crashes AutoCAD because nested transactions are not ended
in the reverse order in which they were started.

Title: Re: Unsafe code and debug
Post by: kaefer on March 11, 2012, 06:49:14 PM
kaefer  apologies for spelling your name wrong in previous post, was typo

Bryco, apologies too for believing your original code was beyond redemption.

If I change the extern declaration and wrapper to that what TheMaster said, viz.
Code - C#: [Select]
  1.         [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl,
  2.             EntryPoint = "acedCmd")]
  3.         extern static ErrorStatus acedCmd(IntPtr pResbuf);
  4.  
  5.         static ErrorStatus Command(ResultBuffer args)
  6.         {
  7.             if (!acadApp.DocumentManager.IsApplicationContext)
  8.                 return acedCmd(args.UnmanagedObject);
  9.             else
  10.                 return ErrorStatus.InvalidContext;
  11.         }

it starts to make sense. It is still very ugly (mixing of manipulation of system variables, managed Editor properies inside transactions and command invokations just doesn't feel right - especially not commands that weren't designed with scripting in mind).

The SendCommand() variant runs (or not, depending on user input) with CommandFlags.Session and copious amounts of Document locking, to make AcadApplication aware of the changes to its state. An exhibit is attached.


Title: Re: Unsafe code and debug
Post by: Bryco on March 12, 2012, 02:28:29 PM
kaefer that seems to work just fine, thanks and well done. I guess the command couldn't be  synchronous without CommandFlags.Session. I tried everything but that thinking it was about working in different drawings not just different threads