C# .NET wrapper library for standard C++ classes
– Enabling you create and access standard C++ class object from C# code
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
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 custom 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.
1 2 3 4 |
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 C++ string class (std::string), we would not be able to marshal the C++ string (std::string) in C# and can not call the C++ method or C++ class function member neither because we can not simply marshal it as C# string or C# StringBuilder, neither of them will work at all. Now, since we have StdString C# .NET class, the wrapper class of C++ string (std::string), we will be able to marshal the C++ string class (std::string) as StdString as shown by the following code,
1 2 3 4 5 6 7 8 9 |
// 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 article, 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 specifically, its the limit of that C++ DLL.