In order for our C# wrapper generator for C++ DLL to automatically generate C# source code for C++ DLL, we would have to have a C# wrapper library for the standard C++ run-time library first. Since we were already writing the C# wrapper generator, we used the C# wrapper generator to create the C# wrapper library for the C++ standard run-time classes after some fine-tuning specifically done to the C# wrapper generator for C++ standard classes.
Here are the list of all the C# wrapper classes in the C# wrapper library and the corresponding standard C++ classes.
StdString (std::string) StdStringBuf (std::stringbuf) StdStringIterator (std::string::iterator) StdStringReverseIterator (std::string::reverse_iterator) StdStringConstIterator (std::string::const_iterator) StdStringConstReverseIterator (std::string::const_reverse_iterator) StdWString (std::wstring) StdWStringBuf (std::wstringbuf) StdWStringIterator (std::wstring::iterator) StdWStringReverseIterator (std::wstring::reverse_iterator) StdWStringConstIterator (std::wstring::const_iterator) StdWStringConstReverseIterator (std::wstring::const_reverse_iterator) StdIOSBase (std::ios_base) StdIOS (std::ios) StdIOStream (std::iostream) StdStream (std::stream) StdStreamBuf (std::streambuf) StdIStream (std::istream) StdOStream (std::ostream) StdStreamPos (std::streampos) StdIFStream (std::ifstream) StdOFStream (std::ofstream) StdFStream (std::fstream) StdFileBuf (std::filebuf) StdWIOS (std::wios) StdWIOStream (std::wiostream) StdWStream (std::wstream) StdWStreamBuf (std::wstreambuf) StdWIStream (std::wistream) StdWOStream (std::wostream) StdWIFStream (std::wifstream) StdWOFStream (std::wofstream) StdWFStream (std::wfstream) StdWFileBuf (std::wfilebuf)
C++ is one of most widely used programming languages, and std::string is one of the most widely used C++ standard classes. In a C DLL, we use char *, const char* as a string parameter, and in C# you can use string, String, StringBuilder which are automatically marshaled by .NET framework to map the C style string. But, for std::string used in C DLL or C++ DLL, we do not have that luxury any more. The problem is that .NET does not marshal std::string, you simply can not use .NET string to marshal a c++ std::string which is a C++ class instance, it is not just a pointer to a buffer like char*.
Let’s start discussing StdString, the C# wrapper class for C++ std::string.
The following is the metadata of the StdString C# .NET class. It is exactly what I copied from VS studio IDE.
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); } } }
As you can see, the C# wrapper class, StdString has the exact same interface of the corresponding C++ std::string class in .NET, you can use it just like you use it in C++ language, it also provides a Marshaller class which shall allow it to be used as a customer marshaller.
Let’s look at an example of how we can use StdString.
Assuming we have a c style method in a DLL named sample.dll.
void __stdcall GetMyMessage(std::string& message) { message = "A message sent from native DLL."; }
Without the C# .NET Wrapper library and the StdString C# wrapper class for the std::string class, we would not able to marshal the std::string in C#, we can not marshal it as string or StringBuilder, both of them won’t work. Now, since we have StdString wrapper class, we will be able to marshal the std::string as StdString as following,
// std::string& can be marshalled as IntPtr since StdString has conversion // operator defined. [DllImport("Sample.dll", CallingConvention=CallingConvention.StdCall)] public extern static void GetMyMessage(IntPtr message); // You can also define the P/Invoke alternatively as following since the class has // a custom marshaller. [DllImport("Sample.dll", CallingConvention=CallingConvention.StdCall)] public extern static void GetMyMessage([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(StdString.Marshaller))] StdString message);
One last thing we would want to be clear at the end of this blog, since the underlying C++ DLL only works with certain version of C++ run-time library, and specifically, Microsoft Visual C++ run-time library, the C# .NET Wrapper library will only with that version of C++ run-time.