Author Topic: Calling .net LispFunction while processing DBX  (Read 8913 times)

0 Members and 1 Guest are viewing this topic.

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Calling .net LispFunction while processing DBX
« on: October 25, 2012, 01:21:39 PM »
I'm calling a .net LispFunction while processing through DBX.
The LispFunction originally uses the Application.DocumentManager.MdiActiveDocument to search for the supplied ename/ObjectId, then I tweaked it to cycle the Application.DocumentManager looking for the ObjectId.  If the drawing isn't open, then it will not find the ObjectId/ename. The function using MdiActiveDocument, of course, is not going to find the supplied ename from another doc.

So is there a way to get the appropriate Database to GetObject() the ObjectId I supply when it came from a document opened in lisp via DBX?
That's a mouthfull

Is there a way to use my Lispfunction in this manner via DBX object?

Edit Notes: I reread this.  It's not easy to explain. sorry. 
Using LeeMac ODBX wrapper http://www.lee-mac.com/odbxbase.html
« Last Edit: October 25, 2012, 02:05:55 PM by CADDOG »

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Calling .net LispFunction while processing DBX
« Reply #1 on: October 25, 2012, 03:41:29 PM »
I'm not sure I understand what you trying to do, but I would pass the handle of the entity that you want, as that doesn't change per opening.  As far as I understand it, object ids are assigned per opening, so they can change, but the handle will not change.
Tim

I don't want to ' end-up ', I want to ' become '. - Me

Please think about donating if this post helped you.

BlackBox

  • King Gator
  • Posts: 3770
Re: Calling .net LispFunction while processing DBX
« Reply #2 on: October 25, 2012, 03:49:33 PM »
Tim, if I am understanding the OP correctly, he's trying to refactor his code to access the ObjectDBX Document Object in lieu of the Application.DocumentManager.MdiActiveDocument Object.

Never tried this before myself, but perhaps this quick search result may be of help:

Quote from: AutoCAD .NET Developer's Guide

Use COM Interoperability with .NET

Microsoft Visual Studio can utilize both native .NET and COM interfaces in the same project. By utilizing COM interop, you can migrate existing code that might have been written in Visual Basic 6 or VBA without having to completely rewrite it. To access AutoCAD automation objects from a project created in Microsoft Visual Studio, create references to the following files:

    The AutoCAD 2010 type library, acax18enu.tlb, located at <drive>:\Program Files\Common Files\Autodesk Shared.

    The AutoCAD/ObjectDBX Common 18.0 type library, axdb18enu.tlb, located at <drive>:\Program Files\Common Files\Autodesk Shared.

<snip>
"How we think determines what we do, and what we do determines what we get."

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Re: Calling .net LispFunction while processing DBX
« Reply #3 on: October 25, 2012, 04:33:56 PM »
RenderMan is correct.
I'm so new at .NET it's not even funny (3 weeks).  Let alone the API of Autocad.
So your suggestion, which I remember reading sounds like the missing link.

I'm going to try two different ways, because I want to stay away from COM if possible.  The reason?  My current LispFunction is so simple, and doesn't need to be in .net.  This go-around is a proof of concept as I migrate more 'complex lisp' or 'impossible to do in lisp' functions to .net.  Bulk processing is a major part of years of lisp code that has been tweaked to work with dbx (getvar, selectionsets, etc).  If I fail, then it's a realization that I need to develop the more complicated bulk processing in .net.  That's a hard bullet to bite right now, due to the learning curve.

First I'm going to shoot at opening the dwg by suppying the path\file and use ReadDwgFile() - which means I'm openign the file twice (ha).  Once in dbx and the other .net

Second I'm going to attempt COM if the first attempt falls flat (at this point it's like looking into a cave without a flashlight).  If I do supply the vla-item to lisp, do you know what LispDataType it will be assigned to?
Currently, I get the following error:
Code: [Select]
invalid data type or data overflow: #<VLA-OBJECT IAcadDocument 0000000028dcfc60>The LispFunction itself is made to never fail (ha), and return either t or nil in all situations.  I think that # is supposed to be telling me something (clue).

Thanks for the feed back fellas.
« Last Edit: October 25, 2012, 04:39:02 PM by CADDOG »

BlackBox

  • King Gator
  • Posts: 3770
Re: Calling .net LispFunction while processing DBX
« Reply #4 on: October 25, 2012, 04:40:47 PM »
Not sure that's the best approach (not that I can offer better at the moment), attempting to open the file twice, once in ObjectDBX, and once in .NET (isn't that still opening within DocumentManager, unless using RealDWG? :?).

As for what LispDataType enums are available, just search for AutoDesk.AutoCAD.Runtime.LispDataType in Object Browser:wink:
"How we think determines what we do, and what we do determines what we get."

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Re: Calling .net LispFunction while processing DBX
« Reply #5 on: October 25, 2012, 05:02:07 PM »
...
...(isn't that still opening within DocumentManager, unless using RealDWG? :?).

I don't know.  :ugly: Breaking till it works is my preferred method to figure something out.
I am not a Card Carrying member of RealDWG.
See I thought dbx is using the documentmanger, but as I said before (strike-through), I cycled through the documents in documentmanager with no results.  This almost makes me believe that dbx is operating in a seperate application instance???
There's just so many things I'm ignorant on, I can't draw conclusions.  Doesn't help if it hasn't been attempted before.
Edit:
You know what...I think that tells me what I should be doing.


As for what LispDataType enums are available, just search for AutoDesk.AutoCAD.Runtime.LispDataType in Object Browser:wink:

Did that.  Didn't see anything that could equal the container for a vla-object, even if I did use COM in my project, I don't think I can pass a vla-object from lisp to a COM enabled .net project.
Thinking I am going to have to overload the ResultBuffer...
Just kidding.

BlackBox

  • King Gator
  • Posts: 3770
Re: Calling .net LispFunction while processing DBX
« Reply #6 on: October 25, 2012, 05:34:47 PM »
I too am very new to .NET development, so take what I have said with a grain of salt.

I know that both ObjectDBX, and RealDWG function outside of the AutoCAD application, but I am unsure of their being on the same, or different thread (above my knowledge base). Also, I have never used RealDWG before.

Hopefully someone much more knowledgeable will be along shortly.
"How we think determines what we do, and what we do determines what we get."

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Re: Calling .net LispFunction while processing DBX
« Reply #7 on: October 25, 2012, 07:57:02 PM »
Grain of salt...
Naw, bouncing ideas off of anybody sometimes produces results, by thinking beyond the box you hold yourself to.  Sometimes I'll vent to my wife about some project frustrations, and the patient and adoring wife she is, she just nods cuz she knows she just needs to be attentive and give me an ear...  And then BAM!  An idea strikes me, and I'm an hour away from my coding computer at work. HA.

But I think I'm going to drop it.  I don't see anyone attempting to work with dbx in lisp and use functions in .net.  It just so happens, this project requires me to mass edit files, and I needed something quicker and more reliable than my current xref status tester in lisp.

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Calling .net LispFunction while processing DBX
« Reply #8 on: October 25, 2012, 10:21:10 PM »
What are you trying to do?  Maybe theSwamp has a solution already posted somewhere.  You can search the Show Your Stuff area of xref stuff, as I have posted some items in there; Lisp and .Net.
Tim

I don't want to ' end-up ', I want to ' become '. - Me

Please think about donating if this post helped you.

BlackBox

  • King Gator
  • Posts: 3770
Re: Calling .net LispFunction while processing DBX
« Reply #9 on: October 26, 2012, 06:43:25 AM »
So is there a way to get the appropriate Database to GetObject() the ObjectId I supply when it came from a document opened in lisp via DBX?

I've not tested this myself with ObjectDBX, but in lieu of Application.DocumentManager.MdiActiveDocument Document Object to access the Database , you might consider using HostApplicationServices.WorkingDatabase instead.

Again, you've not posted any code, so it's hard to tell what other changes may be necessary.
"How we think determines what we do, and what we do determines what we get."

gile

  • Gator
  • Posts: 2518
  • Marseille, France
Re: Calling .net LispFunction while processing DBX
« Reply #10 on: October 26, 2012, 07:09:22 AM »
What about using Database.ReadDwgFile() .NET method instead of ObjectDBX ?
Speaking English as a French Frog

BlackBox

  • King Gator
  • Posts: 3770
Re: Calling .net LispFunction while processing DBX
« Reply #11 on: October 26, 2012, 07:44:37 AM »
What about using Database.ReadDwgFile() .NET method instead of ObjectDBX ?

Gile - Thank you for suggesting this!

Database.ReadDwgFile() may be exactly what I was after in order to successfully port one of my Civil 3D routines from Visual LISP/ObjectDBX to .NET
"How we think determines what we do, and what we do determines what we get."

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Re: Calling .net LispFunction while processing DBX
« Reply #12 on: October 26, 2012, 01:13:47 PM »
What about using Database.ReadDwgFile() .NET method instead of ObjectDBX ?
That what I was saying I'd try first - kinda - more like a hybrid of the two.
I'm going to try two different ways...

First I'm going to shoot at opening the dwg by suppying the path\file and use ReadDwgFile() - which means I'm openign the file twice (ha).  Once in dbx and the other .net

Second I'm going to attempt COM...
But this is a transition.  I'll get to prompting, processing, iterating a drawing list in .net eventually.  Many of my batch processors were based on nested optional calls to dbx (process-current or select-files), and it will take some time to clean them up and modularize them to supply a list of files (strings) to lisp functions; Not to mention how many things I don't know about AutoCAD API vs. what I DO know about AutoLisp/VisualLisp.  My current intro attempt at making LispFunctions in .net was to provide functions that are near impossible to do in lisp (or just haven't been figured out yet - like obtaining a complete status on xref's as there's only so much you can get off of dxf 70 & 71) I'm not ready to convert entire programs into .net, Yet. 

I think I just picked the wrong project (which needs batching) to build my first .net LispFunction.  Would have worked out well, if I was only using in the current autocad session.

I've dropped this anyway, and gone back to what I know.  Will progress further the next go-around.

Thank you so much guys.  I appreciate the guidance and will be back for more.

TheMaster

  • Guest
Re: Calling .net LispFunction while processing DBX
« Reply #13 on: October 30, 2012, 09:47:25 AM »
I've already finished in lisp so I hope nobody spends too much more time looking at this one.
For historical purposes.  Here was the function.

......

//This one fails because the ename/ObjectId (of course) is not going to be the same with the same drawing in two different sessions.
//Handle is my only other option me thinks, but then I might have to make two different flavors, one for a handle and drawing, and the other for ObjectId in current drawing.
//I don't prefer to make handling Handles the default - I'd rather ObjectId's and avoid conversion/confusion.
//Would have to invent a way to detect if supplied var is ename or handle.  Or just try catch i guess??  Use an overload once the var type is discovered.  Also might need to cycle the drawings open in DocumentManager to avoid opening twice if it's already open in user session of autocad (non dbx).


As I'm sure you've already reconed, ObjectIds are database-specific and so are handles.

You can't identify an object in one database using an ObjectId or handle of an object in another database. In the case of a BlockTableRecord representing an xref, the only thing they have in common is the path of the xref'd drawing file.

However, you're jumping through a lot of hoops here, needlessly.

On the LISP side, you can open the database using ObjectDBX, get the vla-object of the AcadBlock representing the xref, and pass that to (vlax-vla-object->ename), and then just pass the result to your LispFunction, which will get the ObjectId of the AcadBlock/BlockTableRecord.

The ObjectId class has a Database property, that returns the containing database, and that's what you would use to access anything else in the same database. (vlax-vla-object->ename) along with the Database property of the ObjectId is also the key to solving the more general problem of passing a database that was opened via ObjectDBX in LISP or another ActiveX consumer, to managed code. You just pass the entity name of any object in that database, and the ObjectId that comes out on the managed side, gives you the containing Database.

« Last Edit: October 30, 2012, 09:59:31 AM by TT »

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Re: Calling .net LispFunction while processing DBX
« Reply #14 on: October 30, 2012, 01:26:52 PM »
The ObjectId class has a Database property, that returns the containing database, and that's what you would use to access anything else in the same database. (vlax-vla-object->ename) along with the Database property of the ObjectId is also the key to solving the more general problem of passing a database that was opened via ObjectDBX in LISP or another ActiveX consumer, to managed code. You just pass the entity name of any object in that database, and the ObjectId that comes out on the managed side, gives you the containing Database.
There are times when even the solution is frustrating.  Fighting for hours, sometimes even days, while the simplest of properties and direct routes evade.

I think I love you man.   :kewl:

Seriously though; Knowing you can get to the object when in dbx opens the door for so many other things I wanted to do.

Thank you.

Code - C#: [Select]
  1. [LispFunction("cd:ename-xref-isresolved")]
  2. public bool Ename_xref_isResolved(ResultBuffer rb)
  3. {
  4.     if (rb == null)
  5.     { return false; }
  6.     TypedValue[] args = rb.AsArray();
  7.     if (args.Length > 1 || (LispDataType)args[0].TypeCode != LispDataType.ObjectId)
  8.     { return false; }
  9.     ObjectId ObjId = (ObjectId)args[0].Value;
  10.     using (Transaction tr = ObjId.Database.TransactionManager.StartTransaction())
  11.     {
  12.         try
  13.         {
  14.             BlockTableRecord btr = (BlockTableRecord)tr.GetObject(ObjId, OpenMode.ForRead, false);
  15.             if (btr.IsResolved)
  16.                 return true;
  17.         }
  18.         catch (System.Exception)
  19.         {
  20.             tr.Abort();
  21.             return false;
  22.         }
  23.     }
  24.     return false;
  25. }

BlackBox

  • King Gator
  • Posts: 3770
Re: Calling .net LispFunction while processing DBX
« Reply #15 on: October 30, 2012, 02:21:41 PM »
FWIW -

Gile offers some LispException Classes at the bottom of this post that I have found to be very useful for my own LispFunction Methods.
"How we think determines what we do, and what we do determines what we get."

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Re: Calling .net LispFunction while processing DBX
« Reply #16 on: October 30, 2012, 02:40:32 PM »
LispException

You read my mind.  Thanks RenderMan.

BlackBox

  • King Gator
  • Posts: 3770
Re: Calling .net LispFunction while processing DBX
« Reply #17 on: October 30, 2012, 02:43:43 PM »
LispException

You read my mind.  Thanks RenderMan.

Thank Gile, as I am but the mirror:

Quote
There are two ways of spreading light, to be the candle or the mirror that reflects it - Edith Wharton
"How we think determines what we do, and what we do determines what we get."

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Re: Calling .net LispFunction while processing DBX
« Reply #18 on: October 30, 2012, 03:30:18 PM »
Absolutely, a big thank you to Gile.  I've followed many of his posts suggestions.

TheMaster

  • Guest
Re: Calling .net LispFunction while processing DBX
« Reply #19 on: October 30, 2012, 09:00:12 PM »
FWIW -

Gile offers some LispException Classes at the bottom of this post that I have found to be very useful for my own LispFunction Methods.

I can't remember how long ago it was that I started on this, but I know I never finished it. It's mostly useful with lisp functions that take 'fixed' argument lists. The plan was to extend it to support optional arguments, and repeatable arguments, but I never got around to that.   :oops:

Here's some examples from the console (without the stacktrace):

Code - Text: [Select]
  1.  
  2. Command: (foobar 1 2 3)
  3. System.ArgumentException:
  4.    Error: Bad argument type (index = 1)
  5.    Usage: (foobar int real string)
  6.  
  7. Command: (foobar 99 2.5 "hello")
  8. (foobar): Arguments are correct
  9. nil
  10.  
  11. Command: (foobar "fubar")
  12. System.ArgumentException:
  13.    Error: Incorrect number of arguments
  14.    Usage: (foobar int real string)
  15.  
  16.  


And here's the code:

Code - C#: [Select]
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using Autodesk.AutoCAD.Runtime;
  7. using System.Reflection;
  8. using Autodesk.AutoCAD.DatabaseServices;
  9. using Autodesk.AutoCAD.EditorInput;
  10. using Autodesk.AutoCAD.ApplicationServices;
  11.  
  12. namespace LispHelpers
  13. {
  14.  
  15.    /// <summary>
  16.    ///
  17.    /// A crude implementation of contract enforcement for LISP
  18.    /// functions using Attributes.
  19.    ///
  20.    /// The example below should be self-explainatory.
  21.    ///
  22.    /// </summary>
  23.    public class RequiredArgumentsAttribute : System.Attribute
  24.    {
  25.       LispDataType[] argtypes = null;
  26.       public RequiredArgumentsAttribute( params LispDataType[] types )
  27.       {
  28.          this.argtypes = types ?? new LispDataType[0];
  29.       }
  30.  
  31.       public static TypedValue[] Check( ResultBuffer args, MethodBase method )
  32.       {
  33.          LispFunctionAttribute lfa = method.GetAttribute<LispFunctionAttribute>();
  34.          if( lfa == null )
  35.             throw new ArgumentException( "Method must have the [LispFunction] Attribute applied to it" );
  36.          RequiredArgumentsAttribute rqa = method.GetAttribute<RequiredArgumentsAttribute>();
  37.          LispDataType[] prototype = rqa.argtypes;
  38.          string func = lfa.GlobalName;
  39.          TypedValue[] values = args != null ? args.AsArray() : new TypedValue[0];
  40.          if( prototype == null )
  41.             return values;
  42.          if( values.Length == 0 && prototype.Length == 0 )
  43.             return values;
  44.          if( values.Length != prototype.Length )
  45.             Error( "Incorrect number of arguments", func, prototype );
  46.          for( int i = 0; i < values.Length; i++ )
  47.          {
  48.             if( ! CheckArg( values[i], prototype[i] ) )
  49.             {
  50.                Error( string.Format( "Bad argument type (index = {0})", i ), func, prototype );
  51.             }
  52.          }
  53.          return values;
  54.       }
  55.  
  56.       private static void Error( string msg, string func, LispDataType[] args )
  57.       {
  58.          string errorMsg = string.Format( "\n   Error: {0}\n   Usage: {1}\n\n", msg,
  59.             FormatArgs( func, args ) );
  60.          throw new ArgumentException( errorMsg );
  61.       }
  62.  
  63.       static bool CheckArg( TypedValue v, LispDataType type )
  64.       {
  65.          short code = (short) v.TypeCode;
  66.          object value = v.Value;
  67.          LispDataType argType = (LispDataType) code;
  68.          short typeCode = (short) type;
  69.          if( code == typeCode )
  70.             return true;
  71.          if( type.EqualsAny( LispDataType.Int16, LispDataType.Int32 ) &&
  72.             argType.EqualsAny( LispDataType.Int32, LispDataType.Int16 ) )
  73.             return true;
  74.          return false;
  75.       }
  76.  
  77.       static string FormatArgs( string func, LispDataType[] args )
  78.       {
  79.          if( args == null || args.Length == 0 )
  80.             return "(no arguments)";
  81.          return string.Format( "({0} {1})", func,
  82.             string.Join( " ", args.Select( a => typeNames[a] ).ToArray() ) );
  83.       }
  84.  
  85.       static Dictionary<LispDataType, string> typeNames = ( (LispDataType[]) Enum.GetValues( typeof( LispDataType ) ) ).ToDictionary(
  86.          v => v, v => v.ToString() );
  87.  
  88.       // currently not used
  89.       static Dictionary<LispDataType, Type> typeMap = new Dictionary<LispDataType, Type>();
  90.  
  91.       static Dictionary<Type, LispDataType> types = new Dictionary<Type, LispDataType>();
  92.  
  93.       static RequiredArgumentsAttribute()
  94.       {
  95.          // Not all types can be mapped to a LispDataType
  96.          types[typeof( ObjectId )] = LispDataType.ObjectId;
  97.          types[typeof( SelectionSet )] = LispDataType.SelectionSet;
  98.          types[typeof( Int32 )] = LispDataType.Int32;
  99.          types[typeof( Int16 )] = LispDataType.Int16;
  100.          types[typeof( double )] = LispDataType.Double;
  101.          types[typeof( string )] = LispDataType.Text;
  102.          
  103.          typeNames[LispDataType.ObjectId] = "ename";
  104.          typeNames[LispDataType.ListBegin] = "list";
  105.          typeNames[LispDataType.Angle] = "real";
  106.          typeNames[LispDataType.Int16] = "int";
  107.          typeNames[LispDataType.Int32] = "int";
  108.          typeNames[LispDataType.Text] = "string";
  109.          typeNames[LispDataType.T_atom] = "T/nil";
  110.          typeNames[LispDataType.DottedPair] = "alist";
  111.          typeNames[LispDataType.SelectionSet] = "Selection Set";
  112.          typeNames[LispDataType.Orientation] = "real";
  113.          typeNames[LispDataType.Double] = "real";
  114.       }
  115.    }
  116.  
  117.    public static class ExtensionMethods
  118.    {
  119.       public static TypedValue[] ValidateRequiredArguments( this ResultBuffer args, MethodBase method )
  120.       {
  121.          return RequiredArgumentsAttribute.Check( args, method );
  122.       }
  123.  
  124.       public static T GetAttribute<T>( this ICustomAttributeProvider provider ) where T : System.Attribute
  125.       {
  126.          T[] a = (T[]) provider.GetCustomAttributes( typeof( T ), false );
  127.          if( a != null && a.Length > 0 )
  128.             return (T) a[0];
  129.          else
  130.             return null;
  131.       }
  132.  
  133.       public static bool EqualsAny<T>( this T value, params T[] args )
  134.       {
  135.          return args.Contains( value );
  136.       }
  137.    }
  138.  
  139.    public static class RequiredArgumentsAttributeExample
  140.    {
  141.       /// Example: This function requires an int, a real, and a string,
  142.       /// in that order. e.g., (foobar 99 2.5 "Hello") Note that range
  143.       /// checking is currently not performed on integers and Int32
  144.       /// and Int16 are treated the same (TODO).
  145.  
  146.       [RequiredArguments( LispDataType.Int32, LispDataType.Double, LispDataType.Text )]
  147.       [LispFunction( "foobar" )]
  148.       public static object FooBar( ResultBuffer args )
  149.       {
  150.          // validate all arguments:
  151.          TypedValue[] arguments = args.ValidateRequiredArguments( MethodInfo.GetCurrentMethod() );
  152.  
  153.          Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( "(foobar): Arguments are correct\n" );
  154.          return null;
  155.       }
  156.    }
  157. }
  158.  
  159.  

TheMaster

  • Guest
Re: Calling .net LispFunction while processing DBX
« Reply #20 on: October 30, 2012, 09:13:54 PM »
The ObjectId class has a Database property, that returns the containing database, and that's what you would use to access anything else in the same database. (vlax-vla-object->ename) along with the Database property of the ObjectId is also the key to solving the more general problem of passing a database that was opened via ObjectDBX in LISP or another ActiveX consumer, to managed code. You just pass the entity name of any object in that database, and the ObjectId that comes out on the managed side, gives you the containing Database.
There are times when even the solution is frustrating.  Fighting for hours, sometimes even days, while the simplest of properties and direct routes evade.

I think I love you man.   :kewl:

Seriously though; Knowing you can get to the object when in dbx opens the door for so many other things I wanted to do.

Thank you.

Code - C#: [Select]
  1. [LispFunction("cd:ename-xref-isresolved")]
  2. public bool Ename_xref_isResolved(ResultBuffer rb)
  3. {
  4.     if (rb == null)
  5.     { return false; }
  6.     TypedValue[] args = rb.AsArray();
  7.     if (args.Length > 1 || (LispDataType)args[0].TypeCode != LispDataType.ObjectId)
  8.     { return false; }
  9.     ObjectId ObjId = (ObjectId)args[0].Value;
  10.     using (Transaction tr = ObjId.Database.TransactionManager.StartTransaction())
  11.     {
  12.         try
  13.         {
  14.             BlockTableRecord btr = (BlockTableRecord)tr.GetObject(ObjId, OpenMode.ForRead, false);
  15.             if (btr.IsResolved)
  16.                 return true;
  17.         }
  18.         catch (System.Exception)
  19.         {
  20.             tr.Abort();
  21.             return false;
  22.         }
  23.     }
  24.     return false;
  25. }

You're welcome.

One minor issue with the code you're showing is that it should throw exceptions when the arguments are not what they should be, rather than just returning false. False means the xref is not resolved, but that doesn't tell the calling code there's a bug that must be fixed. Throwing an exception in a LispFunction triggers an error in the calling LISP code, which is what should happen if the arguments weren't correct.

You can use Gile's solution or something like the class I posted just now to check the arguments coming from LISP and raise an error if they're not what you expect.

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Re: Calling .net LispFunction while processing DBX
« Reply #21 on: November 01, 2012, 02:04:09 PM »
I hear ya.
Until I was pointed to the above goodies, I wan't quite sure how to throw an error that lisp would interpret (hadn't experimented either).  Not to mention, I skipped Chapter 7 in my book ' Wrox - Beginning Visual C# 2010' for Error Handling.

I'm leaning toward how clean this keeps the actual functions...
MethodInfo is new to me as well.  Back to the books.  Thanks again.

TheMaster

  • Guest
Re: Calling .net LispFunction while processing DBX
« Reply #22 on: November 01, 2012, 07:46:57 PM »
After I posted that code, I came across a design prototype for a much more complete code contract API for LISP, that was never actually implemented:

Here's an excerpt from the file (which is not currently working)

Code - C#: [Select]
  1.  
  2. // Lisp Code Contract design prototype
  3.  
  4.    namespace Argument
  5.    {
  6.       /// Proposed attribute classes:
  7.  
  8.       public abstract class ArgumentAttribute : System.Attribute {}
  9.      
  10.       // VariantAttribute accepts multiple types:
  11.       public class VariantAttribute : ArgumentAttribute {}
  12.      
  13.       // Type-specific attributes:
  14.       public class IntegerAttribute : ArgumentAttribute {}
  15.      
  16.       public class StringAttribute : ArgumentAttribute {}
  17.      
  18.          // A string argument that must be the name of a layer, linetype, etc.
  19.          public class SymbolNameAttribute : StringAttribute {}
  20.          
  21.             // Example of further specialization:
  22.             public class LayerNameAttribute : SymbolNameAttribute {}
  23.            
  24.          // A string argument that must be the name of a file
  25.          public class FileNameAttribute : StringAttribute {}
  26.          
  27.       public class ObjectIdAttribute : ArgumentAttribute {}
  28.      
  29.       public class Point3dAttribute : ArgumentAttribute {}
  30.      
  31.       public class DoubleAttribute : ArgumentAttribute {}
  32.      
  33.       // A double argument representing an angle/orientation:
  34.       public class AnglularAttribute : DoubleAttribute {}
  35.  
  36.       // A list argument:
  37.       public class ListArgumentAttribute : ArgumentAttribute {}
  38.      
  39.       // The above design supports specialization of any
  40.       // type deriving from ArgumentAttribute, that can
  41.       // deal with more-specialized requirements, and be
  42.       // easily reused.
  43.    }
  44.  
  45. // Example
  46.  
  47.       /// (foobar <string> <ename> <point> [<int> [<real>]] ):
  48.       ///
  49.       /// In all ArgumentAttribute-based attributes, the first
  50.       /// integer argument is the 0-based index of the argument's
  51.       /// position, which must be distinct. This is required
  52.       /// because the .NET runtime does not preserve the order
  53.       /// in which attribues appear in source code.
  54.  
  55.       /// First argument must be a string that cannot be empty
  56.       /// or contain only white space:
  57.      
  58.       [Argument.String( 0, AcceptEmpty = false, AcceptOnlyWhiteSpace = false )]
  59.  
  60.       /// Second argument must be the entity name of a LINE, LWPOLYLINE,
  61.       /// or ARC entity. ExactMatch = true specifies that derived types
  62.       /// are rejected.
  63.      
  64.       /// Either of these can be used:
  65.      
  66.       /// Specifying the names of the runtime classes:
  67.       [Argument.ObjectId( 1, "AcDbPolyline", "AcDbLine", "AcDbArc", ExactMatch = true )]
  68.  
  69.       /// - or ----      
  70.      
  71.       /// Specifying the managed wrapper types:
  72.       [Argument.ObjectId( 1, typeof( Polyline ), typeof( Line ), typeof( Arc ), ExactMatch = true )]
  73.  
  74.       // Third argument must be a point list (x y z),
  75.       [Argument.Point3d( 2 )]
  76.  
  77.       // Fourth argument is optional. If supplied, it must be a positive integer:
  78.      
  79.       [Argument.Integer( 3, Optional = true, AcceptNegative = false, AcceptZero = false )]
  80.      
  81.       // Fifth argument is optional. If supplied, it must be a double
  82.       // in the range 0.0 to 100.0. If not supplied, a default value
  83.       // of 50.0 will be inserted into the resulting TypedValue[]
  84.       // array automatically:
  85.      
  86.       [Argument.Double( 4, Optional = true, Min = 0.0, Max = 100.0, Default = 50.0 )]
  87.  
  88.       [LispFunction( "foobar" )]
  89.       public static object FooBar( ResultBuffer args )
  90.       {
  91.          // validate all arguments:
  92.          TypedValue[] arguments = args.GetValidatedValues( MethodInfo.GetCurrentMethod() );
  93.  
  94.          Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( "(foobar): Success\n" );
  95.          return null;
  96.       }
  97.  
  98. /// Sans comments:
  99.  
  100.       [LispFunction( "foobar" )]
  101.       [Argument.String( 0, AcceptEmpty = false, AcceptOnlyWhiteSpace = false )]
  102.       [Argument.ObjectId( 1, typeof( Polyline ), typeof( Line ), typeof( Arc ), ExactMatch = true )]
  103.       [Argument.Point3d( 2 )]
  104.       [Argument.Integer( 3, Optional = true, AcceptNegative = false, AcceptZero = false )]
  105.       [Argument.Double( 4, Optional = true, Min = 0.0, Max = 100.0, Default = 50.0 )]
  106.       public static object FooBar( ResultBuffer args )
  107.       {
  108.          TypedValue[] arguments = args.GetValidatedValues( MethodInfo.GetCurrentMethod() );
  109.          Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( "(foobar): Success\n" );
  110.          return null;
  111.       }
  112.  
  113.  
« Last Edit: November 01, 2012, 08:11:10 PM by TT »

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Re: Calling .net LispFunction while processing DBX
« Reply #23 on: November 06, 2012, 02:53:00 PM »
Much more precise.  Is that a teaser??!!  Do you want to collaborate? Is there a collaborative effort already in place?

TheMaster

  • Guest
Re: Calling .net LispFunction while processing DBX
« Reply #24 on: November 06, 2012, 06:06:28 PM »
Much more precise.  Is that a teaser??!!  Do you want to collaborate? Is there a collaborative effort already in place?

No, not a teaser, it's just my sketchy ideas for what I wanted to do, but never got around to it.

If someone wants to take a stab at it, I'll be happy to help.