I'm trying to adapt the code used to answer this question here into my own code. I want to be able to run lisp using the commands and methods I've written:
public class MyCommands
{
// Type of resbuf element
const int RTNONE = 5000; /* No result */
const int RTREAL = 5001; /* Real number */
const int RTPOINT = 5002; /* 2D point X and Y only */
const int RTSHORT = 5003; /* Short integer */
const int RTANG = 5004; /* Angle */
const int RTSTR = 5005; /* String */
const int RTENAME = 5006; /* Entity name */
const int RTPICKS = 5007; /* Pick set */
const int RTORINT = 5008; /* Orientation */
const int RT3DPOINT = 5009; /* 3D point - X, Y, and Z */
const int RTLONG = 5010; /* Long integer */
const int RTVOID = 5014; /* Blank symbol */
const int RTLB = 5016; /* list begin */
const int RTLE = 5017; /* list end */
const int RTDOTE = 5018; /* dotted pair */
const int RTNIL = 5019; /* nil */
const int RTDXF0 = 5020; /* DXF code 0 for ads_buildlist only */
const int RTT = 5021; /* T atom */
const int RTRESBUF = 5023; /* resbuf */
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")]
private static extern int acedEvaluateLisp(string lispLine, out IntPtr result);
public static ResultBuffer AcadEvalLisp(string arg)
{
IntPtr rb = IntPtr.Zero;
acedEvaluateLisp(arg, out rb);
if (rb != IntPtr.Zero)
{
try
{
ResultBuffer rbb = DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer;
return rbb;
}
catch
{
return null;
}
}
return null;
}
// Define Command "CSharpToLisp"
// Only for testing we can define this function.
//
// Example:
// Command: CSharpToLisp
// Enter lisp expression: (+ 100 50 30 20 10)
// -----------------------------
// 5003 -> 210
// -----------------------------
[CommandMethod("CSharpToLisp")]
public static void test()
{
PromptResult rs = AcadApp.DocumentManager.MdiActiveDocument.Editor.GetString("\nEnter lisp expression: ");
if (rs.Status == PromptStatus.OK && rs.StringResult != "")
{
ResultBuffer rb = AcadEvalLisp(rs.StringResult);
if (rb != null)
{
PrintResbuf(rb);
}
else
{
AcadApp.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nError in evaluation");
}
}
}
// This code was posted by Tony Tanzillo:
// http://discussion.autodesk.com/thread.jspa?messageID=5094658
private static void PrintResbuf(ResultBuffer rb)
{
string s = "\n-----------------------------";
foreach (TypedValue val in rb)
{
s += string.Format("\n{0} -> {1}", val.TypeCode,
val.Value.ToString());
s += "\n-----------------------------";
}
AcadApp.DocumentManager.MdiActiveDocument.Editor.WriteMessage(s);
}
I load my DLL and run the command CSHARPTOLISP in AutoCAD. The test input I've been using is (+ 1 1) which outputs 2 as expected but then shows this error message
I'm assuming I need to adjust the entry point but am not sure how to go about doing that...
Solved! Go to Solution.
Solved by _gile. Go to Solution.
Try this:
using System; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; namespace InvokeLispSample { public class CommandMethods { /* This TEST command calls the 'foo' LISP function which requires a list as argument * and returns the reversed list. * * (defun foo (l) (reverse l)) (vl-acad-defun "foo") */ [CommandMethod("Test", CommandFlags.Modal)] public void Test() { // build the arguments list ResultBuffer args = new ResultBuffer( new TypedValue((int)LispDataType.Text, "foo"), new TypedValue((int)LispDataType.ListBegin), new TypedValue((int)LispDataType.Text, "test"), new TypedValue((int)LispDataType.Int16, 42), new TypedValue((int)LispDataType.Double, 25.4), new TypedValue((int)LispDataType.ListEnd)); // call the LISP fuction anf get the return value ResultBuffer result = Application.Invoke(args); // print the return value Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; ed.WriteMessage(result.ToString()); } } }
It's a lot cleaner to use Application.Invoke(), but fwiw, about the entry point, as of AutoCAD 2013 it is exported from accore.dll instead of acad.exe.
Also, the function has two pointer args, which causes a difference in the name mangling between 32 and 64 bit binaries. Looks like you're using the 32-bit one.
32-bit: ?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z
64-bit: ?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z
Thanks for your help _gile! I've implemented your code like this:
[CommandMethod("Test", CommandFlags.Modal)]
public void Test()
{
// build the arguments list
ResultBuffer args = new ResultBuffer(
new TypedValue((int)LispDataType.Text, "HELLO"),
//new TypedValue((int)LispDataType.ListBegin),
//new TypedValue((int)LispDataType.Text, "T-600"),
//new TypedValue((int)LispDataType.Text, "t")
//new TypedValue((int)LispDataType.ListEnd)
);
// call the LISP fuction anf get the return value
ResultBuffer result = Autodesk.AutoCAD.ApplicationServices.Application.Invoke(args);
// print the return value
Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage("result.toString()");
}
I wrote a simple LISP function to test it that looks like this:
(DEFUN C:HELLO ()
setq hello "HELLO WORLD"
)
However, upon calling TEST in AutoCAD it throws an invalidInput error on my Invoke call.
If your LISP function is as above (there're missing parents in the one you posted):
(DEFUN C:HELLO () (setq hello "HELLO WORLD") )
It requires none argument, so to call it from .NET, the argument for Application.invoke() should only contains the LISP function name: "C:HELLO" here.
CommandMethod("Test", CommandFlags.Modal)] public void Test() { // build the arguments list ResultBuffer args = new ResultBuffer(
new TypedValue((int)LispDataType.Text, "C:HELLO")); // call the LISP fuction and get the return value ResultBuffer result = Application.Invoke(args); // print the return value Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; ed.WriteMessage(result.ToString()); }
You may read the LISP docs for (vl-acad-defun) and the .NET ones for Application.Invoke().
PS: if you want to see the LISP function return value at the commande prompt, do not quote result.Tostring() in the last line of upper code.
All arguments but "Hello" were commented out in my last post. Does the function have to start with C: or can it just be called HELLO?
To be able to invoke a LISP function from an external ObjectARX or .NET application, you have to prefix the LISP function name with "C:" (in this case, this prefix is part of the function name) or register it with vl-acad-defun (please read the help doc for this function).
I appreciate all your help on this. Right now my method looks like this
[CommandMethod("Test", CommandFlags.Modal)]
public void Test()
{
// build the arguments list
ResultBuffer args = new ResultBuffer(new TypedValue((int)LispDataType.Text, "HELLO"));
// call the LISP fuction anf get the return value
ResultBuffer result = Autodesk.AutoCAD.ApplicationServices.Application.Invoke(args);
// print the return value
Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage(result.ToString());
}
and my LISP file looks like this
(vl-acad-defun HELLO ()
setq hello "HELLO WORLD"
)
When I run it, it pops this error message
I don't understand what I'm missing. I looked at the help document you linked and it didn't give any indication I missed something
I showed an example of (vl-acad-defun) in my first reply.
Using the "c:" prefix
LISP function:
(defun c:hello ()
(setq hello "HELLO WORLD")
)
C# code
[CommandMethod("Test", CommandFlags.Modal)] public void Test() { ResultBuffer args = new ResultBuffer( new TypedValue((int)LispDataType.Text, "c:hello")); ResultBuffer result = Application.Invoke(args); Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; ed.WriteMessage(result.ToString()); }
Using (vl-acad-defun)
LISP code:
(defun hello () (setq hello "HELLO WORLD") ) (vl-acad-defun 'hello)
C# code
[CommandMethod("Test", CommandFlags.Modal)] public void Test() { ResultBuffer args = new ResultBuffer( new TypedValue((int)LispDataType.Text, "hello")); ResultBuffer result = Application.Invoke(args); Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; ed.WriteMessage(result.ToString()); }
I have a really simple question about your code. Where does the LISP code go in your C# program. I see the function in C# where you invoke the LISP function, but where do you put the actual code:
(defun c:hello ()
(setq hello "HELLO WORLD")
)?
Thanks, really struggling here!
~Vonnie
Pay close attention to giles answer (msg 2 in this thread).
The first element in "args" is the name of the lisp command "foo" - the definition of foo is shown in the comments (defun foo).
Then you use Application.Invoke(args) to actually run the lisp. If foo.lsp is found, it will execute and return the resultbuffer.
Hi Gilles,
I want to call this lisp
(defun c:hello ()
(command "_circle" "0,0" "3,3")
(princ)
)
My question is:
What kind of lispdatatype i need to use for this function defined.
I will appreciate your help
Hi,
As the LISP function have no argument, the ResultBuffer must only contain the LISP function name (c:hello) which i a text (string)..
Application.Invoke(new ResultBuffer(new TypedValue((int)LispDataType.Text, "c:hello")));
But, one more time, most of the time it's much simpler to convert LISP routines into .NET than trying to solve the problems due to calling LISP from .NET.
I totally agree with @dgorsman's replies in this topic.
Hi _gile!
I have copied this code to my project, but i had an error - "Application" does not contain a definition for "Invoke"".
I have set links to libraries - AcMgd and AcDbMgd, Framework v.3.5 (for Autocad 2009).
Maybe Framework 3.5 doesn't support this method?
And what is another way to run LISP from C#?
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; namespace ClassLibrary4 { public class Class1 { [CommandMethod("Test", CommandFlags.Modal)] public void Test() { ResultBuffer args = new ResultBuffer( new TypedValue((int)LispDataType.Text, "c:hello")); ResultBuffer result = Application.Invoke(args); Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; ed.WriteMessage(result.ToString()); } } }
I don't recall what release it showed up in, but in earlier releases, there was no Application.Invoke.
In that case, you have to P/Invoke the acedInvoke() method.
Searching this forum on acedInvoke() should turn up code that shows it its done.