1
votes

There are a lot of articles about some specific problem using DllImport. Alas quite often I see different replies to the same question. For instance some say if a c++ function returns a a char* and an int* strLen, then some people say I should use a StringBuilder in my dllImport statement and others say return byte[], some have a marshall statement in the dllImport, some don't. Some answers seem needed because of old C# / .net versions.

So the question is: If the dll call from c++ is fairly straightforward, without strange calling conventions, or other strange items, what should the corresponding DllImport functions be if you have output char* and size or input char * and size?

c++ .h
bool SendString(const char* pSendStr, long strSize);
bool ReadString(char* pReadStr, long& readStrSize); 

What are the corresponding DllImports? replace the instr and outstr with string? stringbuilder? char[]? byte[]? Is any marshal statement needed?

2
const char* == string and char* == StringBuilder (normally)leppie
what about char ***Mathias Colpaert

2 Answers

7
votes
bool SendString(const char* pSendStr, long strSize);

This function is the easy one. The text is sent from the caller to the callee. The p/invoke is declared like this:

[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool SendString(string SendStr, int Len);

Note that I'm assuming the cdecl calling convention since that is the default for C++ code. And also do note that long in C++ on Windows is 32 bits wide. So it matches int in C#.

When you call the function you need to pass the string and its length. However, the normal convention is for null-terminated strings to be used so the length parameter is not needed. I'd declare the unmanaged function like this:

bool SendString(const char* pSendStr);

And the p/invoke like this:

[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool SendString(string SendStr);

The other function is a little more complex. You've declared it like this:

bool ReadString(char* pReadStr, long& readStrSize); 

Here the caller allocates the buffer which is populated by the callee. You can use StringBuilder for the text and let the marshaler do the work for you. The p/invoke is:

[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool ReadString(StringBuilder ReadStr, ref int Len);

The convention is that you supply the length of the provided buffer. In turn the function will let you know how many characters it wrote. You'd call the function like this:

int len = 256;
StringBuilder ReadStr = new StringBuilder(len);
bool succeeded = ReadString(ReadStr, ref len);
if (succeeded)
{
    string str = ReadStr.ToString();
}
2
votes

As leppie wrote, what you usually want is:

[DllImport(my.dll)]
static extern bool SendString(string sendString, int stringSize);
[DllImport(my.dll)]
static extern bool ReadString(StringBuilder readString, ref int readStringSize);

This would do automatic conversion to Unicode (and back) for you.

If you want precise access to your char*, you would use byte[]. This way no conversion is done and you have more control on what is going on. Usually you won't need that. One use case might by when your char* can include \0 chars.