Michael: Earlier today I was doing some digging using Lutz Roeder's .NET Reflector and it seems that the CommandMethod attribute registers the command using the acmgd.Autodesk.AutoCAD.Runtime.ICommandLineCallable interface. As I can tap into any other aspect of AutoCAD's .NET api via ironpython I'm going to assume that I can somehow tap into this and register a command. The only problem is I don't have a clue how...yet.
If you want to take a look at what C# code I have so far you can find it @ http://pyacaddotnet.googlecode.com/svn/trunk/PyAcadDotNet.cs Also I have some (lousy) samples that I use for testing IronPython in AutoCAD. You can find them here: http://pyacaddotnet.googlecode.com/svn/trunk/Samples/ Tim On 7/23/07, Michael Foord <[EMAIL PROTECTED]> wrote: > I'm pretty sure that it is impossible to dynamically generate classes > decorated with attributes at runtime using 'pure C#'. > > The two alternatives are either to use the Reflection.Emit API - which > is probably the best way but requires more digging than I have time for > at this point in time. > > Another alternative is to dynamically generate C# and compile to in > memory assemblies. This is less memory efficient because it requires an > assembly and type object for every command you wish to add. It is a lot > faster than you might expect. > > Attached are two files. One is 'generate.py'. It has a function called > 'Generate' which compiles C# using the CodeDom API with the > CSharpCodeProvider. > > As an example I have provided another file - DllImport.py. This uses > Generate to compile the C# from my screenshot example : > > http://www.voidspace.org.uk/ironpython/winforms/part10.shtml > > It works fine. :-) > > The specific call is : > > assembly = Generate(unmanaged_code, 'UnamangedCode', inMemory=True) > clr.AddReference(assembly) > from UnmanagedCode import User32, GDI32 > > (You can also use it to save assemblies to disk.) > > For your use you will need to pass in a list of references (absolute > file paths) to the AutoCAD DLLs that your C# code is dependent on. > > I think you need to generate C# code that looks something like: > > > using Autodesk.AutoCAD.ApplicationServices; > using Autodesk.AutoCAD.DatabaseServices; > using Autodesk.AutoCAD.EditorInput; > using Autodesk.AutoCAD.Runtime; > > namespace AutoCADStuff > > { > public class AutoCADStuffBase > { > [CommandMethod("myname")] > public void method > { > _method(); > } > > public virtual void _method > { > return null; > } > } > > In IronPython you can call 'Generate', then import and subclass > AutoCADStuffBase and override '_method': > > class AutoCADStuff(AutoCADStuffBase): > def _method(self): > some code ... > > I have used this approach with Silverlight assemblies for creating > classes with methods marked with attributes. > > I hope this helps. > > Please no one post this code as is - I'll write up and post an article > shortly. :-) > > Michael Foord > > Michael Foord wrote: > > Tim Riley wrote: > > [snip..] > > > >> AutoCAD's .NET API allows > >> you to register a command that is callable from the AutoCAD via a > >> CommandMethod attribute[1]. Since IP doesn't support attributes I'd like > >> to develop some sort of decorator that would clone the functionality of > >> the CommandMethod attribute. So I could execute > >> python code like: > >> > >> @commandmethod("test") > >> def tester: > >> print "test worked" > >> > >> and it would register the test command so I could call it from the > >> command line. > >> > >> > >> > > > > Right - so you want to dynamically create methods (or functions) with > > attributes. The attribute takes the name that the command will be > > exposed with. > > > > I wonder if AutoCAD even supports these being dynamically created? > > > > If it does I wonder if we can do it by creating an inner class in C#. > > The method marked with the attribute would call down to another method > > (marked as virtual) that we can override from IronPython. > > > > I'm not exactly sure of the C# syntax/semantics for inner classes - (a > > class factory) - so I'll have to do some digging. (i.e. will it allow us > > to set the argument to the attribute at runtime rather than compile time > > and can we return classes or only instances.) > > > > AFAIK, attributes are normally bound at compile time rather than > > runtime, so using C# to create classes in this way may not work. > > > > The proper way (*sigh*) is to use the Reflection.Emit API to add > > attributes. I've experimented Reflection.Emit - but never got as far as > > adding attributes dynamically. (You need to 'program in bytecode' using > > this API - which isn't too bad but has a bit of a learning curve > > associated.) > > > > A *third* approach is to dynamically generate C# and compile to in > > memory assemblies (using the CodeDOM API if I remember correctly). That > > turns out to be surprisingly easy. > > > > Michael Foord > > http://www.voidspace.org.uk/ironpython/index.shtml > > > > > >> [1] As an example: > >> http://through-the-interface.typepad.com/through_the_interface/2007/07/updating-a-spec.html > >> > >> > >> Tim > >> > >> On 7/23/07, Michael Foord <[EMAIL PROTECTED]> wrote: > >> > >> > >>> Tim Riley wrote: > >>> > >>> > >>>> Michael: > >>>> > >>>> Thanks for the reply. However when reading it my eyes glazed over. Is > >>>> there any way you could provide a simple man like myself with some > >>>> example code for me to peruse? > >>>> > >>>> > >>>> > >>> What do you want to achieve? Write the IronPython code you would like - > >>> and I will try and provide a C# stub that you can subclass. (You will > >>> need a C# compiler - Visual Studio Express C# is probably the most > >>> straightforward if you are using Windows.) > >>> > >>> Michael Foord > >>> http://www.voidspace.org.uk/ironpython/index.shtml > >>> > >>> > >>> > >>>> Tim > >>>> > >>>> On 7/23/07, Michael Foord <[EMAIL PROTECTED]> wrote: > >>>> > >>>> > >>>> > >>>>> Tim Riley wrote: > >>>>> > >>>>> > >>>>> > >>>>>> I know that IronPython doesn't support attributes but does anyone know > >>>>>> of a workaround that will allow IP code to use them? > >>>>>> > >>>>>> > >>>>>> > >>>>>> > >>>>> The IronPython team are strangely quiet every time someone asks this... > >>>>> ;-) > >>>>> > >>>>> A lot of people would like an answer to this question. Currently the > >>>>> only way is to use stub C# classes. > >>>>> > >>>>> Michael Foord > >>>>> http://www.voidspace.org.uk/ironpython/index.shtml > >>>>> > >>>>> > >>>>> > >>>>> > >>>>> > >>>>>> _______________________________________________ > >>>>>> users mailing list > >>>>>> [email protected] > >>>>>> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com > >>>>>> > >>>>>> > >>>>>> > >>>>>> > >>>>>> > >>>>> _______________________________________________ > >>>>> users mailing list > >>>>> [email protected] > >>>>> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com > >>>>> > >>>>> > >>>>> > >>>>> > >>>> _______________________________________________ > >>>> Users mailing list > >>>> [email protected] > >>>> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com > >>>> > >>>> > >>>> > >>>> > >>> _______________________________________________ > >>> Users mailing list > >>> [email protected] > >>> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com > >>> > >>> > >>> > >> _______________________________________________ > >> Users mailing list > >> [email protected] > >> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com > >> > >> > >> > > > > _______________________________________________ > > Users mailing list > > [email protected] > > http://lists.ironpython.com/listinfo.cgi/users-ironpython.com > > > > > > > import clr > clr.AddReference('System.Drawing') > > from System.Drawing import Bitmap, Image > > from generate import Generate, LoadAssembly > > > unmanaged_code = """ > using System; > using System.Collections.Generic; > using System.Text; > using System.Runtime.InteropServices; > > namespace UnmanagedCode > { > public class GDI32 > { > [DllImport("GDI32.dll")] > public static extern IntPtr CreateCompatibleDC(IntPtr hdc); > > [DllImport("GDI32.dll")] > public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int > nWidth, > int nHeight); > > [DllImport("GDI32.dll")] > public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); > > [DllImport("GDI32.dll")] > public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int > nYDest, > int nWidth, int nHeight, IntPtr > hdcSrc, > int nXSrc, int nYSrc, int dwRop); > > [DllImport("GDI32.dll")] > public static extern bool DeleteDC(IntPtr hdc); > > [DllImport("GDI32.dll")] > public static extern bool DeleteObject(IntPtr hObject); > } > > public class User32 > { > [DllImport("user32.dll")] > public static extern IntPtr GetDesktopWindow(); > > [DllImport("user32.dll")] > public static extern IntPtr GetTopWindow(IntPtr hWnd); > > [DllImport("user32.dll")] > public static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd); > > [DllImport("User32.dll")] > public static extern IntPtr GetWindowDC(IntPtr hWnd); > > [DllImport("User32.dll")] > public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC); > > } > } > """ > > assembly = Generate(unmanaged_code, 'UnamangedCode', inMemory=True) > > clr.AddReference(assembly) > > from UnmanagedCode import User32, GDI32 > > def ScreenCapture(x, y, width, height): > hdcSrc = User32.GetWindowDC(User32.GetDesktopWindow()) > hdcDest = GDI32.CreateCompatibleDC(hdcSrc) > hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height) > GDI32.SelectObject(hdcDest, hBitmap) > > # 0x00CC0020 is the magic number for a copy raster operation > GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, 0x00CC0020) > result = Bitmap(Image.FromHbitmap(hBitmap)) > User32.ReleaseDC(User32.GetDesktopWindow(), hdcSrc) > GDI32.DeleteDC(hdcDest) > GDI32.DeleteObject(hBitmap) > return result > > image = ScreenCapture(0, 0, 50, 400) > for y in range(image.Height): > row = [] > for x in range(image.Width): > color = image.GetPixel(x, y) > value = color.R + color.G + color.B > if value > 384: > row.append(' ') > else: > row.append('X') > print ''.join(row) > > import clr > > from System.Environment import CurrentDirectory > from System.IO import Path, Directory > > from System.CodeDom import Compiler > from Microsoft.CSharp import CSharpCodeProvider > > > def Generate(code, name, references=None, outputDirectory=None, > inMemory=False): > CompilerParams = Compiler.CompilerParameters() > > if outputDirectory is None: > outputDirectory = Directory.GetCurrentDirectory() > if not inMemory: > CompilerParams.OutputAssembly = Path.Combine(outputDirectory, name + > ".dll") > CompilerParams.GenerateInMemory = False > else: > CompilerParams.GenerateInMemory = True > > CompilerParams.TreatWarningsAsErrors = False > CompilerParams.GenerateExecutable = False > CompilerParams.CompilerOptions = "/optimize" > > for reference in references or []: > CompilerParams.ReferencedAssemblies.Add(reference) > > provider = CSharpCodeProvider() > compile = provider.CompileAssemblyFromSource(CompilerParams, code) > > if compile.Errors.HasErrors: > raise Exception("Compile error: %r" % list(compile.Errors.List)) > > if inMemory: > return compile.CompiledAssembly > return compile.PathToAssembly > > > def LoadAssembly(name, namespace=None): > clr.AddReference(name) > namespace = __import__(namespace or name) > return namespace > > _______________________________________________ > Users mailing list > [email protected] > http://lists.ironpython.com/listinfo.cgi/users-ironpython.com > > _______________________________________________ Users mailing list [email protected] http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
