1
\$\begingroup\$

I have the following class

public class ExrDepthMapExtractor : IDepthMapExtractor
{
    public RawDepthMap GetDepthMap(string filePath)
    {
        return DepthMapExtractorService.ExtractDepthMapAs1DArray(filePath, Callbacks.ProgressCallback);
    }
}

This calls the static C++ API wrapper

public static class DepthMapExtractorService
{
    [DllImport("Blundergat.OpenExr.Adapter.dll", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.I1)]
    private static unsafe extern bool ExtractDepthMapAs1DArray(
        out ItemsSafeHandle vectorHandle,
        out double* points, 
        out int width,
        out int height, 
        string filePath,
        Callbacks.ProgressFunc progressCallback);

    [DllImport("Blundergat.OpenExr.Adapter.dll", CallingConvention = CallingConvention.Cdecl)]
    private static unsafe extern bool Release(IntPtr itemsHandle);

    public static unsafe RawDepthMap ExtractDepthMapAs1DArray(string filePath,
        Callbacks.ProgressFunc progressCallback)
    {
        using (InternalExtractDepthMapAs1DArrayWrapper(filePath, out RawDepthMap rdm, progressCallback))
        {
            rdm.Source = filePath;
            return rdm;
        }
    }

    private static unsafe ItemsSafeHandle InternalExtractDepthMapAs1DArrayWrapper(
        string filePath, out RawDepthMap rdm, Callbacks.ProgressFunc progressCallback)
    {
        double* pixels;
        int width, height;
        ItemsSafeHandle itemsHandle;

        if (!ExtractDepthMapAs1DArray(out itemsHandle, out pixels, out width, out height, filePath, progressCallback))
            throw new InvalidOperationException();

        var pixelList = new List<double>();
        for (int i = 0; i < width * height; i++)
            pixelList.Add(pixels[i]);

        rdm = new RawDepthMap() 
        { 
            Source = filePath, 
            DepthMapArray = pixelList,
            Height = height, 
            Width = width
        };

        return itemsHandle;
    }

    public class ItemsSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        public ItemsSafeHandle() : base(true) { }

        protected override bool ReleaseHandle()
        {
            return Release(handle);
        }
    }
}

In my C++ I Release the ArrayHandle as follows

#include "wrappers.h"

bool ExtractDepthMapAs1DArray(ArrayHandle* arrayHandle, 
    double** pixels, 
    int* width,
    int* height, 
    const char* filePath, 
    ProgressFunc progressCallback)
{
    try
    {
        auto v = new std::vector<double>();
        ExrDepthMapExtractor exrExtractor;
        *v = exrExtractor.extractDepthMap(filePath, *width, *height, progressCallback);

        *arrayHandle = reinterpret_cast<ArrayHandle>(v);
        *pixels = v->data();

        if (progressCallback != NULL)
        {
            std::string strFilePath(filePath);
            std::string message = "Extracted depth map from \"" + strFilePath + "\" successfully";
            if (progressCallback != NULL)
                progressCallback(message.c_str());
        }
        return true;
    }
    catch (...)
    {
        return false;
    }
}

bool Release(ArrayHandle arrayHandle)
{
    auto items = reinterpret_cast<std::vector<double>*>(arrayHandle);
    delete items;
    return true;
}

I have not used this before and the implementation was taken from [SO] (https://stackoverflow.com/questions/31417688/passing-a-vector-array-from-unmanaged-c-to-c-sharp), however, the way this is done does not look right to me and MSDN is different. The code seems to work fine with no memory leaks, but am I doing this the right way?

\$\endgroup\$
2
  • \$\begingroup\$ Can you link to the MSDN implementation? \$\endgroup\$
    – butt
    Jan 9, 2020 at 23:25
  • \$\begingroup\$ Sorry I got this wrong way around. Links added. \$\endgroup\$
    – MoonKnight
    Jan 9, 2020 at 23:28

1 Answer 1

2
\$\begingroup\$

What don't you like about it? There's a very small space where a C++ exception could occur leaking the vector memory. If the ExrDepthMapExtractor or extractDepthMap throws, there's no way to clean the vector memory. You could just put something in the catch block.

You could memcpy the pixel memory from C++ to C# instead of looping. Or share memory the other direction, by pinning it and having the C++ side write into the provided memory. That's nice because it allows the garbage collector safely deal with the memory, but requires safeguards to prevent .NET from doing anything to the memory while C++ is using it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.