GEGEMATIC Posté(e) le 11 février 2010 Posté(e) le 11 février 2010 ça y est, j'ai trouvé un truc qui marche, en C# : Il reste un message d'erreur après l'évaluation, j'espère que j'arriverais à le comprendre ...mais bon, on peu appeller un lisp sans sendcommand, c'est déjà pas mal ... // // Only for AutoCAD 2007+ // This class is using undocumented function acedEvaluateLisp // using System; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.EditorInput; using System.Runtime.InteropServices; using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application; [assembly: CommandClass(typeof(Rivilis.CSharpToLisp))] namespace Rivilis { public class CSharpToLisp { // // From adscodes.h : // // 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")] extern private static int acedEvaluateLisp(string lispLine, out IntPtr result); static public 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; } 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); } // 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")] static public 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"); } } } } } ----------------------------------------------------------------------Site: https://www.g-eaux.frBlog: http://g-eaux.over-blog.com
(gile) Posté(e) le 11 février 2010 Posté(e) le 11 février 2010 Salut, Tu as trouvé chez les "pointures" (Tony Tanzillo et Alexander Rivilis excusez du peu !...) Malgré ça, il semble qu'il y ait un problème avec les expressions qui retournent des listes, dommage pour évaluer LISP... Ces expressions provoquent une erreur dans la méthode PrintResbuf(), j'ai donc un peu modifié le code et j'en ai profité pour remplacer val.TypeCode par (LispDataType)val.TypeCode qui retourne quelque chose de plus explicite que les codes 50## (et rend inutile l'assignation de toutes les constantes). On voit clairement que si l'expression LISP retourne une liste, le ResultBuffer ne contient qu'une TypedValue égale à Nil.// // Only for AutoCAD 2007+ // This class is using undocumented function acedEvaluateLisp // using System; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.EditorInput; using System.Runtime.InteropServices; using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application; [assembly: CommandClass(typeof(Rivilis.CSharpToLisp))] namespace Rivilis { public class CSharpToLisp { [system.Security.SuppressUnmanagedCodeSecurity] [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] extern private static int acedEvaluateLisp(string lispLine, out IntPtr result); static public 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; } static void PrintResbuf(ResultBuffer rb) { string s = "\n-----------------------------"; foreach (TypedValue val in rb) { if (val.TypeCode == (short)LispDataType.Nil) s += string.Format("\n{0} -> nil", (LispDataType)val.TypeCode); else s += string.Format("\n{0} -> {1}", (LispDataType)val.TypeCode, val.Value.ToString()); s += "\n-----------------------------"; } AcadApp.DocumentManager.MdiActiveDocument. Editor.WriteMessage(s); } // 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")] static public 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"); } } } } } Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
GEGEMATIC Posté(e) le 12 février 2010 Auteur Posté(e) le 12 février 2010 salut,Bizare, que ça marche pas complètement ...incroyable qu'on arrive pas à faire en .Net ce qu'on faisait en VBA avec la class VLAX.as tu testé l'adaptation de VLAX en c# que j'ai postée ?mon pb avec .Net, ce que je n'ai pas encore compris les méthodes de debogage,donc je n'arrive pas à comprendre ce qui ne marche pasgégé ----------------------------------------------------------------------Site: https://www.g-eaux.frBlog: http://g-eaux.over-blog.com
GEGEMATIC Posté(e) le 12 février 2010 Auteur Posté(e) le 12 février 2010 au fait, comment ça se fait que ça marche chez toi avec AutoCAD App,alors que j'ai déclaré AcadApp ?tu as un déclaration générale de AutoCAD App dans un module ? la vraie difficulté avec .Net, c'est d'avoir un projet tamplate qui marche :tous ceux que j'ai trouvé sont incomplets.par exemple, je n'arrive pas à savoir quand on est obligé de charger manuellement une référence, et quand ça se fait tout seul ... c'est vraiment les écueils de .netGégé ----------------------------------------------------------------------Site: https://www.g-eaux.frBlog: http://g-eaux.over-blog.com
(gile) Posté(e) le 12 février 2010 Posté(e) le 12 février 2010 Salut, Bizare, que ça marche pas complètement ...incroyable qu'on arrive pas à faire en .Net ce qu'on faisait en VBA avec la class VLAX. Je ne saurais dire pourquoi ça ne fonctionne pas avec les listes. La routine utilise la méthode P/Invoke qui permet d'invoquer des classes natives (ARX/C++) non 'managées' (acedEvaluateLisp) et je dois dire que ça dépasse mes maigres connaissances. as tu testé l'adaptation de VLAX en c# que j'ai postée ? Je n'ai pas essayé VLAX, je vais le faire. mon pb avec .Net, ce que je n'ai pas encore compris les méthodes de debogage,donc je n'arrive pas à comprendre ce qui ne marche pas Je pense qu'il est relativement incontournable de se procurer un bouquin sur .NET et Visual Studio (C# ou VB) pour apprendre les fondements de VS du langage choisi et de la POO. Sans cela on risque de limiter très vite son utilisation de .NET à une programmation procédurale qui n'apporte pas grand chose par rapport au LISP ou au VBA. au fait, comment ça se fait que ça marche chez toi avecAutoCAD App,alors que j'ai déclaré AcadApp ?tu as un déclaration générale de AutoCAD App dans un module ? C'est une erreur de copié/collé, j'ai bien AcadApp dans le code que j'ai testé. Je corrige. la vraie difficulté avec .Net, c'est d'avoir un projet tamplate qui marche :tous ceux que j'ai trouvé sont incomplets. J'utilise DotNetARX et des modèles construits à partir de celui généré par cet applicatif.J'ai essayé de faire un tuto sur AcadLabs. Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
GEGEMATIC Posté(e) le 12 février 2010 Auteur Posté(e) le 12 février 2010 Pour ce qui était du bug que j'avais, Alexander m'a expliqué, ça vient de A64Entry point is not found! E.g. you must replace '?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z' withcorrect string from AutoCAD.exe. I have not AutoCAD x64, that is why I can not provide correct string.You need found it youself with help of utility dumpbin.exe:dumpbin.exe /EXPORTS AutoCAD.exe > acad.txtIn file AutoCAD.txt search substring "acedEvaluateLisp" and find full string such as '?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z', donc pour A64, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] ça a été assez pénible de faire un dumpbin sous v64, mais c'est assez intéressant...on découvre le ventre d'autocad ... En tout cas, une barrière est levée : on peu évaluer silencieusement du lisp depuis .Net.même si .net n'est pas capable de lire directement le résultat, dès l'instant ou les variables lisp sont correctement remplies, ça marche : ci joint une suite d'exemples : Commande: CSharpToLisp Enter lisp expression: (setq z '( 1 2 3 4))(1 2 3 4) -----------------------------Int16 -> 1-----------------------------Commande: CSharpToLisp Enter lisp expression: z -----------------------------Int16 -> 1-----------------------------Int16 -> 2-----------------------------Int16 -> 3-----------------------------Int16 -> 4-----------------------------Commande: (setq z (list 1 2 3 4))(1 2 3 4) Commande: CSharpToLisp Enter lisp expression: z -----------------------------Int16 -> 1-----------------------------Int16 -> 2-----------------------------Int16 -> 3-----------------------------Int16 -> 4-----------------------------Commande: CSharpToLisp Enter lisp expression: (setq z "String")"String" -----------------------------Nil -> nil-----------------------------Commande: CSharpToLisp Enter lisp expression: z -----------------------------Text -> String-----------------------------Commande: CSharpToLisp Enter lisp expression: (setq z '( 1 2 3 ))(1 2 3) -----------------------------Nil -> nil-----------------------------Commande: CSharpToLisp Enter lisp expression: z -----------------------------Point3d -> (1,2,3)-----------------------------Commande: !z(1 2 3) Commande: CSharpToLisp Enter lisp expression: (setq z '( 1 2 3 4)) (1 2 3 4) -----------------------------Int16 -> 1-----------------------------Commande: !z(1 2 3 4) ----------------------------------------------------------------------Site: https://www.g-eaux.frBlog: http://g-eaux.over-blog.com
GEGEMATIC Posté(e) le 12 février 2010 Auteur Posté(e) le 12 février 2010 en addition, le mail d'Alexander, qui précise certaines choses : g> It seems that the result of a lisp routine cannot be passed to .net ifg> its a string, or a list. 1) string must be returned to .NET without problem2) I have a problem only with lists looking like DXF-list, but was not correct:'((0 . "entity-type") (1 . 1) (8 . "LAYER") ... )With list such as '("string" 0.1 2 3 "string") - I have not problem.3) vla-object can not be returned (IMHO) g> I don't have found the way of checking if AutoCAD is 64 or 32 bits, exceptg> this :http://www.csharp411.com/determine-if-your-c-application-is-64-bit/ Best Regards,Alexander Rivilis ----------------------------------------------------------------------Site: https://www.g-eaux.frBlog: http://g-eaux.over-blog.com
(gile) Posté(e) le 12 février 2010 Posté(e) le 12 février 2010 Très intéressant et à creuser (en attendant qu'Autodesk réponde aux souhaits des développeurs...).Le problème avec P/Invoke (utilisation de classes non managées), c'est une moins bonne compatibilité entre les versions (ObjectARX étant modifié à chaque nouveau format d'AutoCAD). Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
(gile) Posté(e) le 12 février 2010 Posté(e) le 12 février 2010 la vraie difficulté avec .Net, c'est d'avoir un projet tamplate qui marche :tous ceux que j'ai trouvé sont incomplets. Si tu utilises AutoCAD 2010, regarde ici. Ce modèle crée une classe MyPlugin qui implémente IExtensionApplication et une classe MyCommands qui crée l'ébauche de plusieurs types de commandes et d'une fonction LISP. Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
(gile) Posté(e) le 13 février 2010 Posté(e) le 13 février 2010 ça a été assez pénible de faire un dumpbin sous v64, mais c'est assez intéressant... Une méthode donnée par Kean Walmsley :Copier/coller ceci dans un fichier "findapi.bat" enregistré dans le répertoire d'installation d'AutoCAD : @echo offif "%1" == "" goto usage:normalfor %%i IN (*.exe *.dll *.arx *.dbx *.ocx *.ddf) DO dumpbin /exports %%i | findstr "%%i %1"goto end:usageecho findapi "function name":end Dans la console d'invite de commande de Visual Studio (elle sait où trouver dumpbin.exe), se placer dans le répertoire d'installation d'AutoCAD et lancer : C:\Program Files\AutoCAD 2010>findapi acedEvaluateLisp > results.txt Le résultat sera copié dans le fichier results.txt. Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
(gile) Posté(e) le 23 mai 2010 Posté(e) le 23 mai 2010 Salut, J'ai trouvé un truc plus simple avec acedInvoke sur TheSwamp.Pas besoin d'EntryPoint donc on devrait éviter les problèmes de compatibilité entre versions. La méthode InvokeLisp requiert un ResultBuffer comme arguments dont la valeur du premier TypedValue est le nom de la fonction LISP invoquée, les TypedValue suivants sont les arguments requis par la fonction LISP.Le résultat de l'évaluation de la fonction est retourné sous forme de ResulBuffer. Côté LISP, si la fonction invoquée n'a pas le préfixe c:, il faut utiliser vl-acad-defun pour la rendre accessible aux applications ObjectARX. Exemple avec une fonction LISP simple qui requiert une liste de chaînes comme argument :(defun foo (l) (mapcar 'strcase l)) (vl-acad-defun 'foo) using System; using System.Runtime.InteropServices; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; namespace InvokeLispSample { public class Class1 { [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() { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; // Argument pour InvokeLisp ResultBuffer resbuf = new ResultBuffer(); // Nom de la fonction LISP : foo resbuf.Add(new TypedValue((int)LispDataType.Text, "foo")); // Argument de la fonction LISP : ("test" "invoke" "lisp") resbuf.Add(new TypedValue((int)LispDataType.ListBegin)); resbuf.Add(new TypedValue((int)LispDataType.Text, "test")); resbuf.Add(new TypedValue((int)LispDataType.Text, "invoke")); resbuf.Add(new TypedValue((int)LispDataType.Text, "lisp")); resbuf.Add(new TypedValue((int)LispDataType.ListEnd)); // Résultat de l'évaluation de (foo '("test" "invoke" "lisp")) ResultBuffer result = InvokeLisp(resbuf); foreach (TypedValue tv in result) { ed.WriteMessage(tv.Value + " "); } } } } Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
(gile) Posté(e) le 13 juillet 2010 Posté(e) le 13 juillet 2010 Salut, Avec 2011, plus besoin d'utiliser de code non managé, la nouvelle méthode .NET ApplicationServices.Invoke() encapsule la fonction ARX acedInvoke(). Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
GEGEMATIC Posté(e) le 17 décembre 2010 Auteur Posté(e) le 17 décembre 2010 Salut Giles,je sort d'un long tunnel ,et je vois que les sujets avance, sans qu'on ai besoin de travailler : le bonheur !Je n'ai malheureusement pas eu le temps de poursuivre mes investigation en .Net, je vais être obligé de repartir à zéro.pour le VAST promis, c'est pour quand ?A+Gégé ----------------------------------------------------------------------Site: https://www.g-eaux.frBlog: http://g-eaux.over-blog.com
krunch Posté(e) le 10 décembre 2012 Posté(e) le 10 décembre 2012 Bonjour Désolé de revenir sur des vieux sujets mais cette dernière méthode de (gile) m'intéresse beaucoup (j'ai besoin d'une échange avec le Lisp) et je n'arrive pas à la faire marcher, j'ai un message d'erreur de Netload que je ne sais pas interpréter ... A tout hasard j'ai remplacé les IntPtr par des Int32/64 (version 2010) mais ça ne convient pas à acedInvoke (et l'aide ne me dit pas grand chose là dessus). Sinon, est ce que cette méthode permettrait de renvoyer des objets, des listes, des listes de listes .. ?
GEGEMATIC Posté(e) le 10 décembre 2012 Auteur Posté(e) le 10 décembre 2012 Salut Krunchpour ce sujet, tu peux regarder cette conversation :liaison lisp netJe n'ai jamais eu le temps de finir le modèle tout fait, mais avec ce qu'il y a de posté, la liaison marche définitivement et très simplement dans les 2 sens. ----------------------------------------------------------------------Site: https://www.g-eaux.frBlog: http://g-eaux.over-blog.com
krunch Posté(e) le 10 décembre 2012 Posté(e) le 10 décembre 2012 Salut.Oui j'ai lu ce sujet, mais c'est du VB ..
GEGEMATIC Posté(e) le 10 décembre 2012 Auteur Posté(e) le 10 décembre 2012 Salut.Oui j'ai lu ce sujet, mais c'est du VB .. ça ne change pas grand chose, l'exemple sont je suis partit était bien en c#... L'interret de la méthode est de s'affranchir des contraintes d'appel avec l'usage d'eval. ça ne doit pas être très compliqué à traduire en c# ----------------------------------------------------------------------Site: https://www.g-eaux.frBlog: http://g-eaux.over-blog.com
krunch Posté(e) le 10 décembre 2012 Posté(e) le 10 décembre 2012 la liaison marche définitivement et très simplementLe 'très simplement' est décidément une notion bien relative ... Alors il y avait sans doute une bourde de ma part dans l'erreur du Netload (un "Test" doublon dans la solution d'après ce que j'ai compris) Il me semble que j'arrive maintenant à faire marcher la méthode 'InvokeLisp' de (gile) ci-dessus ou encore celle-ci, par contre tel quel il y a une erreur dans la commande de Test non ? Commande: test ; erreur: nombre d'arguments trop important -1
(gile) Posté(e) le 10 décembre 2012 Posté(e) le 10 décembre 2012 Bonjour Désolé de revenir sur des vieux sujets mais cette dernière méthode de (gile) m'intéresse beaucoup (j'ai besoin d'une échange avec le Lisp) et je n'arrive pas à la faire marcher, j'ai un message d'erreur de Netload que je ne sais pas interpréter ... A tout hasard j'ai remplacé les IntPtr par des Int32/64 (version 2010) mais ça ne convient pas à acedInvoke (et l'aide ne me dit pas grand chose là dessus). Sinon, est ce que cette méthode permettrait de renvoyer des objets, des listes, des listes de listes .. ? Il ne faut pas modifier la partie Platform Invoke (P/invoke) qui permet d'appeler la méthode non managée acedInvoke() ni la méthode LispInvoke() (donc bien conserver le type Intptr). Ensuite, pour l'utilisation de cette méthode LispInvoke (comme pour la méthode Invoke() à partir de 2011) l'argument doit être un ResultBuffer dont le premier élément est le nom de la fonction LISP appelée et les suivants les arguments requis par la fonction LISP. Un ResultBuffer est un conteneur d'instances de TypedValue.Une TypedValue est un peu l'équivalent d'une paire pointée en LISP ou d'un variant en VB(A) (même si fondamentalement ça n'a rien à voir) qu'on construit avec un entier (TypeCode) et un objet (Value). Le type sous-jascent de l'objet (Value) doit correspondre au TypeCode. Pour les TypedValue utilisées en interaction avec LISP les TypeCode utilsables se retrouvent dans l'énumération (Enum) LispDataType et permettent de construire des listes, des paires pointées, et tous les types utilisés par AutoLISP. De même la valeur de retour de InvokeLisp (ou Invoke) est de type ResultBuffer et peut donc contenir des TypedValue de tous les TypeCode de l'énumération LispDataType. LispDataType Enum Angle 5004 DottedPair 5018 Double 5001 Int16 5003 Int32 5010 ListBegin 5016 ListEnd 5017 Nil 5019 None 5000 ObjectId 5006 Orientation 5008 Point2d 5002 Point3d 5009 SelectionSet 5007 T_atom 5021 Text 5005 Void 5014 Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
krunch Posté(e) le 10 décembre 2012 Posté(e) le 10 décembre 2012 gile, on a répondu en même temps .. J'ai compris le rôle du ResultBuffer en entrée/sortie, y compris dans ton exemple TEST, mais appliqué tel quel j'ai pourtant une erreur 'Nombre d'arguments trop importants' ..
krunch Posté(e) le 10 décembre 2012 Posté(e) le 10 décembre 2012 Ça y est .. Dans l'exemple ci-dessus il manquait juste l'argument :(defun foo (l) (mapcar 'strcase l)) D'autre part si je définis la fonction foo avec c: j'ai une erreur fatale (avec 2010), il faut définir la fonction normalement + vl-acad-defun ..
Messages recommandés
Créer un compte ou se connecter pour commenter
Vous devez être membre afin de pouvoir déposer un commentaire
Créer un compte
Créez un compte sur notre communauté. C’est facile !
Créer un nouveau compteSe connecter
Vous avez déjà un compte ? Connectez-vous ici.
Connectez-vous maintenant