read data from open drawing

read data from open drawing

Anonymous
Not applicable
3,664 Views
7 Replies
Message 1 of 8

read data from open drawing

Anonymous
Not applicable

I want to read some data from another DWG. This works all fine excipt if the drawing is openend by anohter user. Can I force it to read from a "read-only"-version ? 

 

     Public Function zoekkaders(ByVal tekening As String) As Boolean
            Dim myDWG As ApplicationServices.Document
            Dim myEd As EditorInput.Editor
            Dim myBlock As DatabaseServices.BlockReference
            Dim myfilter(0), myfilter2(0) As DatabaseServices.TypedValue
            myDWG = ApplicationServices.Application.DocumentManager.MdiActiveDocument
            myEd = myDWG.Editor
            Dim db As New DatabaseServices.Database
            If tekening <> "basis" Then
                db = New DatabaseServices.Database(False, True)
                Try
                    db.ReadDwgFile(tekening, System.IO.FileShare.Read, False, "")
                Catch generatedExceptionName As System.Exception
                    myEd.WriteMessage(vbLf & "Unable to read drawing file.")
                End Try
            Else
                db = New DatabaseServices.Database
                db = myDWG.Database
            End If
Using tr As Transaction = db.TransactionManager.StartTransaction() Dim btable As BlockTable = DirectCast(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable) ....
0 Likes
Accepted solutions (1)
3,665 Views
7 Replies
Replies (7)
Message 2 of 8

jabowabo
Mentor
Mentor

Make a copy of the file in a temp location and read that db. You can delete the temp file when done. Here's an example:

           // Read the source database
                try
                {
                    sourceDb.ReadDwgFile(sourceDwgName, FileShare.Read, true, "");
                }
                catch // read will fail if file is open in another instance so make a copy and read it.
                {
                    string tempPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Temp";
                    if (!Directory.Exists(tempPath))
                        Directory.CreateDirectory(tempPath);
                    string tempFileName = Path.GetFileName(sourceDwgName);
                    tempFilePath = tempPath + @"\" + tempFileName;
                    File.Copy(sourceDwgName, tempFilePath, true);
                    sourceDb.ReadDwgFile(tempFilePath, FileShare.Read, true, "");
                    deleteTempFile = true;
                }
0 Likes
Message 3 of 8

Anonymous
Not applicable

this is a solution, but as the dwg's are large this would really slow down my application.

Isn't there another solution ?

 

0 Likes
Message 4 of 8

jabowabo
Mentor
Mentor

Try the code I added above. It may work faster than you expect. I don't know of another method but maybe someone else here does?

0 Likes
Message 5 of 8

norman.yuan
Mentor
Mentor

What error you encounter when the file is opened by another user? How do you know it is opened by another user (in AutoCAD)?

 

When you read the Dwg file into the Database, you used FlieShare.Read flag, it should be sufficient to read the data from the dwg file into Database, whether the drawing is opened in another AutoCAD session or not, unless the dwg file is opened by some application exclusively for writing, which is usually not the case when a drawing is opened in AutoCAD.

 

I strongly suspect that your issue is due to your code, and it very likely happens when you try to run the code repeatedly in the same AotuCAD session. This is because your code DOES not handle side Database correctly (see my comments beside your code):

 

            Dim db As New DatabaseServices.Database //You created a new Database instance and only abandon it wihtout disposing
            If tekening <> "basis" Then
                db = New DatabaseServices.Database(False, True) //Hopefully you dispose this Database instance in code not showing here
                Try
                    db.ReadDwgFile(tekening, System.IO.FileShare.Read, False, "")
                Catch generatedExceptionName As System.Exception
                    myEd.WriteMessage(vbLf & "Unable to read drawing file.")
                End Try
            Else
                db = New DatabaseServices.Database //You created a new Database and abandon it without disposing
                db = myDWG.Database
            End If

It is a common mistake for VB (classical VB or VB.NET) code that "New" keyword is abused in Dim ,,, As New... declaration. Also, in AutoCAD .NET API, if your code creates disposable object that is not to be persisted, your code is responsible to dispose it. If not, AutoCAD may become unstable.

 

In your code, if the variable takening is equal to "basis",  your code ends up creating 2 database instances and only leave them inside AutoCAd running process without being disposed.

 

The code should be like this:

 

Dim db As Database //Notice that there is no "New" keyword, because this reference might be used to point to different Dataabase

If takening.ToLower()<>"basis" Then

    Using db=New Database(False, Treu)   ''the code create a new Database, wrapped inside Using...block, so it will be disposed when work is done

        db.ReadDwgFile(.....,FileShere.Read,...)

        DoSometingWithTransaction(db)

    End Using

Else

    db=myDwg.Database   ''No need to wrap this Database in Using...block, because it is existing Database not created by your code

    DoSomethingWithTransaction(db)

End If

 

Private Sub DoSomethingWithTransaction(db As Database)

    Using tran As Transaction=db.TransactionManager.StartTransaction()

        ......

    End Using

End Sub

 

The code structured in this way only create 1 instance of side database when it is needed and it get disposed as soon as the work is done. I'll bet it would work regardless the dwg file to be read is opened in other AutoCAD session or not.

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 6 of 8

Anonymous
Not applicable

This isn't a solution for the moment. see below my full code.

 

The program have the work as following: I have a dwg that names "dwgwithxref.dwg". In this dwg there is an xref to "testxref.dwg". I now want to search for all the blocks named "VBREDAKGRO" in dwgwithxref.dwg but also in ALL xreffed drawing (in this case also in textxref.dwg).

The program works fine if I have dwgwithxref.dwg open. But if my colleque is working in "testxref.dwg" on the same time, the program goes to Catch generatedExceptionName As System.Exception an the "DosomethingWithTransaction" is'nt executed, so the blocks in the Xrefs aren't found.

 

If I use "using db= new database ..." I have the error:  "variable db hides a variabele in an enclosing block" and I can't run the code.

Without "using db= new database ..." I can run the code, but only if the xref isn't open by a collegue.

 

 

 

 <CommandMethod("searchblock")>
        Public Sub searchblock()
            numberofblocks = 0
            Dim hlpval As Boolean
            Dim myEd As EditorInput.Editor
            Dim myDWG As ApplicationServices.Document
            myDWG = ApplicationServices.Application.DocumentManager.MdiActiveDocument
            myEd = myDWG.Editor
            hlpval = True
            'search in base-drawing
            hlpval = False
            If checkdwg("basis") Then hlpval = True
            'search in xrefs 
            Dim Doc As Document = ApplicationServices.Application.DocumentManager.MdiActiveDocument
            Dim Db As Database = Doc.Database
            Dim XrGraph As XrefGraph = Db.GetHostDwgXrefGraph(False)
            aantalxrefs = 0
            For i = 1 To XrGraph.NumNodes - 1
                aantalxrefs = aantalxrefs + 1
                Dim XrNode As XrefGraphNode = XrGraph.GetXrefNode(i)
                If XrNode.Database <> Nothing Then
                    xrefs(i) = XrNode.Database.Filename
                    If checkdwg(xrefs(i)) Then hlpval = True
                End If
            Next
            MsgBox("I found " & numberofblocks.ToString & " blocks")
        End Sub

        Public Sub DoSomethingWithTransaction(db As Database)
            Dim myBlock As DatabaseServices.BlockReference
            Using tr As Transaction = db.TransactionManager.StartTransaction()
                Dim btable As BlockTable = DirectCast(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable)
                If btable.Has("VBREDAKGRO") Then
                    Dim btr As BlockTableRecord = DirectCast(tr.GetObject(btable("VBREDAKGRO"), OpenMode.ForRead), BlockTableRecord)
                    For Each refId As ObjectId In btr.GetBlockReferenceIds(True, False)
                        myBlock = DirectCast(tr.GetObject(refId, OpenMode.ForRead), BlockReference)
                        Dim owner As BlockTableRecord = DirectCast(tr.GetObject(myBlock.OwnerId, OpenMode.ForRead), BlockTableRecord)
                        If owner.IsLayout Then
                            If myBlock.Name.ToUpper = "VBREDAKGRO" Then
                                numberofblocks = numberofblocks + 1
                            End If
                        End If
                    Next
                End If
                tr.Commit()
            End Using
        End Sub
        Public Function checkdwg(ByVal tekening As String) As Boolean
            Dim myDWG As ApplicationServices.Document
            Dim myEd As EditorInput.Editor
            Dim myfilter(0), myfilter2(0) As DatabaseServices.TypedValue
            Dim mySF As New EditorInput.SelectionFilter(myfilter)
            Dim mySF2 As New EditorInput.SelectionFilter(myfilter2)
            myDWG = ApplicationServices.Application.DocumentManager.MdiActiveDocument
            myEd = myDWG.Editor
            Dim db As DatabaseServices.Database
            If tekening.ToLower <> "basis" Then
                Using db = New Database(False, True) ' here I have the error: variable db hides a variabele in an enclosing block
                    Try
                        db.ReadDwgFile(tekening, System.IO.FileShare.Read, False, "")
                        DoSomethingWithTransaction(db)
                    Catch generatedExceptionName As System.Exception
                        myEd.WriteMessage(vbLf & "Unable to read drawing file.")
                    End Try
                End Using
            Else
                db = New DatabaseServices.Database
                db = myDWG.Database
                DoSomethingWithTransaction(db)
            End If
        End Function

 

0 Likes
Message 7 of 8

norman.yuan
Mentor
Mentor
Accepted solution

OK, since the file you try to open as side database is actually a Xref inside the current drawing, you actually have 2 ways to get information from it:

 

1. Open the drawing as side database. I wasn't completely correct in previous reply: the ReadDwgFile() failing if the drawing is opened in other AutoCAD session is because the second argument used in ReadDwgFile() method - FileShare.Read. This argument is only meant for subsequent file opening after this method successfully opens the file. But in the case of it being already opened by other AutoCAD session/user, you need to use another overloaded call:

 

ReadDwgFile (string, FileOpneMode, ...). 

 

In my test, I have a drawing opened in another AutoCAD session. If I use FileShare.Read in ReadDwgFile(), I get file access exception. If I use FileOpenMode.OpenForReadAndAllShares, ReadDwgFile() succeeds.

 

2. To reach data inside Xrefed drawing, its Database is already in AutoCAD, you do not have to open it as side database.

 

Below is my test code. The scenario: in current open drawing there is an Xref, which has a few block references in modelspace. The Xrefed drawing in the meantime is opened in aother AutoCAD session. The code has 2 commands:

 

Command "UseSideDB" searches the current drawing for the file name of Xrefed file. Then open the found drawing file as side D.

Command "UseXrefDB" searches Xref and then search the Xref's DB.

 

Both commends result in the exactly same output.

 

using System.Collections.Generic;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(ReadXrefDB.MyCommands))]

namespace ReadXrefDB
{
    public class MyCommands 
    {
        [CommandMethod("UseSideDB")]
        public static void RunTest1()
        {
            var doc = CadApp.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;

            string blkNames = "";

            var xrefFiles = GetXrefPaths(doc.Database);

            if (xrefFiles.Length>0)
            {
                foreach (var xrefFile in xrefFiles)
                {
                    if (System.IO.File.Exists(xrefFile))
                    {
                        string names = AccessDataInSideDatabase(xrefFile);
                        if (!string.IsNullOrEmpty(names))
                        {
                            blkNames = blkNames + "\n" + names;
                        }
                    }
                }
            }

            PrintResult(ed, blkNames);
        }

        [CommandMethod("UseXrefDB")]
        public static void RunTest2()
        {
            var doc = CadApp.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;

            string blkNames = "";

            XrefGraph graph = doc.Database.GetHostDwgXrefGraph(false);

            for (int i = 0; i < graph.NumNodes; i++)
            {
                var node = graph.GetXrefNode(i);
                if (node.Database != null)
                {
                    if (node.Database.Filename.ToLower() != doc.Database.Filename.ToLower())
                    {
                        string names = SearchDBForBlockReferences(node.Database);

                        if (!string.IsNullOrEmpty(names))
                        {
                            blkNames = blkNames + "\n" + names;
                        }
                    }
                }
            }


            PrintResult(ed, blkNames);
        }

        private static string[] GetXrefPaths(Database db)
        {
            var paths = new List<string>();
            XrefGraph graph = db.GetHostDwgXrefGraph(false);

            for (int i=0; i<graph.NumNodes; i++)
            {
                var node = graph.GetXrefNode(i);
                if (node.Database!=null)
                {
                    if (node.Database.Filename.ToLower() != db.Filename.ToLower())
                    {
                        paths.Add(node.Database.Filename);
                    }
                }
            }

            return paths.ToArray();
        }

        private static string AccessDataInSideDatabase(string fileName)
        {
            string names = null;

            using (var db = new Database(false, true))
            {
                db.ReadDwgFile(fileName,FileOpenMode.OpenForReadAndAllShare, false, null);
                names= SearchDBForBlockReferences(db);
            }

            return names;
        }

        private static string SearchDBForBlockReferences(Database db)
        {
            var blkNames = new List<string>();

            using (var tran = db.TransactionManager.StartTransaction())
            {
                var model = (BlockTableRecord)tran.GetObject(
                    SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead);

                foreach (var id in model)
                {
                    if (id.ObjectClass.DxfName.ToLower()=="insert")
                    {
                        var bref = (BlockReference)tran.GetObject(id, OpenMode.ForRead);

                        string name;
                        if (bref.Name.IndexOf("|") > 0)
                            name = bref.Name.Split('|')[1]; //Block name is in format of "[Xref Name]|[Block Name]"
                        else
                            name = bref.Name;

                        blkNames.Add(name);
                    }
                }

                tran.Commit();
            }

            return string.Join("\n", blkNames.ToArray());
        }

        private static void PrintResult(Editor ed, string result)
        {
            if (!string.IsNullOrEmpty(result))
                ed.WriteMessage("\nBlocks found in Xrefed drawing: {0}", result);
            else
                ed.WriteMessage("\nNo block reference found in Xrefed drawing.");

            Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
        }
    }
}

HTH

 

Norman Yuan

Drive CAD With Code

EESignature

Message 8 of 8

Anonymous
Not applicable

This is a solution. Thanks. Now I have to implement this in my code.

 

0 Likes