Click here to Skip to main content
11,634,815 members (68,779 online)
Click here to Skip to main content

StringConvertor : A convertor class for managed-unmanaged string conversions that handles memory de-allocations

, 15 May 2005 CPOL 132.5K 1.2K 50
Rate this:
Please Sign up or sign in to vote.
A convertor class for managed-unmanaged string conversions that handles memory de-allocations. Caller need not worry about freeing unmanaged memory allocations.

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

  • May 15, 2005 : Class completed and article first published on The Code Project

[Back to Contents]

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Nish Nishant
United States United States
Nish Nishant is a Software Architect/Consultant based out of Columbus, Ohio. He has over 15 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish is a recipient of the annual Microsoft Visual C++ MVP Award since 2002 (13 consecutive awards as of 2014).

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored
C++/CLI in Action for Manning Publications in 2005, and had previously co-authored
Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his
WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

Contact Nish : You can reach Nish on his google email id voidnish.

Website and Blog

You may also be interested in...

Comments and Discussions

 
Questionhey Pin
Member 851248417-Feb-12 1:59
memberMember 851248417-Feb-12 1:59 
QuestionCompiler troubles Pin
David Fenstemaker31-Jan-12 14:31
memberDavid Fenstemaker31-Jan-12 14:31 
AnswerRe: Compiler troubles Pin
Nishant Sivakumar1-Feb-12 2:12
mvpNishant Sivakumar1-Feb-12 2:12 
GeneralRe: Compiler troubles Pin
David Fenstemaker2-Feb-12 11:04
memberDavid Fenstemaker2-Feb-12 11:04 
GeneralMy vote of 5 Pin
Mark A Manning4-Oct-11 9:25
memberMark A Manning4-Oct-11 9:25 
GeneralCompiler error C2039 Pin
butch8016-Oct-07 16:18
memberbutch8016-Oct-07 16:18 
GeneralI wish I'd seen this before Pin
Asheem mamoowala19-Sep-07 14:09
memberAsheem mamoowala19-Sep-07 14:09 
General[Message Removed] Pin
nompel5-Oct-08 0:03
membernompel5-Oct-08 0:03 
GeneralRe: I wish I'd seen this before Pin
varnk26-Aug-11 7:48
membervarnk26-Aug-11 7:48 
QuestionBunch of compiler errors. Pin
foahchon25-Aug-06 21:56
memberfoahchon25-Aug-06 21:56 
AnswerRe: Bunch of compiler errors. Pin
Nishant Sivakumar26-Aug-06 2:49
staffNishant Sivakumar26-Aug-06 2:49 
QuestionRe: Bunch of compiler errors. Pin
foahchon26-Aug-06 8:09
memberfoahchon26-Aug-06 8:09 
AnswerRe: Bunch of compiler errors. Pin
Nishant Sivakumar26-Aug-06 8:26
staffNishant Sivakumar26-Aug-06 8:26 
GeneralReally Nice Class Pin
luis.filipe.sousa16-Jun-06 1:30
memberluis.filipe.sousa16-Jun-06 1:30 
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! Pin
jrocket17-May-06 3:22
memberjrocket17-May-06 3:22 
QuestionUsage from another assembly? Pin
Doc Lobster24-Apr-06 2:28
memberDoc Lobster24-Apr-06 2:28 
AnswerRe: Usage from another assembly? Pin
Aki99931-Jul-07 5:24
memberAki99931-Jul-07 5:24 
Questionhow to convert std::string to CString,Th U Pin
Yang Jing6-Apr-06 22:50
memberYang Jing6-Apr-06 22:50 
GeneralGreat class! I use it quite a bit! Pin
Tom Archer - MSFT25-Feb-06 13:02
memberTom Archer - MSFT25-Feb-06 13:02 
GeneralRe: Great class! I use it quite a bit! Pin
Nishant Sivakumar26-Feb-06 9:25
staffNishant Sivakumar26-Feb-06 9:25 
GeneralRe: Great class! I use it quite a bit! Pin
Tom Archer - MSFT26-Feb-06 12:30
memberTom Archer - MSFT26-Feb-06 12:30 
GeneralVC++ 2003 Pin
Snuckles27-Jun-05 4:53
memberSnuckles27-Jun-05 4:53 
GeneralRe: VC++ 2003 Pin
Nishant Sivakumar7-Dec-05 10:58
staffNishant Sivakumar7-Dec-05 10:58 
GeneralRe: VC++ 2003 Pin
mpodwyso6-Jun-06 12:39
membermpodwyso6-Jun-06 12:39 
GeneralDon't compile my project Pin
ronin20052001016-Jun-05 5:51
memberronin20052001016-Jun-05 5:51 
GeneralRe: Don't compile my project Pin
Nishant Sivakumar16-Jun-05 6:14
staffNishant Sivakumar16-Jun-05 6:14 
GeneralVisual C++.NET Pin
Wan Ling Li7-Jun-05 11:44
memberWan Ling Li7-Jun-05 11:44 
GeneralRe: Visual C++.NET Pin
Nishant Sivakumar7-Jun-05 18:02
staffNishant Sivakumar7-Jun-05 18:02 
GeneralRe: Visual C++.NET Pin
Wan Ling Li9-Jun-05 15:02
memberWan Ling Li9-Jun-05 15:02 
GeneralCan't create an instance of the StringConvertor Pin
rickengle3-Jun-05 19:47
memberrickengle3-Jun-05 19:47 
GeneralRe: Can't create an instance of the StringConvertor Pin
Nishant Sivakumar3-Jun-05 22:21
staffNishant Sivakumar3-Jun-05 22:21 
GeneralConstructors Pin
Nemanja Trifunovic19-May-05 3:11
memberNemanja Trifunovic19-May-05 3:11 
GeneralRe: Constructors Pin
Nishant Sivakumar19-May-05 5:57
staffNishant Sivakumar19-May-05 5:57 
GeneralConverting String^ to string Pin
Majid Shahabfar18-May-05 8:07
memberMajid Shahabfar18-May-05 8:07 
GeneralRe: Converting String^ to string Pin
Nishant Sivakumar18-May-05 8:22
staffNishant Sivakumar18-May-05 8:22 
GeneralNifty Class Pin
_Abhi_MS_16-May-05 22:17
member_Abhi_MS_16-May-05 22:17 
GeneralRe: Nifty Class Pin
Nishant Sivakumar17-May-05 19:35
staffNishant Sivakumar17-May-05 19:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150728.1 | Last Updated 16 May 2005
Article Copyright 2005 by Nish Nishant
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid