Coffeehouse Thread

6 posts

Marshal.GetFunctionPointerForDelegate

Back to Forum: Coffeehouse
  • User profile image
    BitFlipper

    I just want to give a shout out to Marshal.GetFunctionPointerForDelegate. Oh and DllImport while I'm at it...

    I've been working on one of my company's publicly released C APIs, adding new functionality. This new functionality includes adding a callback function (this C API didn't have any callbacks previously). My own local test code for the API is a C# project, which works great (I can step right into the C code from the C# wrapper code for instance). I used GetFunctionPointerForDelegate for the method I want the C code to call back into my C# code, all works great.

    However our QA dept uses a Java framework for testing purposes. I was tasked to update our JNI code to support the new functionality. Ouch, my head hurts. I mean really? I think this guy said it best.

    For one thing, the JNIEnv function that is supposed to return the method ID refuses to return anything other than 0 no matter how many times I checked and re-checked my code using dozens of examples from online searches.

    People complain about MS all the time but then also refuse to give them credit when they do something right. And yes .Net potentially makes us "lazy" or makes us "stupid" by hiding the complexities, but I'd rather spend my time solving the real problems at the top than wasting valuable time mucking around with arcane details at the bottom.

    Yes I know about JNA but I can't use that since it isn't part of our QA framework and supposedly it could be "ten times slower than hand-written JNI code".

    BTW I also found out that using arrays with JNI could be slow because Java arrays (even arrays of value types) isn't blittable because the array can be non-contiguous in memory. So in your JNI code you have to call JNIEnv::GetByteArrayElements() which returns a pointer to the array, and then JNIEnv::ReleaseByteArrayElements() when you are done. What that really does is allocate a new block of contiguous memory, copies the array over to it, and then afterwards copies that back to the managed array in the release function.

  • User profile image
    evildictait​or

    Just a shout-out for a common gotcha:

    .NET disposes the function pointer when the delegate goes out of scope. Be really careful that your delegate remains in scope for the duration of time that the unmanaged code can call back - otherwise you'll potentially end up with a call to invalid memory triggering a process crash.

    You might want to also think carefully about what this means if an exception is thrown between when you register the callback and the callback occurs - exceptions have a habit of causing large numbers of objects to go out of scope as you go up the callstack.

    You may have to use a try...finally pattern to "unregister" the callback, or wrap the callback in a class with a finalizer to cope with these eventualities.

  • User profile image
    BitFlipper

    Yes the delegate being GC'd is an issue that is resolved with two lines of code. I ran into that a few times and it wasn't clear right away what the problem was. However once you figure it out it is easy to ensure it isn't GC'd. That's about as complex as PInvoke gets (ok and maybe resolving a mismatched calling convention). Compare to JNI.

  • User profile image
    IDWMaster

    Definitely try to implement some sort of "com-like" reference counting mechanism when passing objects to and from unmanaged code. You never know when the GC might collect your delegates, and you're stuck with bad pointers. Can really be hard to diagnose without a library like SysLibs. I usually have a class used for interop with native code and store a Dictionary<long,object> to keep objects alive. When unmanaged code is done using it, simply remove the object from the dictionary (unmanaged code must know the long corresponding to the object). This approach has worked very well when interoperating with C-C++-Google V8 for me, and I'm sure the same approach could also work for Java interop as well.

  • User profile image
    BitFlipper

    @IDWMaster: Yes I actually also have used similar mechanisms. I guess my main point was just that I'm surprised how hard Java makes it to do this stuff.

    I think the reason there is such a day/night difference between PInvoke and JNI is that when MS released .Net, they knew it lacked a lot of functionality that Win32 provided, so they simply had to make this stuff work properly. Java doesn't sit on top of a legacy C API and therefore JNI is just an afterthought.

  • User profile image
    Sven Groot

    , BitFlipper wrote

    I think the reason there is such a day/night difference between PInvoke and JNI is that when MS released .Net, they knew it lacked a lot of functionality that Win32 provided, so they simply had to make this stuff work properly. Java doesn't sit on top of a legacy C API and therefore JNI is just an afterthought.

    I think it's more that Java was designed with the pipe dream of true cross-platform functionality. Being able to call into platform-dependent native libraries is contrary to that goal. By contrast, .Net was primarily designed to only run on Windows; sure the core ECMA CLI is platform independent but that was never the main goal.

Comments closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.