photo credit: Marcin Wichary via photopin cc
As mentioned in the last post, fibers are now inactive in AutoCAD 2015 (the code paths are still there, largely for testing purposes, but should not need to be enabled for typical usage of the product).
Fibers were a technology that Microsoft introduced way back when to help single document (and typically single-threaded) applications adapt more easily to the brave new world of MDI. At least that’s my recollection and understanding. They were basically a mechanism by which applications such as AutoCAD could manage their per-document state, making the relevant data current depending on the activated document. I’ve heard fibers referred to as “manually-scheduled threads”, although that’s likely to be a simplification of the real situation.
So what does this mean to AutoCAD developers, and why do we care?
Aside from making debugging more predictable – Visual Studio hasn’t allowed debugging of applications using fibers for a few releases – this work has brought significant benefits for developers.
The way I understand it – and it’s very likely that my understanding is either incomplete or flawed, as I haven’t spent a significant amount of time looking at the guts of this mechanism – AutoCAD commands depend on state associated with the current document. Moving away from fibers means the execution context of AutoCAD commands is quite different, but it also brings about opportunities that weren’t there before, as we’ve rationalized the way commands can be called, making them first-class, fully supported ways of making use of the huge amount of pre-existing code inside AutoCAD. Which essentially means AutoCAD developers should no longer feel bad (if they ever did ;-) about calling commands from their code. It’s now absolutely valid to do this – it’s just as valid to call commands as it is to make use of a lower-level API.
Prior to AutoCAD 2015, you would generally call AutoCAD commands by firing tokens – command names and the user inputs those commands expected – into AutoCAD’s input throat. From AutoCAD 2015 onwards, there are two approaches for calling commands: subroutine and coroutine. Subroutine command-calling means that you want to execute a command in its entirety: you know exactly how the command should be called and don’t want things to be open-ended. Coroutine command-calling is more open-ended: you don’t have all the arguments to provide to the command – and don’t know exactly how it’s going to proceed – so you’re really calling a partial command.
Without fibers acting as a support structure, these two styles of command-calling now need to be handled separately.
In ObjectARX, for instance, you’ll no longer find acedCommand() or acedCmd(), you have to use either acedCommandS()/acedCmdS() or acedCommandC()/acedCmdC(), depending on the command-calling style you choose. acedCommandS()/acedCmdS() are easy: they work in much the same way as acedCommand()/acedCmd() used to, but only with complete command/argument sets. acedCommandC()/acedCmdC() are more complicated: you need to provide a callback function that will be called to further process input tokens (it’s a form of continuation passing that is very common when dealing with asynchronous method calls, for example). ObjectARX developers should take a look at the ObjectARX Developer’s Guide as well as acedCmdNF.h (NF presumably stands for non-fiber in this context) for more information.
So ObjectARX developers have some work to do if they’re calling commands in AutoCAD 2015, then. What about the other programming environments?
AutoLISP is much simpler: Autodesk has full control over the “virtual machine” that executes LISP code inside AutoCAD, so we can choose which of the two underlying ObjectARX functions to call, depending on the scenario and without requiring a change in the LISP calling code. That said, the ADN team tells me they’ve seen scenarios where a developer specifically needed to specify the subroutine style, at which point they’ve been able to call (command-s …) instead.
For VBA – which has now been updated to VBA 7.1 and is still available as a separate download – I don’t think anything has changed: it still has the AcadDocument.SendCommand() method, which seems to work with partial commands (I’ll update this post if I hear anything different on that front).
.NET is also simple in its own way: subroutine calls are straightforward – they simply run synchronously via Editor.Command() – and because AutoCAD 2015 now makes use of .NET 4.5 – which provides C# with the async/await mechanism for calling asynchronous methods – we’ve been able to implement that mechanism for coroutine calls. These now make use of the compiler’s automatic callback generation via the await keyword to work asynchronously – e.g. await Editor.CommandAsync(…). You’ll need to tag any command method using CommandAsync() with the async modifier, just as you would when using other awaitable methods.
The difference between C# and C++ is analogous to general asynchronous coding: changes have been made at the language level to make asynchrony easier to handle from C#, and in time comparable extensions will make their way into C++ also.
Do bear in mind that to target .NET 4.5 – which is a requirement to use the API enhancements in AutoCAD 2015 – you will need to use Visual Studio 2012 or 2013: VS2010 cannot target .NET 4.5. (ObjectARX developers will have to use the toolset from VS2012 to compile their C++ modules, as that’s the compiler being used to build AutoCAD.) I’m personally now using VS2013 as a primary IDE, but make use of the VS2012 toolset when building ObjectARX modules.
If you’re still using VS2010, you *should* be able to target your .NET application against the class libraries for AutoCAD 2013 or 2014 (as long as you’re not making use of newer APIs) and run that code in AutoCAD 2015, but do make sure you test your application thoroughly.
In tomorrow’s post we’ll take a look at a new capability related to product theming that has been enabled in AutoCAD 2015.
Update:
To give a sense for the differences between Command() and CommandAsync() when used from C#, here are two commands, one that calls INSERT synchronously with the full set of arguments and one that calls it asynchronously, asking the user to enter the insertion point (a very common scenario, as jigging the block to show the block’s graphics is much more complicated than just calling INSERT).
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
namespace CommandCalling
{
public class Commands
{
[CommandMethod("IBS")]
public void InsertBlockSync()
{
var doc =
Application.DocumentManager.MdiActiveDocument;
var ed = doc.Editor;
// Our insertion point is hardcoded at 10,10
ed.Command("_.INSERT", "TEST", "10,10", 1, 1, 0);
ed.WriteMessage("\nWe have inserted our block.");
}
[CommandMethod("IBA")]
async public void InsertBlockAsync()
{
var doc =
Application.DocumentManager.MdiActiveDocument;
var ed = doc.Editor;
// Let's ask the user to select the insertion point
await ed.CommandAsync(
"_.INSERT", "TEST", Editor.PauseToken, 1, 1, 0
);
ed.WriteMessage("\nWe have inserted our block.");
}
}
}