tags:

views:

1284

answers:

5

Hi, I have a C# Windows service application that passes a callback function pointer to a C++ dll. I have defined both the function pointer at the C# side and C++ side to be __stdcall type. Everything works fine till the callback is triggered by the C++ dll and throws an Unhandled Exception Access Violation in 0x04cb0e. The debug stops in the threadex.c under the endthread call in the C# application.

public delegate void NotificationFunc(int notifycode, IntPtr Userdata);

[DllImport("notice.dll")]
void INotify(NotificationFunc notefunc,IntPtr Userdata);//ignore the IntPtr Userdata
.
.
.
NotificationFunc notefunc = new NotificationFunc(Noticallback);
INotify(notefunc, Intptr.zero);

//notice.dll triggers this callback thru the delegate passed in
void Noticallback(int notifycode, IntPtr userdata)
{
Swtich(notifycode)
{
//my actions
}
.
.//Error Exceptions happens here when trying to end the thread/call
}

I know i have to handle the clean up this callback resources as it is a one way call event. I have tried GC & GCHandle to prevent it from being GC but it seems there is always a memory leak or error.Can anyone help? Thanks

+1  A: 

Wikipedia has a fairly decent article on the topic. If I were you, I would use C++/CLI as it's faster and not so strongly bound. This sort of P/Invoke, where you layout all the definitions all over the place, is so error prone. And if you ever change, or if the spec changes on you, for your data types or something like that, C++/CLI will automatically be updated, but unfortunately any explicit P/Invoke's declerations as you have here, will always have to be re-wired.

RandomNickName42
A: 

You must prevent the delegate from being collected by the managed code, using GCHandle.Alloc:

    public delegate void NotificationFunc(int notifycode, IntPtr Userdata);

    [DllImport("notice.dll")]
    static extern void INotify(NotificationFunc notefunc,IntPtr Userdata); // Note IntPtr as the callback type


    NotificationFunc notefunc = new NotificationFunc(Noticallback);

    // Now, allocate a GCHandle to prevent the delegate from being collected
    GCHandle handle = GCHandle.Alloc(notefunc);

    INotify(notefunc, Intptr.Zero);

    // Free the handle when it's no longer needed
    handle.Free();
Bojan Resnik
A: 

I do not have the codes as it is provided to me in dll by another source. When i use GCHandle to free the delegate there will be some memory leak during compilation. Is there any way to end this delegate callback safely before it trys to endthread?

I'm not sure I understand - as long as you call handle.Free() there should be no memory leak. The freeing does not 'end' the callback - it merely allows the garbage collector to collect the delegate.
Bojan Resnik
A: 

Hi,

Thanks for the reply. This is what i have tried. From what i understand from the C++ dll source, It is calling the same callback reference through the whole of my window service application. I am pretty sure they have changed the calling convention as i get a different declaration of function ptr when the _cdecl calling convention was used by the C++ dll. Here i have declared a singleton of the delegate and GChandle to have a reference to the particular address location of the delegate.

//Declaration of the delegate and C++ Dll
public delegate void NotifyFunc(NotifydataStruct notifydata, IntPtr User);

[DllImport("Notify.dll")]
public static extern int RegisterNotifyfunc(NotifyFunc pFunc, IntPtr pUser);


//Single delegate instance declared
private static NotifyFunc notifyfunc = null;


//Function to get the only private instance of the class
public static NotifyFunc getNotifyDelegate
{
    get
   {
         if(notifyfunc==null)
        {
           notifyfunc = new NotifyFunc(CallbackFunc);
        }
        return notifyfunc;
   }
}

//codes called in my init() when i start my C# window service to register the callback

NotifyFunc notifyFunc = getNotifyDelegate; //gets the only delegate in the Class

handle=GCHandle.Alloc(notifyFunc,GCHandleType.Normal);//get a reference to the instance

IntPtr tmpIntPtr = IntPtr.Zero;//Ignore Just a requirement from the C++ Dll
RegisterNotifyfunc(notifyfunc, tmpIntPtr);//register callback delegate in C++ Dll 

//Callback triggered by Unmanaged C++ dll
public static void CallbackFunc(NotifydataStruct notifydata, IntPtr User)
{
 //Do work 
}

When the C++ dll trigger the CallbackFunc(), it runs smoothly till it returns.It always throws " Unhandled exception at 0x003cb8a0 in Server.exe: 0xC0000005: Access violation writing location 0x00000000" and always breaks in the threadex.c in the line below.

 __try {
                _endthreadex (
                    ( (unsigned (WINAPI *)(void *))(((_ptiddata)ptd)->_initaddr) )
                    ( ((_ptiddata)ptd)->_initarg ) ) ;
        }
A: 

Thank you to all that help to provide me clues to the problem.

I have found the problem not in the return of the callback from the C++ dll. In my callback, CallbackFunc(NotifydataStruct notifydata, IntPtr User), the IntPtr User is returned pointing to an invalid memory location 0x003cb8a0 which is unassigned or uninitialized.

Anyone have any clue to get hold of this Intptr before it trys to access the particular block of mem location?