[IronPython] Hosting: Delegates from Ironpython to C#

Dino Viehland dinov at exchange.microsoft.com
Thu Aug 30 09:19:41 PDT 2007


Well at this point we've successfully created the delegate and passed it off to you so it's hard to tell what's going wrong here.  Couple of suggestions on things to try and troubleshoot the issue:
        Instead of handing the delegate you get off to the P/Invoke call can you just turn around and invoke the delegate from C# code and successfully call back into the Python code?
        If that works can you do something like:

class PythonDelegateWrapper {
        private CmdDelegate cmdDelegate;
        public PythonDelegateWrapper(CmdDelegate dlg) {
                cmdDelegate = dlg;
        }

        public void Invoke() {
                cmdDelegate();
        }
}

And then pass "new CmdDelegate(new PythonDelegateWrapper(dlg).Invoke)" to the P/Invoke function?

The reason why I'm proposing this is maybe there's a strange interaction between dynamic methods (which the Python delegate will be) and the P/Invoke call - this might help isolate the issue.

And the final thing that might be interesting to try would be to make sure you keep the reference to the delegate alive during the lifetime of the call.  In theory this should be happening for you automatically -   but if the unmanaged side is going to hold onto this delegate for longer than the duration of the call you'll need to do this anyway.  Given that you're still in the call this shouldn't be an issue but you could set the environment variable COMPlus_MDA=1 to enable CLR Managed Debugging Assistants to see if you get any warnings about that firing.  I don't really believe this could be happening but it would be consistent w/ the exception you're getting.

Those are my 1st two guesses as to what could be going wrong, hopefully one of them will be helpful :).

-----Original Message-----
From: users-bounces at lists.ironpython.com [mailto:users-bounces at lists.ironpython.com] On Behalf Of Tim Riley
Sent: Thursday, August 30, 2007 8:59 AM
To: Discussion of IronPython
Subject: Re: [IronPython] Hosting: Delegates from Ironpython to C#

Dino:

I was trying something similar to what you had posted and was getting
an error. I also just tried the code you gave me with a minor
correction to fix the CommandFlags part and received the error below.
If it helps I have also added the C# code use to call the python file
below the error if that will help at all.


*********ERROR***********
Command: pyfile
System.AccessViolationException: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
   at PyAcadDotNet.PyAcadCmd.RegPyCmd(String cmd_group, String cmd_name, CommandFlags cmd_flags, CmdDelegate cmd_delegate)
   at PyAcadDotNet.PyAcadCmd.PythonRegister(String CommandName, CmdDelegate FuncPointer, CommandFlags flags) in C:\Documents and Settings\TJRiley\My Documents\pyacaddotnet\registercommand.cs:line 65
   at PythonRegister##20(Object , Object , Object )
   at IronPython.Runtime.Calls.CallTarget3.Invoke(Object arg0, Object arg1, Object arg2)
   at IronPython.Runtime.Calls.FastCallable3.Call(ICallerContext context, Object arg0, Object arg1, Object arg2)
   at IronPython.Runtime.Calls.BuiltinFunction.Call(ICallerContext context, Object arg0, Object arg1, Object arg2)
   at IronPython.Runtime.Operations.Ops.CallWithContext(ICallerContext context, Object func, Object arg0, Object arg1, Object arg2)
   at C:\Documents and Settings\TJRiley\My Documents\pyacaddotnet\Samples\commandmethod_test.py##22(ModuleScope )
   at IronPython.Hosting.CompiledCodeDelegate.Invoke(ModuleScope moduleScope)
   at IronPython.Hosting.CompiledCode.Run(ModuleScope moduleScope)
   at IronPython.Hosting.CompiledCode.Execute(EngineModule engineModule, IDictionary`2 locals)
   at IronPython.Hosting.CompiledCode.Execute()
   at PyAcadDotNet.AcadInterface.pythonfile() in C:\Documents and
Settings\TJRiley\My Documents\pyacaddotnet\PyAcadDotNet.cs:line 98
*********ERROR***********


*********CODE**************
using System;
using System.Collections;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;

using AcEd = Autodesk.AutoCAD.EditorInput;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;

using IronPython.Hosting;

namespace PyAcadDotNet
{
  public class AcadInterface : IExtensionApplication
  {
    static internal AcEd.Editor ed =
AcadApp.DocumentManager.MdiActiveDocument.Editor;

    public delegate void TestDelegate();


    public void Initialize()
    {
      ed.WriteMessage("\nPyAcad.NET Loaded Successfully....");
      ed.WriteMessage("\ntype 'pyhelp' for commands....");
    }

    public void Terminate()
    {
      this.Terminate();
    }

    internal delegate void AddReference(object assembly);

    [CommandMethod("pyfile", CommandFlags.Session)]
    static public void pythonfile()
    {
      using (PythonEngine engine = new PythonEngine())
      {
        using (AcadCommandLine myCommandLine = new AcadCommandLine())
        {
          try
          {
            // Create a new instance of PythonEngine and set variables.
            engine.AddToPath(Environment.CurrentDirectory);
            // Send Stdout and Stderr to the AutoCAD command line.
            engine.SetStandardOutput(myCommandLine);
            engine.SetStandardError(myCommandLine);
            engine.Import("clr");
            PyAcadCmd regcmds = new PyAcadCmd();
            engine.Globals.Add("regcmds", regcmds);
            //lets load some AutoCAD assemblies.
            AddReference adr =
engine.CreateMethod<AddReference>("clr.AddReference(assembly)");
            adr(typeof(BlockTableRecord).Assembly);
            adr(typeof(Editor).Assembly);

            // Display an OpenFileDialog and run the script.
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Python files (*.py)|*.py|All files (*.*)|*.*";
            ofd.ShowDialog();

            // Run the file selected by the open file dialog box.
            //engine.ExecuteFile(ofd.FileName);
            CompiledCode cc = engine.CompileFile(ofd.FileName);
            cc.Execute();
          }
          catch (System.Exception e)
          {
            ed.WriteMessage(e.ToString());
          }
        }
      }
    }
 }

  //
  public class AcadCommandLine : Stream
  //Modified version of a class coded by Mike Stall.
  {
    public AcadCommandLine()
    {
      //constructor
    }

    #region unsupported Read + Seek members
    public override bool CanRead
    {
      get { return false; }
    }

    public override bool CanSeek
    {
      get { return false; }
    }

    public override bool CanWrite
    {
      get { return true; }
    }

    public override void Flush()
    {
      //
    }

    public override long Length
    {
      get { throw new NotSupportedException("Seek not supported"); }
// can't seek
    }

    public override long Position
    {
      get
      {
        throw new NotSupportedException("Seek not supported");  // can't seek
      }
      set
      {
        throw new NotSupportedException("Seek not supported");  // can't seek
      }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
      throw new NotSupportedException("Reed not supported"); // can't read
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
      throw new NotSupportedException("Seek not supported"); // can't seek
    }

    public override void SetLength(long value)
    {
      throw new NotSupportedException("Seek not supported"); // can't seek
    }
    #endregion

    public override void Write(byte[] buffer, int offset, int count)
    {
      try
      {
        // Very bad hack: Ignore single newline char. This is because
we expect the newline is following
        // previous content and we already placed a newline on that.
        AcEd.Editor ed = AcadApp.DocumentManager.MdiActiveDocument.Editor;

        if (count == 1 && buffer[offset] == '\n')
          return;

        StringBuilder sb = new StringBuilder();
        while (count > 0)
        {
          char ch = (char)buffer[offset];
          if (ch == '\n')
          {
            ed.WriteMessage(sb.ToString() + "\n");
            sb.Length = 0; // reset.
          }
          else if (ch != '\r')
          {
            sb.Append(ch);
          }

          offset++;
          count--;
        }
        if (sb.Length > 0)
          ed.WriteMessage(sb.ToString() + "\n");
      }
      catch (System.Exception e)
      {
        throw e;
      }
    }
  }
}
*********CODE**************
On 8/30/07, Dino Viehland <dinov at exchange.microsoft.com> wrote:
> I think you should be able to just pass a function object to PythonRegister and it should be converted into a delegate.  For example:
>
> import clr
> clr.AddReference('PyAcadDotNet')
> from PyAcadDotNet import PyAcadCmd
>
> def foo():
>         print 'hello world'
>
> PyAcadCmd.PythonRegister('some command', foo, CommandFlags.Whatever)
>
> Does that not work?
>
> -----Original Message-----
> From: users-bounces at lists.ironpython.com [mailto:users-bounces at lists.ironpython.com] On Behalf Of Tim Riley
> Sent: Wednesday, August 29, 2007 7:10 PM
> To: Discussion of IronPython
> Subject: [IronPython] Hosting: Delegates from Ironpython to C#
>
> I'm embedding IronPython in a C# dll that is hosted inside a program
> called AutoCAD. In order to register commands in AutoCAD from .NET I
> need to P/Invoke a C function inside a .dll. I can do this fairly easy
> from C# but I can't figure out the right way to call my C# wrapper
> from IronPython to have it register the command. I have perused the
> hosting docs for 1.1 and haven't been able to come up with a solution
> that works. Here is my C# code. I either want to call the PyRegCmds
> void or the PythonRegister void.  Both of which expect a delegate.for
> example if I had a python function like:
>
> def test1:
>     print "This is a test".
>
> I can't figure out how to map test to the delegate required in the code below.
> Note: I can call this from C# fine. See :static public void test().
>
> Can anyone give me any pointers? It would be greatly appreciated.
>
>
> code:
>
> using System ;
> using System.Runtime.InteropServices;
> using Autodesk.AutoCAD.Runtime ;
> using Autodesk.AutoCAD.EditorInput;
>
> namespace PyAcadDotNet
> {
>         /// <summary>
>         /// PyAcadCmd Class:
>         /// Used to register commands on the AutoCAD command stack.
>         /// </summary>
>         public class PyAcadCmd
>         {
>                 public PyAcadCmd()
>                 {
>                 }
>                 public delegate void CmdDelegate();
>
>                 /// <summary>
>                 /// RegPyAcadCmd:
>                 /// Registers a delegate (callback) with the AutoCAD command string
>                 /// on the command stack.
>                 /// </summary>
>         [DllImport("PyRegCmd.dll",
>                          CallingConvention=CallingConvention.Cdecl,CharSet = CharSet.Unicode,
>                          EntryPoint = "?RegPyCmd@@YAXPB_W0HP6AXXZ at Z")]
>                 public static extern void RegPyCmd(
>                         string cmd_group,
>                         string cmd_name,
>                         Autodesk.AutoCAD.Runtime.CommandFlags cmd_flags,
>                         [MarshalAs(UnmanagedType.FunctionPtr)] PyAcadCmd.CmdDelegate cmd_delegate);
>
>
>         public static void PythonRegister(string CommandName,
> CmdDelegate FuncPointer, CommandFlags flags)
>         {
>             RegPyCmd("_pycmds", CommandName, flags, FuncPointer);
>         }
>
>         //testing stuff
>         public static void testcommand()
>         {
>             Editor ed =
> Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
>             ed.WriteMessage("\ncb1 delegate seems to work!\n");
>         }
>         [CommandMethod("regcmds")]
>         static public void test() // This method can have any name
>         {
>             CmdDelegate cb1 = new CmdDelegate(PyAcadCmd.testcommand);
>             PythonRegister("testcommand", cb1, CommandFlags.Session);
>         }
>         }
>
>
> }
> _______________________________________________
> Users mailing list
> Users at lists.ironpython.com
> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
> _______________________________________________
> Users mailing list
> Users at lists.ironpython.com
> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
>
_______________________________________________
Users mailing list
Users at lists.ironpython.com
http://lists.ironpython.com/listinfo.cgi/users-ironpython.com


More information about the Users mailing list