callbacks

  • Thread starter Thread starter WAkthar
  • Start date Start date
W

WAkthar

Hi,

can anyone show me how to be able to pass the name of method to a dll
function which will call this method when something inside the dll calling
function happens.
Is this called a callback?

Are there any websites that show how to implement something like this
easily??
 
Yes this is called a callback. You must just remember to "lock" your method
as "unsafe". If you do not so this the garbage collector might move your
methods' code around at some stage, and the DLL might call the "old" address
of your function ... which might not be the correct address ... causing some
problems, with difficult debugging!
 
Nico Gerber said:
Yes this is called a callback. You must just remember to "lock" your
method as "unsafe". If you do not so this the garbage collector might move
your methods' code around at some stage, and the DLL might call the "old"
address of your function ... which might not be the correct address ...
causing some problems, with difficult debugging!

This is not true, code never moves, the GC only moves objects in the GC
heap, code is not store in the GC heap!

Willy.
 
Guys guys...
The guy is looking for code examples...

I hope this will clear some stuff:

On your Cpp side:

You need to define a function pointer such as:
typedef void (__stdcall *LPFN_MANAGED_CALLBACK)(unsigned int x,unsigned
int y);

** don't forget the __stdcall

and a method to receive the EventHandler from the Managed side such as:
void MyCppMethod(LPFN_MANAGED_CALLBACK lpfnManagedCallback)
{
// To call the callback do:
lpfnManagedCallback(10 /*x value*/, 20 /*y value*/);
}


On the C# side:
1) you should define a delegate such as:
public delegate void PointEventHandler(uint x, uint y);

2) a member to hold the PointEventHandler so that it won't be
collected:
private PointEventHandler m_pointCallback;

3) a p/Invoke for the Cpp method:
[DllImport("CppDll.dll", CallingConvention=CallingConvention.StdCall)]
extern private static void CppMethod(PointEventHandler pointCB);

4) you need to create a method to handle the Cpp call:
private void CppCallbackHandler(uint x, uint y)
{
Console.WriteLine("Received a call from Cpp with X={0} & y={1}", x,
y);
}

5) you need to create the callback:
m_pointCallback = new PointEventHandler(CppCallbackHandler);

6) and now you can pass the callback to Cpp side:
CppMethod(m_pointCallback);

Cheers,
Eyal Safran
 
Thanks Eyal,

My scenario is a little different. There are two people involved in the
project.

I am writing the UI part and my collegue is writing a dll which will
communicate with teh serial port.

What we have is the ability for me to call functions that communicate with
the serial port. Now when certain events happen at the serial port, I need
to be notified.
To implement is I thought about using delegates as callbacks. The dll is
also written in C#.

When I create an object from the dll, I pass the function pointer (delegate)
to it. When an event happens on the serial port, this delegate is called and
I can update the UI.

Can you show how I can implement something like this please??

Thanks in advance!
 
Hi,
When I create an object from the dll, I pass the function pointer (delegate)
to it. When an event happens on the serial port, this delegate is called and
I can update the UI.

Can you show how I can implement something like this please??

Thanks in advance!

well it's you lucky day, (I hope thats what you ment).
I already wrapped unmanaged code to interact with the serial port:

[Flags]
public enum CommEvents
{
/// <summary>
/// No events
/// </summary>
EV_NONE = 0x0000,
/// <summary>
/// Any Character received
/// </summary>
EV_RXCHAR = 0x0001,
/// <summary>
/// Received certain character
/// </summary>
EV_RXFLAG = 0x0002,
/// <summary>
/// Transmit Queue Empty
/// </summary>
EV_TXEMPTY = 0x0004,
/// <summary>
/// CTS changed state
/// </summary>
EV_CTS = 0x0008,
/// <summary>
/// DSR changed state
/// </summary>
EV_DSR = 0x0010,
/// <summary>
/// RLSD changed state
/// </summary>
EV_RLSD = 0x0020,
/// <summary>
/// BREAK received
/// </summary>
EV_BREAK = 0x0040,
/// <summary>
/// Line status error occurred
/// </summary>
EV_ERR = 0x0080,
/// <summary>
/// Ring signal detected
/// </summary>
EV_RING = 0x0100,
/// <summary>
/// Printer error occurred
/// </summary>
EV_PERR = 0x0200,
/// <summary>
/// Receive buffer is 80 percent full
/// </summary>
EV_RX80FULL = 0x0400,
/// <summary>
/// Provider specific event 1
/// </summary>
EV_EVENT1 = 0x0800,
/// <summary>
/// Provider specific event 2
/// </summary>
EV_EVENT2 = 0x1000
}

protected CommEvents commEvents = CommEvents.EV_NONE;
private EventHandlerList events;

protected EventHandlerList Events
{
get
{
if (events == null)
{
lock(this)
{
if (events == null)
{
events = new EventHandlerList();
}
}
}
return events;
}
}

// Event objects
private static readonly object EventCharacterReceived;
private static readonly object EventCertainCharacterReceived;
private static readonly object EventTransmitQueueEmpty;
private static readonly object EventCTSStateChanged;
private static readonly object EventDSRStateChanged;
private static readonly object EventRLSDStateChanged;
private static readonly object EventBreakReceived;
private static readonly object EventLineStatusErrorOccurred;
private static readonly object EventRingSignalDetected;
private static readonly object EventPrintErrorOccurred;
private static readonly object EventReceiveBufferIs80PercentFull;
private static readonly object EventProviderSpecificEvent1;
private static readonly object EventProviderSpecificEvent2;

static Port() // static constructor
{
EventCharacterReceived = new object();
EventCertainCharacterReceived = new object();
EventTransmitQueueEmpty = new object();
EventCTSStateChanged = new object();
EventDSRStateChanged = new object();
EventRLSDStateChanged = new object();
EventBreakReceived = new object();
EventLineStatusErrorOccurred = new object();
EventRingSignalDetected = new object();
EventPrintErrorOccurred = new object();
EventReceiveBufferIs80PercentFull = new object();
EventProviderSpecificEvent1 = new object();
EventProviderSpecificEvent2 = new object();
}

// example to an event "Character Received" you register on:
public event EventHandler CharacterReceived // Any Character received
{
add
{
if (Events[EventCharacterReceived] == null)
{
commEvents |= CommEvents.EV_RXCHAR;
SetEvents();
}
Events.AddHandler(EventCharacterReceived, value);
}
remove
{
Events.RemoveHandler(EventCharacterReceived, value);
if (Events[EventCharacterReceived] == null)
{
commEvents &= ~CommEvents.EV_RXCHAR;
SetEvents();
}
}
}

protected void SetEvents()
{
if (commEvents == CommEvents.EV_NONE)
{
return;
}
if (Trace.debug())
{
Trace.debug("Setting {0} communication port events:\n{1}", PortName,
commEvents);
}
bool result = _SetCommMask(hPort, (int)commEvents);
if (result == false)
{
throw new CommunicationException("Failed to SetCommMask (set the
events)");
}
}

[DllImport("kernel32.dll", EntryPoint="SetCommMask",
SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall)]
protected static extern bool _SetCommMask(
Int32 hFile,
int lpEvtMask);

[DllImport("kernel32.dll", EntryPoint="WaitCommEvent",
SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall)]
protected static extern bool _WaitCommEvent(
Int32 hFile,
ref CommEvents Mask,
IntPtr lpOverlap);

public void WaitForEvents()
{
if (commEvents == CommEvents.EV_NONE)
{
throw new CommunicationException("No events have been registered.");
}
CommEvents eventsReceived = CommEvents.EV_NONE;
bool result = _WaitCommEvent(hPort, ref eventsReceived, IntPtr.Zero);
if (result == false)
{
throw new CommunicationException("Failed Waiting for comm events.");
}
// CharacterReceived
object[] args = new object[]{this, EventArgs.Empty};
EventHandler invoker = null;
if ((eventsReceived & CommEvents.EV_RXCHAR) == CommEvents.EV_RXCHAR)
{
invoker = (EventHandler)Events[Port.EventCharacterReceived];
if ( invoker != null)
{
invoker.DynamicInvoke(args);
}
}
}


Remark:
In this code, the user registers on the events, the first registration
on each event adds a flag to the member: "commEvents".
After all the clients unregistered from an the event, the flag is
removed from: "commEvents".

The collection of events is sent to the RS-232 port.

when the WaitForEvents is called, the thread is waiting for any of the
registered events to occur.
When they do, the C# event of the corresponding RS-232 event is
triggered.

Eyal.
 
Back
Top