Author Topic: Transferring PlotSettings (PageStups)  (Read 2818 times)

0 Members and 1 Guest are viewing this topic.

TyroneK

  • Mosquito
  • Posts: 4
Transferring PlotSettings (PageStups)
« on: May 26, 2011, 03:35:06 AM »
Hi everyone,

I'm in need of a little help. I'm trying to push page setups to non-opened drawings and am running into an "eWasOpenForWrite" when saving the drawing.

Anyway here's a simplified version of the code:
Code: [Select]
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;

[assembly: CommandClass(typeof(PageSetups))]
[assembly: ExtensionApplication(typeof(PageSetups))]

public class PageSetups : IExtensionApplication
{
public void Initialize() { }

public void Terminate() { }

[CommandMethod( "TPS" )]
public static void TransferPageSetups()
{
string tgtDwg = @"C:\Test\Drawing2.dwg";
string setupName = "Setup1";
Database tgtDb = new Database( false, true );

try
{
// get the target database
tgtDb.ReadDwgFile( tgtDwg, FileOpenMode.OpenForReadAndWriteNoShare, true, "" );

// get the current database
Database srcDb = HostApplicationServices.WorkingDatabase;

using ( Transaction srcTr = srcDb.TransactionManager.StartTransaction() )
{
// get the pagesetup from the current drawing
var srcPsd = srcTr.GetObject(
srcDb.PlotSettingsDictionaryId, OpenMode.ForRead ) as DBDictionary;
ObjectId srcPsId = srcPsd.GetAt( setupName );
var srcPs = srcTr.GetObject( srcPsId, OpenMode.ForRead ) as PlotSettings;

using ( Transaction tgtTr = tgtDb.TransactionManager.StartTransaction() )
{
PlotSettings newPs = new PlotSettings( false );

// check the dict before
var tgtPsdBefore = tgtTr.GetObject(
tgtDb.PlotSettingsDictionaryId, OpenMode.ForRead ) as DBDictionary;

// copy over the plotsetting and add it to the target db dict
newPs.CopyFrom( srcPs );
newPs.AddToPlotSettingsDictionary( tgtDb );

// check the dict after
try
{
var tgtPsdAfter = tgtTr.GetObject(
tgtDb.PlotSettingsDictionaryId, OpenMode.ForRead ) as DBDictionary;
ObjectId tgtPsId = tgtPsdAfter.GetAt( setupName );
var tgtPs = tgtTr.GetObject( tgtPsId, OpenMode.ForRead ) as PlotSettings;
}
catch { }

tgtTr.Commit();
}

srcTr.Commit();
}

tgtDb.SaveAs( tgtDwg, true, DwgVersion.Newest, tgtDb.SecurityParameters );
}
catch ( Exception ex )
{
Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( ex.Message );
}
finally
{
tgtDb.Dispose();
}
}
}

The problem seems to be with the AddToPlotSettingsDictionary() method as everything else seems to work fine. I included a couple of checks to make sure the plotsetting was being added to the target database properly and as far as I can tell it is.

What I don't quite understand is why essentially the same code works fine if a drawing is already open (and then saved manually).

Any insights would be much appreciated.



TyroneK

  • Mosquito
  • Posts: 4
Re: Transferring PlotSettings (PageStups)
« Reply #1 on: May 26, 2011, 04:41:51 AM »
I think I might have solved my own problem.

I removed the
Code: [Select]
newPs.AddToPlotSettingsDictionary( tgtDb );

and added a long form transaction of opening the DBDictionary for write, setting the entry, then down grading both objects.

Code: [Select]
using ( Transaction tgtTr = tgtDb.TransactionManager.StartTransaction() )
{
PlotSettings newPs = new PlotSettings( false );

// get the target dict
var tgtPsd = tgtTr.GetObject( tgtDb.PlotSettingsDictionaryId, OpenMode.ForWrite ) as DBDictionary;

// copy over the plotsetting and add it to the target dict
newPs.CopyFrom( srcPs );
tgtPsd.SetAt( newPs.PlotSettingsName, newPs );
newPs.DowngradeOpen();
tgtPsd.DowngradeOpen();

tgtTr.Commit();
}

Anything else seemed to result in an "eWasOpenForWrite" or "accessed corrupted or protected memory" crash.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Transferring PlotSettings (PageStups)
« Reply #2 on: May 26, 2011, 07:23:02 AM »
Hey TyroneK,

Welcome to the Swamp.

I believe your problem was from the new PlotSettings
Code: [Select]
PlotSettings newPs = new PlotSettings( false );They were waiting for GC to Dispose them.

Normally by using GetObject() it will be associated with a transaction
Transaction.GetObject() or the Top most transaction if ObjectId.GetObject() used.

So if the transaction is in a 'using' block then transaction will dispose it.


Another simple way is to put the Plot Settings in a using block.

Code: [Select]
        [CommandMethod("TPS")]
        public void TransferPageSetups()
        {
            string tgtDwg = @"C:\Test\Drawing2.dwg";
            string setupName = "Setup1";

            Database currentDb = HostApplicationServices.WorkingDatabase;
            Database targetDb = new Database(false, false);         
            targetDb.ReadDwgFile(tgtDwg, System.IO.FileShare.ReadWrite, false, "");
           
                try
                {
                   
                    using (Transaction targetTrx = targetDb.TransactionManager.StartTransaction())
                    using (Transaction currentTrx = currentDb.TransactionManager.StartTransaction())
                    {
                        // get the pagesetup from the current drawing
                        DBDictionary currentPlotSettingsDict = (DBDictionary)currentTrx.GetObject(currentDb.PlotSettingsDictionaryId, OpenMode.ForRead);
                        ObjectId currentPlotSettingsId = currentPlotSettingsDict.GetAt(setupName);

                        PlotSettings currentPlotSettings = currentTrx.GetObject(currentPlotSettingsId, OpenMode.ForRead) as PlotSettings;


                        using (PlotSettings targetPlotSettings = new PlotSettings(false))
                        {


                            // copy over the plotsetting and add it to the target db dict
                            targetPlotSettings.CopyFrom(currentPlotSettings);
                            targetPlotSettings.AddToPlotSettingsDictionary(targetDb);

                        }
                            currentTrx.Commit();
                            targetTrx.Commit();
                       
                    }
                   
                    targetDb.SaveAs(tgtDwg, DwgVersion.Current);


                }

                catch (System.Exception ex)
                {
                    Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.Message);
                }
                finally
                {
                    //tgtDb.Dispose();
                }
           
        }



I think your database constructor should have false as both parameters
Code: [Select]
Database targetDb = new Database(false, false);Maybe wait to see what others say

TyroneK

  • Mosquito
  • Posts: 4
Re: Transferring PlotSettings (PageStups)
« Reply #3 on: May 26, 2011, 01:05:19 PM »
Thanks for the welcome Jeff. I think I registered for these forums about 2.5 years ago but finally got around to participating.

Quote
I think your database constructor should have false as both parameters

This might well be true. System.Boolean specifying whether or not to associate this database to the current document. This sounds like what I don't want to have happen so I usually specify noDocument = true. Or does true mean that it is associating it?

Is there a reason that
Code: [Select]
PlotSettings newPs = new PlotSettings( false );works fine for an active document but not a db.ReadDwgFile?

Also, I noticed that you commented out  //tgtDb.Dispose();. Is there a reason you wouldn't want to dispose of the source database at the end of the transaction? I assume that the database objects disposed of automatically but does it happen immediately? Disposing of it manually is probably a habit I picked up from Kean.

Thanks for the much cleaner solution. Also I like that stacking of the using blocks; definitely saves on some nesting.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Transferring PlotSettings (PageStups)
« Reply #4 on: May 26, 2011, 02:32:16 PM »
Is there a reason that
Code: [Select]
PlotSettings newPs = new PlotSettings( false );works fine for an active document but not a db.ReadDwgFile?
I would have to see code that it works.


Quote
Also, I noticed that you commented out  //tgtDb.Dispose();. Is there a reason you wouldn't want to dispose of the source database at the end of the transaction? I assume that the database objects disposed of automatically but does it happen immediately? Disposing of it manually is probably a habit I picked up from Kean.
Good Catch,
Should have put it in a using block which takes care of dispose for you.


Quote
The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block; in fact, this is how the using statement is translated by the compiler. The code example earlier expands to the following code at compile time (note the extra curly braces to create the limited scope for the object):
Link to quote above

When ever you create a 'AutoCad Object' with new keyword it is automatically opened for write.
So it is good to put them in a using block

Here is some code then the IL code from IL DASM.

I marked in red the finally and endFinally
I marked in blue the dispose.

Notice how NODOH has no try catch block in C# code but the 'using' statement will create one and cause it to be disposed in a finally block that is generated for you


Code: [Select]
     
        [CommandMethod("DOH")]
        public void DOH()
        {
 
            Line lne = new Line(new Point3d(0, 0, 0), new Point3d(0, 0, 0));

            try
            {
                   
            }
            catch (System.Exception)
            {
               
            }
            finally
            {
                lne.Dispose();
            }
           
        }

        [CommandMethod("NODOH")]
        public void NODOH()
        {       
            using (Line lne = new Line(new Point3d(0, 0, 0), new Point3d(0, 0, 0)))
            {

            }

        }       


DOH
Code: [Select]
.method public hidebysig instance void  DOH() cil managed
{
  .custom instance void [Acmgd]Autodesk.AutoCAD.Runtime.CommandMethodAttribute::.ctor(string) = ( 01 00 03 44 4F 48 00 00 )                         // ...DOH..
  // Code size       95 (0x5f)
  .maxstack  5
  .locals init ([0] class [Acdbmgd]Autodesk.AutoCAD.DatabaseServices.Line lne)
  IL_0000:  nop
  IL_0001:  ldc.r8     0.0
  IL_000a:  ldc.r8     0.0
  IL_0013:  ldc.r8     0.0
  IL_001c:  newobj     instance void [Acdbmgd]Autodesk.AutoCAD.Geometry.Point3d::.ctor(float64,
                                                                                       float64,
                                                                                       float64)
  IL_0021:  ldc.r8     0.0
  IL_002a:  ldc.r8     0.0
  IL_0033:  ldc.r8     0.0
  IL_003c:  newobj     instance void [Acdbmgd]Autodesk.AutoCAD.Geometry.Point3d::.ctor(float64,
                                                                                       float64,
                                                                                       float64)
  IL_0041:  newobj     instance void [Acdbmgd]Autodesk.AutoCAD.DatabaseServices.Line::.ctor(valuetype [Acdbmgd]Autodesk.AutoCAD.Geometry.Point3d,
                                                                                            valuetype [Acdbmgd]Autodesk.AutoCAD.Geometry.Point3d)
  IL_0046:  stloc.0
  .try
  {
    .try
    {
      IL_0047:  nop
      IL_0048:  nop
      IL_0049:  leave.s    IL_0050
    }  // end .try
    catch [mscorlib]System.Exception
    {
      IL_004b:  pop
      IL_004c:  nop
      IL_004d:  nop
      IL_004e:  leave.s    IL_0050
    }  // end handler
    IL_0050:  nop
    IL_0051:  leave.s    IL_005d
  }  // end .try
  [color=red]finally[/color]
  {
    IL_0053:  nop
    IL_0054:  ldloc.0
    [color=blue]IL_0055:  callvirt   instance void [Acdbmgd]Autodesk.AutoCAD.Runtime.DisposableWrapper::Dispose()[/color]
    IL_005a:  nop
    IL_005b:  nop
    [color=red]IL_005c:  endfinally[/color]
  }  // end handler
  IL_005d:  nop
  IL_005e:  ret
} // end of method MyCommands::DOH





NODOH
Code: [Select]
.method public hidebysig instance void  NODOH() cil managed
{
  .custom instance void [Acmgd]Autodesk.AutoCAD.Runtime.CommandMethodAttribute::.ctor(string) = ( 01 00 05 4E 4F 44 4F 48 00 00 )                   // ...NODOH..
  // Code size       93 (0x5d)
  .maxstack  5
  .locals init ([0] class [Acdbmgd]Autodesk.AutoCAD.DatabaseServices.Line lne,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldc.r8     0.0
  IL_000a:  ldc.r8     0.0
  IL_0013:  ldc.r8     0.0
  IL_001c:  newobj     instance void [Acdbmgd]Autodesk.AutoCAD.Geometry.Point3d::.ctor(float64,
                                                                                       float64,
                                                                                       float64)
  IL_0021:  ldc.r8     0.0
  IL_002a:  ldc.r8     0.0
  IL_0033:  ldc.r8     0.0
  IL_003c:  newobj     instance void [Acdbmgd]Autodesk.AutoCAD.Geometry.Point3d::.ctor(float64,
                                                                                       float64,
                                                                                       float64)
  IL_0041:  newobj     instance void [Acdbmgd]Autodesk.AutoCAD.DatabaseServices.Line::.ctor(valuetype [Acdbmgd]Autodesk.AutoCAD.Geometry.Point3d,
                                                                                            valuetype [Acdbmgd]Autodesk.AutoCAD.Geometry.Point3d)
  IL_0046:  stloc.0
  .try
  {
    IL_0047:  nop
    IL_0048:  nop
    IL_0049:  leave.s    IL_005b
  }  // end .try
  [color=red]finally[/color]
  {
    IL_004b:  ldloc.0
    IL_004c:  ldnull
    IL_004d:  ceq
    IL_004f:  stloc.1
    IL_0050:  ldloc.1
    IL_0051:  brtrue.s   IL_005a
    IL_0053:  ldloc.0
    [color=blue]IL_0054:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()[/color]
    IL_0059:  nop
[color=red]    IL_005a:  endfinally[/color]
  }  // end handler
  IL_005b:  nop
  IL_005c:  ret
} // end of method MyCommands::NODOH



Jeff H

  • Needs a day job
  • Posts: 6150
Re: Transferring PlotSettings (PageStups)
« Reply #5 on: May 26, 2011, 02:46:51 PM »
Actually my first intent was to make the current document crap out but I can not get one to save
keep getting
efileshare error

Code: [Select]
        [CommandMethod("DOH")]
        public void DOH()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument; // Active Document
            Editor ed = doc.Editor; // Active Document's Editor
            Database db = doc.Database; // Active Document Database

            string strDWGName = doc.Name;

            Line lne = new Line(new Point3d(0, 0, 0), new Point3d(0, 0, 0));

            try
            {
                object obj = Application.GetSystemVariable("DWGTITLED");

                if (System.Convert.ToInt16(obj) == 0)
                {
                    strDWGName = @"C:\Test\Testing.dwg";
                }


                db.SaveAs(strDWGName, false, DwgVersion.Current, db.SecurityParameters);       
            }
            catch (System.Exception ex)
            {

                ed.WriteMessage(ex.Message);
            }
            finally
            {
                lne.Dispose();
            }
           
        }

        [CommandMethod("NODOH")]
        public void NODOH()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument; // Active Document
            Editor ed = doc.Editor; // Active Document's Editor
            Database db = doc.Database; // Active Document Database
            string strDWGName = doc.Name;

            using (Line lne = new Line(new Point3d(0, 0, 0), new Point3d(0, 0, 0)))
            {

            }

            object obj = Application.GetSystemVariable("DWGTITLED");

            if (System.Convert.ToInt16(obj) == 0)
            {
                strDWGName = @"C:\Test\Testing.dwg";
            }
            db.SaveAs(strDWGName, false, DwgVersion.Current, db.SecurityParameters);       

        }

TyroneK

  • Mosquito
  • Posts: 4
Re: Transferring PlotSettings (PageStups)
« Reply #6 on: May 27, 2011, 09:59:45 PM »
I would have to see code that it works.

I have code that "works" for open documents but it only really works because the object is being disposed of automatically as the command finishes.

Quote from: Output Console
Forgot to call Dispose? (Autodesk.AutoCAD.DatabaseServices.PlotSettings): DisposableWrapper

If I attempt to incorporate a save into the command the same problem happens (unless the PlotSetting is disposed of properly).

Thanks for your insights on using blocks. I had been making use of them, but really only for Transactions and DocumentLocks, but no longer.

Edit:
I forgot to add that constructing a new Database with ( false, false ) instead of ( false, true ) causes problems with other commands in my tool suite. Although for this particular command doesn't seem to matter.
« Last Edit: May 28, 2011, 03:00:06 PM by TyroneK »