Callback functions

  • Thread starter Thread starter Kevin Hutchison
  • Start date Start date
I created a DLL in Visual Embedded C++ 3.0, to use with .NET. It works with
callback functions.
But, on the running occurs the exception 'NotSupported'. The CF don't
support callback functions ?
What can I do ?

Thanx for all. Sorry about my english, isn't very good.
Luciano.


MyDLL.CPP
=========
#include "stdafx.h"
#include "MyDLL.h"

typedef int (__stdcall * FunctionCallback)( int i );

extern "C" __declspec(dllexport) int __stdcall FunctionDLL( FunctionCallback
pFunc ) {
return pFunc( 123 );
}



TestDLL.CS
==========
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System.Text;

namespace TestDLL
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.MainMenu mainMenu1;

public Form1()
{
InitializeComponent();
}

protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code

private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.button1 = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.button1.Location = new System.Drawing.Point(8, 8);
this.button1.Text = "button1";
this.button1.Click += new
System.EventHandler(this.button1_Click);
this.label1.Location = new System.Drawing.Point(16, 64);
this.label1.Size = new System.Drawing.Size(216, 80);
this.label1.Text = "label1";
this.Controls.Add(this.label1);
this.Controls.Add(this.button1);
this.Menu = this.mainMenu1;
this.Text = "Form1";
}
#endregion

// typedef int (__stdcall * FunctionCallback)( int i );
public delegate int FunctionCallback( int i );

[DllImport("DLL.DLL")]
public static extern int FunctionDLL( FunctionCallback pFunc );

public static void Main()
{
Application.Run(new Form1());
}

public delegate int DelegateFunction( int iVal );

public int MyFunction( int iVal ) {
return iVal + 456;
}

private void button1_Click(object sender, System.EventArgs e)
{
int iRet;
FunctionCallback pFunc = new FunctionCallback( MyFunction );
try
{
iRet = FunctionDLL( pFunc ); // <<<<<<<<<
ExceptionNotSupported
}
catch( NotSupportedException E )
{
label1.Text = E.Message;
return;
}
}
}
}
 
FWIW -

Necessity required me to combine the two methods to get our callbacks
working. I ran into some of the issues Mr. Feinman had posted on
previously regarding his SendMessage/Assembler implementation. I
basically "borrowed" his method calling portion and wrote some really
poor C in a dll to map a function pointer to a HWND.

It got me out of a jam, and I am sure someone can (or has) improved on
it. What I did follows.

- K.

<C#>
using System;
using System.Data;
using Microsoft.WindowsCE.Forms;
using System.Runtime.InteropServices;
using System.Reflection;

namespace NETCF.Callback
{
/// <summary>
/// Class that provides synchronous callback functionality.
/// </summary>
public class Callback
{
/// <summary>
/// Reference to function (in C DLL) that maps function pointers to
hWnd values.
/// </summary>
/// <param name="hWnd">Handle of MessageWindow that contains the
hWnd.</param>
/// <returns>Address of function pointer to be used for a callback
function.</returns>
[DllImport("NETCFCallBack", EntryPoint="Register")]
private static extern IntPtr _register( IntPtr hWnd );

/// <summary>
/// IntPtr that contains the address of the pointer function.
/// </summary>
private IntPtr m_pFunction = IntPtr.Zero;

/// <summary>
/// Object that contains the method to be called.
/// </summary>
private object m_object;

/// <summary>
/// String name of the method to be called.
/// </summary>
private string m_method;

/// <summary>
/// Reference to MessageWindow that handles the SendMessage
contents.
/// </summary>
private CallbackWindow m_cbWindow;

#region Properties
/// <summary>
/// Address of function to be used as a callback.
/// </summary>
public IntPtr pFunction
{
get
{
if ( m_pFunction == IntPtr.Zero )
{
m_pFunction = _register( this.m_cbWindow.Hwnd );
}
return m_pFunction;
}
}
public IntPtr CB
{
get
{
return pFunction;
}
}
#endregion

/// <summary>
/// Parametrized constructor that generates the function pointer as
well.
/// </summary>
/// <param name="o">Object that contains the method to be
called.</param>
/// <param name="m">String name of the method to be called.</param>
public Callback( object o, string m )
{
m_object = o;
m_method = m;

m_cbWindow = new CallbackWindow( this );

// Ensure that the pointer is generated.
IntPtr hWnd = this.pFunction;
}


/// <summary>
/// Routine for handling calling the function designated by <Object,
String> pairing, from data in LPARAM value.
/// </summary>
/// <param name="m">Message whose LPARAM contains a pointer to the
stack memory to be used in the function invocation.</param>
private void MessageCallback( ref Message m )
{
MethodInfo mi = this.m_object.GetType().GetMethod( this.m_method,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
);

int NumArgs = mi.GetParameters().Length;

object[] ParamArray = new object[NumArgs];
int i = 0;
for ( i = 0; i < NumArgs; i ++ )
{
int p = Marshal.ReadInt32( m.LParam, i * 4 );
ParameterInfo pi = mi.GetParameters();
if ( pi.ParameterType == typeof(IntPtr) )
{
ParamArray = new IntPtr( p );
}
else
{
ParamArray = Convert.ChangeType(p, pi.ParameterType, null);
}
}

object o = mi.Invoke( this.m_object, ParamArray );
int val;
if ( o == null )
{
val = 0;
}
else if ( o.GetType() == typeof( bool ) )
{
val = (bool) o ? -1:0;
}
else if ( o.GetType() == typeof(void) )
{
val = 0;
}
else
{
val = (int) o;
}
m.Result = new IntPtr(val);
}


/// <summary>
/// Static method for obtaining a callback object.
/// </summary>
/// <param name="o">Object that contains the method to be
called.</param>
/// <param name="m">String name of the method to be called.</param>
/// <returns></returns>
public static Callback Allocate ( object o, string m )
{
return new Callback( o, m );
}


/// <summary>
/// Class implements a MessageWindow that handles processing of
WM_CALLBACK messages.
/// </summary>
public class CallbackWindow : MessageWindow
{
const int WM_CALLBACK = 1025;
private Callback m_cbContainer;

public CallbackWindow( Callback cb )
{
m_cbContainer = cb;
}


protected override void WndProc( ref Message m )
{
switch( m.Msg )
{
case WM_CALLBACK:
m_cbContainer.MessageCallback( ref m );
break;

default:
base.WndProc( ref m );
break;
}

}
}

}

/// <summary>
/// Factory that enables creation of Callbacks and IntPtrs to address
of functions.
/// </summary>
public class CallbackFactory
{
public const int MAX_CALLBACK = 50;

private static Callback[] m_Callbacks = new Callback[ MAX_CALLBACK
];
private static int m_CallbackNumber;

/// <summary>
/// Static function to enable simple access to a callback object.
/// </summary>
/// <param name="o"></param>
/// <param name="m"></param>
/// <returns></returns>
public static Callback AllocateCallback( object o, string m )
{
return Callback.Allocate( o, m );
}

/// <summary>
/// Static function to enable simple access to a function pointer.
/// </summary>
/// <param name="o">Object containing callback function.</param>
/// <param name="m">Name of method to be called in object.</param>
/// <returns></returns>
public static IntPtr pFunction ( object o, string m )
{
// we should stack up the callbacks
m_Callbacks[ m_CallbackNumber ] = new Callback( o, m );
m_CallbackNumber ++;

return m_Callbacks[ m_CallbackNumber - 1 ].pFunction;
}
}

}
</C#>

<NETCFCallback.CPP>
// NETCFCallBack.cpp : Defines the entry point for the DLL
application.
//

#include "stdafx.h"
#include "NETCFCallBack.h"

// This is an example of an exported variable
NETCFCALLBACK_API int nNETCFCallBack=0;
HWND m_hWnd[ MAX_CALLBACKS ];
int m_nCallBacks = 0;
NETCF_INVOKE (m_pfInvokes)[ MAX_CALLBACKS ];

extern "C"
_declspec(dllexport) int Invoke ( HWND hWnd, byte pParams[] )
{
WPARAM wParam = (WPARAM) 0;
LPARAM lParam = (LPARAM) &pParams;
int nRetVal = SendMessage( hWnd, WM_CALLBACK, wParam, lParam );

return nRetVal;
}

extern "C" _declspec(dllexport) int Register ( HWND hWnd )
{
//TODO - support de register... check for multiple calls to same HWND
.... Do I care?

m_hWnd[m_nCallBacks] = hWnd;
m_pfInvokes[m_nCallBacks];
m_nCallBacks ++;

return (int) m_pfInvokes[m_nCallBacks - 1];
}

//Begin the frivolity...
NETCF_CBINVOKE(0);
NETCF_CBINVOKE(1);
NETCF_CBINVOKE(2);
NETCF_CBINVOKE(3);
NETCF_CBINVOKE(4);
NETCF_CBINVOKE(5);
NETCF_CBINVOKE(6);
NETCF_CBINVOKE(7);
NETCF_CBINVOKE(8);
NETCF_CBINVOKE(9);
NETCF_CBINVOKE(10);
NETCF_CBINVOKE(11);
NETCF_CBINVOKE(12);
NETCF_CBINVOKE(13);
NETCF_CBINVOKE(14);
NETCF_CBINVOKE(15);
NETCF_CBINVOKE(16);
NETCF_CBINVOKE(17);
NETCF_CBINVOKE(18);
NETCF_CBINVOKE(19);
NETCF_CBINVOKE(20);
NETCF_CBINVOKE(21);
NETCF_CBINVOKE(22);
NETCF_CBINVOKE(23);
NETCF_CBINVOKE(24);
NETCF_CBINVOKE(25);
NETCF_CBINVOKE(26);
NETCF_CBINVOKE(27);
NETCF_CBINVOKE(28);
NETCF_CBINVOKE(29);
NETCF_CBINVOKE(30);
NETCF_CBINVOKE(31);
NETCF_CBINVOKE(32);
NETCF_CBINVOKE(33);
NETCF_CBINVOKE(34);
NETCF_CBINVOKE(35);
NETCF_CBINVOKE(36);
NETCF_CBINVOKE(37);
NETCF_CBINVOKE(38);
NETCF_CBINVOKE(39);
NETCF_CBINVOKE(40);
NETCF_CBINVOKE(41);
NETCF_CBINVOKE(42);
NETCF_CBINVOKE(43);
NETCF_CBINVOKE(44);
NETCF_CBINVOKE(45);
NETCF_CBINVOKE(46);
NETCF_CBINVOKE(47);
NETCF_CBINVOKE(48);
NETCF_CBINVOKE(49);
//End the frivolity...


BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
//Begin the frivolity...
NETCF_CBASSIGN(0);
NETCF_CBASSIGN(1);
NETCF_CBASSIGN(2);
NETCF_CBASSIGN(3);
NETCF_CBASSIGN(4);
NETCF_CBASSIGN(5);
NETCF_CBASSIGN(6);
NETCF_CBASSIGN(7);
NETCF_CBASSIGN(8);
NETCF_CBASSIGN(9);
NETCF_CBASSIGN(0);
NETCF_CBASSIGN(11);
NETCF_CBASSIGN(12);
NETCF_CBASSIGN(13);
NETCF_CBASSIGN(14);
NETCF_CBASSIGN(15);
NETCF_CBASSIGN(16);
NETCF_CBASSIGN(17);
NETCF_CBASSIGN(18);
NETCF_CBASSIGN(19);
NETCF_CBASSIGN(20);
NETCF_CBASSIGN(21);
NETCF_CBASSIGN(22);
NETCF_CBASSIGN(23);
NETCF_CBASSIGN(24);
NETCF_CBASSIGN(25);
NETCF_CBASSIGN(26);
NETCF_CBASSIGN(27);
NETCF_CBASSIGN(28);
NETCF_CBASSIGN(29);
NETCF_CBASSIGN(30);
NETCF_CBASSIGN(31);
NETCF_CBASSIGN(32);
NETCF_CBASSIGN(33);
NETCF_CBASSIGN(34);
NETCF_CBASSIGN(35);
NETCF_CBASSIGN(36);
NETCF_CBASSIGN(37);
NETCF_CBASSIGN(38);
NETCF_CBASSIGN(39);
NETCF_CBASSIGN(40);
NETCF_CBASSIGN(41);
NETCF_CBASSIGN(42);
NETCF_CBASSIGN(43);
NETCF_CBASSIGN(44);
NETCF_CBASSIGN(45);
NETCF_CBASSIGN(46);
NETCF_CBASSIGN(47);
NETCF_CBASSIGN(48);
NETCF_CBASSIGN(49);
//End the frivolity...
break;

case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
</NETCFCallback.CPP>

<NETCFCallback.H>

// The following ifdef block is the standard way of creating macros
which make exporting
// from a DLL simpler. All files within this DLL are compiled with the
NETCFCALLBACK_EXPORTS
// symbol defined on the command line. this symbol should not be
defined on any project
// that uses this DLL. This way any other project whose source files
include this file see
// NETCFCALLBACK_API functions as being imported from a DLL, wheras
this DLL sees symbols
// defined with this macro as being exported.
#ifdef NETCFCALLBACK_EXPORTS
#define NETCFCALLBACK_API __declspec(dllexport)
#else
#define NETCFCALLBACK_API __declspec(dllimport)
#endif

#define WM_CALLBACK 1025
#define MAX_CALLBACKS 50

// This class is exported from the NETCFCallBack.dll
class NETCFCALLBACK_API CNETCFCallBack {
public:
CNETCFCallBack(void);
// TODO: add your methods here.
};

extern NETCFCALLBACK_API int nNETCFCallBack;

extern "C"
_declspec(dllexport) int fnNETCFCallBack( HWND hWnd );

typedef int (*NETCF_INVOKE) (byte pParam[]);
typedef void (*NETCF_MARKER) (void);

extern "C" _declspec(dllexport) int Invoke ( HWND hWnd, byte pParams[]
);

#define NETCF_CBINVOKE(n) int CBInvoke##n( byte pParams[] ) { return
Invoke( m_hWnd[##n], pParams ); }
#define NETCF_CBASSIGN(n) m_pfInvokes[##n] = CBInvoke##n;

</NETCFCallback.H>

-
Alex Yakhnin said:
Check out this article:

http://msdn.microsoft.com/library/d...dnnetcomp/html/AsynchCallbacks.asp?frame=true

--
Alex Yakhnin, .NET CF MVP
www.intelliprog.com | www.opennetcf.org
My CF Blog: http://blog.opennetcf.org/ayakhnin


----- Kevin Hutchison wrote: -----

Alex Feinman (http://www.alexfeinman.com/Callbacks/Callbacks.htm)
has an implementation of of callback functions, using the MessageWindow
class.

Alternatively, you could write your own C++ implementation using messages
and wrap the callback.

Other than that, we appear to be out of luck!

- K.


Luciano said:
I created a DLL in Visual Embedded C++ 3.0, to use with .NET. It works with
callback functions.
But, on the running occurs the exception 'NotSupported'. The CF don't
support callback functions ?
What can I do ?
Thanx for all. Sorry about my english, isn't very good. Luciano.
MyDLL.CPP
=========
#include "stdafx.h"
#include "MyDLL.h"
typedef int (__stdcall * FunctionCallback)( int i );
extern "C" __declspec(dllexport) int __stdcall FunctionDLL(
FunctionCallback
pFunc ) {
return pFunc( 123 );
}
TestDLL.CS
==========
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System.Text;
namespace TestDLL
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.MainMenu mainMenu1;
public Form1() {
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.button1 = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.button1.Location = new System.Drawing.Point(8, 8);
this.button1.Text = "button1";
this.button1.Click += new
System.EventHandler(this.button1_Click);
this.label1.Location = new System.Drawing.Point(16, 64);
this.label1.Size = new System.Drawing.Size(216, 80);
this.label1.Text = "label1";
this.Controls.Add(this.label1);
this.Controls.Add(this.button1);
this.Menu = this.mainMenu1;
this.Text = "Form1";
}
#endregion
// typedef int (__stdcall * FunctionCallback)( int i );
public delegate int FunctionCallback( int i );
[DllImport("DLL.DLL")]
public static extern int FunctionDLL( FunctionCallback pFunc );
public static void Main()
{
Application.Run(new Form1());
}
public delegate int DelegateFunction( int iVal );
public int MyFunction( int iVal ) {
return iVal + 456;
}
private void button1_Click(object sender, System.EventArgs e)
{
int iRet;
FunctionCallback pFunc = new FunctionCallback( MyFunction );
try
{
iRet = FunctionDLL( pFunc ); // <<<<<<<<<> ExceptionNotSupported
}
catch( NotSupportedException E )
{
label1.Text = E.Message;
return;
}
}
}
}
 
Back
Top