Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get char** using C#? [duplicate]

I need to pass an argument to an unsafe DllImported function in the form of:

[DllImport("third_party.dll")]
private static extern unsafe int start(int argc, char** argv);

I'm assuming it's an array of strings. However when I try to do the following, I get 'Cannot convert from string[] to char**' error. How do I go about getting this to work? Thanks.

string[] argv = new string[] { };
start(0, argv);

EDIT 1: The question was marked as duplicate, but looking at the possible duplicate question, I still do not see how to get this to work.

EDIT 2: To further clafiry the question and required parameters. It looks like your standard argc/argv parameters (parameter count, and then parameter values). The same way you would start a c program: int main(int argc, char** argv); For this particular problem, I don't want to pass any arguments at all (so count is 0).

EDIT 3: I got more information from the 3rd party library vendor. Here it is:

  • the first parameter is the count of arguments
  • the second parameter is an array of null terminated strings
  • the strings are ANSI encoded

EDIT 4: Final edit with a working solution (at least in my case). I would make this the answer, but can't because this question is marked as a duplicate. Here's a link to a question that helped me the most. In the end the dll function expected an array of pointers to buffers with ANSI strings. So my final approach (based off the linked question), was as follows. Create an array in memory to hold the pointers, then allocate each string elsewhere in memory, and write pointers to those strings inside the first pointer array. This code works in production:

[DllImport("third_party.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern int start(Int32 args, IntPtr argv);

public bool start(params string[] arguments)
{
    int result;

    if (arguments == null || arguments.Length == 0)
    {
        result = dll_system_startup(0, IntPtr.Zero);
    }
    else
    {
        List<IntPtr> allocatedMemory = new List<IntPtr>();

        int sizeOfIntPtr = Marshal.SizeOf(typeof(IntPtr));
        IntPtr pointersToArguments = Marshal.AllocHGlobal(sizeOfIntPtr * arguments.Length);

        for (int i = 0; i < arguments.Length; ++i)
        {
            IntPtr pointerToArgument = Marshal.StringToHGlobalAnsi(arguments[i]);
            allocatedMemory.Add(pointerToArgument);
            Marshal.WriteIntPtr(pointersToArguments, i * sizeOfIntPtr, pointerToArgument);
        }

        result = start(arguments.Length, pointersToArguments);

        Marshal.FreeHGlobal(pointersToArguments);

        foreach (IntPtr pointer in allocatedMemory)
        {
            Marshal.FreeHGlobal(pointer);
        }
    }

    return result == 0;
}
like image 990
Eternal21 Avatar asked Jun 09 '16 18:06

Eternal21


2 Answers

I think you might need to use Marshal.

var a = (char*)Marshal.StringToCoTaskMemAuto("myString");
char** = &a;

This is just a guess because I don't have a library that takes char** so I haven't been able to try it.

like image 147
skalpin Avatar answered Oct 05 '22 19:10

skalpin


The equivalent of C's char** is a fully-pinned byte[][] in C# (and by fully-pinned I mean the outer array AND all inner arrays). If you want to pass C# strings you'll have to convert them to byte arrays, for instance with Encoding.ASCII.GetBytes.

like image 26
Asik Avatar answered Oct 05 '22 20:10

Asik