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.
-
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.
Rossj wrote: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.
-
Rossj wrote:

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) ?
in memory. -
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.
-
Tried that, GCHandle.Alloc() throws an exception about non-blittable memory.
Rossj wrote:
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();
} -
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. -
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.
-
Probably not, but the exception is still being thrown
Anyone have a solution?
Rossj wrote:
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.
-
Declare your function pointer as typedef void (*FuncPtr)(int i);
Rossj wrote:
PhrostByte wrote: Probably not, but the exception is still being thrown
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 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 );
}
}

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();
}
}
-
undefined
-
PhrostByte wrote:Probably not, but the exception is still being thrown
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

Still no joy though
Sorry I can't help. -
Mmm. Fixed it.
Needed to compile DLL with /Gz so that it used stdcall ... now it's all funky.
Thread Closed
This thread is kinda stale and has been closed but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.