Posted By: PhrostByte | Nov 4th, 2004 @ 6:49 AM
page 1 of 1
Comments: 16 | Views: 6968
I'm attempting to P/Invoke a C library.  It requires me to pass a delegate which the library will store for later use.

Because I do not use this delegate later in managed code it gets collected.  Easy enough to stop, but pinning it in one memory location is another story.  Is this possible?
You could use GCHandle but possibly different info from Chris Brumme here

Chris Brumme wrote:

Finally, a word on pinning.  I often see applications that aggressively pin managed objects or managed delegates that have been passed to unmanaged code.  In many cases, the explicit pin is unnecessary.  It arises because the developer has confused the requirement of tracking an object instance via a handle with the requirement of keeping the bytes of that object at a fixed location in memory.

 

...The PInvoke layer is hooked into the CLR’s stack crawling mechanism for GC reporting.  So it can defer all overhead related to pinning unless a GC actually occurs while the PInvoke call is in progress.  Applications that explicitly pin buffers around PInvoke calls are often doing so unnecessarily.


PhrostByte wrote:
That is good advice if you are passing a delegate through one function.  In my case the library is storing it.
I am doing:
setdelegate(d); // p/invoked, stores the function pointer
somefunc() // p/invoked, can call function pointer.
the delegate can be moved before or during somefunc()'s execution.


Do you mean moved (in memory) or changed (to point to a different method) ?

If the former you did notice GCHandle.Alloc( myDelegate, GCHandleType.Pinned ) ? In the latter case I don't know - it seems a little unsafe.
PhrostByte wrote:

in memory.


GCHandle.Alloc( myDelegate, GCHandleType.Pinned ) ?

As in

GCHandle handle = GCHandle.Allow( d, GCHandleType.Pinned );
setdelegate(d); // p/invoked, stores the function pointer
somefunc() // p/invoked, can call function pointer.
handle.Free();

Edit: Free is not static, and should only be called once.
PhrostByte wrote:
Tried that, GCHandle.Alloc() throws an exception about non-blittable memory.



One of your delegate parameters probably does not have a common representation in the unmanaged language.  Just a long shot, do you have a bool parameter? Try byte instead.
Before I forget make sure you don't declare your delegate as a local variable .. as in DON'T do ..

public void myMethod(){    
CDelegate d = new CDelegate( someMethod ); SetCallback( d );   
DoSomethingThatWillTriggerTheCallback();
}
PhrostByte wrote:
Am I not able to use marshaling in a delegate?  It compiles fine.

delegate int ReportFilter(IntPtr doc, int lvl, uint line, uint col, [MarshalAs(UnmanagedType.LPStr)] string msg);

edit: still doesn't work when declaring msg as an IntPtr.



I am not sure that the MarshalAs is necessary. I'm just on my way home, if no-one else answers before then I'll look it up later.
PhrostByte wrote:
Probably not, but the exception is still being thrown Sad  Anyone have a solution?


I wrote a simple bit of code to replicate this and it works, or rather I can replicate either of the two problems you mentioned.  Without the GCHandle a NPE is thrown in the dllimport'ed function that calls the delegate from C (after it has actually done the callback), and with the handle I get ArgumentException thrown about non-blittable types.

Please forgive the ugly code ..

C code (updated) wrote:

#include <windows.h>

typedef void (*FuncPtr)( int i );

FuncPtr cb;

__declspec( dllexport )
void SetCallback( FuncPtr ptr )
{
    cb = ptr;
}


__declspec( dllexport )
void SomeFunc()
{
    if ( cb ) {
        cb( 10 );        
    }
    else {
        printf("Failed\n");
    }
}


C# wrote:

    class Class1
    {
        public delegate void MyDelegate( int i );

        MyDelegate deleg;
//        GCHandle handle;

        public Class1()
        {
            deleg = new MyDelegate( SomeCallback );

//            handle = GCHandle.Alloc( deleg, GCHandleType.Pinned );
            SetCallback( deleg );
        }

        // Calls into native code to fire the callback
        public void WorkerMethod()
        {
            SomeFunc();
        }

        public void Close()
        {
//            handle.Free();
        }

        [DllImport("nativecode.dll")]
        public static extern void SetCallback( MyDelegate d );

        [DllImport("nativecode.dll")]
        public static extern void SomeFunc();


        public static void SomeCallback( int i )
        {
            Console.WriteLine( "Callback " + i.ToString() );
        }
       

        [STAThread]
        static void Main(string[] args)
        {
            Class1 c = new Class1();
            c.WorkerMethod();
            c.Close();
        }
    }

Doh! Not only am I forgetting how to write C I can't even post here without messing it up now Smiley

Still no joy though Sad

Sorry I can't help.

Mmm. Fixed it.
Needed to compile DLL with /Gz so that it used stdcall ... now it's all funky.
page 1 of 1
Comments: 16 | Views: 6968
Microsoft Communities