Call c# method from c++ wrap - c#

I have a c# library with this function:
public static int myGetStrings(String sOne, out String sTemp1, out String sTemp2)
{
sTemp1 = sOne+"1";
sTemp2 = sOne+"2";
return 0;
}
My c++ wrapper call c# library:
char sOneCall[256],sTemp1Call[256],sTemp2Call[256];
sprintf(sOneCall,"this is a test");
int iReturnData = myLibraryClass-> myGetStrings(
Marshal::PtrToStringAnsi((IntPtr) (char *)sOneCall),
Marshal::PtrToStringAnsi((IntPtr) (char *)sTemp1Call),
Marshal::PtrToStringAnsi((IntPtr) (char *)sTemp2Call) );
But when I execute my code the variables "sTemp1Call" and "sTemp1Call" are void.
Why? What is my problem? Where i wrong?
Thank you

I am assuming you are using c++/cli. if so there is no need to use marshal you can just pass a string directly.
String ^sOneCall = "this is a test";
String ^sTemp1Call = "";
String ^sTemp2Call = ""
int iReturnData = myLibraryClass-> myGetStrings(sOneCall,sTemp1Call,sTemp2Call);
Your function has out parameters if you want to get the values after the call you will need to keep around the .net object a PtrToStringAnsi copies the strings to a new String^ object so you will need to copy it back to your native ptr.

Related

Receive char ** from a C++ DLL into C# string[]

Despite all the questions, I can't find a suitable answer for doing this.
My goal is to fill a string[] with the use of a DLL that returns a char**.
DLL Declaration :
extern "C" SHTSDK_EXPORT int GetPeerList(SHTSDK::Camera *camera, int* id, int id_size, char** name, int name_size, int* statut, int statut_size);
My import :
[DllImport(libName)]
static public extern int GetPeerList(IntPtr camera, IntPtr id, int id_size, IntPtr name, int name_size, IntPtr statut, int statut_size);
My use in C# code :
StringBuilder[] name = new StringBuilder[nbPeer];
for (int i = 0; i < nbPeer; i++)
{
name[i] = new StringBuilder(256);
}
//Alloc peer name array
GCHandle nameHandle = GCHandle.Alloc(name, GCHandleType.Pinned);
IntPtr pointeurName = nameHandle.AddrOfPinnedObject();
int notNewConnection = APIServices.GetPeerList(cameraStreaming, pointeurId,
nbPeer, pointeurName, nbPeer, pointeurStatut, nbPeer);
// Now I'm supposed to read string with name[i] but it crashes
What did I miss? I really searched on the other topics, I thought this one could work, but still crashing.
Thanks.
I suggest you developing a tiny C++/CLI bridging layer. The purpose of this C++/CLI bridge is to take the string array returned by the DLL in the form of char** raw pointers, and convert it to a .NET string array, that can be consumed in your C# code as a simple string[].
The C++/CLI version of C# string[] (string array) is array<String^>^, e.g.:
array<String^>^ managedStringArray = gcnew array<String^>(count);
You can use the usual syntax with operator[] (i.e. managedStringArray[index]) to assign each string to the array.
You may write some code like this:
// C++/CLI wrapper around your C++ native DLL
ref class YourDllWrapper
{
public:
// Wrap the call to the function of your native C++ DLL,
// and return the string array using the .NET managed array type
array<String^>^ GetPeerList( /* parameters ... */ )
{
// C++ code that calls your DLL function, and gets
// the string array from the DLL.
// ...
// Build a .NET string array and fill it with
// the strings returned from the native DLL
array<String^>^ result = gcnew array<String^>(count);
for (int i = 0; i < count; i++)
{
result[i] = /* i-th string from the DLL */ ;
}
return result;
}
...
}
You may find this article on CodeProject on C++/CLI arrays an interesting reading as well.
P.S. The strings returned from your native DLL are in the form of char-strings. On the other hand, .NET strings are Unicode UTF-16 strings. So you need to clarify what encoding is used to represent text in your native strings, and convert to UTF-16 for .NET strings.

String(33, 0) in VB 6.0 and equivalent in C#

What is the meaning of UserName = String(33, 0) in VB 6.0 and what will be the equivalent in C#.
Please help I'm getting error while converting VB 6.0 code into C#.
Thanks in advance.
String in VB6 is a function that returns a string containing a repeating character string of the length specified.
String(number,character)
example:
strTest = String(5, "a")
' strTest = "aaaaa"
strTest = String(5, 97)
' strTest = "aaaaa" (97 is the ASCII code for "a")
In this case, String(33,0) will return a string containing 33 null characters.
The equivalent in C# would be
UserName = new String('\0', 33);
In VB6, that function creates a string that contains 33 characters, all of whom have zero ordinal value.
Typically you do that because you are about to pass the string to some native function which fills out the buffer. In C# the closest equivalent to that would be to create a StringBuilder instance which you would then pass to the native code in a p/invoke function call.
I think that a direct translation of that single line of code is not particularly useful. That code exists in context and I strongly suspect that the context is important.
So, whilst you could create a new C# string with 33 null characters, what would be the point of that? Since the .net string is immutable, you cannot do very much of interest with it. In your VB6 code you will surely be mutating that object, and so StringBuilder is, in my view, the most likely tool for the job.
I believe you are looking for:
UserName = new String((Char)0, 33);
Reference this for what the VB6 method did.
You can create an function that perform this action, or o can do a extenssion of the class String.
using System;
public class Program
{
public static void Main()
{
Console.WriteLine(strGen("01",3));
}
//param s is the string that you can generete and the n param is the how many times.
private static string strGen(String s, int n){
string r = string.Empty;
for (int x = 1; x <= n; x++)
r += string.Copy(s);
return r;
}
}

How do I marshal a pointer to a series of null-terminated strings in C#?

I need some help with the following. I've got a c++ API (no access to source) and I'm struggling with the methods returning char* attributes, or returned structures containing char* attributes. According to the API's documentation the return value is as follows:
Return Values
If the function succeeds, the return value is a pointer to a series of null-terminated strings, one for each project on the host system, ending with a second null character. The following example shows the buffer contents with <null> representing the terminating null character:
project1<null>project2<null>project3<null><null>
If the function fails, the return value is NULL
The problem I'm having is that the returned pointer in C# only contains the first value... project1 in this case. How can I get the full list to be able to loop through them on the managed side?
Here's the c# code:
[DllImport("vmdsapi.dll", EntryPoint = "DSGetProjectList", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr DSGetProjectList();
Calling method:
IntPtr ptrProjectList = DSAPI.DSGetProjectList();
string strProjectList = Marshal.PtrToStringAnsi(ptrProjectList).ToString();
strProjectList only contains the first item.
Here's the info from the API's header file...
DllImport char *DSGetProjectList dsproto((void));
Here's some sample code from a c++ console app which I've used for testing purposes...
char *a;
a = DSGetProjectList( );
while( *a ) {
printf("a=%s\n", a);
a += 1 + strlen(a);
}
Each iteration correctly displays every project in the list.
The problem is that when converting the C++ char* to a C# string using Marshal.PtrToStringAnsi, it stops at the first null character.
You shouldn't convert directly the char* to a string.
You could copy the char* represented by an IntPtr to a byte[] using Marshal.Copy and then extract as many string as necessary (see Matthew Watson's answer for extracting strings from a managed array), but you'll need to get the multi-string size first.
As leppie suggest you can also extract the first string using Marshal.PtrToStringAnsi then increment the pointer by this string size and extract the next string and so on. You stops when is extracts an empty string (from the last NULL character).
Something like :
IntPtr ptrProjectList = DSAPI.DSGetProjectList();
List<string> data;
string buffer;
do {
buffer = Marshal.PtrToStringAnsi(ptrProjectList);
ptrProjectList += buffer.size() + 1;
data.Add(buffer);
}while(buffer.size() > 0)
This kind of string is called a Multi-String, and it's quite common in the Windows API.
Marshaling them is fiddly. What you have to do is marshal it as a char[] array, rather than a string, and then convert the char[] array to a set of strings.
See here for an example solution. I've copied the relevant code into this answer, but it is copied from the link I gave:
static List<string> MultiStringToList(char[] multistring)
{
var stringList = new List<string>();
int i = 0;
while (i < multistring.Length)
{
int j = i;
if (multistring[j++] == '\0')
break;
while (j < multistring.Length)
{
if (multistring[j++] == '\0')
{
stringList.Add(new string(multistring, i, j - i - 1));
i = j;
break;
}
}
}
return stringList;
}

Migrate function C++ with pointers to C#

I have a function in C++ that returns pointer values​​:
fPosFirst( int &aId, char *aNname, char *aDirectory );
My syntax in c# is:
fPosFirst(ref int aId, String aNname, String aDirectory);
the function returns the id but not the string parameters that anyone knows?
If you want the parameters to be used for returning values then mark them as ref or out.
E.g.
fPosFirst(ref int aId, out string aNname, out string aDirectory);
Assuming that the native function does not "return pointers" but writes characters to the memory locations specified by aNname and aDirectory, you should be able to pass a StringBuilder with a proper capacity to the native function:
void fPosFirst(ref int aId, StringBuilder aNname, StringBuilder aDirectory);
Usage:
var aId = 0;
var aNname = new StringBuilder(260);
var aDirectory = new StringBuilder(260);
fPosFirst(ref aId, aNname, aDirectory);
You need to make the string parameters ref or out if you want to assign to them within the method and have those values available outside the method.
You need to use out keyword before parameters names (example). But actually this is not good practice in C#

how do I set a character at an index in a string in c#?

someString[someRandomIdx] = 'g';
will give me an error.
How do I achieve the above?
If it is of type string then you can't do that because strings are immutable - they cannot be changed once they are set.
To achieve what you desire, you can use a StringBuilder
StringBuilder someString = new StringBuilder("someString");
someString[4] = 'g';
Update
Why use a string, instead of a StringBuilder? For lots of reasons. Here are some I can think of:
Accessing the value of a string is faster.
strings can be interned (this doesn't always happen), so that if you create a string with the same value then no extra memory is used.
strings are immutable, so they work better in hash based collections and they are inherently thread safe.
C# strings are immutable. You should create a new string with the modified contents.
char[] charArr = someString.ToCharArray();
charArr[someRandomIdx] = 'g'; // freely modify the array
someString = new string(charArr); // create a new string with array contents.
Since no one mentioned a one-liner solution:
someString = someString.Remove(index, 1).Insert(index, "g");
If you absolutely must change the existing instance of a string, there is a way with unsafe code:
public static unsafe void ChangeCharInString(ref string str, char c, int index)
{
GCHandle handle;
try
{
handle = GCHandle.Alloc(str, GCHandleType.Pinned);
char* ptr = (char*)handle.AddrOfPinnedObject();
ptr[index] = c;
}
finally
{
try
{
handle.Free();
}
catch(InvalidOperationException)
{
}
}
}
Check out this article on how to modify string contents in C#.
Strings are immutable so they must be converted into intermediate objects before they can be modified.
If you're willing to introduce Also(...):
public static T Also<T>(this T arg, Action<T> act) { act(arg); return arg; }
public static string ReplaceCharAt(this string str, int index, char replacement) =>
new string(str.ToCharArray().Also(arr => arr[index] = replacement));
you can also use Insert() method e.g. somestring.Insert(index,data)

Categories