0

I have a native (c++) function that takes an array of pointers to images as an argument. In my c# API a pointer to an image is kept as a SafeHandle. Now I want to send an array of SafeHandles to my function but then I get into the following error.

System.Runtime.InteropServices.MarshalDirectiveException: Cannot marshal 'parameter #2': Invalid managed/unmanaged type combination (Arrays of SafeHandles are not supported).

Simplified the code looks like this:

Native Side

int concatenate_images(Image **img_ref, Image **images)
{
 // Concatenate the images and put the result in img_ref[0]
}

Managed side

Note that the ImageHandle is derived from the SafeHandle

Image Concatenate(Image[] images)
{
 ImageHandle h = new ImageHandle(); 
 if (!concatenate_images(ref h, handles_array))
     throw new Exception("Failed concatenating images.");
 return new Image(h);
}

private static extern int concatenate_images(ref ImageHandle img_ref, [In, Out] ImageHandle [] images)

I can of course solve the problem by extracting the IntPtr from the SafeHandle and send an array of those objects to my native function. But as I understand it, then I would not have some of the extra safety provided by the SafeHande.

So, how should I send my object to my native function? Do I have to make an array of IntPtr or is there a better way?

AndersG
  • 81
  • 7
  • See following for how to pass arrays to your native code : https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-different-types-of-arrays – jdweng Oct 27 '20 at 10:16
  • @Anders `then I would not have some of the extra safety provided by the SafeHande` - actually, you won't lose anything. `SafeHandle` has special mechanisms to make sure a native handle does not leak during acquisition (when the handle has been generated by the native code but hasn't been stored in the managed memory) and release (when SafeHandle is destructed). It cannot and does not protect from things happening to the object represented by the handle during its lifetime. If you expose the handle value to native code, by IntPtr or not, there is no protection, by design. – GSerg Oct 27 '20 at 10:33
  • The c# code calls the window dll to create a safe handle. What do you think the dll actually does? A lot of people only understand what the application layer does and not what the lower level code actually does. The safe handle adds exceptions so that when memory is accessed outside a range and exception will occur instead of your computer going blue screen. – jdweng Oct 27 '20 at 10:40
  • @jdweng It would appear it is you who misunderstand the mechanism completely. Windows dlls have no idea what a `SafeHandle` is. They create [`HANDLE`s](https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types#handle) which is just a pointer sized number. The C# compiler knows to handle the `SafeHandle` type in a special way. When you use it in a p/invoke declaration, the compiler injects code that creates a `SafeHandle` instance that acquires the native handle returned by the API. `SafeHandle` is not concerned with accessing memory outside of allocated ranges in any way. – GSerg Oct 27 '20 at 10:46
  • @GSerg : What do you think the word SAFE means? Creates an exception if you access memory outside your allocated memory space. – jdweng Oct 27 '20 at 10:55
  • @jdweng I don't know where you get the misinformation that you [regularly spread](https://meta.stackoverflow.com/q/392514/11683), but please stop. The word SAFE means that `SafeHandle` makes sure that a native handle does not leak. `SafeHandle` has absolutely nothing to do with accessing memory outside of allowed ranges. – GSerg Oct 27 '20 at 11:00
  • @GSerg : What do you think leak means then? – jdweng Oct 27 '20 at 11:10
  • @jdweng "Leaked" means "unaccounted for". It means that the handle is not going to be released anymore, because its numeric value has been lost in one way or another, thus holding the resource forever, until destroyed by the OS when the entire process terminates. It is hard to correctly code the transition of a native `HANDLE` to managed memory because an exception can occur after it is generated on the native side, but before it is stored on the C# side. `SafeHandle` implements this complicated mechanism correctly so that you don't have to. – GSerg Oct 27 '20 at 11:15
  • The safety that Im looking for is that the GC or some other thread should not be able to Dispose the handle while it is being used. As I understand it, the SafeHandle provides that. If I extract a set of IntPtr I cannot be sure that will not happen. – AndersG Oct 27 '20 at 13:20
  • @AndersG Which is why [`GC.KeepAlive`](https://stackoverflow.com/q/20098498/11683) exists. You should not need it though because you are supposed to be [`using`](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement) your `SafeHandle`s, and `using` also holds a reference. – GSerg Oct 27 '20 at 13:45

0 Answers0