Author Topic: AAARRRGGGHHH!!!!!!! >.< Cancel active command  (Read 14580 times)

0 Members and 1 Guest are viewing this topic.

WILL HATCH

  • Bull Frog
  • Posts: 450
AAARRRGGGHHH!!!!!!! >.< Cancel active command
« on: March 17, 2013, 02:01:12 AM »
I've got a form that has the ability to open a drawing, but have run into a complication in the event there is an active command so I've attempted to cancel any potentially active command before opening the document
Code - C#: [Select]
  1.                     if (Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.Count > 0)
  2.                     {
  3.                         try
  4.                         {
  5.                             //acedCommand(new object[3] {(int)5005, String.Format("{0}{0}", (char)27), 0});
  6.                             acedCommand(new object[1] { 0 });
  7.                             //AcDb.ResultBuffer args = new AcDb.ResultBuffer();
  8.                             //args.Add(new AcDb.TypedValue(5005, String.Format("{0}{0}", (char)27)));
  9.                             //Command(args);
  10.                             //acedPostCommand("CANCELCMD");
  11.                            
  12.                             //acedPostCommand(String.Format("{0}{0}", (char)27));
  13.                             //System.Threading.Thread.Sleep(0);
  14.                             //Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.SendStringToExecute(String.Format("{0}{0}", (char)27), true, false, true);
  15.                             //((AcadDocument)((Document)Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument).GetAcadDocument()).SendCommand(String.Format("{0}{0}", (char)27));
  16.                             //System.Threading.Thread.Sleep(500);
  17.                         }
  18.                         catch (System.Exception ex)
  19.                         {
  20.                             MessageBox.Show(String.Format("{0}", ex));
  21.                         }
  22.                     }
  23.                     try
  24.                     {
  25.                        
  26.                         ((DocumentCollection)Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager).Open(((FileInfo)item.Tag).FullName, false);
  27.                     }
  28.                     catch (System.Exception ex)
  29.                     {
  30.                         MessageBox.Show(String.Format("{0}\n{1}", ((FileInfo)item.Tag).FullName, ex));
  31.                     }
I get an error COMExeption Invalid execution context and the drawing does not open.

First I tried to find a method of testing for an active command without any luck, would have liked to just tell the user to finish what the hell they were doing before opening another doc.

Then I tried using the SendCommand method through the COM interface.  I still get the same error, however after getting the error I can see at the command line that the escape command has been sent.  I tried to remedy this with a Thread.Sleep call, but I still get the error.  The drawing does however load, but then the focus switches back to the original document, which is pretty user unfriendly  :realmad:

Then I tried using acedPostCommand as was suggested in this post by Kean but I still get the same results.

Then I tried using acedCmd as was suggested here by Tony, as below:
Code - C#: [Select]
  1.             [System.Runtime.InteropServices.DllImport("accore.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "acedCmd")]
  2.             extern static int acedCmd(IntPtr pResbuf);
  3.             unsafe static int Command(Autodesk.AutoCAD.DatabaseServices.ResultBuffer args)
  4.             {
  5.                 if (!Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.IsApplicationContext)
  6.                     return acedCmd((IntPtr)args.UnmanagedObject.ToPointer());
  7.                 else
  8.                     return 0;
  9.             }
  10.  
  11. //Then in my routine:
  12.                             AcDb.ResultBuffer args = new AcDb.ResultBuffer();
  13.                             args.Add(new AcDb.TypedValue(5005, String.Format("{0}{0}", (char)27)));
  14.                             Command(args);
But I'm getting the COM error still and the following error posted to the command line of the active document: Invalid type in acutBuildList() arg #1
Although, there were a couple times where for some unknown reason when I manually cancelled the command the document opened

Then I tried using acedCommand as follows with no sign of any command being sent to execute, same COM error:
Code - C#: [Select]
  1.             [System.Runtime.InteropServices.DllImport("accore.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "acedCommand")]
  2.             extern static int acedCommand(object[] args);
  3.  
  4. //Then in my routine:
  5. acedCommand(new object[3] {(int)5005, String.Format("{0}{0}", (char)27), 0});
  6.  
  7. //Also tried passing just 0 as mentioned in the docs to just cancel a command
  8. acedCommand(new object[1] {0});

Can anyone comment on why it is that this is throwing a COM exception when I'm running purely through .NET by calling the .Open method?

Thanks
« Last Edit: March 17, 2013, 02:04:22 AM by WILL HATCH »

TheMaster

  • Guest
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #1 on: March 17, 2013, 05:13:40 AM »
I've got a form that has the ability to open a drawing, but have run into a complication in the event there is an active command so I've attempted to cancel any potentially active command before opening the document
Code - C#: [Select]
  1.                     if (Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.Count > 0)
  2.                     {
  3.                         try
  4.                         {
  5.                             //acedCommand(new object[3] {(int)5005, String.Format("{0}{0}", (char)27), 0});
  6.                             acedCommand(new object[1] { 0 });
  7.                             //AcDb.ResultBuffer args = new AcDb.ResultBuffer();
  8.                             //args.Add(new AcDb.TypedValue(5005, String.Format("{0}{0}", (char)27)));
  9.                             //Command(args);
  10.                             //acedPostCommand("CANCELCMD");
  11.                            
  12.                             //acedPostCommand(String.Format("{0}{0}", (char)27));
  13.                             //System.Threading.Thread.Sleep(0);
  14.                             //Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.SendStringToExecute(String.Format("{0}{0}", (char)27), true, false, true);
  15.                             //((AcadDocument)((Document)Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument).GetAcadDocument()).SendCommand(String.Format("{0}{0}", (char)27));
  16.                             //System.Threading.Thread.Sleep(500);
  17.                         }
  18.                         catch (System.Exception ex)
  19.                         {
  20.                             MessageBox.Show(String.Format("{0}", ex));
  21.                         }
  22.                     }
  23.                     try
  24.                     {
  25.                        
  26.                         ((DocumentCollection)Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager).Open(((FileInfo)item.Tag).FullName, false);
  27.                     }
  28.                     catch (System.Exception ex)
  29.                     {
  30.                         MessageBox.Show(String.Format("{0}\n{1}", ((FileInfo)item.Tag).FullName, ex));
  31.                     }
I get an error COMExeption Invalid execution context and the drawing does not open.

First I tried to find a method of testing for an active command without any luck, would have liked to just tell the user to finish what the hell they were doing before opening another doc.

Then I tried using the SendCommand method through the COM interface.  I still get the same error, however after getting the error I can see at the command line that the escape command has been sent.  I tried to remedy this with a Thread.Sleep call, but I still get the error.  The drawing does however load, but then the focus switches back to the original document, which is pretty user unfriendly  :realmad:

Then I tried using acedPostCommand as was suggested in this post by Kean but I still get the same results.

Then I tried using acedCmd as was suggested here by Tony, as below:
Code - C#: [Select]
  1.             [System.Runtime.InteropServices.DllImport("accore.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "acedCmd")]
  2.             extern static int acedCmd(IntPtr pResbuf);
  3.             unsafe static int Command(Autodesk.AutoCAD.DatabaseServices.ResultBuffer args)
  4.             {
  5.                 if (!Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.IsApplicationContext)
  6.                     return acedCmd((IntPtr)args.UnmanagedObject.ToPointer());
  7.                 else
  8.                     return 0;
  9.             }
  10.  
  11. //Then in my routine:
  12.                             AcDb.ResultBuffer args = new AcDb.ResultBuffer();
  13.                             args.Add(new AcDb.TypedValue(5005, String.Format("{0}{0}", (char)27)));
  14.                             Command(args);
But I'm getting the COM error still and the following error posted to the command line of the active document: Invalid type in acutBuildList() arg #1
Although, there were a couple times where for some unknown reason when I manually cancelled the command the document opened

Then I tried using acedCommand as follows with no sign of any command being sent to execute, same COM error:
Code - C#: [Select]
  1.             [System.Runtime.InteropServices.DllImport("accore.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "acedCommand")]
  2.             extern static int acedCommand(object[] args);
  3.  
  4. //Then in my routine:
  5. acedCommand(new object[3] {(int)5005, String.Format("{0}{0}", (char)27), 0});
  6.  
  7. //Also tried passing just 0 as mentioned in the docs to just cancel a command
  8. acedCommand(new object[1] {0});

Can anyone comment on why it is that this is throwing a COM exception when I'm running purely through .NET by calling the .Open method?

Thanks

You're passing the ResultBuffer's UnmanagedObject.ToPointer() to acedCmd() which is wrong. You pass it only the UnmanagedObject property, which is an IntPtr, and that's it.

Also, you really don't have to screw around with P/Invoke to use the CommandLine. You can call the
Editor's RunCommand method which in addition to doing the messy stuff, also converts managed types to native types, including SelectionSets.

You can use this reflection-free wrapper to invoke RunCommand(), which is a little faster than calling it via Reflection:

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. using Autodesk.AutoCAD.ApplicationServices;
  8.  
  9. namespace Autodesk.AutoCAD.EditorInput
  10. {
  11.    public static class EditorInputExtensionMethods
  12.    {
  13.       public static PromptStatus Command( this Editor editor, params object[] args )
  14.       {
  15.          if( editor == null )
  16.             throw new ArgumentNullException( "editor" );
  17.          return runCommand( editor, args );
  18.       }
  19.  
  20.       static Func<Editor, object[], PromptStatus> runCommand = GenerateRunCommand();
  21.  
  22.       static Func<Editor, object[], PromptStatus> GenerateRunCommand()
  23.       {
  24.          MethodInfo method = typeof( Editor ).GetMethod( "RunCommand",
  25.             BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
  26.          var instance = Expression.Parameter( typeof( Editor ) );
  27.          var args = Expression.Parameter( typeof( object[] ) );
  28.          return Expression.Lambda<Func<Editor, object[], PromptStatus>>(
  29.             Expression.Call( instance, method, args ), instance, args )
  30.                .Compile();
  31.       }
  32.    }
  33. }
  34.  
  35.  

BlackBox

  • King Gator
  • Posts: 3770
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #2 on: March 17, 2013, 02:15:17 PM »
Why cast the (Namespace qualified) DocumentManager to DocumentCollection in the call to Open()?
"How we think determines what we do, and what we do determines what we get."

TheMaster

  • Guest
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #3 on: March 17, 2013, 03:48:25 PM »
Why cast the (Namespace qualified) DocumentManager to DocumentCollection in the call to Open()?

Most-likely because there was no using statement including the namespace containing the DocumentCollectionExtension class that has the Open() extension method in 2013, but that has absolutely nothing to do with the problem the OP has.

AutoCAD 2013 sets a new standard, insofar as breaking customer applications goes.

TheMaster

  • Guest
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #4 on: March 17, 2013, 03:51:56 PM »
Can anyone comment on why it is that this is throwing a COM exception when I'm running purely through .NET by calling the .Open method?

The DocumentCollection (or DocumentCollectionExtension in 2013) Open() method delegates to the AcadDocument COM wrapper's Open() method under the hood.

Take a look at it with Reflector.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #5 on: March 17, 2013, 05:24:23 PM »
You're passing the ResultBuffer's UnmanagedObject.ToPointer() to acedCmd() which is wrong. You pass it only the UnmanagedObject property, which is an IntPtr, and that's it.
Thanks, I updated my code to reflect with no success.  There was no error however.
Quote

Also, you really don't have to screw around with P/Invoke to use the CommandLine. You can call the
Editor's RunCommand method which in addition to doing the messy stuff, also converts managed types to native types, including SelectionSets.

You can use this reflection-free wrapper to invoke RunCommand(), which is a little faster than calling it via Reflection:

I tried that and still no success or sign of functional operation.
Code - C#: [Select]
  1. Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.Command(String.Format("{0}{0}", (char)27));
I suspected there might be an issue with the active document not being active since the focus was on my form within a palette.  To test this I tried using Editor.WriteMessage and found that my message was being written to the expected document window.

As a work around I'm considering simulating a double click on the .dwg file through explorer.  But I'm unsure of the needed arguments in the process start.

Thanks for your efforts!

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #6 on: March 17, 2013, 05:48:41 PM »
Why cast the (Namespace qualified) DocumentManager to DocumentCollection in the call to Open()?
Tony nailed that one.  Since this is a WPF form that I'm working inside I have used the fewest possible using declarations.  This is the only segment that has any references to autocad.  I was able to cut out the other reference thanks to a post regarding getting the preview from the .dwg file.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #7 on: March 17, 2013, 05:58:08 PM »
You can use this reflection-free wrapper to invoke RunCommand(), which is a little faster than calling it via Reflection:
Update: I tried using Command("^C^C") as is done in scripts... no such luck

BlackBox

  • King Gator
  • Posts: 3770
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #8 on: March 17, 2013, 06:48:45 PM »
Can anyone comment on why it is that this is throwing a COM exception when I'm running purely through .NET by calling the .Open method?

The DocumentCollection (or DocumentCollectionExtension in 2013) Open() method delegates to the AcadDocument COM wrapper's Open() method under the hood.

Take a look at it with Reflector.

Sorry for the perhaps silly question; I'm relegate to Express. As I understand it that precludes my using extensions like Reflector, etc.
"How we think determines what we do, and what we do determines what we get."

TheMaster

  • Guest
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #9 on: March 17, 2013, 08:02:47 PM »
Can anyone comment on why it is that this is throwing a COM exception when I'm running purely through .NET by calling the .Open method?

The DocumentCollection (or DocumentCollectionExtension in 2013) Open() method delegates to the AcadDocument COM wrapper's Open() method under the hood.

Take a look at it with Reflector.

Sorry for the perhaps silly question; I'm relegate to Express. As I understand it that precludes my using extensions like Reflector, etc.

Reflector is a standalone tool, it has an extension for VS but that's not needed to use it.

TheMaster

  • Guest
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #10 on: March 17, 2013, 08:06:25 PM »
You can use this reflection-free wrapper to invoke RunCommand(), which is a little faster than calling it via Reflection:
Update: I tried using Command("^C^C") as is done in scripts... no such luck

If DocumentCollection.IsApplicationContext == true, You can't use the command line or the Command() wrapper for the RunCommand method.

The way to deal with the problem is to not do anything in your UI handler (e.g., button click, etc.) other than to set a flag, and you should add a handler to the Application's Idle event, and in the handler for that event, check and clear the flag, and if it is set, then try using SendStringToExecute() to cancel the current command as per Kean's blog post.

You can't do much from the WPF UI thread, and that seems to be the problem.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #11 on: March 17, 2013, 08:13:20 PM »
You can use this reflection-free wrapper to invoke RunCommand(), which is a little faster than calling it via Reflection:
Update: I tried using Command("^C^C") as is done in scripts... no such luck

If DocumentCollection.IsApplicationContext == true, You can't use the command line or the Command() wrapper for the RunCommand method.

The way to deal with the problem is to not do anything in your UI handler (e.g., button click, etc.) other than to set a flag, and you should add a handler to the Application's Idle event, and in the handler for that event, check and clear the flag, and if it is set, then try using SendStringToExecute() to cancel the current command as per Kean's blog post.

You can't do much from the WPF UI thread, and that seems to be the problem.

My concern doing that would be that if the user gives a double click on the form then sits there wondering why their document hasn't yet opened.  I will try to set up something along the lines of your suggestion, but I will try doing something similar to your suggestion and then set the focus to the document window to finish the process
Thank you!

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #12 on: March 17, 2013, 09:27:15 PM »
You can use this reflection-free wrapper to invoke RunCommand(), which is a little faster than calling it via Reflection:
Update: I tried using Command("^C^C") as is done in scripts... no such luck

If DocumentCollection.IsApplicationContext == true, You can't use the command line or the Command() wrapper for the RunCommand method.

The way to deal with the problem is to not do anything in your UI handler (e.g., button click, etc.) other than to set a flag, and you should add a handler to the Application's Idle event, and in the handler for that event, check and clear the flag, and if it is set, then try using SendStringToExecute() to cancel the current command as per Kean's blog post.

You can't do much from the WPF UI thread, and that seems to be the problem.

I took what you've suggested and since I don't have a strong handle yet on the Application's Idle event or how it works I just created a custom opening command to do the dirty work for me.

Code - C#: [Select]
  1.         [CommandMethod("CustomPaletteOpen", CommandFlags.Session)]
  2.         public void OpenNewDocument()
  3.         {
  4.             DocumentCollection docs = AutoCAD.ApplicationServices.Application.DocumentManager;
  5.             Editor ed = docs.MdiActiveDocument.Editor;
  6.             PromptResult pr = ed.GetString("");
  7.             if (pr.Status==PromptStatus.OK)
  8.             {
  9.                 docs.Open(pr.StringResult);
  10.             }
  11.         }
Which I call as
Code - C#: [Select]
  1. SendStringToExecute(String.Format("{0}{0}CustomPaletteOpen \"{1}\" ", (char)27, ((FileInfo)item.Tag).FullName), true, false, false);

Thanks for the nudge in the right direction!  If you have time I would love to see how you would've done it from the Idle event.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #13 on: March 18, 2013, 04:54:17 AM »
You can use this reflection-free wrapper to invoke RunCommand(), which is a little faster than calling it via Reflection:

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. using Autodesk.AutoCAD.ApplicationServices;
  8.  
  9. namespace Autodesk.AutoCAD.EditorInput
  10. {
  11.    public static class EditorInputExtensionMethods
  12.    {
  13.       public static PromptStatus Command( this Editor editor, params object[] args )
  14.       {
  15.          if( editor == null )
  16.             throw new ArgumentNullException( "editor" );
  17.          return runCommand( editor, args );
  18.       }
  19.  
  20.       static Func<Editor, object[], PromptStatus> runCommand = GenerateRunCommand();
  21.  
  22.       static Func<Editor, object[], PromptStatus> GenerateRunCommand()
  23.       {
  24.          MethodInfo method = typeof( Editor ).GetMethod( "RunCommand",
  25.             BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
  26.          var instance = Expression.Parameter( typeof( Editor ) );
  27.          var args = Expression.Parameter( typeof( object[] ) );
  28.          return Expression.Lambda<Func<Editor, object[], PromptStatus>>(
  29.             Expression.Call( instance, method, args ), instance, args )
  30.                .Compile();
  31.       }
  32.    }
  33. }
  34.  
  35.  

Very nice Tony (as usual). I have now to learn about Linq.Expressions (a nice article here)

It seems to me that the Expression.Parameter() method requires two arguments (the parameter type and its name), when targeting the .NET Framework 3.5.
Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. using Autodesk.AutoCAD.ApplicationServices;
  8.  
  9. namespace Autodesk.AutoCAD.EditorInput
  10. {
  11.    public static class EditorInputExtensionMethods
  12.    {
  13.       public static PromptStatus Command( this Editor editor, params object[] args )
  14.       {
  15.          if( editor == null )
  16.             throw new ArgumentNullException( "editor" );
  17.          return runCommand( editor, args );
  18.       }
  19.  
  20.       static Func<Editor, object[], PromptStatus> runCommand = GenerateRunCommand();
  21.  
  22.       static Func<Editor, object[], PromptStatus> GenerateRunCommand()
  23.       {
  24.          MethodInfo method = typeof( Editor ).GetMethod( "RunCommand",
  25.             BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
  26.          ParameterExpression instance = Expression.Parameter( typeof( Editor ), "ed" ); // <- FW 3.5
  27.          ParameterExpression args = Expression.Parameter( typeof( object[] ), "args" ); // <- FW 3.5
  28.          return Expression.Lambda<Func<Editor, object[], PromptStatus>>(
  29.             Expression.Call( instance, method, args ), instance, args )
  30.                .Compile();
  31.       }
  32.    }
  33. }
  34.  
« Last Edit: March 18, 2013, 05:21:03 AM by gile »
Speaking English as a French Frog

TheMaster

  • Guest
Re: AAARRRGGGHHH!!!!!!! >.< Cancel active command
« Reply #14 on: March 18, 2013, 07:17:28 AM »
Hi Gile, and thanks for pointing that out.

I should've mentioned that the code targets .NET 4.0.