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

Reply via email to