Honest to God, I'm losing my mind right now. Let me just start by saying the "MessageBox" function works perfectly fine in CodeBlocks when I define the text inside of it and what-not; but for whatever reason, Visual Studio is so picky about what can go inside of this MessageBox function.
It kept telling me that whatever I put inside of it was invalid for the LPCWSTR "thing". To circumvent this, I programmed this thing below:
int main()
{
LPCWSTR a;
std::string s = "Please help me.";
a = (LPCWSTR)s.c_str();
LPCWSTR b;
std::string t = "MTS";
b = (LPCWSTR)t.c_str();
MessageBox(0,a,b, MB_OK | MB_ICONSTOP);
}
Instead of it working how I originally thought it would, I instead got THIS as a result:
This is very stressful for me and I know it has been asked once before here, so please don't mark my question as a duplicate. How do I make this code work so my message shows up in ENGLISH and portrays what I'm trying to say clearly?
Thanks ahead of time,
MTS
The reason why it shows up in another language (and usually, it is gibberish in the language you're seeing) is that you're casting the string type instead of using the proper string type:
LPCWSTR b;
std::string t = "MTS";
b = (LPCWSTR)t.c_str();
This code does not turn a narrow, ANSI based string into a wide string. Casting does not convert string types. If you removed the cast, you would see that the compiler gives you an error that the string types / pointers do not match.
Since you're using the MessageBox function, this is actually a call to MessageBoxW, which requires wide string arguments.
The reason in your case why MessageBoxW is called is that MessageBox will either be MessageBoxA or MessageBoxW, depending on the build type (MBCS or Unicode, respectively). For CodeBlocks, you probably had your project set up as MBCS, while on Visual Studio, it is set up as Unicode, thus it worked for one project, and failed on the other.
Thus the fix is to provide a wide string in this case:
LPCWSTR b;
std::wstring t = L"MTS";
b = t.c_str();
or even this:
LPCWSTR b;
std::basic_string<WCHAR> t = L"MTS";
b = t.c_str();
If you want to have the same code work for both MBCS and Unicode builds without coding changes, then the following could also be used:
#include <tchar.h>
//...
LPCTSTR b;
std::basic_string<TCHAR> t = _T("MTS");
b = t.c_str();
This uses the _T (or TEXT macro) to make the string literal either narrow or wide, depending on the build type. The TCHAR will either be narrow or wide, depending on the build type.
The bottom line is this -- if you're calling a function that requires a string, and the compiler gives you an error that the string types do not match, do not try C-style casts to "fix" the compiler error. This is especially the case for strings. Instead, either
1) Provide the correct string types so that casting is never necessary,
or
2) Call the correct function (in this case MessageBoxA) that accepts your string types without having to apply casts.
Related
I have been playing with writing Unicode code using Windows API and have found one thing particularly frustrating.
I made a simple interface for wrapping "MessageBox" into an "alert", reducing number of arguments needed to call it from 4 to 1.
The issue is that this one argument MUST be called with L"My string", which I want to avoid.
Here is my interface so far:
UseCase.hpp
#pragma once
#include <string>
#include <iostream>
/**
* The User should not need to know any Windows API ideally.
* They will have to know L"Str" notation though...
*/
namespace UseCase
{
void alert(const wchar_t *message);
};
UseCase.cpp
#include <Windows.h>
#include "UseCase.hpp"
/**
* Kind-of like javascript:alert(message).
* This is a very common usecase, and heavily simplifies call convention.
*/
void UseCase::alert(const wchar_t *message)
{
MessageBox(0, message, L"message box", MB_OK | MB_ICONEXCLAMATION);
}
main.cpp
#include "UseCase.hpp"
using namespace UseCase;
const wchar_t *msg = L"Привет мир";
int wmain(int argc, wchar_t **argv)
{
alert(msg);
return 0;
}
My concern is that no matter how the main tries to call alert, it has to use L"String" notation which is visually annoying to me.
To elaborate why L is annoying to me, it is for two reasons:
It treats Rvalue string literals differently from variables containing an ascii string.
If you try to call it on an ascii string "hello, world", it will give a confusing error message to an average user.
It's obvious that the string cannot be stored in ascii, and you are trying to assign it to a unicode string, there shouldn't be much getting in a way of an automatic conversion.
Is there a way to get rid of the L"String" convention and make the program automatically treat the function input as wide character input?
Current viable solutions are:
Create a macro for calling messagebox with a string literal that wraps it in L for you. The issue with this approach is the cryptic error messages on variables passed into the function, as L cannot convert variables from ascii to unicode for you.
Others proposed but I haven't fully wrapped my head around implementing them.
Have the user register their string externally and stored as unicode
inside a file/binary cache. I can then provide an interface to load this string as a wide string and call the function using it.
If you want to be able to pass Unicode strings (i.e. use MessageBoxW, which takes wide-character strings), but you want to be able to use plain string literals without the L prefix, you'll need to decide on an encoding to use for the Unicode characters in a narrow string, and then perform a run-time conversion to a wide string according to the encoding.
UTF-8 might be a reasonable start. See this answer for how to convert from/to UTF-8 using standard Windows API.
You can get around this by making the actual call to alert a preprocessor macro, and using the _TEXT() macro provided by MS (or figure out how to do token pasting L ## msg yourself correctly.)
#define DMITRY_ALERT(msg) UseCase::alert(_TEXT(msg))
this will "magically" insert the L in any UNICODE build at the cost of having to use a macro wrapper (that needs to have an UGLY_LONG_NAME to prevent clashes).
Note: That macro can then only be called with string literals, not with a variable.
Whether that is worth the trouble seems doubtful to me.
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.
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
I'm slowly starting to get the hang of the _T stuff in Visual Studio 2008 c++, but a few things still elude me. I can see the benefit of the flexibility, but if I can't get the basics soon, I think I'll go back to the standard way of doing this - much less confusing.
The idea with the code below is that it scans the parameters for -d and then stores the text that follows that in the string variable fileDir. It also ignores any other parameters.
Any help is appreciated.
//Console application
Parameters::Parameters(int argc, _TCHAR* argv[])
{
_Tstring fileDir; // Is there some kind of _t variable to use here for a string?
for (int i = 0; i < argc; i = i + 1)
{
if (_tccmp(argv[i], _T("-d")) == 0) // this appeared to accept anything starting with -
{
i = i + 1;
fileDir = argv[i]
}
}
_tprintf("Parameter value found: %s\n", fileDir);
}
You can use _tstring to represent and std::string with a TCHAR parameter. Also, the name of the function is _tcscmp, not _tccmp so I don't see how that code snippet could even compile?
To be honest, I wouldn't bother with any of that, though. The whole TCHAR stuff was useful back when people were writing code that was to be portable between Windows 9X (which was Ansi internally) and Windows NT (which was/is Unicode internally). These days, there's very little benefit to using the macros.
If you want to do anything, you can switch to wchar_t entirely. That means prefixing string literals with "L", as in L"Some wide-char string" and using the 'w' versions of functions (e.g. std::wstring, wcscmp, etc). But even that might be considered overkill for many scenarios.
If you want to copy a string value and you're using MFC or ATL, your _Tstring can be CString.
If you want to copy a pointer value, your _Tstring can be LPTCSTR. (This works whether or not you're using MFC or ATL.)
As codeka said, your _tccmp needs to be _tcscmp. An alternative, if you're using MFC or ATL, is CString's Compare method.
You didn't show how the caller delivers parameter argv to your constructor. If argv came from your _tmain function, make sure the parameters to _tmain are correct.
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