This question already has an answer here:
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;
}
The equivalent of C's
char**
is a fully-pinnedbyte[][]
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 withEncoding.ASCII.GetBytes
.I think you might need to use Marshal.
This is just a guess because I don't have a library that takes char** so I haven't been able to try it.
Can you change the DllImport declaration parameter type to
StringBuilder[]
? Then you can call the function like: