2

i was wondering if there is other ways to pass null parameters from c# to c++ with combinations using pinvoke.

consider the following codes where each parameters can be null.

c++

bool Range(const int id, double *rangeT0, double *rangeTW, double *rangeV0, double *rangeVW);

c# counterpart

[DllImport(myDLL)]
        [return: MarshalAs(UnmanagedType.I1)]
        internal static extern bool Range(int id, IntPtr rangeT0, IntPtr rangeTW, IntPtr rangeV0, IntPtr rangeVW);

I used IntPtr to be able to pass IntPtr.Zero for c++ null. i already considered using overloaded DllImports but having multiple combinations would mean an overload for each null parameter.

so far i came up with this code. it is running but i think its quite too much for a wrapper.

public static bool RangeWrapper (int id, ref double? rangeT0, ref double? rangeTW, ref double? rangeV0, ref double? rangeVW)
        {
            double[] t0Temp = new double[1];
            double[] twTemp = new double[1];
            double[] v0Temp = new double[1];
            double[] vwTemp = new double[1];

            IntPtr rangeT0Ptr = rangeT0 == null ? IntPtr.Zero : Marshal.AllocHGlobal(sizeof(double));
            IntPtr rangeTWPtr = rangeTW == null ? IntPtr.Zero : Marshal.AllocHGlobal(sizeof(double));
            IntPtr rangeV0Ptr = rangeV0 == null ? IntPtr.Zero : Marshal.AllocHGlobal(sizeof(double));
            IntPtr rangeVWPtr = rangeVW == null ? IntPtr.Zero : Marshal.AllocHGlobal(sizeof(double));

            if (rangeT0.HasValue)
            {
                t0Temp[0] = rangeT0.Value;
                Marshal.Copy(t0Temp, 0, rangeT0Ptr, 1);
            }
            if (rangeTW.HasValue)
            {
                twTemp[0] = rangeTW.Value;
                Marshal.Copy(twTemp, 0, rangeTWPtr, 1);
            }
            if (rangeV0.HasValue)
            {
                v0Temp[0] = rangeV0.Value;
                Marshal.Copy(v0Temp, 0, rangeV0Ptr, 1);
            }
            if (rangeVW.HasValue)
            {
                vwTemp[0] = rangeVW.Value;
                Marshal.Copy(vwTemp, 0, rangeVWPtr, 1);
            }

            bool isSuccessful = Range(sigID, rangeT0Ptr, rangeTWPtr, rangeV0Ptr, rangeVWPtr);

            if (rangeT0Ptr != IntPtr.Zero)
            {
                Marshal.Copy(rangeT0Ptr, t0Temp, 0, 1);
                rangeT0 = t0Temp[0];
            }
            else
            {
                rangeT0 = null;
            }

            if (rangeTWPtr != IntPtr.Zero)
            {
                Marshal.Copy(rangeTWPtr, twTemp, 0, 1);
                rangeTW = twTemp[0];
            }
            else
            {
                rangeTW = null;
            }

            if (rangeV0Ptr != IntPtr.Zero)
            {
                Marshal.Copy(rangeV0Ptr, v0Temp, 0, 1);
                rangeV0 = v0Temp[0];
            }
            else
            {
                rangeV0 = null;
            }
            if (rangeVWPtr != IntPtr.Zero)
            {
                Marshal.Copy(rangeVWPtr, vwTemp, 0, 1);
                rangeVW = vwTemp[0];
            }
            else
            {
                rangeVW = null;
            }

            Marshal.FreeHGlobal(rangeT0Ptr);
            Marshal.FreeHGlobal(rangeTWPtr);
            Marshal.FreeHGlobal(rangeV0Ptr);
            Marshal.FreeHGlobal(rangeVWPtr);

            return isSuccessful;
        }

So my question is, is there any other way to pass multiple nullable parameters without making any changes in the native c++ code?

River
  • 8,585
  • 14
  • 54
  • 67
Rhen Bon
  • 87
  • 7
  • You could do this with a customer marshaler – David Heffernan Jan 23 '17 at 08:17
  • 1
    Even if you don't want to change the native code, consider writing a C++/CLI wrapper to make this more manageable. These parameters look like they want to be a class/struct anyway -- `ref double?` is anything but intuitive in C#. The most effective way to marshal this really is simply as a `double*` -- that is, using unsafe code. (A custom marshaler is an option, but probably a lot more trouble than it's worth, and even less efficient than what you're doing right now.) – Jeroen Mostert Jan 24 '17 at 12:27
  • @JeroenMostert Thanks! I tried using unsafe code and it worked.The wrapper still had to be a **ref double?** though because i want the wrapper to be used like a c# code should be. i let the wrapper handle the conversion of nulls to pointers and pass it to the dll import. – Rhen Bon Jan 27 '17 at 10:22
  • In natural C# code, you'd probably introduce a `class Range` (or `struct`) with `double?` properties. Modifying these properties is much more natural than modifying arguments. Of course the immediate wrapper can use `ref double?` if this makes things more convenient (though with C++/CLI it would be just as easy to pass a managed type) but it makes for awkward C# code in clients to not abstract over that again. (Sometimes this isn't possible due to performance considerations, but in that case you probably want to avoid interop anyway.) – Jeroen Mostert Jan 27 '17 at 10:41
  • Possible duplicate of [How do I handle optional C++ dll struct arguments in C#](https://stackoverflow.com/questions/47997942/how-do-i-handle-optional-c-dll-struct-arguments-in-c-sharp) – River Dec 28 '17 at 23:10

0 Answers0