Author Topic: Get DBMOD for non-active drawing  (Read 6735 times)

0 Members and 1 Guest are viewing this topic.

mohnston

  • Bull Frog
  • Posts: 305
  • CAD Programmer
Get DBMOD for non-active drawing
« on: February 08, 2011, 01:27:07 AM »
Is there a way to find out if a drawing is modified without using the DBMOD system variable?
That variable really should have been put at the document level, or better yet a property of the document or database.
It really isn't a system variable when you think about it.

If I have multiple drawings open and want to know the status (save-wise) of each I don't want to have to activate each drawing to do it.

Is there a way to find out if a drawing is modified without using the DBMOD system variable?
It's amazing what you can do when you don't know what you can't do.
CAD Programming Solutions

kaefer

  • Guest
Re: Get DBMOD for non-active drawing
« Reply #1 on: February 08, 2011, 02:47:51 AM »
If I have multiple drawings open and want to know the status (save-wise) of each I don't want to have to activate each drawing to do it.

Is there a way to find out if a drawing is modified without using the DBMOD system variable?

Not that I know of. But if you can get at the variable with one Interop call, you don't have to activate the document.

(Example with late binding in F#)
Code: [Select]
    let docmods =
        Application.DocumentManager
        |> Seq.cast<obj>
        |> Seq.choose
            (function
                | :? Document as doc ->
                    let adoc = doc.AcadDocument
                    let dbmod =
                        adoc.GetType().InvokeMember(
                            "GetVariable",
                            System.Reflection.BindingFlags.InvokeMethod,
                            null, adoc, [| "DBMOD" |] )
                        |> unbox<int16>
                    Some(doc.Name, dbmod)
                | _ -> None
            )

Alexander Rivilis

  • Bull Frog
  • Posts: 214
  • Programmer from Kyiv (Ukraine)
Re: Get DBMOD for non-active drawing
« Reply #2 on: February 08, 2011, 03:16:16 AM »
You can use P/Invoke: long __cdecl acdbGetDbmod(class AcDbDatabase *pDb) from acdb17.dll (for AutoCAD 2007...2009) or acdb18.dll (for AutoCAD 2010...2012)
EntryPoint = "?acdbGetDbmod@@YAJPAVAcDbDatabase@@@Z" for AutoCAD x86 and
EntryPoint = "?acdbGetDbmod@@YAJPEAVAcDbDatabase@@@Z" for AutoCAD x64
« Last Edit: February 08, 2011, 03:19:50 AM by Alexander Rivilis »

mohnston

  • Bull Frog
  • Posts: 305
  • CAD Programmer
Re: Get DBMOD for non-active drawing
« Reply #3 on: February 09, 2011, 02:54:05 PM »
I could use some help with the PInvoke solution.
I have this that does not work:
Code: [Select]
    class cMFile
    {
        public cMFile()
        { }
        [DllImport("acdb18.dll", CallingConvention = CallingConvention.Cdecl,
                    EntryPoint = "?acdbGetDbmod@@YAJPAVAcDbDatabase@@@Z")]
        public static extern long acdbGetDbmod(class AcDbDatabase *pDb);
       
        internal static void ShowFiles()
        {
            Document adoc = acadApp.DocumentManager.MdiActiveDocument;
            Editor ed = adoc.Editor;
            DocumentCollection dColl = acadApp.DocumentManager;
            foreach (Document doc in dColl)
            {
                ed.WriteMessage(doc.Name + " - ");
                if (acdbGetDbmod(doc.Database) > 0)
                {
                    ed.WriteMessage(" changes not saved\n");
                }
                else
                {
                    ed.WriteMessage(" no changes\n");
                }
            }
        }
    }

It's probably obvious to you that this line is not correct in this context.
public static extern long acdbGetDbmod(class AcDbDatabase *pDb)

I understand that it's looking for a database, but is that a pointer or an object or ?
This is my first real go at pinvoke, thus the dumbness.
It's amazing what you can do when you don't know what you can't do.
CAD Programming Solutions

dan.glassman

  • Guest
Re: Get DBMOD for non-active drawing
« Reply #4 on: February 09, 2011, 02:57:45 PM »
Code: [Select]
[DllImport("acdb18.dll", CallingConvention = CallingConvention.Cdecl,
                    EntryPoint = "?acdbGetDbmod@@YAJPAVAcDbDatabase@@@Z")]
public static extern long acdbGetDbmod([color=red]IntPtr pDb[/color]);

use:

Code: [Select]
acdbGetDbmod(doc.Database.UnmanagedObject);

Alexander Rivilis

  • Bull Frog
  • Posts: 214
  • Programmer from Kyiv (Ukraine)
Re: Get DBMOD for non-active drawing
« Reply #5 on: February 09, 2011, 03:33:32 PM »
Without any testing:
Code: [Select]
using System;
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[assembly: CommandClass(typeof(Rivilis.GetDbMod))]

namespace Rivilis
{
  public class GetDbMod
  {
    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport("acdb18.dll", CallingConvention = CallingConvention.Cdecl,
         EntryPoint = "?acdbGetDbmod@@YAJPAVAcDbDatabase@@@Z")]
    private static extern Int32 acdbGetDbmod18x32(IntPtr db);
    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport("acdb18.dll", CallingConvention = CallingConvention.Cdecl,
         EntryPoint = "?acdbGetDbmod@@YAJPEAVAcDbDatabase@@@Z")]
    private static extern Int64 acdbGetDbmod18x64(IntPtr db);

    public static Int32 acdbGetDbmod(ref Database db)
    {
      switch (IntPtr.Size)
      {
        case 4:
          return (Int32)acdbGetDbmod18x32(db.UnmanagedObject);
        case 8:
          return (Int32)acdbGetDbmod18x64(db.UnmanagedObject);
      }
      return 0;
    }
    //------------------------------------
    // Test of GetDbmod
    //------------------------------------
    [CommandMethod("TestDbMode")]
    static public void TestDbMode()
    {
      Database db = Application.DocumentManager.MdiActiveDocument.Database;
      Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
      ed.WriteMessage("\nDBMODE={0}", acdbGetDbmod(ref db));
    }
 };
}

mohnston

  • Bull Frog
  • Posts: 305
  • CAD Programmer
Re: Get DBMOD for non-active drawing
« Reply #6 on: February 09, 2011, 03:54:52 PM »
Thanks, that made the error go away . . . but

Code: [Select]
        [DllImport("acdb18.dll", CallingConvention = CallingConvention.Cdecl,
                    EntryPoint = "?acdbGetDbmod@@YAJPAVAcDbDatabase@@@Z")]
        public static extern int acdbGetDbmod(IntPtr pDb);
       
        internal static void ShowFiles()
        {
            Document adoc = acadApp.DocumentManager.MdiActiveDocument;
            Editor ed = adoc.Editor;
            DocumentCollection dColl = acadApp.DocumentManager;
            foreach (Document doc in dColl)
            {
                ed.WriteMessage(doc.Name + " - dbMod=");
                int dbm = acdbGetDbmod(doc.Database.UnmanagedObject);
                ed.WriteMessage(dbm.ToString() + "-");
                if (dbm > 0)
                {
                    ed.WriteMessage(" changes not saved\n");
                }
                else
                {
                    ed.WriteMessage(" no changes\n");
                }
               
            }
        }
When I returned a long the result was never 0 which it should be if there are no unsaved changes.
I changed the long to int and got 0 when there were no unsaved changes.
However, the value returned is not the dbMod value as far as I can tell.
DbMod is supposed to be 0 or a bitcode int as follows:
The setting is stored as a bitcode using the sum of the following values:
1 = Object database modified
4 = Database variable modified
8 = Window modified
16 = View modified
32 = Field modified

I'm getting 0 or some large number that doesn't make sense.
1028 if I change a view.
1285 if I modify/add an object
5140 if I pan the view

For my purposes I only need to know whether there are unsaved changes or not so this works.
Thanks for the help.

I suspect the values not matching up to the documentation has something to do with the data type I'm using. (int)
I tried long, uint, Int16, Int32 and none of them returned the appropriate value.
It's amazing what you can do when you don't know what you can't do.
CAD Programming Solutions

kaefer

  • Guest
Re: Get DBMOD for non-active drawing
« Reply #7 on: February 09, 2011, 04:11:12 PM »
Without any testing:
With marginal testing...
Code: [Select]
    public static class DBMod
    {

        [DllImport("acdb18.dll", CallingConvention = CallingConvention.Cdecl,
            EntryPoint = "?acdbGetDbmod@@YAJPAVAcDbDatabase@@@Z")]
        public static extern int acdbGetDbmod(System.IntPtr pDb);

        static object GetVar(this Document doc, params object[] p)
        {
            return doc.AcadDocument.GetType().InvokeMember(
                "GetVariable",
                System.Reflection.BindingFlags.InvokeMethod,
                null, doc.AcadDocument, p );
        }

        [CommandMethod("ShowFiles")]
        public static void ShowFiles()
        {
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
            foreach (Document doc in Application.DocumentManager)
            {
                ed.WriteMessage(
                    "\n{0} {1}/{2} ",
                    doc.Database.Filename,
                    acdbGetDbmod(doc.Database.UnmanagedObject),
                    doc.GetVar("DBMOD")
                );
            }
        }
    }
...and funny results:
Quote
C:\ProgramData\Autodesk\MEP 2010\deu\Template\acadiso.dwt 1285/5
C:\ProgramData\Autodesk\MEP 2010\deu\Template\acadiso.dwt 0/0

mohnston

  • Bull Frog
  • Posts: 305
  • CAD Programmer
Re: Get DBMOD for non-active drawing
« Reply #8 on: February 09, 2011, 04:16:58 PM »
Alexander, saw your post after I posted.
Thank you for the enhanced answer.
I notice that it should work with either 32 or 64 bit. I'll be creating a PInvoke wrapper/helper class.

The returned values still don't make sense but zero is always returned if there are no unsaved changes.

Any idea what the difference is between acdbGetDbmod and acdbGetDbmod18x32?
It's amazing what you can do when you don't know what you can't do.
CAD Programming Solutions

dan.glassman

  • Guest
Re: Get DBMOD for non-active drawing
« Reply #9 on: February 09, 2011, 05:25:53 PM »
The documentation for dbmod must be part truth -- AutoCad is using more bits than documented.  Mask out the undocumented bits to get the expected result.

Code: [Select]
      switch (IntPtr.Size)
      {
        case 4:
          return (Int32)(acdbGetDbmod18x32(db.UnmanagedObject) [color=red]& 0x3d[/color]);
        case 8:
          return (Int32)(acdbGetDbmod18x64(db.UnmanagedObject) [color=red]& 0x3d[/color]);
      }


mohnston

  • Bull Frog
  • Posts: 305
  • CAD Programmer
Re: Get DBMOD for non-active drawing
« Reply #10 on: February 10, 2011, 12:20:02 PM »
Thanks so much for the help.
I ended up with what I needed and more.
Code: [Select]
    public class PInvoke
    {
        const string ACDB_18 = "acdb18.dll";

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport(ACDB_18, CallingConvention = CallingConvention.Cdecl,
             EntryPoint = "?acdbGetDbmod@@YAJPAVAcDbDatabase@@@Z")]
        private static extern Int32 acdbGetDbmod18x32(IntPtr db);
        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport(ACDB_18, CallingConvention = CallingConvention.Cdecl,
             EntryPoint = "?acdbGetDbmod@@YAJPEAVAcDbDatabase@@@Z")]
        private static extern Int64 acdbGetDbmod18x64(IntPtr db);

        public static Int32 acdbGetDbmod(ref Database db)
        {
            switch (IntPtr.Size)
            {
                case 4:
                    return (Int32)(acdbGetDbmod18x32(db.UnmanagedObject) & 0x3d);
                case 8:
                    return (Int32)(acdbGetDbmod18x64(db.UnmanagedObject) & 0x3d);
            }
            return 0;
        }
       
        public static object GetVar(Document doc, params object[] p)
        {
            return doc.AcadDocument.GetType().InvokeMember(
                "GetVariable",
                System.Reflection.BindingFlags.InvokeMethod,
                null, doc.AcadDocument, p);
        }
    }
It's amazing what you can do when you don't know what you can't do.
CAD Programming Solutions