Home > front end >  Pass an array to a C# DLL from C DLL
Pass an array to a C# DLL from C DLL

Time:11-11

I am trying to handle a C DLL which in turn connects to a C# DLL. My problem is when I want to send or receive arrays between C and C# DLLs. I have created the C# DLL and C DLL, so I can modified all the files. Within the C# DLL, I must necessarily have an array of string an another of double because that is where I make use of other functions unrelated to this question. (The declaration of the function that I have to use in C# DLL is Error SetMultipleChannelValues(string[] names,double[] values).

I am using CLI in Visual Studio 2015 to compile and generate all DLLs and projects.

Here it is the C# code that I have:

public static Error SetMultipleSignals(string[] signals, double[] values)
    {
        Error error = null;

        error = workspace.SetMultipleChannelValues(signals, values);
        if (error.IsError)
            Console.WriteLine("[DLL] Error in SetMultipleChannelValues(). Code: "   error.Code   ". Message: "   error.Message);

        return error;
    }

Here it is the C code that I have:

bool setMultipleSignals(double* setSignal_values)
{
    array<double, DEFAULT_SETSIGNAL_SIZE> values;
    for (int index = 0 ; index < DEFAULT_SETSIGNAL_SIZE ;index  )
    {
        values[index] = *(setSignal_values   index);
    }

    if (!veristand_wrapper_cs::VeriStand_dll::SetMultipleSignals(setSignals_path, values)) // This is the call to C# function
        return true;
    else
        return false;
}

Here it is the C header that I have:

#pragma once

#define VERISTAND_WRAPPER_CPP __declspec(dllexport)

#include <string>
#include <iostream>
#include <windows.h>
#include <array>

using namespace std;
using namespace System;
using System::Runtime::InteropServices::Marshal;

#using "veristand_wrapper_cs.dll"   // This is the C# DLL
#define DEFAULT_SETSIGNAL_SIZE  100

extern "C" VERISTAND_WRAPPER_CPP bool setMultipleSignals(double* setSignal_values); // This         function is called from API C  

I pass the double array with a pointer to double as a parameter from my C application, but I have to pass to C# DLL an array, so I have tried to build a whole array of double before pass it. The array setSignals_path is created as a global array in this C DLL as array<String^, DEFAULT_SETSIGNAL_SIZE> setSignals_path;.

The problem is that the call of C# function provides me an error that says that I can not call the function with these arguments. It expects array<System::String^> ^signals, array<double> ^values and I am passing std::array<System::String^, 100Ui64>, std::array<double, 100Ui64>

The concept of my idea is simple. From the C API, pass a pointer to the array of doubles, so that the function of my DLL in C passes the array of doubles and strings to the DLL of C#, and it returns the array of modified doubles to be able to indicate to the API from C that the values ​​have been modified from that memory address.

Would anyone know how to approach this problem or how to do it in some other way to make it work?

CodePudding user response:

It looks like you are using c /cli. So you can actually use .Net types in native code.

In C /cli you need to explicitly declare if a variable is a reference, that is what the ^ is for. Your arrays is declared without this, but your method needs a reference. You will also need to allocate your array on the managed heap. So your array should probably be declared as

array<double>^ values = gcnew array< double>(DEFAULT_SETSIGNAL_SIZE);

But it was a while ago I coded c /cli, so I may have missed something. See How to use arrays in c /cli. It also looks like you may be able to use ref new instead of gcnew

CodePudding user response:

Use following. The size of array must be 100, or add another parameter before the string array indicating length. The string in code is a byte array terminating with '\0'. c must use a method that will read the null terminated string. A double is 8 bytes so the 100 double can be consecutive memory locations (800 bytes).

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct StringArray
        {
            public IntPtr[] unmanagedArray;
        }
        static void Main(string[] args)
        {
            string[] strArray = { "Message 1", "Message 2", "Message 3"};


            StringArray stringStruct = new StringArray();
            stringStruct.unmanagedArray = new IntPtr[strArray.Length];
            
            for (int i = 0; i < strArray.Length; i  )
            {
                stringStruct.unmanagedArray[i] = Marshal.StringToBSTR(strArray[i]);
            }


            IntPtr unmanagedArray = Marshal.AllocHGlobal(Marshal.SizeOf(stringStruct));
            Marshal.StructureToPtr(stringStruct, unmanagedArray, false);
        }
  • Related