Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make only one method of array of any kind to IntPtr

Tags:

c#

Having a method to convert double[] d to IntPtr like:

public static IntPtr DoubleArrayToIntPtr(double[] d)
{
   IntPtr p = Marshal.AllocCoTaskMem(sizeof(double) * d.Length);
   Marshal.Copy(d, 0, p, d.Length);
   return p;
}

What would be the best way to make int[], float[], etc. I was thinking on making one method for each type like adding int[]:

public static IntPtr IntArrayToIntPtr(int[] d)
{
   IntPtr p = Marshal.AllocCoTaskMem(sizeof(int) * d.Length);
   Marshal.Copy(d, 0, p, d.Length);
   return p;
}

1. Could this method be generalized, if so how?

2. Is it possible to get pointer in only one line of code (As Marshal is void method)?

like image 400
edgarmtze Avatar asked Dec 16 '13 21:12

edgarmtze


2 Answers

The best approach would be to make an extension class that did this for you, for each type. You cannot make it generic as Marshal.Copy() does not support a generic type. So you will have to duplicate your method for each type byte, char, int, long, float, double, etc. However, you can make a generic helper method for determining your size to Marshsal.

Something like below could be useful to you:

public static class MarshalExtender
{
    public static IntPtr CopyToCoTaskMem(this byte[] array)
    {
        var ptr = AllocArrayCoTaskMem(array);

        Marshal.Copy(array, 0, ptr, array.Length);
        return ptr;
    }

    // Copy the above method and replace types as needed (int, double, etc).

    // Helper method for allocating bytes with generic arrays.
    static IntPtr AllocArrayCoTaskMem<T>(T[] array)
    {
        var type = typeof(T);
        var size = Marshal.SizeOf(type) * array.Length;
        return Marshal.AllocCoTaskMem(size);
    }
}

The main benefit is in your code you could easily do something like:

var myArray = { 0, 1, 2, 3, 4, 5, ... }
var ptr = myArray.CopyToCoTaskMem();

NOTE: Just be sure to call Marshal.FreeCoTaskMem() when you are done with it! I would recommend your extension method returning an IDisposable object so you can wrap it in a using block.

For example:

public sealed class CoTaskMemoryHandle : IDisposable
{
    bool isDisposed;
    readonly IntPtr handle;

    public IntPtr Handle { get { return handle; } }

    public CoTaskMemoryHandle(IntPtr handle)
    {
        this.handle = handle;
    }

    public void Dispose()
    {
        OnDispose(true);
        GC.SuppressFinalize(this);
    }

    void OnDispose(bool isDisposing)
    {
        if (isDisposed) return;

        if (isDisposing)
        {
            if (handle != IntPtr.Zero)
                Marshal.FreeCoTaskMem(handle);
        }

        isDisposed = true;
    }
}

Then your modified extension class method:

public static CoTaskMemoryHandle CopyToCoTaskMem(this byte[] array)
{
    var ptr = AllocArrayCoTaskMem(array);

    Marshal.Copy(array, 0, ptr, array.Length);
    return new CoTaskMemoryHandle(ptr);
}

This way you can safely encapsulate the Alloc/Free balancing:

using(myArray.CopyToCoTaskMem())
{
   // Do something here..
}
like image 117
Erik Avatar answered Nov 09 '22 05:11

Erik


To answer your first question, yes, it could be generalized:

public static IntPtr ArrayToIntPtr<T>(T[] d) where T : struct
{
     var arrayType = typeof(T);
     IntPtr p = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(T)) * d.Length);
     if (arrayType == typeof(int))
     {
          Marshal.Copy((int[])(object)d, 0, p, d.Length);
     }
     //else if ....other types you want to handle
     return p;
}

I added a type constraint of struct, which limits you to value types (shorts, doubles, ints, structs). You'll obviously need to take care with what you pass in there (like if you pass in a struct).

To address your second question, no, I don't think you can cut that down to a single line, and even if you could, you probably wouldn't want to for readability.

EDIT
I've fixed issues identified by SiLo in the comments, but this code is now very ugly and I can't say I would recommend this approach. It obviously involves a lot of type checks and boxing to get at the right type. In fact, I'd personally recommend following Silo's answer over mine, although I've at least shown you can do this in a quasi-generic way, although certainly breaking the spirit of generics in the first place.

like image 2
Sven Grosen Avatar answered Nov 09 '22 06:11

Sven Grosen