callback on a garbage collected delegate error

G

Guest

Hi,

I have an application using a DLL and callbacks. It generate random the
error "A callback was made on a garbage collected delegate".

I found some articles that the pointer to the delegate has to have a
lifetime reference so that it is not garbage collected. But I could not find
any example how to do this.

Can someone help here ?
 
N

Nicholas Paldino [.NET/C# MVP]

Wilfried,

Just assign the delegate reference to a field in a class that has the
same lifetime as the callback. So if you have a class instance which will
receive the callback, then assign the delegate to a field on the class.

Hope this helps.
 
G

Guest

Hi,

I understeand but I dont understeand how and where.

I have following:

public class Api
{
public Api(Control control)
{
owner = control;
ApiDll.Start();
ApiDll.SetLoggedOn(cbLoggedOn);
// cb... is a callback function in this class
// Set LoggedOn is the procedure to place the pointer in the DLL
// etc...

Then in main form:

public partial class Main : Form
{
private Api api;

public Main()
{
InitializeComponent();
api = new Api(this);
api.OnLoggedOn += api_OnLoggedOn;
// etc..

then in the DLL declaration:

public static class ApiDll
{
private const string dllName = "SmsComfortAPI.dll";

public delegate void OnLoggedOn();
// etc...

[DllImport(dllName)]
public static extern void SetLoggedOn(OnLoggedOn cbLoggedOn);
// etc...


Where and how should I declare the lifetime inctance ?
 
N

Nicholas Paldino [.NET/C# MVP]

Wilfried,

Where are you getting cb from? If it is a member in your Api class,
then the problem is that the class is being collected (not just the
delegate).

You need to call a function to unregister the callback so that it
doesn't try and call your function back. Either that, or you need to have a
static method and pass a delegate to that, and then register with the class
for the callback.
 
G

Guest

Hi Nicholas,
Where are you getting cb from? If it is a member in your Api class,
then the problem is that the class is being collected (not just the
delegate).

the cb... functions are indeed a members of the Api class.
You need to call a function to unregister the callback so that it
doesn't try and call your function back. Either that, or you need to have a
static method and pass a delegate to that, and then register with the class
for the callback.

I dont understeand. English is not my native language eather. How do I make
a function to uregister it ? But if it is unregistered then how will the
callback works ?

The project load a DLL. DLL communicate with a server trough a TCP session.
The callback is needed to get data from server. So I dont understeand the
unregistering...

Should I make a simple project to demonstrate and publish it for download ?
 
N

Nicholas Paldino [.NET/C# MVP]

Wilifried,

You have an API function SetLoggedOn, then it is storing in that dll the
address of the callback function. When someone logs on, it is calling the
callback function.

You have to tell it to stop calling that callback, since your instance
goes out of scope.

What I think you should do is make this event static, and then make your
delegate to a static method.
 
G

Guest

Hi Nicholas,
You have an API function SetLoggedOn, then it is storing in that dll the
address of the callback function. When someone logs on, it is calling the
callback function.

SetLoggedOn installs the callback pointer into the DLL. The DLL calls back
to the pointer when the application is logged on to the server.
You have to tell it to stop calling that callback, since your instance
goes out of scope.

But the DLL needs the callback and other callback for many other events. The
DLL comunicate with a server and data can arrive.
What I think you should do is make this event static, and then make your
delegate to a static method.

I think I tryed already to make the procedure static with same result. But I
tryed in the last hours so many things I'm not sure anymore (including
banging my head on keyboard and monitore)...
 
W

Willy Denoyette [MVP]

message | Hi Nicholas,
|
| > You have an API function SetLoggedOn, then it is storing in that dll
the
| > address of the callback function. When someone logs on, it is calling
the
| > callback function.
|
| SetLoggedOn installs the callback pointer into the DLL. The DLL calls back
| to the pointer when the application is logged on to the server.
|
| > You have to tell it to stop calling that callback, since your
instance
| > goes out of scope.
|
| But the DLL needs the callback and other callback for many other events.
The
| DLL comunicate with a server and data can arrive.
|
| > What I think you should do is make this event static, and then make
your
| > delegate to a static method.
|
| I think I tryed already to make the procedure static with same result. But
I
| tryed in the last hours so many things I'm not sure anymore (including
| banging my head on keyboard and monitore)...
|
| --
| rgds, Wilfried
| http://www.mestdagh.biz

We have to see how and where you create an instance of your delegate, so,
please post a short but complete repro that illustrates your issue.
incomplete code snips like you posted aren't of great help.


Willy.
 
G

Guest

Hi,

Yes you are right of course. I made a simple application to demonstrate my
problem. It is containing only the nececary. I also made source for download
if that is more easy on http://www.mestdagh.biz/kieken/CrashTest.zip The dll
I made for the demo is just calling the callback with the same data. this is
the complete project. Using the garbage collector I can let it reproduce the
error 1 on 2 times.

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace DllCrashDemo
{
public partial class Form1 : Form
{
private Api api;

public Form1()
{
InitializeComponent();
api = new Api(this);
api.OnShow += api_OnShow;
}

void api_OnShow(object sender, Api.ShowItArgs e)
{
listBox.Items.Add(e.text);
}

private void crashButton_Click(object sender, EventArgs e)
{
api.show("This is a crashtest");
}
}

public class Api
{
private Control owner;

public Api(Control control)
{
owner = control;
ApiDll.SetShow(cbShow);
}

public void show(string text)
{
ApiDll.ShowIt(text);
GC.Collect();
}

public class ShowItArgs : EventArgs
{
public string text;
}
public delegate void Show(object sender, ShowItArgs e);
public event Show OnShow;
private void cbShow(string text)
{
if (OnShow == null)
return;
ShowItArgs e = new ShowItArgs();
e.text = text;
owner.Invoke(OnShow, new object[] { this, e });
}
}

public static class ApiDll
{
private const string crashDllName = @"C:\Program
Files\Borland\Delphi7\Projects\CrashDll.dll";

public delegate void OnShow([MarshalAs(UnmanagedType.LPStr)]string
txt);

[DllImport(crashDllName)]
public static extern void
ShowIt([MarshalAs(UnmanagedType.LPStr)]string txt);
[DllImport(crashDllName)]
public static extern void SetShow(OnShow cbShow);
}
}
 
J

Jon Skeet [C# MVP]

Wilfried Mestdagh said:
Yes you are right of course. I made a simple application to demonstrate my
problem. It is containing only the nececary. I also made source for download
if that is more easy on http://www.mestdagh.biz/kieken/CrashTest.zip The dll
I made for the demo is just calling the callback with the same data. this is
the complete project. Using the garbage collector I can let it reproduce the
error 1 on 2 times.

Ah - I don't know whether it's because I missed something before or
not, but I hadn't realised you were talking about unmanaged code
calling you back. That explains why the garbage collector was able to
collect the delegate in the first place.

There are various ways you could handle this, but the main thing is
that you'll need to keep a reference to the delegate within managed
code.
 
G

Guest

Hi,

I understeand. Only I'm not sure how to do it. I'm trying to assign a
pointer to the callback method in unsafe code, but I miss the syntax
somewhere I think.

Can you give example ?
 
J

Jon Skeet [C# MVP]

Wilfried Mestdagh said:
I understeand. Only I'm not sure how to do it. I'm trying to assign a
pointer to the callback method in unsafe code, but I miss the syntax
somewhere I think.

Can you give example ?

Here's one alternative implementation of ApiDll:

public static class ApiDll
{
private const string crashDllName = @"C:\Program
Files\Borland\Delphi7\Projects\CrashDll.dll";

public delegate void OnShow([MarshalAs(UnmanagedType.LPStr)]
string
txt);

private static OnShow showDelegate;

[DllImport(crashDllName)]
public static extern void
ShowIt([MarshalAs(UnmanagedType.LPStr)]string txt);
[DllImport(crashDllName)]
private static extern void SetShow(OnShow cbShow);

public static void SetShowDelegate (OnShow cbShow)
{
showDelegate = cbShow;
SetShow(cbShow);
}
}

Now, that's fine so long as you only ever want one delegate registered
at a time - the static variable will prevent the delegate from being
garbage collected. Are you okay with that limitation?
 
G

Guest

Hi,

this works great. Thank you :) I was already trying to make a class with a
void* to the callback function but did not found the right syntax. Possible
also a solution?
Now, that's fine so long as you only ever want one delegate registered
at a time - the static variable will prevent the delegate from being
garbage collected. Are you okay with that limitation?

Yes. There are several different callbacks, but each callback will only have
1 and the same callback function during the liftime of the application.

The same limitation is that there only may be 1 incstance of the Api class
right ? Maybe it is better technique to make the Api class also a static
class ?
 
J

Jon Skeet [C# MVP]

Wilfried Mestdagh said:
this works great. Thank you :) I was already trying to make a class with a
void* to the callback function but did not found the right syntax. Possible
also a solution?

I'm not sure whether you're asking a question there or not (or what it
is if you are). The equivalent to some uses of "void*" in .NET is just
to use "object" - if you want a reference to absolutely anything,
that's the type to use.
Yes. There are several different callbacks, but each callback will only have
1 and the same callback function during the liftime of the application.

The same limitation is that there only may be 1 incstance of the Api class
right ? Maybe it is better technique to make the Api class also a static
class ?

It sounds like it, yes. That makes me nervous in terms of OO design,
but I think it's really suggested by the restriction anyway.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top