Click here to Skip to main content
Email Password   helpLost your password?

Contents

Overview

This article presents a C++/CLI class StringConvertor which can be used to make conversions between System::String and native types like a char*, BSTR etc. The class keeps track of allocated unmanaged memory handles and frees them automatically, so the caller needn't worry about freeing up unmanaged resources.

The class has several constructor overloads that accept various native types (as well as managed types) like char*, __wchar_t* etc and internally maintains a System::String object that can be extracted using the String operator or the ToString override. There are also various properties each exposing a native type like char*, BSTR etc which can be used to convert the string to your preferred unmanaged type.

The class reference follows with suitable examples. Make sure you read the Class usage tips section at the end though.

[Back to Contents]

Class Reference : StringUtilities::StringConvertor

Namespace

The StringConvertor class is declared in the StringUtilities namespace.

Constructors

StringConvertor(String^ s)

Initializes a new instance of the StringConvertor class using the specified System::String object. Throws an exception if a nullptr is passed to it.
String^ str1 = "Hello world";
StringConvertor sc1(str1);

StringConvertor(const char* s)

Initializes a new instance of the StringConvertor class using the specified const char*. Throws an exception if a nullptr (equates to null for native objects)is passed to it.
const char* str2 = "Apples and oranges";
StringConvertor sc2(str2);

StringConvertor(const __wchar_t* s)

Initializes a new instance of the StringConvertor class using the specified const __wchar_t*. Throws an exception if a nullptr (equates to null for native objects)is passed to it.
const __wchar_t* str3 = L"Apples and oranges";
StringConvertor sc3(str3);

StringConvertor(array<Char>^ s)

Initializes a new instance of the StringConvertor class using the specified CLI array of type Char. Throws an exception if a nullptr is passed to it.
array<Char>^ str4 = {'H', 'e', 'y', '.'};
StringConvertor sc4(str4);

StringConvertor(BSTR s)

Initializes a new instance of the StringConvertor class using the specified BSTR. Throws an exception if a nullptr (equates to null for native objects)is passed to it.
BSTR str5 = SysAllocString(L"Interesting");
StringConvertor sc5(str5);
SysFreeString(str5);

StringConvertor(std::string s)

Initializes a new instance of the StringConvertor class using the specified std::string. No exception(s) thrown.
std::string str6 = "STL is kinda handy";
StringConvertor sc6(str6);

StringConvertor(std::wstring s)

Initializes a new instance of the StringConvertor class using the specified std::wstring. No exception(s) thrown.
std::wstring str7 = L"STL is kinda handy";
StringConvertor sc7(str7);

Methods

virtual String^ ToString() override

Override that converts the StringConvertor to a System::String (returns the underlying String object).
StringConvertor sc1(str1);
int len = sc1.ToString()->Length;
Console::WriteLine(len);

Operators

operator String^()

Operator overload that converts the StringConvertor to a System::String (returns the underlying String object).
StringConvertor sc1(str1);
Console::WriteLine(sc1); //Operator String^ invoked

Properties

All properties in the class are read-only.

interior_ptr<const Char> InteriorConstCharPtr

Returns an interior pointer to the underlying string. The pointer is of type const Char so you can only read through it.
interior_ptr<const Char> p1 = sc1.InteriorConstCharPtr;
for(interior_ptr<const Char> tmp=p1; *tmp; tmp++)
    Console::Write(*tmp);

interior_ptr<Char> InteriorCharPtr

Returns an interior pointer to the underlying string that can be written to. Use with extreme care, as you are directly editing the System::String's internal character buffer.
interior_ptr<Char> p2 = sc2.InteriorCharPtr;
for(interior_ptr<Char> tmp=p2; *tmp; tmp++)
    if(Char::IsLower(*tmp)) //swap case

        *tmp = Char::ToUpper(*tmp);
    else
        *tmp = Char::ToLower(*tmp);        
Console::WriteLine(sc2);

char* NativeCharPtr

Returns a char* (allocated on the native heap). The class handles cleaning up of this memory.
char* p3 = sc1.NativeCharPtr;
printf("%s \r\n", p3);

__wchar_t* NativeWideCharPtr

Returns a __wchar_t* (allocated on the native heap). The class handles cleaning up of this memory.
__wchar_t* p4 = sc1.NativeWideCharPtr;
printf("%S \r\n", p4);

Note - I've included a generic text mapping, NativeTCharPtr, for these two properties so you can use it with an LPTSTR. Since it's a #define, intellisense will not detect it. NativeTCharPtr maps to NativeWideCharPtr if _UNICODE is defined, else to NativeCharPtr.

LPTSTR p5 = sc1.NativeTCharPtr;
_tprintf(_T("%s \r\n"),p5);

array<Char>^ CharArray

Returns a CLI array of Chars.
array<Char>^ arr1 = sc5.CharArray;
for each(Char c in arr1)
    Console::Write(c);

BSTR BSTRCopy

Returns a BSTR allocated natively (unmanaged heap). The class handles cleaning up of this memory.
BSTR b1 = sc5.BSTRCopy;
printf("BSTR contains %S \r\n", b1);

Each call creates a new BSTR, and if you change the contents of the returned BSTRs, newly returned BSTRs will contain the original string; for example, in the below snippet, b1 and b2 are not the same string any longer.

BSTR b1 = sc5.BSTRCopy;
b1[0] = L'X';
BSTR b2 = sc5.BSTRCopy;

If for any reason, you want to change the string represented by the StringConvertor, use the InteriorCharPtr property.

std::string STLAnsiString

Returns an std::string object.
std::string s1 = sc5.STLAnsiString;
printf("%s \r\n", s1.c_str());

std::wstring STLWideString

Returns an std::wstring object.
std::wstring s2 = sc5.STLWideString;
printf("%S \r\n", s2.c_str());

[Back to Contents]

Class usage tips

Two of the primary purposes of the class are (1) to make it easy to convert from System::String to unmanaged types and vice-versa and (2) to free the user from the responsibility of freeing up the unmanaged memory allocations. The various properties and constructors handle the first role and the destructor handles the second one. When I say destructor, I mean destructor (not the finalizer), which means the destructor needs to get called as soon as the object goes out of scope or has served its purpose. For that, it's recommended that you either use the auto-variable non-handle declaration format or explicitly call delete on the StringConvertor object after use.

void SomeFunc(...)
{
    StringConvertor s1(...);

    //...


} //Destructor gets called

or

StringConvertor^ s1 = gcnew StringConvertor(...);

//...


delete s1; //Destructor gets called

Try to avoid making a StringConvertor a member of a long-living class (or better still, never make it a class-member). Always try and limit the scope and life-time of StringConvertor objects. While, a finalizer has been provided (for unanticipated circumstances), ideally, the finalizer should never need to be called. To help you with avoiding this, in debug builds, an exception gets thrown if the finalizer is ever called and in release builds, Trace::WriteLine is used to write a warning message to any active trace listeners.

When you have functions returning unmanaged strings (like a char*), you need to make sure that the pointer you return is allocated by you separately (since the pointer returned by StringConvertor will be freed when the StringConvertor goes out of scope. The snippets below show two functions, one returning a BSTR and the other returning a char*.

BSTR ReturnBSTR(String^ s)
{
    return SysAllocString(StringConvertor(s).BSTRCopy);
}
char* ReturnCharPointer(String^ s)
{
    StringConvertor sc(s);
    char* str = new char[sc.ToString()->Length + 1];
    strcpy(str, sc.NativeCharPtr);
    return str;
}

And when you've done using the strings returned by these functions, you need to free the pointers manually.

BSTR s = ReturnBSTR(gcnew String("Hello there"));
printf("%S \r\n",s);
SysFreeString(s);
char* str = ReturnCharPointer(gcnew String("Hello again"));
printf("%s \r\n",str);
delete[] str;

[Back to Contents]

Design notes

Most of the data conversions make use of the Marshal class from the System::Runtime::InteropServices namespace. Now most of these functions allocate memory in the native heap and the class uses two vector<> objects to keep track of all allocated memory handles. One vector<> stores all the pointers that need to be freed using Marshal::FreeHGlobal while the other one stores all the BSTR pointers that need to be freed using Marshal::FreeBSTR. The initialization and clean-up of these vectors is done by the StringConvertorBase class which is an abstract ref class that's the base class for the StringConvertor class.

Initially, I had designed the class so that the unmanaged pointers are cached, so that multiple calls to any of the properties that returned pointers to the native heap always returned the cached pointer. But I soon saw that this meant that, if the caller modifies data using one of these pointers, it dirties the internal state of the StringConvertor object which was obviously something that should not be allowed. Considering that, managed-unmanaged transitions should not be over-used and that in most frequent situations, the required life-time of the unmanaged data is very small, usually for making a function call, I assumed that any user of this class with any level of adequacy as a programmer would never abuse the class to such an extent that there'd be lots of unmanaged memory lying un-freed. So the vectors and the unmanaged pointers/handles they contain are freed up in the class destructor (as well as in the finalizer as a safety measure).

As earlier mentioned, if for any reason, you want to change the internal string of the StringConvertor object, use the InteriorCharPtr property and write to the buffer directly - but be very careful how you use it.

Why I left CString out?

Conversions to CString and from CString are so trivial, that it didn't make sense to add them to this class.

CString str("Hello");
String^ s = gcnew String(str); //CString to String

CString strnew(s); //String to CString

[Back to Contents]

Conclusion

I believe I've covered all the basic data types and that these types can be used to create any other more complex data types. For example, the BSTR can be used to make a CComBSTR or a _bstr_t, while the char* (or __wchar_t*) can be used to make a CString.

Suggestions and feedback are welcome.

[Back to Contents]

History

[Back to Contents]

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralCompiler error C2039
butch80
17:18 16 Oct '07  
Hi I found this class while desperately looking for a way to cast a .NET string to char* in C++

I tried to include the StringConvertor in my project (Visual Studio 2005, VC++, managed library) and got 18 C2039 errors when I tried to compile it.

The compiler complains about FreeHGlobal and FreeBTSTR (among others) not being members of System::Runtime::InteropServices::Marshal, which they obviously are ...

I found something on MSDN but I couldn't make much of it. The last example pointed out some problems with the release of resources.

Any help would be really appreciated!!

Sebastian
GeneralI wish I'd seen this before
Asheem mamoowala
15:09 19 Sep '07  
Thanks so much for this. It re-iterated my approach
General[Message Removed]
nompel
1:03 5 Oct '08  
Spam message removed
QuestionBunch of compiler errors.
foahchon
22:56 25 Aug '06  
When I first tried to include the header file for this class in a CLR project, it said it couldn't find wtypes.h, nor windef.h. So I went out and downloaded these and put them in my include directory. Now when I try to compile, I get about 25 errors. Are there particular wtypes.h and windef.h files I need for VC++.NET 2005?
AnswerRe: Bunch of compiler errors.
Nishant Sivakumar
3:49 26 Aug '06  
What edition of VC++ 2005 are you using?

Regards,
Nish
Nish’s thoughts on MFC, C++/CLI and .NET (my blog)
Currently working on C++/CLI in Action for Manning Publications.
Also visit the Ultimate Toolbox blog (New)

QuestionRe: Bunch of compiler errors.
foahchon
9:09 26 Aug '06  
I am using Express Edition.
AnswerRe: Bunch of compiler errors.
Nishant Sivakumar
9:26 26 Aug '06  
foahchon wrote:
I am using Express Edition.


Then you'd probably need to download, install and configure the Platform SDK appropriately.

Regards,
Nish
Nish’s thoughts on MFC, C++/CLI and .NET (my blog)
Currently working on C++/CLI in Action for Manning Publications.
Also visit the Ultimate Toolbox blog (New)

GeneralReally Nice Class
luis.filipe.sousa
2:30 16 Jun '06  
Hi.

I just want to thank for this code sample.
This is exactly what I need, since I have to perform some conversions
from System::String to char *.
I'm new to Visual C++, and had to write a module in Managed C++, your articles and code samples are very usefull.
Keep up with the good work.
Best regards

Luis Filipe de Sousa
GeneralGreat class!
jrocket
4:22 17 May '06  
Great class, thank you for sharing.
The only thing I had to change was to fix the spelling of "convertor" to "converter" Smile

GeneralUsage from another assembly?
Doc Lobster
3:28 24 Apr '06  
Hi Nish,

your class is really great! I got one problem, though - After I moved the class from the main assembly into a utility assembly, it stopped working. I figured to declare both classes public, so they are visible across assembly borders. But when I try to use it, I get the following compile warning/errors:


StringUtilities::StringConvertor FileNameConv(FileName); // warning C4679
// (StringUtilities::StringConvertor::.ctor could not be imported)


openDocument(FileName.STLWideString); // error
// ("StringUtilities::StringConvertor::STLWideString::get":candidate function(s) not accessible)


After looking up the error C3767 on msdn, I found that native types are private for an assembly, but they could be made public by #pragma make_public(type). I tried this, but the next compiler C2158 error told me:
"#pragma make_public directive is currently supported for native non-template types only. "

Is it correct that STL objects cannot be used over assembly boundaries?

Best Regards,

Andreas
GeneralRe: Usage from another assembly?
Aki999
6:24 31 Jul '07  
There is a way to use the Conversion-Functions across Managed-DLL Borders:

For this problem I have used an unmanaged class. And the managed parameters and return-values are wrapped by the auto_gcroot class:

Here is a part of that code:

// C_ManagedHelper.h
class MANAGEDCOMMON_API C_ManagedHelper
{
public:
static CString StringToCString(msclr::auto_gcroot strSource);
static std::string StringToStdString(msclr::auto_gcroot strSource);
static msclr::auto_gcroot CStringToString(CString strSource);
static msclr::auto_gcroot StdStringToString(std::string strSource);
};

// C_ManagedHelper.cpp
CString C_ManagedHelper::StringToCString(msclr::auto_gcroot strSource)
{
CString mfcnew((String^)strSource.get());
return mfcnew;
}

std::string C_ManagedHelper::StringToStdString(msclr::auto_gcroot strSource)
{
IntPtr p = Marshal::StringToHGlobalAnsi((String^)strSource);
std::string stlnew(static_cast(p.ToPointer()));
Marshal::FreeHGlobal(p);
return stlnew.c_str();
}

msclr::auto_gcroot C_ManagedHelper::CStringToString(CString strSource)
{
return gcnew String(strSource);
}

msclr::auto_gcroot C_ManagedHelper::StdStringToString(std::string strSource)
{
return gcnew String(strSource.c_str());
}

Generalhow to convert std::string to CString,Th U
Yang Jing
23:50 6 Apr '06  
how to convert std::string to CString?Th U
GeneralGreat class! I use it quite a bit!
Tom Archer - MSFT
14:02 25 Feb '06  
As one of the 5 people on earth that write mixed-mode applications Smile I have to say that this class was a huge-timesaver for me. Thanks Nish.

The only thing that threw me for the a loop was the verbiage, "auto-variable non-handle declaration format". Definitely took me a few minutes to figure out that were simply saying I should either declare a StringConvertor on the stack or make sure I delete it if allocated dynamically. Smile

Anyway, great work and thanks much!!

Tom Archer (blog)
Program Manager
MSDN Online (Windows Vista and Visual C++)
MICROSOFT
GeneralRe: Great class! I use it quite a bit!
Nishant Sivakumar
10:25 26 Feb '06  
Tom Archer - MSFT wrote:
As one of the 5 people on earth that write mixed-mode applications I have to say that this class was a huge-timesaver for me. Thanks Nish.


Thanks Tom! There was a memory leak bug in 2 of the methods that I had fixed in my local copy, but never got around to updating the CP article. I have updated the CP article now (today in fact). If you don't want to re-download it (perhaps if you've made some mods on your own), you can change the functions shown below (unless you had done that already).

//Returns an std::string object
property std::string STLAnsiString
{
std::string get()
{
IntPtr ptr = Marshal::StringToHGlobalAnsi(m_String);
if(ptr != IntPtr::Zero)
{
std::string tmp(reinterpret_cast<char*>(static_cast<void*>(ptr)));
Marshal::FreeHGlobal(ptr);
return tmp;
}
return std::string();
}
}

//Returns an std::wstring object
property std::wstring STLWideString
{
std::wstring get()
{
IntPtr ptr = Marshal::StringToHGlobalUni(m_String);
if(ptr != IntPtr::Zero)
{
std::wstring tmp(reinterpret_cast<__wchar_t*>(static_cast<void*>(ptr)));
Marshal::FreeHGlobal(ptr);
return tmp;
}
return std::wstring();
}
}


Tom Archer - MSFT wrote:
The only thing that threw me for the a loop was the verbiage, "auto-variable non-handle declaration format". Definitely took me a few minutes to figure out that were simply saying I should either declare a StringConvertor on the stack or make sure I delete it if allocated dynamically.


LOL - sorry about that Smile

Tom Archer - MSFT wrote:
Anyway, great work and thanks much!!


Thanks again, Tom.

Regards,
Nish
Nish’s thoughts on MFC, C++/CLI and .NET (my blog)
The Ultimate Grid - The #1 MFC grid out there!

GeneralRe: Great class! I use it quite a bit!
Tom Archer - MSFT
13:30 26 Feb '06  
Cool. I appreciate the updates as I had a memory leak that I hadn't had time to track down yet!

Tom Archer (blog)
Program Manager
MSDN Online (Windows Vista and Visual C++)
MICROSOFT
GeneralVC++ 2003
Snuckles
5:53 27 Jun '05  
I see from previous threads posted to you that this only works in the 2005 environment. How would I use this class in a .NET 2003 environment?

I'm basically looking to convert a CString to a String class. I see that gcnew is not available until 2005 if I'm not mistaken.

Thanks.

GeneralRe: VC++ 2003
Nishant Sivakumar
11:58 7 Dec '05  
Sorry, the class won't work in VC++ 2003.
GeneralRe: VC++ 2003
mpodwyso
13:39 6 Jun '06  
I have a VC++ 2003 version available at my blog here: http://geekswithblogs.net/podwysocki/archive/2006/06/06/80967.aspx if anyone is interested.

Matthew Podwysocki
http://www.geekswithblogs.net/Podwysocki
GeneralDon't compile my project
ronin200520010
6:51 16 Jun '05  
I don't compile successfly my project which including the file StringConvertor.h
It's my main :

#include "stdafx.h"
#include "StringConvertor.h"
#using

using namespace std;
using namespace System;


int _tmain()
{
using namespace StringUtilities;
// TODO : remplacez l'exemple de code ci-dessous par votre code.
Console::WriteLine(S"TEST");
StringConvertor L_StringConvertor(S"Hello World DIOUF 123456789");
std::string var_std_string = L_StringConvertor.STLAnsiString;
System::Console::WriteLine(var_std_string.c_str());

return 0;
}


GeneralRe: Don't compile my project
Nishant Sivakumar
7:14 16 Jun '05  
What version of VC++ are you using? You need VC++ 2005 as the syntax used is not compatible with older versions of VC++ (like VC++ 2002 or VC++ 2003).

GeneralVisual C++.NET
Wan Ling Li
12:44 7 Jun '05  
Hi,

Apologises for what maybe a stupid question, but I am trying use your class with Visual C++ .Net standard edition(I am just moving into the .net world). It seems like the compiler does not like the header file. Do I need to have a C# compiler for this to work?

I get the following compiler error:
StringConvertor.h(26) : error C2144: syntax error : 'StringUtilities::StringConvertorBase' should be preceded by ';'
the line looks like this:
ref class StringConvertorBase abstract

Thanks,

L.
GeneralRe: Visual C++.NET
Nishant Sivakumar
19:02 7 Jun '05  
You need VC++ 2005 as the syntax used is not compatible with older versions of VC++ (like VC++ 2002 or VC++ 2003).
GeneralRe: Visual C++.NET
Wan Ling Li
16:02 9 Jun '05  
Thanks for the reply. Damn, it's so hard to keep up with all these language changes...still it keeps us coders in jobs...we just have to re-invent the wheel every couple of years Smile
GeneralCan't create an instance of the StringConvertor
rickengle
20:47 3 Jun '05  
Hi I added the "StringConvertor.h" headedr to my Visual C++ 2005 project. I want to convert from a managed string to a LPTSTR. But when I compile I get the error:

Error 1 error C2065: 'StringConvertor' : undeclared identifier

My code snippet looks like this:

String^ pszDeviceNameXX;

StringConvertor sc1(pszDeviceNameXX);
pszDeviceName = sc1.NativeTCharPtr;

I get the error on this line: StringConvertor sc1(pszDeviceNameXX);

Any ideas what is going on?

Thanks and nice job on this!

GeneralRe: Can't create an instance of the StringConvertor
Nishant Sivakumar
23:21 3 Jun '05  
The class is in the StringUtilities namespace.

Put using namespace StringUtilities; on top of your code.


Last Updated 16 May 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010