Quantcast
Channel: xInterop C++ .NET Bridge
Viewing all articles
Browse latest Browse all 31

Marshaling C++ string, a std::string or std::wstring in C#

$
0
0

Marshaling C++ std::string in C# is possible and doable and it is done

Marshaling C++ std::string in C# is difficult, developers asked questions for help on the web everywhere for many years since .NET was born and PInvoke Interop became a must between C++ and .NET.(C++/CLI uses PInvoke as well, it is implicit P/Invoke, the actual P/Invoke implementation is invisible to the developers, but it still uses P/Invoke), I have a list of questions asked on the web here, they are all different situations, but all they want to do is to able to marshal or access the std::string from C#.

C# Strings in C++

Interface to C++ (unmanaged) DLL and std::string or std::wstring

Pass C# string to C++ and pass C++ result (string, char*.. whatever) to C#

 

All the answers point out that there is no way you can do in C# because the nature of std:string. Is that really true that you really do not have any way to marshal a std::string in C#? It is really a simple object in C++, right?

A std::string in C++, is not a sequence of char like char*, char[], it is an instance of C++ class, which means you will have to access it through the interface of the std::string class. You can not simply marshal a std::string in C++ to string, StringBuilder in C#, it is not going to work, it is just wrong, the .NET run-time will throw out memory access violation exception.

C# Wrapper Library for C++ Run-Time Classes

All that said, it is difficult to create and access a std::string from C#, but the truth is that you can, you can access any instance of C++ class from C# if it can be accessed from C++ code via dynamic linking. That is where the C# Wrapper Library for C++ run-time comes into play, it will make the std::string to C# string marshaling so easy. The C# Wrapper Library implements wrapper classes for most of the run-time standard C++ classes. It is certainly a big helper when automatically generating a C# wrapper for C++ DLL, It is also very useful when you have C DLL involving some standard C++ classes in its interface of C style methods. For any standard classes, such as std::string, std::wstring, std::iostream, std::fstream, you won’t be able to create or access them without such a wrapper library in C#.

A C++ std::string can be created and accessed from C# by using its C# wrapper class, StdString.(Same to std::wstring, it has wrapper class, StdWString)

Metadata of StdString, the C# wrapper class of std::string

I am showing the complete metadata of the C# wrapper class of StdString.

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

namespace NSpeech.Win32.StdLib
{
    public class StdString : StdLibExpressBaseClass
    {
        public StdString();
        public StdString(IntPtr handle);
        public StdString(StdString that);
        public StdString(string s);
        public StdString(StdString str, ulong pos);
        public StdString(StdStringCIterator first, StdStringCIterator last);
        public StdString(StdStringCRIterator first, StdStringCRIterator last);
        public StdString(string s, ulong n);
        public StdString(ulong n, char c);
        public StdString(IntPtr thisObject, IntPtr thatObject, bool initialize);
        public StdString(StdString str, ulong pos, ulong len);

        public static implicit operator string(StdString o);
        public static implicit operator StdString(string o);

        protected override int StorageSize { get; }
        public string TemplateClassName { get; }

        public char this[ulong index] { get; set; }

        public StdString append(StdString right);
        public StdString append(string ptr);
        public StdString append(StdStringCIterator first, StdStringCIterator last);
        public StdString append(StdStringCRIterator first, StdStringCRIterator last);
        public StdString append(string first, string last);
        public StdString append(string ptr, ulong count);
        public StdString append(ulong count, char ch);
        public StdString append(StdString right, ulong roff, ulong count);
        public StdString assign(StdString right);
        public StdString assign(string ptr);
        public StdString assign(StdStringCIterator first, StdStringCIterator last);
        public StdString assign(StdStringCRIterator first, StdStringCRIterator last);
        public StdString assign(string first, string last);
        public StdString assign(string ptr, ulong count);
        public StdString assign(ulong count, char ch);
        public StdString assign(StdString right, ulong roff, ulong count);
        public char at(ulong off);
        public char back();
        public StdStringCIterator begin();
        public string c_str();
        public ulong capacity();
        public StdStringCIterator cbegin();
        public StdStringCIterator cend();
        protected override void CleanupNativeResource();
        public void clear();
        public int compare(StdString right);
        public int compare(string ptr);
        public int compare(ulong off, ulong n0, StdString right);
        public int compare(ulong off, ulong n0, string ptr);
        public int compare(ulong off, ulong n0, string ptr, ulong count);
        public int compare(ulong off, ulong n0, StdString right, ulong roff, ulong count);
        public ulong copy(StringBuilder ptr, ulong count, ulong off);
        public StdStringCRIterator crbegin();
        public StdStringCRIterator crend();
        public string data();
        public bool empty();
        public StdStringCIterator end();
        public StdStringIterator erase(StdStringCIterator p);
        public StdStringIterator erase(StdStringCIterator first, StdStringCIterator last);
        public StdString erase(ulong off, ulong count);
        public ulong find(char ch, ulong off);
        public ulong find(StdString right, ulong off);
        public ulong find(string ptr, ulong off);
        public ulong find(string ptr, ulong off, ulong count);
        public ulong find_first_not_of(char ch, ulong off);
        public ulong find_first_not_of(StdString right, ulong off);
        public ulong find_first_not_of(string ptr, ulong off);
        public ulong find_first_not_of(string ptr, ulong off, ulong count);
        public ulong find_first_of(char ch, ulong off);
        public ulong find_first_of(StdString right, ulong off);
        public ulong find_first_of(string ptr, ulong off);
        public ulong find_first_of(string ptr, ulong off, ulong count);
        public ulong find_last_not_of(char ch, ulong off);
        public ulong find_last_not_of(StdString right, ulong off);
        public ulong find_last_not_of(string ptr, ulong off);
        public ulong find_last_not_of(string ptr, ulong off, ulong count);
        public ulong find_last_of(char ch, ulong off);
        public ulong find_last_of(StdString right, ulong off);
        public ulong find_last_of(string ptr, ulong off);
        public ulong find_last_of(string ptr, ulong off, ulong count);
        public char front();
        public StdStringIterator insert(StdStringCIterator where);
        public StdStringIterator insert(StdStringCIterator p, char c);
        public StdString insert(ulong off, StdString right);
        public StdString insert(ulong off, string ptr);
        public void insert(StdStringCIterator p, StdStringCIterator first, StdStringCIterator last);
        public void insert(StdStringCIterator p, StdStringCRIterator first, StdStringCRIterator last);
        public void insert(StdStringCIterator where, string first, string last);
        public void insert(StdStringCIterator p, ulong n, char c);
        public StdString insert(ulong off, string ptr, ulong count);
        public StdString insert(ulong off, ulong count, char ch);
        public StdString insert(ulong off, StdString right, ulong roff, ulong count);
        public ulong length();
        public ulong max_size();
        protected IntPtr operator_get_set(ulong off);
        public StdString operator_plus_equal(char ch);
        public StdString operator_plus_equal(StdString right);
        public StdString operator_plus_equal(string ptr);
        public void pop_back();
        public void push_back(char ch);
        public StdStringCRIterator rbegin();
        public StdStringCRIterator rend();
        public StdString replace(StdStringCIterator i1, StdStringCIterator i2, StdString str);
        public StdString replace(StdStringCIterator i1, StdStringCIterator i2, string s);
        public StdString replace(ulong off, ulong n0, StdString right);
        public StdString replace(ulong off, ulong n0, string ptr);
        public StdString replace(StdStringCIterator i1, StdStringCIterator i2, StdStringCIterator first, StdStringCIterator last);
        public StdString replace(StdStringCIterator first, StdStringCIterator last, string first2, string last2);
        public StdString replace(StdStringCIterator i1, StdStringCIterator i2, string s, ulong n);
        public StdString replace(StdStringCIterator i1, StdStringCIterator i2, ulong n, char c);
        public StdString replace(ulong off, ulong n0, string ptr, ulong count);
        public StdString replace(ulong off, ulong n0, ulong count, char ch);
        public StdString replace(ulong off, ulong n0, StdString right, ulong roff, ulong count);
        public void reserve(ulong newcap);
        public void resize(ulong newsize);
        public void resize(ulong newsize, char ch);
        public ulong rfind(char ch, ulong off);
        public ulong rfind(StdString right, ulong off);
        public ulong rfind(string ptr, ulong off);
        public ulong rfind(string ptr, ulong off, ulong count);
        public void shrink_to_fit();
        public ulong size();
        public StdString substr(ulong off, ulong count);
        public void swap(StdString right);
        public override string ToString();

        public class Marshaller : ICustomMarshaler
        {
            public Marshaller();

            public void CleanUpManagedData(object ManagedObj);
            public void CleanUpNativeData(IntPtr pNativeData);
            public static ICustomMarshaler GetInstance(string pstrCookie);
            public int GetNativeDataSize();
            public IntPtr MarshalManagedToNative(object ManagedObj);
            public object MarshalNativeToManaged(IntPtr pNativeData);
        }
    }
}

 

C# Wrapper Generator for C++ DLL

The class of StdString was generated by C# Wrapper Generator for C++ DLL automatically, it was pre-generated and put in the C# wrapper library for C++ runtime for convenience, since there won’t be difference between a pre-generated one and the one generated for a custom C++ DLL later, and a Custom C++ DLL mostly won’t have a std::string exported in the DLL anyway.

If you look into the StdString class, you will find all the methods you are familiar in C++. If you have pieces of code you have from C++ to manipulate the strings, you have do the same in C# now, the only thing is it will affect the performance because you will be calling each C++ method through P/Invoke.

Example of marshaling std::string from C#

Let’s write a real C function involving accepting a std::string and returning a std::string.

#include <string>

extern "C" __declspec(dllexport) std::string Encrypt(const std::string& s)
{
    // It is an example, it does not really encrypt anything for the std::string of s.
    return s;
}

There are different ways to declare the P/Invoke signature in C#.

You can either marshal std::string to IntPtr or StdString depending on your preference.

Marshal std::string to IntPtr

// Let's just marshal everything to pointer using IntPtr, to C#, they are really just 
// memory pointer no matter whey they represent in native C++.
[DllImport("SampleDll.DLL")]
public static IntPtr Encrypt(IntPtr text);

// Calling the method
string text = "My Text";
// A returned IntPtr can be used to construct a StdString and then a 
// C# string.
string encryptedText = new StdString(Encrypt(new StdString(text));

Marsahl std::string to StdString

// Let's marshal std::string to StdString which can be converted to and converted// from a C# string implicitly
[DllImport("SampleDll.DLL")]
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StdStringVector.Marshaller))]
public static StdString Encrypt([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StdStringVector.Marshaller))] StdString text);

// Calling the method
string text = "My Text";
// A C# string can be converted to StdString implicitly.
// A StdString can be converted to C# string implicitly.
string encryptedText = Encrypt(text);

You have seen how a std::string can be marshaled from C# to C++ by using StdString from the C# Wrapper Library for C++ DLL. Without such a library, passing or accessing a std::string from C# is just so difficult, P/Invoke do not necessarily mean difficult, with certain tools such as the C# Wrapper Library for C++ DLL.

 

 

 


Viewing all articles
Browse latest Browse all 31

Trending Articles