Passing C# structure with string array to c++ function which accepts void * for c# structure and char** for c# string array

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public String[] Parameters;

is an inline array. The C++ declaration that matches is:

char* Parameters[2];

But you are trying to match it to:

char** Parameters;

and that's completely different.

You will need to marshal this by hand. In the C# struct declare Parameters to be IntPtr. Then allocate native memory with Marshal.AllocHGlobal to hold an array of pointers. And then populate those pointers with pointers to your strings.

[StructLayout(LayoutKind.Sequential)]
public struct TestInfo
{
    public int TestId;
    public IntPtr Parameters;
}

static void Main(string[] args) // no need for unsafe
{
    TestInfo testInfo;
    testInfo.TestId = 1;
    testInfo.Parameters = Marshal.AllocHGlobal(2*Marshal.SizeOf(typeof(IntPtr)));
    IntPtr ptr = testInfo.Parameters;
    Marshal.WriteIntPtr(ptr, Marshal.StringToHGlobalAnsi("foo"));
    ptr += Marshal.SizeOf(typeof(IntPtr));
    Marshal.WriteIntPtr(ptr, Marshal.StringToHGlobalAnsi("bar"));
    TestAPI(ref testinfoObj);
    // now you want to call FreeHGlobal, I'll leave that code to you
}

An alternative would be to use a pinned IntPtr[] and put that in testInfo.Parameters.


This is really more of an expansion/extension to David's answer, but here's one way to wrap up the custom marshalling:

public struct LocalTestInfo
{
    public int TestId;
    public IEnumerable<string> Parameters;

    public static explicit operator TestInfo(LocalTestInfo info)
    {
        var marshalled = new TestInfo
            {
                TestId = info.TestId, 
            };
        var paramsArray = info.Parameters
            .Select(Marshal.StringToHGlobalAnsi)
            .ToArray();
        marshalled.pinnedHandle = GCHandle.Alloc(
            paramsArray, 
            GCHandleType.Pinned);
        marshalled.Parameters = 
            marshalled.pinnedHandle.AddrOfPinnedObject();
        return marshalled;
    }
}

[StructLayout(LayoutKind.Sequential)]
public struct TestInfo : IDisposable
{
    public int TestId;
    public IntPtr Parameters;

    [NonSerialized]
    public GCHandle pinnedHandle;

    public void Dispose()
    {
        if (pinnedHandle.IsAllocated)
        {
            Console.WriteLine("Freeing pinned handle");
            var paramsArray = (IntPtr[])this.pinnedHandle.Target;
            foreach (IntPtr ptr in paramsArray)
            {
                Console.WriteLine("Freeing @ " + ptr);
                Marshal.FreeHGlobal(ptr);
            }
            pinnedHandle.Free();
        }
    }
}

Note for my test I swapped over to CDecl:

[DllImport(@"Test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestAPI(ref TestInfo info);

Also I think you had a typo in the C++ side:

extern "C" 
__declspec(dllexport) int TestAPI(void *data)
{
    TestInfo *cmd_data_ptr= NULL;
    cmd_data_ptr = (TestInfo*) data;
    printf("ID is %d \r\n",cmd_data_ptr->TestId);

    // char**, not char*
    char** paramsArray = ((char **)cmd_data_ptr->Parameters);
    for(int i = 0; i < 3; i++)
    {
        printf("value: %s \r\n",paramsArray[i]);
    }
    return 0;
}

And a test rig:

static void Main(string[] args)
{
    var localInfo = new LocalTestInfo()
    {
        TestId = 1,
        Parameters = new[]
        {
            "Foo", 
            "Bar",
            "Baz"
        }
    };
    TestInfo forMarshalling;
    using (forMarshalling = (TestInfo)localInfo)
    {
        TestAPI(ref forMarshalling);                
    }
}

The reverse marshalling operator is left as an exercise to the reader, but should basically look like the inverse of the explicit TestInfo operator.


Comments

  1. Evans

    • 2016/10/24

    Pass by Value. Pass by Value, means that a copy of the data is made and stored by way of the name of the parameter.

  2. Melvin

    • 2018/3/8

    Parameter Passing Techniques in C/C++. There are different ways in which parameter data can be passed into and out of methods and functions. Let us assume that a function B () is called from another function A (). In this case A is called the “caller function” and B is called the “called function or callee function”.

  3. Carl

    • 2021/7/15

    Pass by Reference. A reference parameter "refers" to the original data in the calling function.

  4. Martin

    • 2015/3/2

    C C++ Server Side Programming Programming. In C we can pass parameters in two different ways. These are call by value, and call by address, In C++, we can get another technique. This is called Call by reference. Let us see the effect of these, and how they work.

  5. Harvey

    • 2020/1/13

    . Passing a pointer is also known as passing a value by reference.

  6. Xavier

    • 2016/3/18

    In C, Pass-by-reference is simulated by passing the address of a variable (a pointer) and dereferencing that address within the function to read or write the actual variable. This will be referred to as "C style pass-by-reference." Source: www-cs-students.stanford.edu

  7. Gael

    • 2017/8/9

    . You cannot pass function to another function as parameter. But, you can pass function reference to another function using function pointers.

  8. Caiden

    • 2017/7/8

    Passing Arrays as Function Arguments in C, If you want to pass a single-dimension array as an argument in a function, you would have to declare a formal parameter in one of following three ways and all t

  9. Salvador

    • 2015/12/31

    pointer, because a pointer is a reference to a variables memory location.

  10. Emmanuel

    • 2020/4/29

    Example program – passing structure to function in C by value: In this program, the whole structure is passed to another function by value. It means the whole structure is passed to another function with all members and their values.

  11. Flores

    • 2020/7/2

    Parameter Passing Techniques in C/C++ · Pass By Value : This method uses in-mode semantics. Changes made to formal parameter do not get 

  12. Hunter

    • 2017/1/9

    using C. This is a short introduction to the Message Passing Interface (MPI) designed to convey the fundamental operation and use of the interface. This introduction is designed for readers with some background programming C, and should deliver enough information to allow readers to write and run their own (very simple) parallel C programs

  13. Dervishi

    • 2020/12/3

    In call by value parameter passing method, the copy of actual parameter values are copied to formal parameters and these formal parameters are used in called 

  14. Grasso

    • 2016/5/3

    Answer (1 of 19): Yes, it is a passing grade. But most important is to figure out why you made the “C”. I recall one course in which I made a C and it was a tough course.

  15. Allen

    • 2021/6/17

    Arguments in C and C++ language are copied to the program stack at run time, where they are read by the function. These arguments can either be values in 

  16. Foster

    • 2019/10/28

    Because you're passing the value of the pointer to the method and then dereferencing it to get the integer that is pointed to.

  17. Caden

    • 2021/5/26

    1. Call by value · Here the values of the variables are passed by the calling function to the called function. · If any value of the parameter in the called 

Comments are closed.

Recent Posts