TheSwamp
Code Red => .NET => Topic started by: mohnston 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?
-
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#)
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
)
-
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
-
I could use some help with the PInvoke solution.
I have this that does not work:
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.
-
[DllImport("acdb18.dll", CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?acdbGetDbmod@@YAJPAVAcDbDatabase@@@Z")]
public static extern long acdbGetDbmod([color=red]IntPtr pDb[/color]);
use:
acdbGetDbmod(doc.Database.UnmanagedObject);
-
Without any testing:
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));
}
};
}
-
Thanks, that made the error go away . . . but
[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.
-
Without any testing:
With marginal testing...
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:
C:\ProgramData\Autodesk\MEP 2010\deu\Template\acadiso.dwt 1285/5
C:\ProgramData\Autodesk\MEP 2010\deu\Template\acadiso.dwt 0/0
-
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?
-
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.
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]);
}
-
Thanks so much for the help.
I ended up with what I needed and more.
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);
}
}