Sunday, March 28, 2010

How-To : IronPython and callback delegates with 'out' or 'ref' parameters

Following up on the earlier post, having found time to engage the brain a little...

Let's do the int flavoured version, everything non-default:

namespace Callback
{
using System;
public delegate IntPtr Hook(ref int handled);
public static class Callback
{
public static object[] MakeCallback(Hook callback)
{
int handled = 17;
IntPtr handle = callback(ref handled);
return new object[] { handle, handled };
}
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

and then in IronPython ask the variable about itself

import clr
clr.AddReferenceToFile('Callback.dll')
from Callback import *
from System import IntPtr
def callback(x):
print type(x)
print dir(x)
return IntPtr(42)
test = Callback.MakeCallback(callback)
print test
view raw gistfile1.py hosted with ❤ by GitHub

Now we get

>"\Program Files\IronPython 2.6\ipy.exe" callback.py
<type 'StrongBox[int]'>
['Equals', 'GetHashCode', 'GetType', 'MemberwiseClone', 'ReferenceEquals', 'ToSt
ring', 'Value', '__class__', '__delattr__', '__doc__', '__format__', '__getattri
bute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__re
pr__', '__setattr__', '__str__', '__subclasshook__']
Array[object]((<System.IntPtr object at 0x000000000000002B [42]>, 17))

That Value member looks interesting, so...

import clr
clr.AddReferenceToFile('Callback.dll')
from Callback import *
from System import IntPtr
def callback(x):
x.Value = 23
return IntPtr(42)
test = Callback.MakeCallback(callback)
print test
view raw gistfile1.py hosted with ❤ by GitHub

which gives us

>"\Program Files\IronPython 2.6\ipy.exe" callback.py
Array[object]((<System.IntPtr object at 0x000000000000002B [42]>, 23))

which is what we were looking for.

Rebuilding the C# assembly with out rather than ref and re-instating the print lines shows that the argument is still passed as a StrongBox, so there is no change on the IronPython side.

Armed with this piece of knowledge about the type, I tried Googling again, and didn't find any obvious mentions of callbacks -- just the converse matter of calling .Net methods that take out or ref parameters. But that at least is enough to reassure me that this is not just some completely undocumented hack that would be subject to change in later versions.

Note also that if I had written

print x.GetType().ToString()

(which is what I did the first time around) rather than

print type(x)

I would have gotten the result System.Int32, which gives no clue that adding

print dir(x)

is at all something worth doing.

No comments :