stringbuilder as out parameter - c#

extern "C"
__declspec(dllexport)
bool FillString(LPWSTR OutName)
{
LPWSTR out = L"TheName\0";
int len = wcslen(out);
memcpy(
OutName,
out,
len * sizeof(wchar_t));
return true;
}
That is function in my c/c++ dll, the following is my call from c#...
[DllImport(#"My.dll", EntryPoint = "FillString", CallingConvention = CallingConvention.Cdecl)]
public static extern bool MyFunction([MarshalAsAttribute(UnmanagedType.LPWStr)] StringBuilder Name);
var fromdll = new StringBuilder(64);
// I do not know length of out string (Name), but it is null terminated
bool IsFilled = MyFunction(fromdll);
Console.WriteLine(fromdll);
The output is
TheName???
Can anyone help me to get the output...?
TheName

You need to copy one more character. '\0' is called string terminator. Without it C, C++ and PInvoke in .NET do not recognize end of string. If you are copying wcslen characters, zero is not copied. There are many solutions:
Use mentioned wcscpy to copy string with \0
Copy one more
character with memcpy(OutName, out, (len + 1) * sizeof(wchar_t)).
Moreover, it's a good idea to pass buffer (StringBuilder) size. C/C++ lets you write outside variable boudaries. This size enables to avoid that.

Related

DLLImport c++ functions with char* as input or as output parameters

I have two c++ functions that I want to DllImport:
bool SendString(const char* pSendStr, long strSize);
bool ReadString(char* pReadStr, long& readStrSize);
There are a lot of articles that write how to DllImport strings.
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?
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();
}
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.

Decoding IntPtr to MultiString

I need to extract the environment strings from a call to CreateEnvironmentBlock( out IntPtr lpEnvironment, IntPtr hToken, bool bInherit ), so as to put them in dictionary based on the variable name and its value.
When this function returns, lpEnvironment receives a pointer to the
new environment block. The environment block is an array of
null-terminated Unicode strings. The list ends with two nulls
(\0\0).
I cannot easily use Marshal.Copy as I do not know the block's length. I'm wondering if there is an easy way to move along it or determine what to copy to something I can then convert more easily. One thought was to pass the out IntPtr lpEnvironment as out char [] lpEnvironment.
Any suggestions?
You dont know the whole length for Marshal.Copy(), however you know the length of the first string if you perform Marshal.PtrToString(). With that knowledge you can advance to the next string and so on, until you read an empty string, that indicates only \0 was present at that address, which is the end of the multi string.
Note that the following code assumes Unicode charset is available and uses it.
[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment,IntPtr hToken,bool bInherit);
static IEnumerable<string> ExtractMultiString(IntPtr ptr)
{
while (true)
{
string str = Marshal.PtrToStringUni(ptr);
if (str.Length == 0)
break;
yield return str;
ptr = new IntPtr(ptr.ToInt64() + (str.Length + 1 /* char \0 */) * sizeof(char));
}
}

passing string from C++ to C# using void pointer

I am having C++ code like this just to try out to get the string to C# application from c++ dll.
int GetValue(void *pBuffer)
{
int x = 0;
String^ temp;
temp = "TestStringtest1test2";
memcpy(pBuffer, &temp,sizeof(temp));
Console::WriteLine(temp);
return x;
}
on c# side all I am doing is
[DllImport("Cplusplusapp.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int GetValue(StringBuilder pBuffer);
StringBuilder Buffer = new StringBuilder(100);
int x = GetValue(Buffer);
Console.WriteLine(Buffer);
I have tried marshalling and various other suggestions but I am not able to understand why I am getting garbage values.Its fairly simple but what is that I am missing.
Address of temp IS NOT string itself then memcpy won't work as expected. Moreover sizeof(String) will return size of System.String object (4 bytes on 32 bit machine), not string length.
If you need to copy a value from a managed string to an unmanaged pointer you have to follow next steps.
First of all System.String is always UNICODE then you won't have char* but wchar_t* (but you can convert it to what you need with usual helper macros). To convert a string from System.String to wchar_t* call Marshal::StringToBSTR():
String^ temp = L"My string";
BSTR bstr = Marhshal::StringToBSTR(temp);
memcpy(pBuffer, bstr, SysStringLength(bstr));
Marshal::FreeBSTR(bstr);
Of course if you do not need to use a System.String you can skip all of these simply not assigning your string to System.String. Change it to wchar_t* or char* according to how you'll import that function (CharSet.Ansi or not), take a look to other Marshal::StringToXYZ functions, I would create e temporary string from System::String and then I would strcpy to destination buffer (check functions with Ansi suffix for char* versions). See this post here on SO for an example of that.

Using Delphi's stuct arrays and strings in C#

I've been trying to invoke a method that have been created in Delphi in the following way:
function _Func1(arrParams: array of TParams): Integer;stdcall;
type
TParams = record
Type: int;
Name: string;
Amount : Real;
end;
My code is:
[DllImport("some.dll", EntryPoint = "_Func1", CallingConvention = CallingConvention.StdCall)]
public static extern int Func(
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.Struct)] TParams[] arrParams)
And the struct is:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TParams
{
public int Type;
[MarshalAs(UnmanagedType.AnsiBStr)]
public string Name;
public double Amount;
}
When I am calling this method I'm getting the error:
Cannot marshal field 'Name' of type 'TParams': Invalid managed/unmanaged type combination (String fields must be paired with LPStr, LPWStr, BStr or ByValTStr).
However none of those combinations works, as Delphi's strings are prefixed with its length and it is Ansi for sure (I've tried it with other string parameters). Does anyone have a clue how to solve this?
There are two main problems with this, use of open arrays and use of Delphi string.
Open arrays
Delphi open arrays are implemented by passing a pointer to the first element of the array and an extra parameter specifying the index of the last item, high in Delphi terminology. For more information see this answer.
Delphi strings
The C# marshaller cannot interop with a Delphi string. Delphi strings are private types, only to be used internally to a Delphi module. Instead you should use a null-terminated string, PAnsiChar.
Putting it all together you can write it like this:
Delphi
type
TParams = record
_Type: Integer;//Type is a reserved word in Delphi
Name: PAnsiChar;
Amount: Double;
end;
function Func(const arrParams: array of TParams): Integer; stdcall;
C#
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct TParams
{
public int Type;
public string Name;
public double Amount;
}
[DllImport("some.dll")]
public static extern int Func(TParams[] arrParams, int high);
TParams[] params = new TParams[len];
...populate params
int retval = Func(params, params.Length-1);
To compliment David's answer, you can marshal to a Delphi string, but it's ugly. In C#, you have to replace all of your strings in the struct with IntPtr.
private static IntPtr AllocDelphiString(string str)
{
byte[] unicodeData = Encoding.Unicode.GetBytes(str);
int bufferSize = unicodeData.Length + 6;
IntPtr hMem = Marshal.AllocHGlobal(bufferSize);
Marshal.WriteInt32(hMem, 0, unicodeData.Length); // prepended length value
for (int i = 0; i < unicodeData.Length; i++)
Marshal.WriteByte(hMem, i + 4, unicodeData[i]);
Marshal.WriteInt16(hMem, bufferSize - 2, 0); // null-terminate
return new IntPtr(hMem.ToInt64() + 4);
}
This can directly be sent to Delphi, where it'll properly be read as a string.
Remember that you must free this string when you're done with it. However, GlobalFree() can't be called directly on the pointer to the string, because it doesn't point to the start of the allocation. You'll have to cast that pointer to a long, then subtract 4, then cast it back to a pointer. This compensates for the length prefix.

Return string from unmanaged dll to C#

I'm sorry to ask this here since I'm sure it must be answered "out there", but I've been stuck on this for several months now, and none of the solutions I've found have worked for me.
I have the following VB code that works:
Declare Function DeviceSendRead Lib "unmanaged.dll" (ByVal sCommand As String, ByVal sReply As String, ByVal sError As String, ByVal Timeout As Double) As Integer
Dim err As Integer
Dim outstr As String
Dim readstr As String
Dim errstr As String
outstr = txtSend.Text
readstr = Space(4000)
errstr = Space(100)
Timeout = 10
err = DeviceSendRead(outstr, readstr, errstr, Timeout)
and I am trying to implement it in a C# project. The best equivalent I have been able to find is:
[DllImport("unmanaged.dll")] public static extern int DeviceSendRead(String outstr, StringBuilder readstr, StringBuilder errstr, double Timeout);
int err;
StringBuilder readstr = new StringBuilder(4000);
StringBuilder errstr = new StringBuilder(100);
err = DeviceSendRead(txtSend.Text, readstr, errstr, 10);
However, when I run this, the application freezes and I must force quit it. By experimenting with ref and out, I have occasionally managed to make it crash rather than freeze, but the only "progress" I have achieved is to replace the dll function call with:
DeviceSendRead(txtSend.Text, null, null, 10);
This prevents the crash, but of course does nothing (that I can detect). I'm therefore assuming that it's the manner of passing the two return string parameters that is causing the problem. If anyone can suggest what I might be doing wrong, I'd be very happy to hear it. Thanks.
I have reached an answer, which I will record here for completeness, with grateful thanks to all those who pointed me in the right direction.
According to this post elsewhere, the use of .NET Reflector on similar VB code suggests the need to use the string type in place of my StringBuilder, as suggested here by Alex Mendez, JamieSee and Austin Salonen, together with explicit marshaling, as suggested by Nanhydrin, but utilising the unmanaged type VBByRefStr rather than AnsiBStr. The final key to the puzzle is that the string parameter then needs to be passed by reference using the ref keyword.
I can confirm that this works, and that my final working C# code is therefore:
[DllImport("unmanaged.dll", CharSet = CharSet.Ansi)]
public static extern short DeviceSendRead(
[MarshalAs(UnmanagedType.VBByRefStr)] ref string sCommand,
[MarshalAs(UnmanagedType.VBByRefStr)] ref string sReply,
[MarshalAs(UnmanagedType.VBByRefStr)] ref string sError,
double Timeout);
short err;
string outstr = txtSend.Text;
string readstr = new string(' ', 4000);
string errstr = new string(' ', 100);
err = DeviceSendRead(ref outstr, ref readstr, ref errstr, 10);
I hope this is useful to others facing a similar issue.
Try this:
[DllImport("unmanaged.dll")]
public static extern int DeviceSendRead(string outString, string readString, string errorString, double timeout);
int err;
string outstr;
string readstr;
string errstr =
outstr = txtSend.Text;
readstr = new string(' ', 4000);
errstr = new string(' ', 100);
double timeout = 10;
err = DeviceSendRead(outstr, readstr, errstr, timeout);
Try this as an equivalent:
string readstr = new string(' ', 4000);
string errstr = new string(' ', 1000);
Default Marshalling for strings
Default Marshalling behaviour
You may need to be more specific in your dllimport declaration and add in some MarshalAs attributes, if you have more details on what type of strings the called function is expecting (Ansi, Unicode, null terminated, etc.) then that would help.
In fact it expecting null terminated strings could perhaps explain why it's hanging rather than erroring out.
[DllImport("unmanaged.dll", EntryPoint="DeviceSendRead")]
public static extern int DeviceSendRead(string outString, [MarshalAs(UnmanagedType.AnsiBStr)]string readString, string errorString, double timeout);
You might also need to explicitly state that your parameters are input, output, or both by using the parameter attributes [In, Out].
[DllImport("unmanaged.dll", EntryPoint="DeviceSendRead")]
public static extern int DeviceSendRead(string outstr, string readstr, string errstr, double Timeout);
You cannot marshal a StringBuilder here. There are some rules to follow for marshalling StringBuilder (see CLR Inside Out: Marshaling between Managed and Unmanaged Code):
StringBuilder and Marshaling
The CLR marshaler has built-in knowledge of the StringBuilder type and
treats it differently from other types. By default, StringBuilder is
passed as [InAttribute, OutAttribute]. StringBuilder is special
because it has a Capacity property that can determine the size of the
required buffer at run time, and it can be changed dynamically.
Therefore, during the marshaling process, the CLR can pin
StringBuilder, directly pass the address of internal buffer used in
StringBuilder, and allow the contents of this buffer to be changed by
native code in place.
To take full advantage of StringBuilder, you'll need to follow all of
these rules:
1.Don't pass StringBuilder by reference (using out or ref). Otherwise, the CLR will expect the signature of this argument to be wchar_t **
instead of wchar_t *, and it won't be able to pin StringBuilder's
internal buffer. Performance will be significantly degraded.
2.Use StringBuilder when the unmanaged code is using Unicode. Otherwise, the CLR will have to make a copy of the string and convert
it between Unicode and ANSI, thus degrading performance. Usually you
should marshal StringBuilder as LPARRAY of Unicode characters or as
LPWSTR.
3.Always specify the capacity of StringBuilder in advance and make sure the capacity is big enough to hold the buffer. The best practice
on the unmanaged code side is to accept the size of the string buffer
as an argument to avoid buffer overruns. In COM, you can also use
size_is in IDL to specify the size.
Rule 3 doesn't seem like it is satisied here.

Categories

Resources