0

I am very new to C# and C/C++. I wrote a code in C that stored 2 variables in an array and return the array. Using the P/invoke method I created a DLL library of this C function and imported the library to C#. While calling the array, I am unable to get the array from the C#. Need your kind help. here is C code

#include <stdio.h>

extern __declspec(dllexport) int* sum();
int* sum()
{
    int a = 50, b = 80, sum, neg;
    sum = a + b;
    neg = b - a;
    int arr[2] = { sum, neg };
    return arr;
}

Now the C# code is...

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication
{
    class Program
    {
        [DllImport("Project2.dll")]
        unsafe public static extern int* sum();
        static void Main(string[] args)
        {
            unsafe
            {
                int* array2 = sum();

                Console.WriteLine("Value: " + array2[0]);
            }
            
        }
    }
}

Please let me know how can I get the array.

2

1 Answer 1

2

There are multiple ways to return arrays, but in all cases you must solve two problems:

  • be able to tell .NET the count of items in the array;
  • use a common memory allocator for the native and .NET Size. The allocator of choice is the COM allocator as it's known to .NET;

Here are two examples using .NET safe code:

Native code 1:

extern __declspec(dllexport) SAFEARRAY * sumSafeArray()
{
    int a = 50, b = 80, sum, neg;
    sum = a + b;
    neg = b - a;
    int arr[2] = { sum, neg };

    // SAFEARRAY is a OLE/COM automation type well known to Windows and .NET
    // (note it implicitly uses the COM allocator too)
    SAFEARRAY* psa = SafeArrayCreateVector(VT_I4, 0, 2);
    void* data;
    SafeArrayAccessData(psa, &data);
    CopyMemory(data, arr, sizeof(arr));
    SafeArrayUnaccessData(psa);
    return psa;
}

Managed code 1:

[DllImport("mydll")]
[return: MarshalAs(UnmanagedType.SafeArray)]
private static extern int[] sumSafeArray();

foreach (var i in sumSafeArray())
{
    Console.WriteLine(i);
}

Native code 2:

extern __declspec(dllexport) void sumArray(int** out, int* count)
{
    int a = 50, b = 80, sum, neg;
    sum = a + b;
    neg = b - a;
    int arr[2] = { sum, neg };

    // always use COM allocator
    *out = (int*)CoTaskMemAlloc(sizeof(arr));
    CopyMemory(*out, arr, sizeof(arr));
    *count = sizeof(arr) / sizeof(int);
}

Managed code 2:

[DllImport("mydll")]
private static extern void sumArray(out IntPtr array, out int size);

sumArray(out var ptr, out var size);
var ints = new int[size];
Marshal.Copy(ptr, ints, 0, size);
Marshal.FreeCoTaskMem(ptr);
foreach (var i in ints)
{
    Console.WriteLine(i);
}

And one example using .NET unsafe code:

Native code:

extern __declspec(dllexport) int* sum(int* count)
{
    int a = 50, b = 80, sum, neg;
    sum = a + b;
    neg = b - a;
    int arr[2] = { sum, neg };

    // always use COM allocator
    int* out = (int*)CoTaskMemAlloc(sizeof(arr));
    CopyMemory(out, arr, sizeof(arr));
    *count = sizeof(arr) / sizeof(int);
    return out;
}

Managed code:

[DllImport("mydll")]
private unsafe static extern int* sum(out int count);

var ints = sum(out var count);
for (var i = 0; i < count; i++)
{
    Console.WriteLine(*ints);
    ints++;
}

You could also use another allocator (new/malloc/custom, etc.) but in this case you would need to expose/export to .NET the corresponding "free" method and do the free yourself when needed.

Sign up to request clarification or add additional context in comments.

8 Comments

Great answer, but you're missing finallys for the Marshal.FreeCoTaskMem
@Charlieface - I'm not missing anything, the code doesn't handle any possible error.
There is not necessarily any need to handle any errors with catch blocks, the calling code can do that. But you must be able to safely back out code without memory leaks, and for that you need a finally block. This is the equivalent of a using block: no error handling, just ensuring that things are disposed properly. Or putting it another way: if you don't care enough to ensure safe memory release, why care which allocater you use? Just allocate from wherever and let it leak
@Simon Mourier Great Answer, But I am using C language for native code. Whenever I put your code in C language it shows me this error. "ALT Requires C++ Compilation (Use .CPP suffix) atlbase.h" But I cannot use C++ suffix as my functions are written in C language and also I am very new to C++/C programming.
@sulemanAlikazmi - My code (note I had left an extern "C" in my answer that I've just removed) doesn't use any C++ and it doesn't use ATL either. It just needs "windows.h" to compile. It looks like your code is using ATL, but I can't say.
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.