Hi Tony!
I try to use this code in AutoCAD 2009, but i have no result.
My LISP code
(defun testfunc (/ a b) (princ "\nTESTFUNC called: ") (prin1 a) (prin1 b) ;; return a list of results: (list 99 "Just ditch the LISP and forget this nonsense" pi) ) (vl-acad-defun 'testfunc)
I loaded dll, run LISP function in command line (testfunc) and have @"TESTFUNC called: nilnil(99 "Just ditch the LISP and forget this nonsense"
3.14159)". But when i run command in dll ("testinvoke" in my code), I have no messages. And in debugger i saw "set" return "-5001". What I am doing in wrong way?
Try this:
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Runtime; using System; using System.Runtime.InteropServices; namespace PInvokeAcadinvokeSample { public class Commands { [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl)] extern static private int acedInvoke(IntPtr rbIn, out IntPtr rbOut); public static ResultBuffer InvokeLisp(ResultBuffer resbuf) { IntPtr rb = IntPtr.Zero; acedInvoke(resbuf.UnmanagedObject, out rb); return (ResultBuffer)DisposableWrapper.Create(typeof(ResultBuffer), rb, true); } [CommandMethod("TEST")] public void Test() { ResultBuffer args = new ResultBuffer(new TypedValue((int)LispDataType.Text, "testfunc")); ResultBuffer returnValue = InvokeLisp(args); if (returnValue != null) { var ed = Application.DocumentManager.MdiActiveDocument.Editor; foreach (TypedValue value in returnValue.AsArray()) { ed.WriteMessage("\n{0}", value.Value); } } } } }
Thank you for the help, but AutoCAD show an error like this:
"System.Reflection.TargetInvocationException: The destination of the call created an exception. ---> System.InvalidOperationException: The operation is invalid due to the current state of the object."
The code @_gile posted requires that there be a LISP function named "testfunc" defined and registered using vl-acad-defun.
(vl-acad-defun (defun testfunc () (princ "\n(testfunc) called...") "Here is the result of testfunc" ) )
@alexmorewrote:Thank you for the help, but AutoCAD show an error like this:
"System.Reflection.TargetInvocationException: The destination of the call created an exception. ---> System.InvalidOperationException: The operation is invalid due to the current state of the object."
I recall there being a problem with calling acedInvoke and modeless dialogs.
These days, I have only found one approach that is rock solid, though a bit more involved.
In a nutshell:
1) make a string which is some serialized list of input data, in my case I use xml.
That way I can send in a lot of params without wrapping in dxf codes which acad messes up in some cases.
2) call a lisp from .net that sets the string to a global var in my lisp program using AcAp.Invoke(pargs)
the pargs is a result buffer with function name and serialized string
3) call my c: lisp function that actually does something, example: CommandLine.PostCommand("(C:INS-SEGMENT-NET) ").
It gets its input params from the global var I set, deserializing the string to a list.
4) it sets another global var which I retrieve with a lisp I call from .net with AcAp.Invoke
Sounds crazy, but it is critical to use CommandLine.PostCommand when synchronous behavior is desired from modeless palette.
Of course, my most powerful progs use those, so I have a few critical lisp functions I have not rewritten in .net yet.
the actual code in use looks like this, complete with conditionals for bricscad:
public static bool InsertSegsGlist(int index, CE.Point stPt, CE.Glist inGlist, int BrgStyle, out CE.Point newStPt, out CE.Glist newGlist, out List<int> insIndicies) { bool res = false; newGlist = new Glist(); newStPt = null; insIndicies = new List<int>(); using (DocumentLock doclock = AcAp.DocumentManager.MdiActiveDocument.LockDocument()) { try { List<object> stPtLst = new List<object>() { stPt.X, stPt.Y }; List<object> gsegs = new List<object>() { null }; if (inGlist.Segments.Count > 0) gsegs = inGlist.ToListObject(); List<object> infoLst = new List<object>() { index, stPtLst, gsegs, BrgStyle }; List<object> pLst = new List<object>() { "INS-SEGMENT-NET-GVAR", infoLst }; string xmlstr = HANestedAList.ObjPropListToListReadString(pLst); //set lisp global var with string ResultBuffer pargs = new ResultBuffer(); pargs.Add(new TypedValue(5005, "C:SET-CTGLOBALVAR")); pargs.Add(new TypedValue(5005, xmlstr)); #if ACAD AcAp.Invoke(pargs); //send data XAppAcad.FocusToDwgView(); //must tell acad to call command from .net to run the lisp, this is better than PostCommand - not! //AcAp.DocumentManager.MdiActiveDocument.SendStringToExecute("CmdInsertSegsGlist ", false, false, true); CommandLine.PostCommand("(C:INS-SEGMENT-NET) "); //use because lisp command fails with invoke method #endif #if BCAD Bricscad.Global.Editor.Invoke(pargs); XAppAcad.FocusToDwgView(); pargs = new ResultBuffer(); pargs.Add(new TypedValue(5005, "C:INS-SEGMENT-NET")); Bricscad.Global.Editor.Invoke(pargs); #endif //now get global var back //RETURNS LAYER NAME, LIST OF GEOMETRIC DATA AND ENTITY NAMES FOR HIGHLIGHTING List<object> retInfo = HALispVars.GetCTGVar("INS-SEGMENT-NET-GVAR", XAppAcad.ListDelim, XAppAcad.DPair); if (retInfo != null) { List<object> pt = (List<object>)retInfo[0]; newStPt = new CivilEngine.Point((double)pt[0], (double)pt[1]); newGlist.FillFromObjList((List<object>)retInfo[1]); List<object> oints = (List<object>)retInfo[2]; insIndicies = oints.Cast<int>().ToList(); return true; } else return false; } catch { } return res; } }
internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties
Hello!
What exactly do you mean - environment, software, full code?
The error may be due to my local settings etc.
Hello!
I try to use your code.
Application is loaded, i saw ther result:
"Command: (testfunc)
(testfunc) called..."Here is the result of testfunc""
I loaded dll by _netload, set command "_test" in command line and i have an error like this:
""System.Reflection.TargetInvocationException: The destination of the call created an exception. ---> System.InvalidOperationException: The operation is invalid due to the current state of the object.""
I will to use acedEvaluateLisp, it's ok, but in this way i must to print only one line of the code but not LSP file fully.
Anyway, Thank you for the help.
I can't tell you why that happens (sorry, no time to test it), but it could be because the code you're using doesn't check the result of acedInvoke(), or if there was a value assigned to the ResultBuffer out parameter.
Here's @_gile code with those checks:
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Runtime; using System; using System.Runtime.InteropServices; namespace PInvokeAcadinvokeSample { public class Commands { [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl)] extern static private int acedInvoke(IntPtr rbIn, out IntPtr rbOut); public static ResultBuffer InvokeLisp(ResultBuffer resbuf) { IntPtr rb = IntPtr.Zero; Interop.CheckAds(acedInvoke(resbuf.UnmanagedObject, out rb)); if(rb != IntPtr.Zero) return (ResultBuffer)DisposableWrapper.Create(typeof(ResultBuffer), rb, true); else return null; } [CommandMethod("TEST")] public void Test() { ResultBuffer args = new ResultBuffer(new TypedValue((int)LispDataType.Text, "testfunc")); ResultBuffer returnValue = InvokeLisp(args); if (returnValue != null) { var ed = Application.DocumentManager.MdiActiveDocument.Editor; foreach (TypedValue value in returnValue.AsArray()) { ed.WriteMessage("\n{0}", value.Value); } } } } }