Wednesday, September 17, 2008

.NET Compact Framework unmanaged callbacks: bool not invited

The .NET Compact Framework 2.0 is supposed to support unmanaged callbacks: you pass a delegate into C/C++ code via P/Invoke and the Framework converts it to a thunk. It passes the thunk to C/C++, and when the C/C++ code calls the thunk, the thunk calls your delegate.

Previously I had success passing strings:
// C# declaration
public delegate void ExceptionArgumentDelegate([MarshalAs(UnmanagedType.LPWStr)]
string message, [MarshalAs(UnmanagedType.LPWStr)] string paramName);
// C++ declaration
typedef void (__stdcall* ExceptionArgumentCallback_t)
(const wchar_t *, const wchar_t *);
void SetCallback(ExceptionArgumentCallback_t callback);
// P/Invoke declaration
[DllImport("DllNameHere", EntryPoint="SetCallback")]
public static extern void SetCallback(ExceptionArgumentDelegate callback);

But I couldn't pass the following delegate type:
// C# declaration
public delegate bool ProgressCallback([MarshalAs(UnmanagedType.LPWStr)]
string message, int itemsProcessed, int totalItems);
// C++ declaration
typedef bool (__stdcall *ProgressCallback)(PCTSTR, int, int);

I get a NotSupportedException with the rather vague error message "0x80131515". Thanks Microsoft! Well, it turns out that a callback is not allowed to have a bool return type. The solution: change the return type on the C# side to int, then return 0 instead of false and 1 instead of true. Just to be on the safe side, you might want to change the C++ side to return int also. But that wasn't needed in my case (XScale PXA processor).

Interestingly, my cursory examination suggests you are allowed to pass bool as an argument, and even ref bool works (ref bool corresponds to bool* on the C++ side).

Friday, September 12, 2008

Simulating covariant return types

For several years, Microsoft engineers have refused to add support for covariant return types, a trivially simple feature that should have been in the CLR from the beginning.

Suppose you want to write a Clone() method that returns a copy of the current object. If implementing an interface, you can use this workaround in C# that uses explicit interface implementation:
class MyCloneable : ICloneable {
public MyCloneable Clone() { ... }
object ICloneable.Clone() { return Clone(); }
}

The above workaround is okay for implementing an interface, but what if you are writing a class hierarchy, and you want a Clone() method that is virtual but has the appropriate return type?
class BaseNode : ICloneable
{
object ICloneable.Clone() { return Clone(); }
public virtual BaseNode Clone() { ... }
}
class ComplexNode : BaseNode
{
override BaseNode BaseNode.Clone() { return Clone(); } // Error!
public ComplexNode Clone() { ... }
}

Oops, the workaround that you use for interfaces is illegal for class inheritance. There is still a solution, though:
class BaseNode : ICloneable
{
object ICloneable.Clone() { return Clone(); }
public BaseNode Clone() { BaseNode c; Clone(out c); return c; }
protected virtual void Clone(out BaseNode clone) { ... }
}
class ComplexNode : BaseNode
{
public new ComplexNode Clone() { ComplexNode c; Clone(out c); return c; }
protected override void Clone(out BaseNode clone) { clone = Clone(); }
protected virtual void Clone(out ComplexNode clone) { ... }
}

That's right. You need six Clone() methods. The last method is virtual in case you want to make a class derived from ComplexNode, e.g. VeryComplexNode:
class VeryComplexNode : ComplexNode
{
public new VeryComplexNode Clone() { VeryComplexNode c; Clone(out c); return c; }
protected override void Clone(out BaseNode clone) { clone = Clone(); }
protected override void Clone(out ComplexNode clone) { clone = Clone(); }
protected virtual void Clone(out VeryComplexNode clone) { ... }
}

Without covariant return types, you have to to define an additional virtual function for each additional derived class.