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