CADxp: Liaison LISP/.NET - CADxp

Aller au contenu

Page 1 sur 1
  • Vous ne pouvez pas commencer un sujet
  • Vous ne pouvez pas répondre à ce sujet

Liaison LISP/.NET (exécuter du lisp dans .net)

#1 L'utilisateur est hors-ligne   GEGEMATIC 

  • ceinture noire 1er dan
  • Groupe : Membres
  • Messages : 661
  • Inscrit(e) : 04-novembre 05

Posté 21 septembre 2012 - 16:58

Bonjour,
Gile avait déjà expliqué pas mal de choses dans un sujet vieux de 2 ans, mais on ne trouve rien de bien en forme sur le Web

Pour faire une petite synthèse, en VB (nul n'est parfait)
Voici ce que j'ai mis dans le fichier AcadExtensions.vb
Il y a des auteurs crédités, mais je ne sais pas vraiment si ils sont les seuls à avoir participé:
'Classe pour évaluation lisp dans .net
'ajoutée le 20/09/2012
'http://www.theswamp.org/index.php?topic=35714.0

'LispDataType
'None           5000
'Double         5001
'Point2d        5002
'Int16          5003
'Angle          5004
'Text           5005
'ObjectId       5006
'SelectionSet   5007
'Orientation    5008
'Point3d        5009
'Int32          5010
'Void           5014
'ListBegin      5016
'ListEnd        5017
'DottedPair     5018
'Nil            5019
'T_atom (T)     5021

'PromptStatus
'None           5000
'Modeless       5027
'Other          5028
'OK             5100
'Keyword        -5005
'Cancel         -5002
'Error          -5001

Imports System.Runtime.InteropServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput

Namespace AcadExtensions
    ' Credits to Tony Tanzillo, Alexander Rivilis, Kerry Brown...

    ''' <summary>
    ''' Provides methods to comunicate with AutoLISP.
    ''' </summary>
    Public Class LispExtensions
        <System.Security.SuppressUnmanagedCodeSecurity()> _
        <DllImport("acad.exe", EntryPoint:="acedInvoke", _
        CharSet:=CharSet.Unicode, CallingConvention:=CallingConvention.Cdecl)> _
        Private Shared Function acedInvoke(ByVal args As IntPtr, ByRef result As IntPtr) As Integer
        End Function

        ''' <summary>
        ''' Invoke a LISP function.
        ''' The LISP function must be defined as an external subroutine using the c: prefix or invoking vl-acad-defun.
        ''' This is no more mandatory since A2011 as the managed Application.Invoke() method wraps acedInvoke.
        ''' </summary>
        ''' <param name="args">The function name (string) following by the function arguments.</param>
        ''' <returns>The LISP function return value or null if failed.</returns>
        Public Shared Function InvokeLisp(ByVal args As ResultBuffer) As ResultBuffer
            Dim ip As IntPtr = IntPtr.Zero
            Dim status As Integer = acedInvoke(args.UnmanagedObject, ip)
            If status = CInt(PromptStatus.OK) AndAlso ip <> IntPtr.Zero Then
                Return ResultBuffer.Create(ip, True)
            End If
            Return Nothing
        End Function

        <System.Security.SuppressUnmanagedCodeSecurity()> _
        <DllImport("acad.exe", EntryPoint:="acedPutSym", _
        CharSet:=CharSet.Unicode, CallingConvention:=CallingConvention.Cdecl)> _
        Private Shared Function acedPutSym(ByVal args As String, ByVal result As IntPtr) As Integer
        End Function

        ''' <summary>
        ''' Set a LISP variable value.
        ''' </summary>
        ''' <param name="name">The variable name.</param>
        ''' <param name="rb">The variable value</param>
        Public Shared Sub SetLispSym(ByVal name As String, ByVal rb As ResultBuffer)
            acedPutSym(name, rb.UnmanagedObject)
        End Sub

        <System.Security.SuppressUnmanagedCodeSecurity()> _
        <DllImport("acad.exe", EntryPoint:="acedGetSym", _
        CharSet:=CharSet.Unicode, CallingConvention:=CallingConvention.Cdecl)> _
        Private Shared Function acedGetSym(ByVal args As String, ByRef result As IntPtr) As Integer
        End Function

        ''' <summary>
        ''' Get a LISP variable value.
        ''' </summary>
        ''' <param name="name">The variable name.</param>
        ''' <returns>The variable value or null if failed.</returns>
        Public Shared Function GetLispSym(ByVal name As String) As ResultBuffer
            Dim ip As IntPtr = IntPtr.Zero
            Dim status As Integer = acedGetSym(name, ip)
            If status = CInt(PromptStatus.OK) AndAlso ip <> IntPtr.Zero Then
                Return ResultBuffer.Create(ip, True)
            End If
            Return Nothing
        End Function
    End Class
End Namespace


Puis voici ce que j'ai mis dans une classe de commande autocad :
Vous pourrez donc tester quelques exemples réalisés à partir des exemples de Gile.

(ne vous étonnez pas des commentaires naifs de mon cru que l'on trouve dans le code: Je note bêtement tout ce que je remarque et que je ne comprends pas ...)

Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Runtime
'necessaire quand est dans un autre module :
'Imports AcadExtensions

'lorsque qu'une autre classe de commands est présente dans  le projet, il faut désactiver sa compilation pour que les 
'CommandMethod fonctionnent



Namespace LispVbsample

    Public Class Commands

        <CommandMethod("EvalCustomLisp")> _
        Public Sub EvalCustomLisp()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor

            ' create a result buffer containing a LISP list
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.ListBegin)),
                New TypedValue(CInt(LispDataType.Int16), 12),
                New TypedValue(CInt(LispDataType.Text), "toto"),
                New TypedValue(CInt(LispDataType.T_atom)),
                New TypedValue(CInt(LispDataType.ListEnd)))

            ' bind the list to a 'lst1' LISP variable
            AcadExtensions.LispExtensions.SetLispSym("lst1", input)

            ' call the 'foo' Lisp function which binds the reversed list to 'lst2'
            ' (defun foo () (setq lst2 (reverse lst1))) (vl-acad-defun 'foo)
            AcadExtensions.LispExtensions.InvokeLisp(New ResultBuffer(New TypedValue(CInt(LispDataType.Text), "foo")))

            ' get the 'lst2' variable value
            Dim output As ResultBuffer = AcadExtensions.LispExtensions.GetLispSym("lst2")

            ' print the value to the commande line
            For Each tv As TypedValue In output
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
        End Sub

        <CommandMethod("EvalCustomLispWithArg")> _
        Public Sub EvalCustomLispWithArg()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor

            ' create a result buffer containing a LISP expression containing a function call, with a single list argument  
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.Text), "fooArg"),
                New TypedValue(CInt(LispDataType.ListBegin)),
                New TypedValue(CInt(LispDataType.Int16), 12),
                New TypedValue(CInt(LispDataType.Text), "toto"),
                New TypedValue(CInt(LispDataType.T_atom)),
                New TypedValue(CInt(LispDataType.ListEnd)))

            'à ce stade, input ressemble à ça: 
            ' "fooArg" '( 12 "toto" 'T)
            'On note que les parenthèses de l'évaluation de la fonction ne sont pas nécessaires, mais celles de la liste d'arguments le sont
            'si la fonction accepte plusieurs arguments, pas de parenthèses !
           

            'copier coller suivant dans fenetre texte autocad pour définition fooarg :
            ' (defun fooArg ( lst1 ) (setq lst2 (reverse lst1))) (vl-acad-defun 'fooArg)

            ' call the 'fooArg' Lisp function which binds the reversed list to 'lst2'
            AcadExtensions.LispExtensions.InvokeLisp(input)

            ' get the 'lst2' variable value
            Dim output As ResultBuffer = AcadExtensions.LispExtensions.GetLispSym("lst2")

            ' print the value to the commande line
            For Each tv As TypedValue In output
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
        End Sub

        <CommandMethod("EvalAutoLispWithArg")> _
        Public Sub EvalAutoLispWithArg()

            
            ' create a result buffer containing a LISP list
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.Text), "Alert"),
                New TypedValue(CInt(LispDataType.Text), "toto a zéro"))

            'copier coller suivant dans fenetre texte autocad pour définition commande autolisp "alert"
            '(vl-acad-defun 'alert)

            ' call the 'fooArg' Lisp function which binds the reversed list to 'lst2'
            AcadExtensions.LispExtensions.InvokeLisp(input)

        End Sub
        'conclusion: on peut tout faire avec le lisp
        'contrainte : définir chaque commande lisp appelée par .Net avec un vl-acad-defun (pas besoin de c:)
        'même les commandes autolisp natives ont besoin de ce vl-acad-defun !
        'donc il vaut mieux bien organiser son code lisp pour grouper les intéractions avec .net


    End Class
End Namespace

---------------------------------------------------------------------- PowerClic sur http://www.g-eaux.com
0

#2 L'utilisateur est hors-ligne   (gile) 

  • ceinture rouge et blanche 8em dan
  • Groupe : Moderateurs
  • Messages : 10687
  • Inscrit(e) : 02-septembre 05

Posté 22 septembre 2012 - 09:04

Salut,

Pour les version 2011 et ultérieures, il existe une méthode "gérée" (managed) qui permet d'éviter le P/Invoke de la méthode non gérée acedInvoke() : Application.Invoke() qui a pour signature :

C#
public static unsafe ResultBuffer Invoke(ResultBuffer args);


VB
Public static unsafe Function Invoke(args As ResultBuffer) As ResultBuffer

Gilles Chanteau - gileCAD -
Développements sur mesure pour AutoCAD
Image IPB
0

#3 L'utilisateur est hors-ligne   GEGEMATIC 

  • ceinture noire 1er dan
  • Groupe : Membres
  • Messages : 661
  • Inscrit(e) : 04-novembre 05

Posté 24 septembre 2012 - 10:55

Salut,
donc pour être clair, on peut remplacer InvokeLisp par
        
Public Shared Function InvokeLispManaged(ByVal args As ResultBuffer) As ResultBuffer
             Return Autodesk.AutoCAD.ApplicationServices.Application.Invoke(args)
        End Function

et pour tester :
<CommandMethod("EvalCustomLisp")> _
        Public Sub EvalCustomLisp()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor

            ' create a result buffer containing a LISP list
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.ListBegin)),
                New TypedValue(CInt(LispDataType.Int16), 12),
                New TypedValue(CInt(LispDataType.Text), "toto"),
                New TypedValue(CInt(LispDataType.T_atom)),
                New TypedValue(CInt(LispDataType.ListEnd)))

            ' bind the list to a 'lst1' LISP variable
            AcadExtensions.LispExtensions.SetLispSym("lst1", input)

            ' call the 'foo' Lisp function which binds the reversed list to 'lst2'
            ' (defun foo () (setq lst2 (reverse lst1))) (vl-acad-defun 'foo)
            AcadExtensions.LispExtensions.InvokeLisp(New ResultBuffer(New TypedValue(CInt(LispDataType.Text), "foo")))
            'suivant en managé:
            Dim retour As ResultBuffer
            retour = AcadExtensions.LispExtensions.InvokeLispManaged(New ResultBuffer(New TypedValue(CInt(LispDataType.Text), "foo")))
            ' print the value to the commande line
            ed.WriteMessage(vbLf & "Retour fonction:")
            For Each tv As TypedValue In retour
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
            ed.WriteMessage(vbLf & "Lst2:")
            ' get the 'lst2' variable value
            Dim output As ResultBuffer = AcadExtensions.LispExtensions.GetLispSym("lst2")

            ' print the value to the commande line
            For Each tv As TypedValue In output
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
        End Sub

Par contre, dans la mesure où il n'existe pas de methode putlisp ou getlisp, je pense qu'il faudrait les implémenter à partir de Invoke(), puisqu'on peut faire un invoke de la fonction (eval (read "mysym"))
---------------------------------------------------------------------- PowerClic sur http://www.g-eaux.com
0

#4 L'utilisateur est hors-ligne   GEGEMATIC 

  • ceinture noire 1er dan
  • Groupe : Membres
  • Messages : 661
  • Inscrit(e) : 04-novembre 05

Posté 24 septembre 2012 - 18:10

Donc voila la même chose en utilisant invoke()
J'ai ajouté une astuce pour s'affranchir du vl-acad-defun de tout ce que l'on va évaluer par .Net, le recours à eval
Donc dans mon AcadExtension.vb

'Classe pour évaluation lisp dans .net
'ajoutée le 20/09/2012
'http://www.theswamp.org/index.php?topic=35714.0

'LispDataType
'None           5000
'Double         5001
'Point2d        5002
'Int16          5003
'Angle          5004
'Text           5005
'ObjectId       5006
'SelectionSet   5007
'Orientation    5008
'Point3d        5009
'Int32          5010
'Void           5014
'ListBegin      5016
'ListEnd        5017
'DottedPair     5018
'Nil            5019
'T_atom (T)     5021

'PromptStatus
'None           5000
'Modeless       5027
'Other          5028
'OK             5100
'Keyword        -5005
'Cancel         -5002
'Error          -5001

Imports System.Runtime.InteropServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Runtime

Namespace AcadExtensions


    ' Credits to Tony Tanzillo, Alexander Rivilis, Kerry Brown...

    ''' <summary>
    ''' Provides methods to comunicate with AutoLISP.
    ''' </summary>
    Public Class LispExtensions



        

        ''' <summary>
        ''' Invoke a LISP function using managed invoke() method.
        ''' The LISP function must be defined as an external subroutine using the c: prefix or invoking vl-acad-defun.
        ''' This is no more mandatory since A2011 as the managed Application.Invoke() method wraps acedInvoke.
        ''' </summary>
        ''' <param name="args">The function name (string) following by the function arguments.</param>
        ''' <returns>The LISP function return value or null if failed.</returns>
        Public Shared Function InvokeLispManaged(ByVal args As ResultBuffer) As ResultBuffer
            Return Autodesk.AutoCAD.ApplicationServices.Application.Invoke(args)
        End Function



        'accompagner cette commande la commande lisp suivante:
        '(defun ManagedGetLispSym ( strSym ) (eval (read strSym)))(vl-acad-defun 'ManagedGetLispSym)
        'par commodité, on peut la créer automatiquement au chargement
        Public Shared Function ManagedGetLispSym(ByVal name As String) As ResultBuffer
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.Text), "ManagedGetLispSym"),
                New TypedValue(CInt(LispDataType.Text), name))

            'à ce stade, input ressemble à ça: 
            ' "ManagedGetLispSym" "name"

            Return AcadExtensions.LispExtensions.InvokeLispManaged(input)
 
        End Function

        'accompagner cette commande la commande lisp suivante:
        '(defun ManagedPutLispSym ( strSym RsbValue) (set (read strSym) RsbValue))(vl-acad-defun 'ManagedPutLispSym)
        ''' <summary>
        ''' Set a LISP variable value using managed invoke().
        ''' </summary>
        ''' <param name="name">The variable name.</param>
        ''' <param name="rb">The variable value</param>
        Public Shared Function ManagedPutLispSym(ByVal name As String, ByVal rb As ResultBuffer)
            GetEditor.WriteMessage(vbLf & "Entrée ManagedPutLispSym:")
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.Text), "ManagedPutLispSym"),
                New TypedValue(CInt(LispDataType.Text), name)
               )
            'GetEditor.WriteMessage(vbLf & "Input")
            'For Each tv As TypedValue In input
            'GetEditor.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            'Next
            'GetEditor.WriteMessage(vbLf & "Input + rb")
            'rb.UnmanagedObject
            For Each val As TypedValue In rb
                input.Add(val)
            Next

            'à ce stade, input ressemble à ça: 
            ' "ManagedPutLispSym" "name" rbvalue
            'For Each tv As TypedValue In input
            'GetEditor.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            'Next
            Return AcadExtensions.LispExtensions.InvokeLispManaged(input)
            ' print the value to the commande line


        End Function

        'Ajoute les parenthèses à un resultBuffer représentant une liste
        'utile  si on veut réutiliser un retour de fonction lisp comme arguments d'une autre fonction lisp
        Public Shared Function AddParenthesisToresultBuffer(ByVal rb As ResultBuffer) As ResultBuffer
            Dim tv() As TypedValue = rb.AsArray
            If (1 < UBound(tv, 1)) Then
                If Not (tv(0).TypeCode = 5016) Then

                    GetEditor.WriteMessage(vbLf & "Liste sans parenthèses")
                    Dim input As New ResultBuffer(
                        New TypedValue(CInt(LispDataType.ListBegin))
                       )

                    'Ajoute les valeurs
                    For Each val As TypedValue In rb
                        input.Add(val)
                    Next
                    input.Add(New TypedValue(CInt(LispDataType.ListEnd)))
                    Return input

                Else
                    Return rb
                End If
            Else
                Return rb
            End If

        End Function

    End Class
End Namespace

Et dans mon LispSample.vb :

Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Runtime
'necessaire quand est dans un autre module :
'Imports AcadExtensions

'lorsque qu'une autre classe de commands est présente dans  le projet, il faut désactiver sa compilation pour que les 
'CommandMethod fonctionnent



Namespace LispVbsample

    Public Class Commands

        Implements IExtensionApplication



        Private ObjectData As System.Data.DataTable

        Public Sub AddDocColEventCreated()
            AddHandler Application.DocumentManager.DocumentCreated, _
            AddressOf InitialiseLispExtension
            GetEditor().WriteMessage(vbNewLine & "Initialisation des exemples lisp par evenement")
        End Sub
        Private Sub InitialiseLispExtension()
            GetEditor().WriteMessage(vbNewLine & "Création des routines lisp utiles à AcadExtension par evenement sur la création de document ")
            Dim doc As Autodesk.AutoCAD.ApplicationServices.Document
            doc = Application.DocumentManager.MdiActiveDocument
            doc.SendStringToExecute("(defun ManagedGetLispSym ( strSym ) (eval (read strSym)))(vl-acad-defun 'ManagedGetLispSym) ", False, False, False)
            doc.SendStringToExecute("(defun ManagedPutLispSym ( strSym RsbValue) (set (read strSym) RsbValue))(vl-acad-defun 'ManagedPutLispSym) ", False, False, False)
            GetEditor().WriteMessage(vbNewLine & "Routines lisp ManagedGetLispSym ManagedPutLispSym créées ")
        End Sub

        Private Sub Initialize() Implements IExtensionApplication.Initialize
            ' Initialize your plug-in application here
            'ajout de l'evenement pour les prochanis docs
            AddDocColEventCreated()
            InitialiseLispExtension()
           
        End Sub

        Private Sub Terminate() Implements IExtensionApplication.Terminate
            ' Do plug-in application clean up here
        End Sub

        'la seule fonction exemple qui utilise invoke() 
        <CommandMethod("ManagedEvalCustomLisp")> _
        Public Sub ManagedEvalCustomLisp()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor
            Dim retour As ResultBuffer

            ' create a result buffer containing a LISP list
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.ListBegin)),
                New TypedValue(CInt(LispDataType.Int16), 12),
                New TypedValue(CInt(LispDataType.Text), "toto"),
                New TypedValue(CInt(LispDataType.T_atom)),
                New TypedValue(CInt(LispDataType.ListEnd)))

            ' bind the list to a 'lst1' LISP variable
            AcadExtensions.LispExtensions.ManagedPutLispSym("lst1", input)


            ' call the 'foo' Lisp function which binds the reversed list to 'lst2'
            ' (defun foo () (setq lst2 (reverse lst1))) (vl-acad-defun 'foo)

            'Appel lisp en managé:
            retour = AcadExtensions.LispExtensions.InvokeLispManaged(New ResultBuffer(New TypedValue(CInt(LispDataType.Text), "foo")))
            ' print the value to the commande line
            ed.WriteMessage(vbLf & "Retour fonction:")
            For Each tv As TypedValue In retour
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
            ed.WriteMessage(vbLf & "Lst2:")
            ' get the 'lst2' variable value
            Dim output As ResultBuffer = AcadExtensions.LispExtensions.ManagedGetLispSym("lst2")
            ' print the value to the commande line
            For Each tv As TypedValue In output
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
            ed.WriteMessage(vbLf & "Put lisp managé, création de la liste lst4 avec le retour de fonction, !lst4 pour vérification:")
            'ajoute des parenthèses à retour
            retour = AcadExtensions.LispExtensions.AddParenthesisToresultBuffer(retour)
            'version managée putLisp
            AcadExtensions.LispExtensions.ManagedPutLispSym("lst4", retour)
        End Sub

        
        


    End Class
End Namespace


A noter la création directe des deux routines lisp ManagedGetLispSym et ManagedPutLispSym, ce qui est bien pratique.
Mais pour tester ManagedEvalCustomLisp, il ne faut pas oublier de définir
(defun foo () (setq lst2 (reverse lst1))) (vl-acad-defun 'foo)
à la ligne de commande.
---------------------------------------------------------------------- PowerClic sur http://www.g-eaux.com
0

#5 L'utilisateur est hors-ligne   GEGEMATIC 

  • ceinture noire 1er dan
  • Groupe : Membres
  • Messages : 661
  • Inscrit(e) : 04-novembre 05

Posté 12 décembre 2012 - 15:56

Salut à tous, et surtout à Krunch, car je m'aperçois que je n'avais jamais posté la version définitive :
A la fin, j'ai mis des fonctions exemples qui te montrent comment évaluer directement une fonction lisp avec des arguments :
EvalCustomLispWithArg

elle permet d'évaluer des trucs du genre : (macommandelisp '(avec une liste) "etunechaine")

exemple d'utilisation directement à la ligne de commande :
Commande: EVALLISPEXPRESSION
Expression lisp à évaluer (avec les parenthèses) ?: (alert "coucou")
nil

Retour expression évaluée :
Type: 5019 Value:


Le code :
dans LispToNetExtension.vb
'Classe pour évaluation lisp dans .net
'ajoutée le 20/09/2012
'http://www.theswamp.org/index.php?topic=35714.0

'LispDataType
'None           5000
'Double         5001
'Point2d        5002
'Int16          5003
'Angle          5004
'Text           5005
'ObjectId       5006
'SelectionSet   5007
'Orientation    5008
'Point3d        5009
'Int32          5010
'Void           5014
'ListBegin      5016
'ListEnd        5017
'DottedPair     5018
'Nil            5019
'T_atom (T)     5021

'PromptStatus
'None           5000
'Modeless       5027
'Other          5028
'OK             5100
'Keyword        -5005
'Cancel         -5002
'Error          -5001



Imports System.Runtime.InteropServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices

Namespace AcadExtensions


    ' Credits to Gile chanteau, Tony Tanzillo, Alexander Rivilis, Kerry Brown...

    ''' <summary>
    ''' Provides methods to comunicate with AutoLISP.
    ''' </summary>
    Public Class LispExtensions

        Dim ed


        ''' <summary>
        ''' Invoke a LISP function using managed invoke() method.
        ''' The LISP function must be defined as an external subroutine using the c: prefix or invoking vl-acad-defun.
        ''' Obsolète :
        ''' Lui préférer ManagedEvalStrLispExpression, qui est plus commed, car elle permet d'évaluer des fonctions qui
        ''' ne sont pas vl-acad-defun ou c:
        ''' </summary>
        ''' <param name="args">The function name (string) following by the function arguments.</param>
        ''' <returns>The LISP function return value or null if failed.</returns>
        Public Shared Function InvokeLispManaged(ByVal args As ResultBuffer) As ResultBuffer
            Return Autodesk.AutoCAD.ApplicationServices.Application.Invoke(args)
        End Function

        'accompagner cette commande la commande lisp suivante:
        '(defun ManagedEvalStrLispExpression ( strLspExp ) (eval (read strLspExp)))(vl-acad-defun 'ManagedEvalStrLispExpression)
        'par commodité, on peut la créer automatiquemen au chargement
        Public Shared Function ManagedEvalStrLispExpression(ByVal exp As String) As ResultBuffer
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.Text), "ManagedEvalStrLispExpression"),
                New TypedValue(CInt(LispDataType.Text), exp))

            'à ce stade, input ressemble à ça: 
            ' "ManagedEvalStrLispExpression" "(myLispExpression arg1 arg2 ... argn)"

            Return AcadExtensions.LispExtensions.InvokeLispManaged(input)

        End Function


        'read a lisp symbol
        'accompagner cette commande la commande lisp suivante:
        '(defun ManagedGetLispSym ( strSym ) (eval (read strSym)))(vl-acad-defun 'ManagedGetLispSym)
        'par commodité, on peut la créer automatiquemen au chargement
        Public Shared Function ManagedGetLispSym(ByVal name As String) As ResultBuffer
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.Text), "ManagedGetLispSym"),
                New TypedValue(CInt(LispDataType.Text), name))

            'à ce stade, input ressemble à ça: 
            ' "ManagedGetLispSym" "name"

            Return AcadExtensions.LispExtensions.InvokeLispManaged(input)

        End Function

        'put value to a lisp symbol
        'accompagner cette commande la commande lisp suivante:
        '(defun ManagedPutLispSym ( strSym RsbValue) (set (read strSym) RsbValue))(vl-acad-defun 'ManagedPutLispSym)
        ''' <summary>
        ''' Set a LISP variable value using managed invoke().
        ''' </summary>
        ''' <param name="name">The variable name.</param>
        ''' <param name="rb">The variable value</param>
        Public Shared Function ManagedPutLispSym(ByVal name As String, ByVal rb As ResultBuffer)
            GetEditor.WriteMessage(vbLf & "Entrée ManagedPutLispSym:")
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.Text), "ManagedPutLispSym"),
                New TypedValue(CInt(LispDataType.Text), name)
               )
            'GetEditor.WriteMessage(vbLf & "Input")
            'For Each tv As TypedValue In input
            'GetEditor.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            'Next
            'GetEditor.WriteMessage(vbLf & "Input + rb")
            'rb.UnmanagedObject
            For Each val As TypedValue In rb
                input.Add(val)
            Next

            'à ce stade, input ressemble à ça: 
            ' "ManagedPutLispSym" "name" rbvalue
            'For Each tv As TypedValue In input
            'GetEditor.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            'Next
            Return AcadExtensions.LispExtensions.InvokeLispManaged(input)
            ' print the value to the commande line


        End Function

        'Ajoute les parenthèses à un resultBuffer représentant une liste
        'utile  si on veut réutiliser un retour de fonction lisp comme arguments d'une autre fonction lisp
        Public Shared Function AddParenthesisToresultBuffer(ByVal rb As ResultBuffer) As ResultBuffer
            Dim tv() As TypedValue = rb.AsArray
            If (1 < UBound(tv, 1)) Then
                If Not (tv(0).TypeCode = 5016) Then

                    GetEditor.WriteMessage(vbLf & "Liste sans parenthèses")
                    Dim input As New ResultBuffer(
                        New TypedValue(CInt(LispDataType.ListBegin))
                       )

                    'Ajoute les valeurs
                    For Each val As TypedValue In rb
                        input.Add(val)
                    Next
                    input.Add(New TypedValue(CInt(LispDataType.ListEnd)))
                    Return input

                Else
                    Return rb
                End If
            Else
                Return rb
            End If

        End Function

    End Class
End Namespace



dans myPlugin.vb

Imports System
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.EditorInput

' This line is not mandatory, but improves loading performances
<Assembly: ExtensionApplication(GetType(AutoCAD_VB_plug_in___lisp1.MyPlugin))> 

Namespace AutoCAD_VB_plug_in___lisp1

    ' This class is instantiated by AutoCAD once and kept alive for the 
    ' duration of the session. If you don't do any one time initialization 
    ' then you should remove this class.
    Public Class MyPlugin
        Implements IExtensionApplication


        Public Sub AddDocColEventCreated()
            AddHandler Application.DocumentManager.DocumentCreated, _
            AddressOf InitialiseLispExtension
            GetEditor().WriteMessage(vbNewLine & "Initialisation des exemples lisp par evenement")
        End Sub

        Private Sub InitialiseLispExtension()
            'Si plusieurs dll  chargées contiennent la classe AcadExtension, chacune recréera ces fonctions lisp:
            'Car pour l'instant, il n'est pas possible de tester l'existance de ces commandes tant qu'elles n'existent pas !
            'sauf en utilisant la comamnde non managée GetLispSym, mais ce n'est pas le but.
            GetEditor().WriteMessage(vbNewLine & "Création des routines lisp utiles à AcadExtension par evenement sur la création de document ")
            Dim doc As Autodesk.AutoCAD.ApplicationServices.Document
            doc = Application.DocumentManager.MdiActiveDocument
            'fonction lisp pour évaluer un symbole lisp, passé sous forme de chaine de carractère, ex : "myvar"
            'est utilisée par la Shared Function ManagedGetLispSym
            doc.SendStringToExecute("(defun ManagedGetLispSym ( strSym ) (eval (read strSym)))(vl-acad-defun 'ManagedGetLispSym) ", False, False, False)
            'fonction lisp pour évaluer une expression lisp, passé sous forme de chaine de carractère,avec les parenthèses ex : "(ManagedGetLispSym \"myvar\")"
            'en réalité, est complètement identique à ManagedGetLispSym, mais permet d'éviter la confusion entre l'évalusatio nd'un symbole et d'une expression
            'est utilisée par la Shared Function ManagedEvalStrLispExpression
            doc.SendStringToExecute("(defun ManagedEvalStrLispExpression ( strLspExp ) (eval (read strLspExp)))(vl-acad-defun 'ManagedEvalStrLispExpression) ", False, False, False)
            'fonction lisp pour affecter une valeur à un symbole lisp passé sous forme de chaine de carractère et d'un resultbuffer, ex : ManagedPutLispSym "myvar" MyRsbvalue
            'est utilisée par la Shared Function ManagedPutLispSym
            doc.SendStringToExecute("(defun ManagedPutLispSym ( strSym RsbValue) (set (read strSym) RsbValue))(vl-acad-defun 'ManagedPutLispSym) ", False, False, False)
            GetEditor().WriteMessage(vbNewLine & "Routines lisp ManagedEvalStrLispExpression, ManagedGetLispSym, ManagedPutLispSym créées ")
        End Sub

        Public Sub Initialize() Implements IExtensionApplication.Initialize
            ' Add one time initialization here
            ' One common scenario is to setup a callback function here that 
            ' unmanaged code can call. 
            ' To do this:
            ' 1. Export a function from unmanaged code that takes a function
            '    pointer and stores the passed in value in a global variable.
            ' 2. Call this exported function in this function passing delegate.
            ' 3. When unmanaged code needs the services of this managed module
            '    you simply call acrxLoadApp() and by the time acrxLoadApp 
            '    returns  global function pointer is initialized to point to
            '    the C# delegate.
            ' For more info see: 
            ' http:'msdn2.microsoft.com/en-US/library/5zwkzwf4(VS.80).aspx
            ' http:'msdn2.microsoft.com/en-us/library/44ey4b32(VS.80).aspx
            ' http:'msdn2.microsoft.com/en-US/library/7esfatk4.aspx
            ' as well as some of the existing AutoCAD managed apps.

            ' Initialize your plug-in application here
            'ajout de l'evenement pour les prochains docs
            AddDocColEventCreated()
            InitialiseLispExtension()

        End Sub

        Public Sub Terminate() Implements IExtensionApplication.Terminate
            ' Do plug-in application clean up here
        End Sub

        'GetEditor est utile pour afficher des messages
        Public Shared Function GetEditor() As Autodesk.AutoCAD.EditorInput.Editor
            GetEditor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor
        End Function

      'fonction exemple qui utilise invoke() 
        <CommandMethod("ManagedEvalCustomLisp")> _
        Public Sub ManagedEvalCustomLisp()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor
            Dim retour As ResultBuffer

            ' create a result buffer containing a LISP list
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.ListBegin)),
                New TypedValue(CInt(LispDataType.Int16), 12),
                New TypedValue(CInt(LispDataType.Text), "toto"),
                New TypedValue(CInt(LispDataType.T_atom)),
                New TypedValue(CInt(LispDataType.ListEnd)))

            ' bind the list to a 'lst1' LISP variable
            AcadExtensions.LispExtensions.ManagedPutLispSym("lst1", input)


            ' call the 'foo' Lisp function which binds the reversed list to 'lst2'
            ' (defun foo () (setq lst2 (reverse lst1))) (vl-acad-defun 'foo)

            'Appel lisp en managé:
            retour = AcadExtensions.LispExtensions.InvokeLispManaged(New ResultBuffer(New TypedValue(CInt(LispDataType.Text), "foo")))
            ' print the value to the commande line
            ed.WriteMessage(vbLf & "Retour fonction:")
            For Each tv As TypedValue In retour
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
            ed.WriteMessage(vbLf & "Lst2:")
            ' get the 'lst2' variable value
            Dim output As ResultBuffer = AcadExtensions.LispExtensions.ManagedGetLispSym("lst2")
            ' print the value to the commande line
            For Each tv As TypedValue In output
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
            ed.WriteMessage(vbLf & "Put lisp managé, création de la liste lst4 avec le retour de fonction, !lst4 pour vérification:")
            'ajoute des parenthèses à retour
            retour = AcadExtensions.LispExtensions.AddParenthesisToresultBuffer(retour)
            'version managée putLisp
            AcadExtensions.LispExtensions.ManagedPutLispSym("lst4", retour)
        End Sub

    

        'fonction exemple qui utilise invoke(), l'expression lisp est directement entrée à la ligne de commande Autocad
        'exemple : evaluer "(Prompt \"\nCoucou\")"
        <CommandMethod("EvalLispExpression")> _
        Public Sub EvalLispExpression()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor
            Dim retour As ResultBuffer
            Dim strEval As String = ed.GetString("Expression lisp à évaluer (avec les parenthèses) ?").StringResult

            'Appel lisp en managé:
            retour = AcadExtensions.LispExtensions.ManagedEvalStrLispExpression(strEval)
            ' print the value to the commande line
            ed.WriteMessage(vbLf & "Retour expression évaluée :")
            For Each tv As TypedValue In retour
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
            
        End Sub

        'fonction exemple qui utilise invoke(), le nom de la variable lisp est directement entrée à la ligne de commande Autocad
        'exemple : evaluer "myVar"
        <CommandMethod("EvalLispSymbole")> _
        Public Sub EvalLispSymbole()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor
            Dim strEval As String = ed.GetString("Nom de la variable lisp à évaluer ?").StringResult

            
            ed.WriteMessage(vbLf & "ManagedGetLispSym " & strEval & " :")
            ' get the 'lst2' variable value
            Dim output As ResultBuffer = AcadExtensions.LispExtensions.ManagedGetLispSym(strEval)
            ' print the value to the commande line
            ed.WriteMessage(vbLf & "Value of  the lisp symbol '" & strEval & " as resultBuffer :")
            For Each tv As TypedValue In output
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
            ed.WriteMessage(vbLf & "Put lisp managé, création de la liste lst avec le retour d'évaluation , !lst pour vérification que lst = " & strEval & " :")
            'ajoute des parenthèses à retour
            output = AcadExtensions.LispExtensions.AddParenthesisToresultBuffer(output)
            'version managée putLisp
            AcadExtensions.LispExtensions.ManagedPutLispSym("lst", output)
        End Sub


        'Exemples suivants, évaluation  lisp en code non managé aced (obsolete)
        <CommandMethod("EvalCustomLisp")> _
        Public Sub EvalCustomLisp()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor

            ' create a result buffer containing a LISP list
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.ListBegin)),
                New TypedValue(CInt(LispDataType.Int16), 12),
                New TypedValue(CInt(LispDataType.Text), "toto"),
                New TypedValue(CInt(LispDataType.T_atom)),
                New TypedValue(CInt(LispDataType.ListEnd)))

            ' bind the list to a 'lst1' LISP variable
            AcadExtensions.LispExtensions.SetLispSym("lst1", input)


            ' call the 'foo' Lisp function which binds the reversed list to 'lst2'
            ' (defun foo () (setq lst2 (reverse lst1))) (vl-acad-defun 'foo)
            AcadExtensions.LispExtensions.InvokeLisp(New ResultBuffer(New TypedValue(CInt(LispDataType.Text), "foo")))

            ed.WriteMessage(vbLf & "Lst2:")
            ' get the 'lst2' variable value
            Dim output As ResultBuffer = AcadExtensions.LispExtensions.GetLispSym("lst2")

            ' print the value to the commande line
            For Each tv As TypedValue In output
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next

        End Sub

        <CommandMethod("EvalCustomLispWithArg")> _
        Public Sub EvalCustomLispWithArg()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor

            ' create a result buffer containing a LISP expression containing a function call, with a single list argument  
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.Text), "fooArg"),
                New TypedValue(CInt(LispDataType.ListBegin)),
                New TypedValue(CInt(LispDataType.Int16), 12),
                New TypedValue(CInt(LispDataType.Text), "toto"),
                New TypedValue(CInt(LispDataType.T_atom)),
                New TypedValue(CInt(LispDataType.ListEnd)))

            'à ce stade, input ressemble à ça: 
            ' "fooArg" '( 12 "toto" 'T)
            'On note que les parenthèses de l'évaluation de la fonction ne sont pas nécessaires, mais celles de la liste d'arguments le sont
            'si la fonction accepte plusieurs arguments, pas de parenthèses !


            'copier coller suivant dans fenetre texte autocad pour définition fooarg :
            ' (defun fooArg ( lst1 ) (setq lst2 (reverse lst1))) (vl-acad-defun 'fooArg)

            ' call the 'fooArg' Lisp function which binds the reversed list to 'lst2'
            AcadExtensions.LispExtensions.InvokeLisp(input)

            ' get the 'lst2' variable value
            Dim output As ResultBuffer = AcadExtensions.LispExtensions.GetLispSym("lst2")

            ' print the value to the commande line
            For Each tv As TypedValue In output
                ed.WriteMessage(vbLf & "Type: {0}" & vbTab & "Value: {1}", tv.TypeCode, tv.Value)
            Next
        End Sub

        <CommandMethod("EvalAutoLispWithArg")> _
        Public Sub EvalAutoLispWithArg()


            ' create a result buffer containing a LISP list
            Dim input As New ResultBuffer(
                New TypedValue(CInt(LispDataType.Text), "Alert"),
                New TypedValue(CInt(LispDataType.Text), "toto a zéro"))

            'copier coller suivant dans fenetre texte autocad pour définition commande autolisp "alert"
            '(vl-acad-defun 'alert)

            ' call the 'fooArg' Lisp function which binds the reversed list to 'lst2'
            AcadExtensions.LispExtensions.InvokeLisp(input)

        End Sub
        'conclusion: on peut tout faire avec le lisp, et on a plus besoin de 
        'définir chaque commande lisp appelée par .Net avec un vl-acad-defun ni besoin de c:


    End Class

End Namespace

---------------------------------------------------------------------- PowerClic sur http://www.g-eaux.com
0

#6 L'utilisateur est hors-ligne   krunch 

  • ceinture marron
  • Groupe : Membres
  • Messages : 233
  • Inscrit(e) : 25-janvier 03
  • LocationLyon

Posté 13 décembre 2012 - 12:17

Ok merci bien

Je suis resté sur la version de gile postée ailleurs qui a l'avantage de rester en C# (pour moi c'est déjà suffisamment compliqué comme ça ..) et que je remets ci dessous.
Sur mon 2010 ça marche, à la réserve près qu'un appel de c:function provoque une erreur fatale, je dois utiliser le vl-acad-defun.

J'ai vu que ta méthode permets d'éviter le vl-acad-defun, mais sinon pas d'inconvénient a priori ?

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", EntryPoint = "acedInvoke", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    	extern static private int acedInvoke(IntPtr args, out IntPtr result);

    	/// Invoke a LISP function.
    	/// The LISP function must be defined as an external subroutine using the c: prefix or invoking vl-acad-defun.
    	/// This is no more mandatory since A2011 as the managed Application.Invoke() method wraps acedInvoke.
    	public static ResultBuffer InvokeLisp(ResultBuffer args)
    	{
        	IntPtr ip = IntPtr.Zero;
        	int status = acedInvoke(args.UnmanagedObject, out ip);
        	if (status == (int)PromptStatus.OK && ip != IntPtr.Zero)
            	return ResultBuffer.Create(ip, true);
        	return null;
    	}


    	// Test avec :
    	// (defun foo (l) (mapcar 'strcase l))
    	// (vl-acad-defun 'foo)
    	[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 + " ");
        	}
    	}
	}
}

0

#7 L'utilisateur est hors-ligne   GEGEMATIC 

  • ceinture noire 1er dan
  • Groupe : Membres
  • Messages : 661
  • Inscrit(e) : 04-novembre 05

Posté 13 décembre 2012 - 15:18

Non non, ton truc marche je pense.
Mais à l'usage, le vl-acad-defun est très contraignant, car il t'oblige à n'utiliser que des commandes lisp sans arguments.
Ce n'est pas la majorité de mes routines.
Ce qui m'interresse dans la liaison net / lisp, c'est de pouvoir utiliser l'interface net avec des fonctions lisp.
Avec le vl-acad-defun, ce n'étais pas possible pour moi.
Bon courage,
Gégé
---------------------------------------------------------------------- PowerClic sur http://www.g-eaux.com
0

#8 L'utilisateur est hors-ligne   (gile) 

  • ceinture rouge et blanche 8em dan
  • Groupe : Moderateurs
  • Messages : 10687
  • Inscrit(e) : 02-septembre 05

Posté 13 décembre 2012 - 18:29

Citation

Mais à l'usage, le vl-acad-defun est très contraignant, car il t'oblige à n'utiliser que des commandes lisp sans arguments.


Pas du tout, dans l'exemple donné par krunch, on appelle la fonction foo définie comme telle :
(defun foo (l) (mapcar 'strcase l))
(vl-acad-defun 'foo)

Cette fonction requiert bien un argument : une liste de chaînes, et retourne une autre liste de chaines. Elle est appelée depuis la commande .NET "Test" avec l'argument requis.
Gilles Chanteau - gileCAD -
Développements sur mesure pour AutoCAD
Image IPB
0

#9 L'utilisateur est hors-ligne   GEGEMATIC 

  • ceinture noire 1er dan
  • Groupe : Membres
  • Messages : 661
  • Inscrit(e) : 04-novembre 05

Posté 13 décembre 2012 - 18:58

salut,
Autant pour moi, ça date de septembre, je ne me rappelais plus de cette histoire de vl-acad-defun, pourtant j'avais fait des tests avec le même système ...

Maintenant, je pense quand même que :
retour = AcadExtensions.LispExtensions.ManagedEvalStrLispExpression("(alert "\"ça marche\")")
c'est vraiment ce qu'il y a de plus simple pour faire communiquer lisp et .net
et je n'ai pas à me soucier de faire un vl-acad-defun pour les milliers de routines dont je dispose.

Par contre, je n'ai à peine eu le temps de m'en servir, c'est dommage.
---------------------------------------------------------------------- PowerClic sur http://www.g-eaux.com
0

Partager ce sujet :


Page 1 sur 1
  • Vous ne pouvez pas commencer un sujet
  • Vous ne pouvez pas répondre à ce sujet

1 utilisateur(s) en train de lire ce sujet
0 membre(s), 1 invité(s), 0 utilisateur(s) anonyme(s)