Trouble using HDF.PInvoke in c#

Hi,

This is my first post.

Ultimately, I would like to use the HDF.PInvoke package to read an image into an array, but I can’t even reference the HDF.PInvoke namespace.

Here’s a minimal example showing the problem I’m seeing:

  1. Start Visual Studio 2022
  2. Make sure the C++ 2013 redistributable is installed.
  3. Create a new project (C# Console App, .NET 7.0 Framework)
  4. Install the “HDF.PInvoke” Nuget package (version 1.10.6.1)
  5. Build the project
  6. Observe no errors
  7. In Program.cs, add the the namespace reference: using HDF.PInvoke;
  8. Build the project
  9. Observe the following error: “The type or namespace name ‘HDF’ could not be found (are you missing a using directive or an assembly reference?)”
  10. In the project folder, I see the bin32 and bin64 folders containing the hdf5 dlls, but I don’t see the HDF.Pinvoke.dll anywhere.

If anyone can point me in the right direction, it would be marvelous.

Thanks!

After flailing around with HDF.PInvoke for a while, I tried the HDF.PInvoke.NETStandard Nuget package. Visual Studio was able to resolve my namespace reference (using HDF.PInvoke).

For a basic idea of how to open files, groups, and datasets, I read through the HDF5 User Guide, specifically sections

  • The HDF5 File
  • HDF5 Groups
  • HDF5 Datasets

I used the Object Browser in Visual Studio to browse the API for methods, enums, etc. referenced in the User Guide above. The names match closely. You can get this info from the HDF.PInvoke.NETStandard API reference, but there’s no additional info there, so you might as well use the IDE.

Finally, I wanted to read an array, but I couldn’t figure out how to pass in a pointer to an array in c#. Eventually, it occurred to me to check the unit tests for the API. There, I found you need to get a pinned GCHandle, and pass the AddrOfPinnedObject.

Here’s an example,

using HDF.PInvoke;
using System.Runtime.InteropServices;

namespace HDF5PInvokeHell
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("application start");

            // Build file path
            var folder = AppDomain.CurrentDomain.BaseDirectory;
            string fileName = Path.Combine(folder, ".//resources//sample.h5");

            // Open file
            Console.WriteLine("opening file");
            var fileId = H5F.open(fileName, H5F.ACC_RDONLY);
            WriteStatusMessage(fileId, "open file");

            // Open group
            var groupName = "Group0/GroupA";
            Console.WriteLine($"opening group: {groupName}");
            var groupId = H5G.open(fileId, groupName);
            WriteStatusMessage(groupId, "open group");

            // open dataset
            Console.WriteLine("opening dataset");
            var datasetId = H5D.open(groupId, "Dataset0", H5F.ACC_RDONLY);
            WriteStatusMessage(datasetId, "open dataset");

            // get dataset type
            Console.WriteLine("getting datatype id");
            var datatypeId = H5D.get_type(datasetId);
            WriteStatusMessage(datatypeId, "get datatype id");

            // get datatype class id
            Console.WriteLine("getting datatype class id");
            var datatypeClassId = H5T.get_class(datatypeId);
            WriteStatusMessage((int)datatypeClassId, "get datatype class id");

            // read dataset
            Console.WriteLine("reading dataset");
            var myArray = new float[512, 512];
            var myArrayHandle = GCHandle.Alloc(myArray, GCHandleType.Pinned);
            WriteStatusMessage(H5D.read(datasetId, H5T.NATIVE_FLOAT, H5S.ALL, H5S.ALL, H5P.DEFAULT, myArrayHandle.AddrOfPinnedObject()), "read dataset");

            // close dataset
            Console.WriteLine("closing dataset");
            WriteStatusMessage(H5D.close(datasetId), "close dataset");

            // close group
            Console.WriteLine($"closing group: {groupName}");
            WriteStatusMessage(H5G.close(groupId), "close group");

            // close file
            Console.WriteLine("closing file");
            WriteStatusMessage(H5F.close(fileId), "close file");

            Console.WriteLine("Application end");
        }

        private static void WriteStatusMessage(long status, string message)
        {
            // H5* methods that return a status code follow the convention:
            //  success = 0
            //  failure = -1
            // H5* methods that return a value follow a similar convention:
            //  readSuccess => value = value
            //  readFailure => value < 0
            if (status >= 0)
                Console.WriteLine($"{message}: OK");
            else
                Console.WriteLine($"{message}: FAIL");
        }
    }
}

Glad to hear you figured it out. Many .NET developers find the PInvoke experience “revolting.” Have you looked at PureHDF? We took it for a spin during a recent HDF clinic.

Best, G.

PureHDF is really easier to use,
But as I had to integrate HDF5 in a Unity Project, the very recent C# frameworks used by this library rendered it completely unusable in Unity.
While HDFP.Invoke allowed me to run HDF5 even within an environment that is mainly compatible with C# Standard 2.0

1 Like

Hi @frederic.seraphine,

You may want to check HDFql, a high-level declarative language to handle HDF5 data, as it works perfectly fine in Unity with the most recent C# frameworks.

Hope it helps!

1 Like

This looks amazing and I would love to use it if possible!
However, while I can easily grab the sources for HDF5.P-INVOKE and build on Android or IOS, or let’s be crazy … PlayStation … I am not exactly sure if I can do the same with HDFql given the fact that only the binaries seem to be accessible, and while I just looked at the website on surface level, the binaries seem to be built mainly for desktop with a Windows, Linux, and MacOS version.
If I wanted to build for other platforms does HDFql offer a different kind of License?