2

I am calling a method from C# like this:

[DllImport(@"pHash.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ph_dct_videohash(string file, ref int length);

And here is the method I am calling from the library

ulong64* ph_dct_videohash(const char *filename, int &Length){

    CImgList<uint8_t> *keyframes = ph_getKeyFramesFromVideo(filename);
    if (keyframes == NULL)
        return NULL;

    Length = keyframes->size();

    ulong64 *hash = (ulong64*)malloc(sizeof(ulong64)*Length);
    //some code to fill the hash array
    return hash;
}

How can I read the ulong array from the IntPtr?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
  • I think if you declare the return type of the import as `ulong[]` the compiler and/or the runtime will do the dirty work for you. – phoog Jul 10 '15 at 20:26
  • 1
    It is a memory leak, *somebody* has to call free() to release the memory again. You cannot make that call, you therefore cannot pinvoke this function. It is poorly designed, it should have provided the option for the caller to pass the buffer. Then it is easy *and* efficient. – Hans Passant Jul 10 '15 at 20:58
  • I tried to do this and got error – Yuri Schneider Jul 10 '15 at 21:05
  • 1
    @Yuri So fix your code rather than choosing the wrong solution – David Heffernan Jul 11 '15 at 03:47

2 Answers2

3

While the Marshal class doesn't provide any methods for dealing with ulongs directly, it does offer you Marshal.Copy(IntPtr, long[], int, int) which you can use to get a long array and then cast the values to ulongs.

The following works for me:

[DllImport("F:/CPP_DLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern IntPtr uint64method(string file, ref int length);

static ulong[] GetUlongArray(IntPtr ptr, int length)
{
    var buffer = new long[length];
    Marshal.Copy(ptr, buffer, 0, length);
    // If you're not a fan of LINQ, this can be
    // replaced with a for loop or
    // return Array.ConvertAll<long, ulong>(buffer, l => (ulong)l);
    return buffer.Select(l => (ulong)l).ToArray();
}

void Main()
{
    int length = 4;
    IntPtr arrayPointer = uint64method("dummy", ref length);
    ulong[] values = GetUlongArray(arrayPointer, length);
}
cbr
  • 12,563
  • 3
  • 38
  • 63
  • What about wrong values within long/ulong conversation? A lot of returned values has values bigger than long maximum value – Yuri Schneider Jul 10 '15 at 21:03
  • @YuriSchneider Please consider this: `0xFFFFFFFFFFFFFFFF`, which is the maximum value for a 64-bit integer, is `18446744073709551615` as an unsigned integer. When cast to an signed integer, the value becomes `-1` if the signed number representation used is [two's complement](https://en.wikipedia.org/wiki/Signed_number_representations#Two.27s_complement) (most systems use two's complement). It's all about **representation**. The underlying data doesn't change here because `ulong` and `long` are both 64-bit integers. – cbr Jul 10 '15 at 21:11
  • @YuriSchneider If you want to see for yourself, please set one of your array's values to `0xFFFFFFFFFFFFFFFF`. In C#, `Marshal.Copy` the array. Then see what the value is. Then cast it to an `ulong` and see the value again. – cbr Jul 10 '15 at 21:13
2

Consider just using unsafe code:

IntPtr pfoo = ph_dct_videohash(/* args */);
unsafe {
    ulong* foo = (ulong*)pfoo;
    ulong value = *foo;
    Console.WriteLine(value);
}
Mitch
  • 21,223
  • 6
  • 63
  • 86
  • Adding to this answer: If you wish to get all of your values from the array, just create a new ulong array and use a for loop. e.g. `newArray[i] = foo[i];` – cbr Jul 10 '15 at 21:17