I wrote the typemaps for the use case of passing callbacks from C# to C++; I didn't pay attention to the case of passing them back from C++ to C# (I didn't know it was possible to pass them back so easily!)
I suspect you can fix this by defining csout and csvarout typemaps. Besides changing IntPtr to CSTYPE in the "out=" attributes, add something like this to the macro (note, I didn't test it):
%typemap(csout) TYPE, TYPE&
{
CSTYPE ret = $imcall;$excode
return ret;
}
%typemap(csvarout) TYPE, TYPE& "CSTYPE"
%{
get {
CSTYPE ret = $imcall;$excode
return ret;
}
%}
From: Joshua Foster [mailto:joshua70448@...]
Sent: Sunday, July 25, 2010 10:00 AM
To: swig-user@...
Subject: [Swig-user] Question about C# callbacks
I've made a lot of progress in wrapping a C library into a C# DLL, but I've got another question. I'm trying to implement a C callback in C# code. I'm using the %cs_callback code posted to this mailing list by David Piepgrass last year:
///////////////////////////////////////////////////////////////////////////
// cs_callback is used to marshall callbacks. It allows a C# function to
// be passed to C++ as a function pointer through P/Invoke, which has the
// ability to make unmanaged-to-managed thunks. It does NOT allow you to
// pass C++ function pointers to C#.
//
// Anyway, to use this macro you need to declare the function pointer type
// TYPE in the appropriate header file (including the calling convention),
// declare a delegate named after CSTYPE in your C# project, and use this
// macro in your .i file. Here is an example:
//
// in C++ header file (%include this header in your .i file):
// typedef void (__stdcall *Callback)(PCWSTR);
// void Foo(Callback c);
//
// in C# code:
// public delegate void CppCallback([MarshalAs(UnmanagedType.LPWStr)]
// string message);
//
// in your .i file:
// %cs_callback(Callback, CppCallback)
//
// As an alternative to specifying __stdcall on the C++ side, in the .NET
// Framework (but not the Compact Framework) you can use the following
// attribute on the C# delegate in order to get compatibility with the
// default calling convention of Visual C++ function pointers:
// [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
//
// Remember to invoke %cs_callback BEFORE any code involving Callback.
%define %cs_callback(TYPE, CSTYPE)
%typemap(ctype) TYPE, TYPE& "void*"
%typemap(in) TYPE %{ $1 = ($1_type)$input; %}
%typemap(in) TYPE& %{ $1 = ($1_type)&$input; %}
%typemap(imtype, out="IntPtr") TYPE, TYPE& "CSTYPE"
%typemap(cstype, out="IntPtr") TYPE, TYPE& "CSTYPE"
%typemap(csin) TYPE, TYPE& "$csinput"
%enddef
That works for the type definition, but I'm still having trouble with another class. This other class has a few properties, and one of those properties is a callback. The generated code for this class tries to use IntPtr where it should be using the callback I've passed for CSTYPE into %cs_callback. I've tweaked the %cs_callback code so that the typemaps for imtype and cstype are out="CSTYPE", and that fixed most of the problems, but a couple remained. Particularly, in the getter for the property, the callback is being retrieved and stored in an IntPtr (problem #1), then converted to a SWIGTYPE_p_f_int_unsigned_long_p_q_const__void__unsigned_long object and returned (problem #2). I'm able to make the code work by simply returning the retrieved callback as a CSTYPE delegate. How would I go about automating this, so I don't have to modify the generated C# code?
The callback as defined in the original H file:
typedef DWORD (WINAPI *lgLcdOnSoftButtonsCB)(IN int device,
IN DWORD dwButtons,
IN const PVOID pContext);
The class with a property of type lgLcdOnSoftButtonsCB:
typedef struct
{
// Set to NULL if no softbutton notifications are needed
lgLcdOnSoftButtonsCB softbuttonsChangedCallback;
PVOID softbuttonsChangedContext;
} lgLcdSoftbuttonsChangedContext;
The delegate handler I've defined in my C# code:
public delegate uint OnSoftButtonsHandler(int device, uint dwButtons, object pContext);
The call to %cs_callback in my I file:
%define %cs_callback(TYPE, CSTYPE)
%typemap(ctype) TYPE, TYPE& "void*"
%typemap(in) TYPE %{ $1 = ($1_type)$input; %}
%typemap(in) TYPE& %{ $1 = ($1_type)&$input; %}
%typemap(imtype, out="CSTYPE") TYPE, TYPE& "CSTYPE"
%typemap(cstype, out="CSTYPE") TYPE, TYPE& "CSTYPE"
%typemap(csin) TYPE, TYPE& "$csinput"
%enddef
%cs_callback(lgLcdOnSoftButtonsCB, OnSoftButtonsHandler)
The generated code for the property in lgLcdSoftbuttonsChangedContext:
public OnSoftButtonsHandler softbuttonsChangedCallback {
set {
LCDWrapperPINVOKE.lgLcdSoftbuttonsChangedContext_softbuttonsChangedCallback_set(swigCPtr, value);
}
get {
IntPtr cPtr = LCDWrapperPINVOKE.lgLcdSoftbuttonsChangedContext_softbuttonsChangedCallback_get(swigCPtr);
SWIGTYPE_p_f_int_unsigned_long_p_q_const__void__unsigned_long ret = (cPtr == IntPtr.Zero) ? null : new SWIGTYPE_p_f_int_unsigned_long_p_q_const__void__unsigned_long(cPtr, false);
return ret;
}
}
My "fixed" version of the generated code:
public OnSoftButtonsHandler softbuttonsChangedCallback {
set {
LCDWrapperPINVOKE.lgLcdSoftbuttonsChangedContext_softbuttonsChangedCallback_set(swigCPtr, value);
}
get {
return LCDWrapperPINVOKE.lgLcdSoftbuttonsChangedContext_softbuttonsChangedCallback_get(swigCPtr);
}
}
Thanks for the help!!!
Josh
|