C#: calling C++ DLL with char** argument

2019-07-25 21:54发布

问题:

I would like to call this C++ function from my C# code:

void GetArrayOfNames(char** names, int nbOfNames);

To call it in C++, I just define an array of char*:

char* aNames[20];

And allocate each name in a loop:

for(int i-0; i<20; i++)
{
    aNames[i] = new char[50];
}

Then call:

GetArrayOfNames(aNames, 20);

In my C# code, I have:

[DllImport("MyDLL.dll")]
unsafe static extern void GetArrayOfNames(char** ppNames, int nbOfNames);

Now, how do I do the memory allocation and call GetArrayOfNames? Also, any way of not having to declare my function as "unsafe"?

回答1:

Probably:

static extern void GetArrayOfNames([MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr] StringBuilder[] args, int count);

// Call using
StringBuilder[] arr = new StringBuilder[20];
for (int i = 0; i < arr.Length; i++)
{
    arr[i] = new StringBuilder(50);
}
GetArrayOfNames(arr, arr.Length);


回答2:

As I understand char** is just a reference to string. I created a small test for myself and it worked fine:

class Program
{
    [DllImport("TestCppLib.dll", CharSet = CharSet.Ansi, EntryPoint = "?fnTestCppLib@@YAHPAPAD@Z", CallingConvention=CallingConvention.Cdecl)]
    extern static int fnTestCppLib(ref string s);
    static void Main(string[] args)
    {
        var s = "some";
        var t = fnTestCppLib(ref s);
        Debug.Assert(s == "test");
    }
}

The implementation of function in C++ is:

TESTCPPLIB_API int fnTestCppLib(char ** str)
{
    *str = "test";
    return 42;
}


回答3:

This ended up working:

static extern int GetArrayOfNames(IntPtr[] astr, int iLength);

And called/setup like this:

IntPtr[] intArr = new IntPtr[200];
for (int i = 0; i < intArr.Length; i++)
 {
  intArr[i] = Marshal.AllocHGlobal(256);
}

int nbOfNames = 2;
GetArrayOfNames(intArr, nbOfNames);

And to put back in a string:

string tmp;
tmp = Marshal.PtrToStringAnsi(intArr[i]);


回答4:

Can i help you? my sample code in C++:

//CamMethods.cpp:
#include <iostream>
using namespace std;
#include "CamPS.h"

namespace Export {
    char * CameraPS::CamStart(char *s){
        return "teste";
    };
}

//CamPs.h
namespace Export{
    class CameraPS
    {
    public:
        _declspec(dllexport) char * _stdcall CamStart(char *s);
    };
}

and in C# i call:

using UnityEngine;
using System;
using System.Collections;
using System.Runtime.InteropServices;

public class DllTeste : MonoBehaviour
{
    public int x;
    [DllImport ("PS_Cam.dll", EntryPoint="CamStart", CallingConvention = CallingConvention.StdCall)]
    private static extern IntPtr CamStart (int s);//i can put <string> in same place of <int>


    void Start ()
    {

    }

    void Update ()
    {
//      IntPtr a =  CamStart(x);
//      string b = Marshal.PtrToStringAnsi(a);  
//      Debug.Log(b);
        Debug.Log(Marshal.PtrToStringAnsi(CamStart(x)));
    }
}

Sorry for my english.