SaveAs in COM hanging AutoCAD
I'm implementing an application which uses COM in AutoCAD's ObjectARX interface to automate drawing actions, such as open and save as.
According to the documentation, I should be able to call AcadDocument.SaveAs() and pass in a filename, a "save as type" and a security parameter. The documentation explicitly statses that if security is NULL, no security related operation is attempted. It doesn't, however, give any indication of the correct object type to pass as the "save as type" parameter.
I've tried calling SaveAs with a filename and null for the remaining arguments, but my application hangs on that method call and AutoCAD appears to crash - you can still use the ribbon but can't do anything with the toolbar and can't close AutoCAD.
I've got a feeling that it's my NULL parameters causing grief here, but the documentation is severely lacking in the COM/VBA department. In fact it says the AcadDocument class doesn't even have a SaveAs method, which it clearly does.
Has anyone here implemented the same thing? Any guidance?
The alternative is I use the SendCommand() method to send a _SAVEAS command, but my application is managing a batch of drawing and needs to know a) if the save fails, and b) when the save completes (which I'm doing by listening to the EndSave event.)
EDIT
Here's the code as requested - all it's doing is launching AutoCAD (or connecting to the running instance if it's already running), opening an existing drawing, then saving the document to a new location (C:\Scratch\Document01B.dwg.)
using (AutoCad cad = AutoCad.Instance)
{
// Launch AutoCAD
cad.Launch();
// Open drawing
cad.OpenDrawing(@"C:\Scratch\Drawing01.dwg");
// Save it
cad.SaveAs(@"C:\Scratch\Drawing01B.dwg");
}
Then in my AutoCad class (this._acadDocument is an instance of the AcadDocument class.)
public void Launch()
{
this._acadApplication = null;
const string ProgramId = "AutoCAD.Application.18";
try
{
// Connect to a running instance
this._acadApplication = (AcadApplication)Marshal.GetActiveObject(ProgramId);
}
catch (COMException)
{
/* No instance running, launch one */
try
{
this._acadApplication = (AcadApplication)Activator.CreateInstance(
Type.GetTypeFromProgID(ProgramId),
true);
开发者_如何学C }
catch (COMException exception)
{
// Failed - is AutoCAD installed?
throw new AutoCadNotFoundException(exception);
}
}
/* Listen for the events we need and make the application visible */
this._acadApplication.BeginOpen += this.OnAcadBeginOpen;
this._acadApplication.BeginSave += this.OnAcadBeginSave;
this._acadApplication.EndOpen += this.OnAcadEndOpen;
this._acadApplication.EndSave += this.OnAcadEndSave;
#if DEBUG
this._acadApplication.Visible = true;
#else
this._acadApplication.Visible = false;
#endif
// Get the active document
this._acadDocument = this._acadApplication.ActiveDocument;
}
public void OpenDrawing(string path)
{
// Request AutoCAD to open the document
this._acadApplication.Documents.Open(path, false, null);
// Update our reference to the new document
this._acadDocument = this._acadApplication.ActiveDocument;
}
public void SaveAs(string fullPath)
{
this._acadDocument.SaveAs(fullPath, null, null);
}
From the Autodesk discussion groups, it looks like the second parameter is the type to save as, and may be required:
app = new AcadApplicationClass();
AcadDocument doc = app.ActiveDocument;
doc.SaveAs("d:\Sam.dwg",AcSaveAsType.acR15_dwg,new Autodesk.AutoCAD.DatabaseServices.SecurityParameters());
Since you are in AutoCAD 2010, the type should be incremented to acR17_dwg or acR18_dwg.
Judging by the link to AutoDesk's forum on this topic, it sounds like as you need to close the object after saving...and remove the null's...If I were you, I'd wrap up the code into try
/catch
blocks to check and make sure there's no exception being thrown!
I have to question the usage of the using clause, as you're Launch
ing another copy aren't you? i.e. within the OpenDrawing
and Save
functions you are using this._acadApplication
or have I misunderstood?
using (AutoCad cad = AutoCad.Instance) { try{ // Launch AutoCAD cad.Launch(); // Open drawing cad.OpenDrawing(@"C:\Scratch\Drawing01.dwg"); // Save it cad.SaveAs(@"C:\Scratch\Drawing01B.dwg"); }catch(COMException ex){ // Handle the exception here } } public void Launch() { this._acadApplication = null; const string ProgramId = "AutoCAD.Application.18"; try { // Connect to a running instance this._acadApplication = (AcadApplication)Marshal.GetActiveObject(ProgramId); } catch (COMException) { /* No instance running, launch one */ try { this._acadApplication = (AcadApplication)Activator.CreateInstance( Type.GetTypeFromProgID(ProgramId), true); } catch (COMException exception) { // Failed - is AutoCAD installed? throw new AutoCadNotFoundException(exception); } } /* Listen for the events we need and make the application visible */ this._acadApplication.BeginOpen += this.OnAcadBeginOpen; this._acadApplication.BeginSave += this.OnAcadBeginSave; this._acadApplication.EndOpen += this.OnAcadEndOpen; this._acadApplication.EndSave += this.OnAcadEndSave; #if DEBUG this._acadApplication.Visible = true; #else this._acadApplication.Visible = false; #endif // Get the active document // this._acadDocument = this._acadApplication.ActiveDocument; // Comment ^^^ out? as you're instantiating an ActiveDocument below when opening the drawing? } public void OpenDrawing(string path) { try{ // Request AutoCAD to open the document this._acadApplication.Documents.Open(path, false, null); // Update our reference to the new document this._acadDocument = this._acadApplication.ActiveDocument; }catch(COMException ex){ // Handle the exception here } } public void SaveAs(string fullPath) { try{ this._acadDocument.SaveAs(fullPath, null, null); }catch(COMException ex){ // Handle the exception here }finally{ this._acadDocument.Close(); } }
Thought I'd include some links for your information.
- 'Closing Autocad gracefully'.
- 'Migrating AutoCAD COM to AutoCAD 2010'.
- 'Saving AutoCAD to another format'
Hope this helps, Best regards, Tom.
I've managed to solve this in a non-optimal, very imperfect way so I'd still be interested to hear if anyone knows why the SaveAs method crashes AutoCAD and hangs my application.
Here's how I did it:
When opening a document or creating a new one, turn off the open/save dialog boxes:
this._acadDocument.SetVariable("FILEDIA", 0);
When saving a document, issue the _SAVEAS command passing in "2010" as the format and the filename (fullPath):
string commandString = string.Format(
"(command \"_SAVEAS\" \"{0}\" \"{1}\") ",
"2010",
fullPath.Replace('\\', '/'));
this._acadDocument.SendCommand(commandString);
When exiting AutoCAD turn file dialog prompting back on (probably isn't necessary but just makes sure):
this._acadDocument.SetVariable("FILEDIA", 1);
With C# and COM, when there are optional arguments, you need to use Type.Missing
instead of null:
this._acadDocument.SaveAs(fullPath, Type.Missing, Type.Missing);
But since Visual Studio 2010, you can simply omit the optional arguments:
this._acadDocument.SaveAs(fullPath);
精彩评论