How to assign a value to a TCHAR array - c++

I have a TCHAR array in my C++ code which I want to assign static strings to it.
I set an initial string to it via
TCHAR myVariable[260] = TEXT("initial value");
Everything works fine on this. However, when I split it in two lines as in
TCHAR myVariable[260];
myVariable = TEXT("initial value");
it bugs and gives a compiler error:
error C2440: '=': cannot convert from 'const char [14]' to 'TCHAR [260]'
shouldn't the TEXT() function do exactly what I want here? convert the given string to TCHARs? Why does it work, when putting the two lines together? What do I have to change in order to get it working?
Some other confusing thing I have encountered:
I've searched the internet for it and have seen that there are also _T() and _TEXT() and __T() and __TEXT(). What are they for? Which of them should I use in what environment?

The reason the assignment doesn't work has very little to do with TCHARs and _T. The following won't work either.
char var[260];
var = "str"; // fails
The reason is that in C and C++ you can't assign arrays directly. Instead, you have to copy the elements one by one (using, for example, strcpy, or in your case _tcscpy).
strcpy(var, "str");
Regarding the second part of your question, TEXT, _T and the others are macros, that in Unicode builds turn a string literal to a wide-string literal. In non-Unicode builds they do nothing.

See avakar's answer for the direct answer. I was going to add this as a comment, but it really is a freestanding recommendation. I will warn you up from that this will sound like a rant but it does come from using TCHAR and then working issues for a few years before trying to remove it from a rather large codebase.
Make sure that you really understand the effect of using TCHAR arrays and their friends. They are deceptively difficult to use correctly. Here is a short list of things that you have to watch out for:
sizeof(TCHAR) is conditional: Scrutinize code that contains this. If it is doing anything other than calculating the size that is being passed to malloc() or memcpy() then it is probably wrong.
TCHAR is a type alias: Since TCHAR is nothing more than a typedef, it is really easy to write things like wcscpy(tszStr, wszAry) and be none the wiser. Basically, TCHAR is either char or wchar_t so overload selections might surprise you.
wsprintf() and swprintf() are different: This is a special case of the previous but it bears special consideration since it is so easy to make a mistake here!
If you want to use TCHAR, then make sure that you compile both UNICODE and MBCS versions regularly. If you do not do this, then you are probably going to undo any advantage that you are trying to gain by using them in the first place.
Here are two recommendations that I have for how to not use TCHAR in the first place when you are writing C++ code.
Use CString if you are using MFC or are comfortable tying yourself to MSFT.
Use std::string or std::wstring in conjunction with the type-specific API - use CreateFileA() and CreateFileW() where appropriate.
Personally, I choose the latter but the character encoding and string transcoding issues are just a nightmare from time to time.

Related

C++ C2440 Error, cannot convert from 'std::_String_iterator<_Elem,_Traits,_Alloc>' to 'LPCTSTR'

Unfortunately I have been tasked with compiling an old C++ DLL so that it will work on Win 7 64 Bit machines. I have zero C++ experience. I have muddled through other issues, but this one has stumped me and i have not found a solution elsewhere. The following code is throwing a C2440 compiler error.
The error: "error C2440: '' : cannot convert from 'std::_String_iterator<_Elem,_Traits,_Alloc>' to 'LPCTSTR'"
the code:
#include "StdAfx.h"
#include "Antenna.h"
#include "MyTypes.h"
#include "objbase.h"
const TCHAR* CAntenna::GetMdbPath()
{
if(m_MdbPath.size() <= 0) return NULL;
return LPCTSTR(m_MdbPath.begin());
}
"m_MdbPath" is defined in the Antenna.h file as
string m_MdbPath;
Any assistance or guidance someone could provide would be extremely helpful. Thank you in advance. I am happy to provide additional details on the code if needed.
std::string has a .c_str() member function that should accomplish what you're looking for. It will return a const char* (const wchar_t* with std::wstring). std::string also has an empty() member function and I recommend using nullptr instead of the NULL macro.
const TCHAR* CAntenna::GetMdbPath()
{
if(m_MdbPath.empty()) return nullptr;
return m_MdbPath.c_str();
}
The solution:
const TCHAR* CAntenna::GetMdbPath()
{
if(m_MdbPath.size() <= 0) return NULL;
return m_MdbPath.c_str();
}
will work if you can guarantee that your program is using the MBCS character set option (or if the Character Set option is set to Not Setif you're using Visual Studio), since a std::string character type will be the same as the TCHAR type.
However, if your build is UNICODE, then a std::string character type will not be the same as the TCHAR type, since TCHAR is defined as a wide character, not a single-byte character. Thus returning c_str() will give a compiler error. So your original code, plus the tentative fix of returning std::string::c_str() may work, but it is technically wrong with respect to the types being used.
You should be working directly with the types presented to your function -- if the type is TCHAR, then you should be using TCHAR based types, but again, a TCHAR is a type that changes definition depending on the build type.
As a matter of fact, the code will fail miserably at runtime if it were a UNICODE build and to fix the compiler error(s), you casted to an LPCTSTR to keep the compiler quiet. You cannot cast a string pointer type to another string pointer type. Casting is not converting, and merely casting will not convert a narrow string into a wide string and vice-versa. You will wind up with at the least, odd characters being used or displayed, and worse, a program with random weird behavior and crashes.
In addition to this you never know when you really will need to build a UNICODE version of the DLL, since MBCS builds are becoming more rare. Going by your original post, the DLL's usage of TCHAR indicates that yes, this DLL may (or even has) been built for UNICODE.
There are a few alternate solutions that you can use. Note that some of these solutions may require you to make additional coding changes beyond the string types. If you're using C++ stream objects, they may also need to be changed to match the character type (for example, std::ostream and std::wostream)
Solution 1. Use a typedef, where the string type to use depends on the build type.
For example:
#ifdef UNICODE
typedef std::wstring tchar_string;
#else
typedef std::string tchar_string;
#endif
and then use tchar_string instead of std::string. Switching between MBCS and UNICODE builds will work without having to cast strin types, but requires coding changes.
Solution 2. Use a typedef to use TCHAR as the underlying character type in the std::basic_string template.
For example:
typedef std::basic_string<TCHAR> tchar_string;
And use tchar_string in your application. You get the same public interface as std::(w)string and this allows you to switch between MBCS and UNICODE build seamlessly (with respect to the string types).
Solution 3. Go with UNICODE builds and drop MBCS builds altogether
If the application you're building is a new one, then UNICODE is the default option (at least in Visual Studio) when creating a new application. Then all you really need to do is use std::wstring.
This is sort of the opposite of the original solution given of making sure you use MBCS, but IMO this solution makes more sense if there is only going to be one build type. MBCS builds are becoming more rare, and basically should be used only for legacy apps.

How to Deal with Varying String types?

I have to work with an API that is using Microsoft's TCHAR macros and such, so I was wondering if I could use C++ in a way to simplify the task. So i was wondering if there is a way to support implicit conversion and why/why not std::string doesn't support converting from a smaller char size:
#include <Windows.h>
using String = std::basic_string<TCHAR>; // say TCHAR = wchar_t or equivalent
String someLiteralString = "my simple ansi string"; // Error here obviously
// some polymorphic class...
const TCHAR* MyOverriddenFunction() override { return someLiteralString.c_str(); }
// end some polymorphic class
The reason implicit conversion isn't supported is that conversion can be complicated. The simple case is when the string to convert is pure ASCII as in your example, but there's no way to guarantee that. The creators of the standard wisely stayed away from that problem.
If you don't know whether your strings are wide-character or not, you can use Microsoft's _T() macro around each string literal to generate the proper characters. But you say you don't want to do that.
Modern Windows programming always uses wide characters in the API. Chances are your program is too, otherwise the code you've shown would not cause an error. It's very unlikely that once you've used wide characters you'll switch back to narrow ones. A simple one-character change to your literals will make them wide-character to match the string type:
String someLiteralString = L"my simple ansi string";
Use the (ATL/MFC) CStringT class, it will make your life much easier.
http://msdn.microsoft.com/en-us/library/ms174284(v=vs.80).aspx

How do you convert C++ _tcscpy, _tcscat to Delphi?

I'm converting this code from C++ to Delphi but I don't get the following part of the code. Can anyone explain me what the following code means; what's happening to the szBuff buffer ?
I'm pretty sure it's such kind of formatting (replacement), but I don't even know what is expected as a result and I can't find any sensible documentation of the used functions (maybe I'm just a lame :)
Can anyone help me with the translation of this code to Delphi (or direct me to proper documentation) ?
I don't like this how do you convert kind of questions by myself, so I mentioned at least function names in the question title so it might searchable to someone else in the future.
function TSecInfo.BuildSecurityAttributes(var SecAttrs: TSecurityAttributes): boolean;
var
pszSidUser: PChar;
szBuff: array [0..1024] of Char;
begin
// pszSidUser at this time contains user SID like this
// S-1-5-21-1454471165-1004336348-1606980848-5555
// TCHAR szBuff[1024]; // I'm not sure with array [0..1024] of Char;
_tcscpy(szBuff, _T("D:"));
_tcscat(szBuff, _T("(A;;GA;;;"));
_tcscat(szBuff, pszSidUser);
_tcscat(szBuff, _T(")"));
_tcscat(szBuff, _T("(A;;GWGR;;;AN)"));
_tcscat(szBuff, _T("(A;;GWGR;;;WD)"));
...
_tcscat(szBuff, _T("S:(ML;;NW;;;S-1-16-0)"));
end;
For those who are interested in what's the whole code from the link about I can tell it should be a trick how to access network pipes for writing as an anonymous user on Windows Vista above. To the whole article follow this link.
Thanks for your time
Regards
_tcscpy and _tcscat are TCHAR macro versions of C standard library functions strcpy and strcat for copying and concatenating C strings. They evaluate to ANSI or Unicode versions depending on whether or the type of project you are targeting. It's really C code rather than C++ code in my view.
In Delphi you would simply use string variables like this:
function TSecInfo.BuildSecurityAttributes(var SecAttrs: TSecurityAttributes): boolean;
var
pszSidUser: PChar;
Buff: string;
begin
// pszSidUser at this time contains user SID like this
// S-1-5-21-1454471165-1004336348-1606980848-5555
Buff := 'D:(A;;GA;;;'+pszSidUser+')(A;;GWGR;;;AN)(A;;GWGR;;;WD)S:(ML;;NW;;;S-1-16-0)';
SomeOtherWindowsAPICall(PChar(Buff));
end;
Presumably in the C code there is a call to another Windows API function that receives an LPCTSTR. The C code will pass szBuff but you can simply pass PChar(Buff) as I have shown above.
The C code is using a fixed length buffer because it doesn't have available a dynamically allocated string class like Delphi's string or std::string in C++. Fixed length buffers like this often lead to buffer overruns. In Delphi don't use a fixed length buffer if you can avoid it.
This is a classic example of why languages with built in string handling are so much easier to work with than C.
It looks like the code is using TCHARS, basically they are a macro which makes going from unicode to non-unicode easier. _tcscpy is copying the parameter to szBuff, _tcscat is appending the parameter to szBuff. If you are familar with strcpy and strcat they do the same thing.
_tcscpy(szBuff, _T("D:")); //szBuff == "D:"
_tcscat(szBuff, _T("(A;;GA;;;")); //szBuff == "D:A;;GA;;;"
...

C++ TCHAR[] to string

I have this method which receives a path through a TCHAR szFileName[] variable, which contains something like C:\app\...\Failed\
I'd like to sort through it so I can verify if the name of the last folder on that path is in fact, "Failed"
I thought that using something like this would work:
std::wstring Path = szFileName;
string dirpath2;
dirpath2 = Path.substr(0,5);
But I get the following error:
Error 6 error C2679: binary '=' : no
operator found which takes a
right-hand operand of type
'std::basic_string<_Elem,_Traits,_Ax>'
(or there is no acceptable
conversion)
Needless to say, I'm very new to C++, and I've been looking for an answer for a while now, but I haven't had any luck, so any help would be appreciated :)
Either you’re consistently using wstring (the wide character variant) or string (the “normal” variant).
Since you’re getting a TCHAR (which can be either wchar_t or char, depending on compiler flags), the appropriate type to use would be a tstring, but that doesn’t exist. However, you can define a typedef for it:
typedef std::basic_string<TCHAR> tstring;
Now you can consistently use the same string type, tstring, for all your operations.
dirpath2 has to be a std::wstring as well. There are ways to convert between the two, but they involve changing the character encoding and that seems like more than you're asking for.
I like to simply not use TCHAR. Today, there is rarely is there a need to enable or disable the UNICODE macros and create both an ASCII and a Unicode version of a program. Just always use wstring, wchar_t, and the Windows API functions that end in 'W'.
If you're working on something where you don't have control over the above, Konrad's typedef answer is more practical than mine.

CFileImageLoader(LPCTSTR lpszFileName);

I have a class which expects a LPCTSTR.
When i call :
new CFileImageLoader(_T("Splash02.png"))
OR
new CFileImageLoader("Splash02.png")
both don't work.
Why ?
I'm new to cpp...
Thanks
Jonathan d.
This issue is a combination of C++ issues and Windows specific issues.
C++ defines two types of strings, regular and wide. A regular string looks like:
const char *str = "regular string";
while a wide string looks like:
const wchar_t *wstr = L"wide string";
With just standard C++, you have to decide when you write your library whether to use regular or wide strings.
Windows has defined a pseudo type called tchar. With tchar you write something like:
LPCTSTR tstr = _T("regular or wide string");
Whether this is actually a regular (char *) or a wide (wchar_t *) string depends on whether you compile your code for Unicode or not.
Since the function is specified as taking an LPCTSTR, it needs to be called with the appropriate type for how you are compiling.
If you know you are only going to be building with or without Unicode support, you can skip all the TCHAR stuff and directly use either wchar_t or char respectively.
Since CFileImageLoader("Splash02.png") is not working, you must be compiling with Unicode support enabled. You can change that to CFileImageLoader(L"Splash02.png") and commit to always using Unicode or you can change it to CFileImageLoader(_T("Splash02.png")) and let the macro magic do the work.
"both don't work" - could you maybe be a tiny, tiny little bit more specific?
If you compile with _UNICODE defined, then the second shouldn't even compile.
You're also just passing a filename, not a full path. Maybe your image loader class can't find the file because it uses a differen CWD path as you expect. Try passing the full path instead.
Maybe your image library can't support to open PNG format file.
Try passing the full path instead.
Maybe you need to call some initialization functions which provide by your image library