Jump to content

Recommended Posts

Posted

As Reference Manager can only change the path to External References, and not the name of the reference drawing itself (which I need to do), I'm looking into correcting a set of drawings using ObjectDBX.

 

I've been successful at adding XREFs to ODBX drawings in the past using vla-AttachExternalReference, but am not understanding why I am receiving this error when attempting to detach:

 

; error: Automation Error. Description was not provided.

 

Here is my 'working' code at the moment:

 

(defun c:FOO  (/ sheets path dbx n oXref)
 (vl-load-com)
 (if (and (setq sheets (vl-directory-files
                         (setq path "[color=blue]SomePath[/color]\\")
                         "*.dwg"
                         1))
          (setq dbx (vla-GetInterfaceObject
                      (vlax-Get-Acad-Object)
                      (strcat "ObjectDBX.AxDbDocument."
                              (substr (getvar 'acadver) 1 2)))))
   (progn
     (foreach sheet  sheets
       (vl-catch-all-apply
         'vla-open
         (list dbx (strcat path sheet)))
       (vlax-for x  (setq oLayoutBlock
                           (vla-get-block
                             (vla-item (vla-get-layouts dbx)
                                       "Model")))
         (if
           (and (= "AcDbBlockReference" (vla-get-objectname x))
                (vl-position
                  (setq n (vla-get-effectivename x))
                  '("[color=blue]XREF1[/color]" "[color=blue]XREF2[/color]"))
                (= :vlax-true
                   (vla-get-isxref
                     (setq
                       oXref (vla-item (vla-get-blocks dbx) n)))))
            [b][color=red]([/color][/b]vla-detach oXref)))                                       [color=green];<- This line errors
[/color]        (vl-catch-all-apply
         'vla-saveas
         (list dbx (strcat path sheet))))
     (setq dbx (vl-catch-all-apply 'vlax-release-object (list dbx)))))
 (princ))

 

Note - Error checking not included, as this is only working code.

Posted

I think you need to reference the document as the first parameter and then the xref as a second

(vla-detach dbx oXref)

Posted

Soliver, I appreciate the suggestion.

 

Despite the parameters listed in the Developer Documentation, I tried your suggestion anyways with no success.

 

See the error returned:

 

; error: ActiveX Server returned the error: unknown name: Detach

 

... As the ODBX Document Object does not inherit the Detach Method.

 

 

Detach Method

 

Detaches an external reference (xref) from a drawing.

 

Signature

object.Detach()

 

Object

 

Block

The object this method applies to.

 

Remarks

 

Detaching an xref removes the xref from the current drawing. All copies of the xref are erased, and the xref definition is deleted. All xref-dependent symbol table information (such as layers and linetypes) is deleted from the current drawing.

 

You cannot detach an xref that contains other xrefs.

  • 1 year later...
Posted
RenderMan,

Did you ever resolve this?

 

Knowing full well that I am willingly, officially zombifying this thread....

 

Wow I was bad about posting my solution back then... I did, and will dig through my toolbox to see if I cannot find what I used. I've learned a lot since back then, and may have changed code for other uses, etc..

 

Is there something specific your needing other than what I describe above?

Posted

Of course, were I coding this new, I'd probably use the .NET API... Specifically ReadDwgFile() and Autodesk.AutoCAD.Internal.Utils.WcMatch(string, string) Methods. :geek:

Posted

Is there something specific your needing other than what I describe above?

Specifically. Detaching an xref from a drawing that is opened with dbx.

I'm converting a lisp that originally only functioned in the current session. Now I wish to convert to batch processing with dbx.

I'm just not there in .net yet.

 

vla-detach works in the current session, but fails in dbx - exactly the problem you had. How did you wind up detaching while in dbx?

 

One suggestion was to vla-delete = failed

The post suggested by SOliver also did not solve. Couldn't really tell if Joro was getting results and Lee was trying to troubleshoot..??, or if Lee hadn't tested the concept first?? Either way, it was a similar issue. Lee's code generated the Automation error without vla-catch-all-apply.

 

I would have started a new post, but I didn't see that age of this post in my browser. Thanks for looking back into this.

Posted

It was so long ago, I honestly do not remember how I got around this... I'll see what I can find, and post back.

Posted

As far as I know, XRefs are not loaded in drawings opened with ObjectDBX and hence cannot be detached.

Posted
Is there any substance to ronjonp's post about removing references to the xrefs'?

http://www.cadtutor.net/forum/showthread.php?p=299135#post299135

 

What happened when you tested this for yourself? :P

 

I've just tested the Erase() Method here, and all is well... Just be sure to erase the entity, and not the block definition (even though you need to test this for IsXref), and also test for locked layers, and restore as needed.

 

HTH

Posted (edited)
What happened when you tested this for yourself? :P

I did, but was attempting to delete the blockdef - which of course generated an automation error

 

I've just tested the Erase() Method here, and all is well... Just be sure to erase the entity, and not the block definition (even though you need to test this for IsXref), and also test for locked layers, and restore as needed.

 

HTH

That would be a solution if I were trying to create an Unreferenced xref. The xref's I'm removing are based on the user's selection to

Remove Unreferenced

Remove Unloaded

Remove FileNotFound

or any combination

 

Namely, my testing first centered on Unreferenced types, which means there are no inserts to Erase() (vla-erase).

 

I sure do like to create lisp packages that function wholy without other languages, but I'm starting to finally understand autolisp's limitations.

 

A complete side note... vla-IsXref will also return nested xref's. There's a way to elliminate nested xref's in lisp, but it involves making a list of subnested xref's and their xref's, etc., so if a tested xref is a member of the nested list, then it's a nest, not a direct. LeeMac has a routine that does that (xreflist me thinks). Negating that, I used a seperate .net LispFunction that simply returns the result of .net .IsFromExternalReference, which eliminates attempts to detach a nested xref.

 

The following code bits are my final the result... minus good LispFunction var handling (see http://www.theswamp.org/index.php?topic=43063.msg483195#msg483195)

 

The following is the function called during the dbx operations.

The var "cd: xrefclean: operation" is a vl-propagate'd and contains the initial command that obtained the user's options.

(defun cd:clean-xref (dbxdoc /
 cd:detach
 status
 statuses
 blk
 blocks
 blockname
 detach
 outdata
 ename
 reflist
 ref
 vla_ref
 )
(defun cd:detach (xref /)
 ;(vla-detach xref)
 ;(vla-delete xref)
 ;(vla-erase xref)
 (cd:xref-detach (vlax-vla-object->ename xref))
 );defun cd:detach
(setq blocks (vla-get-Blocks doc))
;make a collection list of all true xref block enames
(vlax-for blk blocks
 (if (and (= :vlax-true (vla-get-isxref blk))
   (setq ename (vlax-vla-object->ename blk))
   (cd:block-isxref ename))
;(setq blockname (vla-get-name blk)) ;get it now before it's deleted
;(setq blockpath (vla-get-path blk)))
;build a list first, then submit to .net to get results all at once.
(setq reflist (append reflist (list ename)))
);if
 );vlax-for blk in blocks
(if (and (< 0 (length reflist))
 (setq statuses (cd:xref-status reflist)))
 (foreach ref statuses
(setq detach nil
  status (cdr ref)
  vla_ref (vlax-ename->vla-object (car ref))
  blockname (vla-get-name vla_ref)
  blockpath (vla-get-path vla_ref))
(if (and (not detach)
 (wcmatch cd:xrefclean:operation "*R*")
 (= status "Unreferenced"))
  (setq detach (cd:detach vla_ref))
  )
(if (and (not detach)
 (wcmatch cd:xrefclean:operation "*F*")
 (or (= status "FileNotFound")
  (= status "Unresolved")))
  (setq detach (cd:detach vla_ref))
  )
(if (and (not detach)
 (wcmatch cd:xrefclean:operation "*L*")
 (= status "Unloaded"))
  (setq detach (cd:detach vla_ref))
  )
(if (and (not detach)
 (wcmatch cd:xrefclean:operation "*A*")
 (or (= status "FileNotFound")
  (= status "Unloaded")
  (= status "Unresolved")
  (= status "Unreferenced")))
  (setq detach (cd:detach vla_ref))
  )
(setq outdata (cons (list blockname
    status
    (if detach "Detached" "Remains Attached")
    blockpath)
  outdata)
  )
);foreach ref in reflist
 );if
outdata
);defun cd:clean-xref

 

-Split into two posts.

Edited by wishbonesr
Posted

-Continued

The first step was to ensure the block def wasn't a nested block.

[LispFunction("cd:block-isxref")]
public bool Block_isXref(ResultBuffer rb)
{
if (rb == null)
{ return false; }
TypedValue[] args = rb.AsArray();
if (args.Length > 1 || (LispDataType)args[0].TypeCode != LispDataType.ObjectId)
{ return false; }
ObjectId ObjId = (ObjectId)args[0].Value;
using (Transaction tr = ObjId.Database.TransactionManager.StartTransaction())
{
 try
 {
  BlockTableRecord btr = (BlockTableRecord)tr.GetObject(ObjId, OpenMode.ForRead, false);
  if (btr.IsFromExternalReference)
   return true;
 }
 catch (System.Exception)
 {
  tr.Abort();
  return false;
 }
}
return false;
}

 

The next step was to get an accurate status of the xref. It was important that the function only invoked .ResolveXrefs() once per dbx drawing, thus the need to send it a list of enames/objectid's to work with.

[LispFunction("cd:xref-status")]
public object Xref_Status(ResultBuffer rb)
{
if (rb == null)
{ return false; }
TypedValue[] args = rb.AsArray();
ObjectId ObjId = new ObjectId();
if (args.Length == 1)
{ ObjId = (ObjectId)args[0].Value; }
else { ObjId = (ObjectId)args[1].Value; }
using (Transaction tr = ObjId.Database.TransactionManager.StartTransaction())
{
 ResultBuffer res = new ResultBuffer();
 try
 {
  res.Add(new TypedValue((int)LispDataType.ListBegin));
  if (!HostApplicationServices.WorkingDatabase.Equals(ObjId.Database))
  ObjId.Database.ResolveXrefs(true, false);
  foreach (TypedValue arg in args)
  {
   try
   {
    BlockTableRecord btr = (BlockTableRecord)tr.GetObject((ObjectId)arg.Value, OpenMode.ForRead, false);
    res.Add(new TypedValue((int)LispDataType.ListBegin));
    res.Add(new TypedValue((int)LispDataType.ObjectId, (ObjectId)arg.Value));
    res.Add(new TypedValue((int)LispDataType.Text, btr.XrefStatus.ToString()));
    res.Add(new TypedValue((int)LispDataType.DottedPair));
   }
   catch (System.Exception)
   {
   }
  }
  res.Add(new TypedValue((int)LispDataType.ListEnd));
  return res;
 }
 catch (System.Exception)
 {
  tr.Abort();
  res.Add(new TypedValue((int)LispDataType.ListEnd));
  return false;
 }
}
}

 

And finally, function to detach the file using .net.

[LispFunction("cd:xref-detach")]
public Boolean Xref_Detach(ResultBuffer rb)
{
if (rb == null)
{ return false; }
TypedValue[] args = rb.AsArray();
if (args.Length > 1 || (LispDataType)args[0].TypeCode != LispDataType.ObjectId)
{ return false; }
ObjectId objId = (ObjectId)args[0].Value;
using (Transaction tr = objId.Database.TransactionManager.StartTransaction())
{
 try
 {
  BlockTableRecord btr = (BlockTableRecord)tr.GetObject(objId, OpenMode.ForRead, false);
  if (btr.IsFromExternalReference == true)
  {
   objId.Database.DetachXref(objId);
   tr.Commit();
   return true;
  }
  else return false;
 }
 catch (System.Exception)
 {
  tr.Abort();
  return false;
 }
}
}

Posted

Sorry to nitpick, but the cd : detach and cd : xrefclean : operation LispFunction Methods appear to be missing, and you've posted cd : xref-detach (the Xref_Detach() Method) instead.

 

Also, you really should make use of either Gile's LispException* Classes that I mentioned in your other thread at TheSwamp, or the code contract that Tony offered in his prototype design, rather than simply having an empty catch statement that neither handles the exception, nor reports to you what went wrong.

 

They may be interchangeable (I honestly have not tried it for myself), but generally one would return bool value in lieu of a Boolean Class type... Compare your Block_isXref() and Xref_Detach() Methods.

 

Just to throw the idea out there for your consideration, might I suggest that instead of creating separate LispFuntion Methods for each Block_isXref(), Xref_Status(), and Xref_Detach(), that you instead consider (for this specific task) coding a single, (bit-coded?) XrefDetachIf() Method.

 

While functional, and also an enhancement to LISP as is, I think you'll find a significant increase in performance, if you simply take a step back and plan your plug-in around what you're actually attempting to do here.

 

Were you to use a single XrefDetachIf() Method, you would reduce your overhead as you would need only a single Transaction for each call to the LispFunction Method, as compared to using each of the three LispFunction Methods you posted above.

 

Considering what your LISP does now....

 

For each DBX Document, you iterate the Block Collection, and test each using Block_isXref() (1+ Transaction for each call) in order to build a list of Block References that are External References.

 

Next, you iterate the resultant list of External References, testing for Xref_Status() (1+ Transaction for each call)... This step you do good, by utilizing the ResultBuffer to pass ObjectId[] as an argument.

 

However, were you to effectively code a XrefDetachIf() LispFunction Method, you would preclude the need to build a valid list of External References, and then iterate that list to determine which are of a particular status in the first place, and simply pass ObjectId[] for a given DBX Document's Block's eNames (as you do for Xref_Status() ), and the internal .NET logic would minimize the processing, sorting, and 'meat' of the LISP routine for you. :geek:

 

HTH

Posted

Pseudo code:

       /// <summary>
       /// First argument - ObjectId[] of enames
       /// Second argument - Bit-coded value for external 
       ///     reference statuses to filter for:
       ///     
       ///         1   FileNotFound
       ///         2   Nested
       ///         4   Unloaded
       ///         8   Unreferenced
       ///         
       /// Third argument - Reload unloaded references (T / nil)
       ///         
       /// Example: 
       ///     (Xref-Detach-If listOfXrefEnames 9 T)
       /// </summary>
       /// <param name="res"></param>
       /// <returns></returns>
       [LispFunction("Xref-Detach-If")]
       public ResultBuffer XrefDetachIf(ResultBuffer res)
       {

       }

Posted (edited)

I've formed a response twice, and this forum has timed me out twice and it lost my composition. Actually more than twice, but I copied my response before preview a few times. This time though I just forgot. Must mean I should not post today.

 

Is it like 3 minutes or something????

 

Well... I guess the point of my reply was a big thank you for the pointers and guidance.

 

 

...cd : detach and cd : xrefclean : operation LispFunction Methods appear to be missing, and you've posted cd : xref-detach (the Xref_Detach() Method) instead.

cd : detach is a nested defun. It's there

cd : xrefclean : operation is a text var, not a defun, begotten from the c:xrefclean wrapper command

Here's a snip of that section

(initget "L R F A LR LF LRF LFR RL RF RFL RLF FL FR FLR FRL")
(princ "\nYou can use any combination like RF or FL")
(setq cd:xrefclean:operation (getkword "\nXref Cleaner: Detach [un(R)eferenced/Not-(F)ound/Un(L)oaded/(A)ll-Non-Displayed] <A>: "))

Also, you really should make use of either Gile's LispException* Classes that I mentioned in your other thread at TheSwamp, or the code contract that Tony offered in his prototype design, rather than simply having an empty catch statement that neither handles the exception, nor reports to you what went wrong.

Referenced that post, and gave mea culpas :)

They may be interchangeable (I honestly have not tried it for myself), but generally one would return bool value in lieu of a Boolean Class type... Compare your Block_isXref() and Xref_Detach() Methods.

Good catch. vs autocomplete

 

The rest of your suggestion is sound advice. What I accidentally found out, while proving your suggestion was that vla-get-isxref provides a considerable time savings, as it filters out blocks defs that are not xref's at all.

Elapsed milliseconds / relative speed for 512 iteration(s):
   (CD:XREFTEST1 XREFLIST)......13634 / 19.58 <fastest>
   (CD:XREFTEST2 XREFLIST).....266996 / 1.00 <slowest>

The above xreflist is a 1432 length vla-object collection of vla-get-blocks. 383 are actuall xref's and 9 are direct xref's.

Both functions use your suggested method of combining the cd : block-isxref and cd : xref-status into a single function. I left the determination of whether to detach to lisp.

So the difference in the above function tests are that cd : xreftest1 included a pre-test for vla-get-isxref, while cd : xreftest2 sent every block def to be tested in .net.

 

After reminiscing. I believe I will still keep it seperate. Each Lisp Function I've created has a specific role; that thought actually did go into creating the functions, as my intention was to expand lisp in the spirit of lisp. Where logic is kept in lisp, and the lispfunctions provide expanded capability.

 

For instance. It could be thought that having a function to return boolean for IsUnloaded, is IsResolved, and IsFromExternalReference, might be condiered obsolete if I just returned the string for XrefStatus, however I can envision scenarios where each would serve purpose in the situation needed.

 

I just realized I took this thread over. I'm gratefull I stumbled on your original post, because I'd be further behind the curve than I already am.

 

Sincerely,

Edited by wishbonesr
Forum kept timing out. But finally edited to include intended post.
Posted
I've formed a response twice, and this forum has timed me out twice and it lost my composition. Actually more than twice, but I copied my response before preview a few times. This time though I just forgot. Must mean I should not post today.

 

Is it like 3 minutes or something????

 

Well... I guess the point of my reply was a big thank you for the pointers and guidance.

 

You're welcome; I'm happy to help. :beer:

 

Perhaps I should have broken things out a bit more, but I wanted to 'get it all in' before I left work to vote. Again, as you already know from my participation over at TheSwamp, I am no expert when it comes to the finer points of .NET, but I genuinely hope that my comments are of help to you.

Posted

I just realized I took this thread over. I'm gratefull I stumbled on your original post, because I'd be further behind the curve than I already am.

 

No worries; I too am glad you chimed in. I've been wanting to delve more and more into .NET development, but often do not make the time to plan out my own initiatives (I do it all in my spare time), so more often than not helping others with their interests is something I look for first.

 

Besides, I think your LispFunction Methods would make a good addition to this thread, where I may very well code that XrefDetachIf() LispFunction also. :beer:

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...