How to convert a dynamic string to wchar_t to change background - c++

I've been looking around the internet for some solutions however they all convert it from a constant string. Here's a piece of code I took to convert strings to wchar_t without additional libraries. What I'm trying to do is, I want to change the background of my windows computer with my background. Now I can't assume that the folder that I downloaded is in C:\Downloads because some people change their downloads folder or maybe they moved the whole folder to another location. So in the first code, I'm trying to get the path of the .exe file.
string GetExePath() {
char buffer[MAX_PATH];
GetModuleFileNameA(NULL, buffer, MAX_PATH);
string::size_type pos = string(buffer).find_last_of("\\/");
return string(buffer).substr(0, pos + 1);//gets the first character in path up to the final backslash
}
Next I'm going to grab the picture that I want to make as my background in the same folder as the .exe file.
//error on the third parameter
int return_value = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, L(string)(GetExePath() + "\\picture.png"), SPIF_UPDATEINIFILE);
Error (active) E0040 expected an identifier
Error (active) E0020 identifier "L" is undefined
Error (active) E0254 type name is not allowed
Error (active) E0018 expected a ')'
After a while, I replaced the return type of the function and so it would return wchar_t*.
const wchar_t* GetExePath() {
char buffer[MAX_PATH];
GetModuleFileNameA(NULL, buffer, MAX_PATH);
string::size_type pos = string(buffer).find_last_of("\\/");
string path = string(buffer).substr(0, pos + 1);
path += "\\HandleCamWallpaperwithgradient.png";
cout << path << endl;
wstring wide;
for (int i = 0; i < path.length(); ++i){
wide += wchar_t(path[i]);
}
const wchar_t* result = wide.c_str();
return result;
}
However, the third parameter is showing an error saying
Error E0167 argument of type "const wchar_t *" is incompatible with parameter of type "PVOID"
So how can I fix it?
Edit: Someone thought that this was a duplicate and it isn't. How to convert string to wstring in C++ is NOT correlated with this question as the one who is asking on that thread is asking help for special characters.

Call the Unicode version GetModuleFileNameW() in the first place so you don't have to convert.
Also, never return a pointer to a string that is a local variable of a function (unless it is static)! Otherwise you will be returning a dangling pointer. Instead, return a std::wstring similar to your first version. You can use std::wstring directly as the buffer by using the "pointer-to-first-character" trick.
std::wstring GetExePath() {
std::wstring buffer(MAX_PATH, L'\0'); // reserve buffer
int len = GetModuleFileNameW(NULL, &buffer[0], buffer.size() );
buffer.resize(len); // resize to actual length
string::size_type pos = buffer.find_last_of(L"\\/");
return buffer.substr(0, pos + 1);//gets the first character in path up to the final backslash
}
The second error can be fixed like this:
std::wstring path = GetExePath() + L"picture.png";
int return_value = SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, &path[0], SPIF_UPDATEINIFILE);
The pvParam parameter of SystemParametersInfoW is a pointer to non-const data, so we have to use the "pointer-to-first-character" trick here again (to avoid ugly const_cast).
With C++17, this could be written as a one-liner:
int return_value = SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (GetExePath() + L"picture.png").data(), SPIF_UPDATEINIFILE);
Other things to improve, left as an exercise:
Check for the error condition ERROR_INSUFFICIENT_BUFFER as described in the comments of GetModuleFileName() MSDN reference so you can support pathes longer than MAX_PATH.

Related

Is Win32 error code 122 somewhat benign in this case?

The following line results in GetLastError() return error code 122 (=ERROR_INSUFFICIENT_BUFFER)
CString str = CString("'") + _T("%s") + CString("'");
But this happens only under VS2005 and doesn't happen in VS2015. Still I see no memory corruption or anything in VS2005 and the str variable does contain the correct value. Is that still an issue to be concerned about give the error code?
The reason this seems to happen is because of concatenation of wide character and simple character strings and the fix is to simply sorround both remaining strings with _T("") so the code line would look like:
CString str = CString(_T("'")) + _T("%s") + CString(_T("'"));
But what does the error code 122 really means in original line when only one string was Unicode? What wrong has really happened or is it more like a warning in this case?
GetLastError() is only meaningful after some system call has returned an error. Since your code does not have any system call, GetLastError() can return anything.
Maybe the value you see is the last error from the last system call that failed. Or maybe it is some error that happened from inside the CString class, but it is handled there.
TL;DR; No error here.
In VS 2015 you can reproduce the error with CString("a") (if Unicode is set) or just CStringW("a")
#include <iostream>
#include <atlstr.h>
int main()
{
CStringW("a");
DWORD err = GetLastError();
std::cout << err << "\n"; //<= error 122, ERROR_INSUFFICIENT_BUFFER
return 0;
}
This happens because CString uses WinAPI MultiByteToWideChar to convert ANSI "a" to Unicode L"a". Debugging through the source code in "atlmfc\include\cstringt.h", we see that at some point it calls the following function:
static int __cdecl GetBaseTypeLength(_In_z_ LPCSTR pszSrc) throw()
{
// Returns required buffer size in wchar_ts
return ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pszSrc, -1, NULL, 0 )-1;
}
For some reason there is a -1 at the end. I don't know why that's there, it might be necessary for other CString functions but in this case it ends up causing ERROR_INSUFFICIENT_BUFFER error in the next call to MultiByteToWideChar. The conversion can be roughly simplified to following:
int main()
{
int nDestLength = MultiByteToWideChar(CP_ACP, 0, "a", -1, NULL, 0) - 1;
wchar_t *pszDest = new wchar_t[32];
//ERROR_INSUFFICIENT_BUFFER occurs here because nDestLength is short by 1:
MultiByteToWideChar(CP_ACP, 0, "a", -1, pszDest, nDestLength);
DWORD err = GetLastError();
std::cout << err << "\n";
return 0;
}
nDestLength is too small because it doesn't account for null terminator. CString sorts this out later but the error remains. That's a good reason not to pay attention to GetLastError unless the function fails.
As you noted, this error can be avoided by using the _T macro, because CString would no longer need MultiByteToWideChar. Or better yet, use the L prefix, or CString::Format
CString str = CString(L"'") + L"%s" + CString(L"'");

strstr always returns null

I am trying to get a substring from a CString using C++. For that I am using strstr function. But it is not working at al
CString str = m_sectionDataList->GetNext(pos);
char* chToMatch = (char*)(LPCTSTR)str;
char *match = "=";
//char * sMatched = strstr(ch, match);
if (strstr(match, chToMatch) != NULL) {
MessageBox(NULL, str, L"Done", 1);
}
You are passing arguments in the incorrect order. strstr expects first argument to be scanned string, and second should be a match. Right now you are searching your target string in the one byte = template, which will most certainly fail.
Finally I've found it. Need to use a macro of C++ and you will found it converted.
CT2A ascii(str, CP_UTF8);
now you can just access it using ascii.m_psz and its buffer also.

Converting string to LPCTSTR

I encountered a problem during writting my code. I use a function which take as an argument object which type is LPCSTR. The object declaration looks like shown below:
LPCTSTR lpFileName;
Firstly, I used defined variable, which was futher assign to lpFileName like this:
#define COM_NR L"COM3"
lpFileName = COM_NR
Using this manner, I could easily pass lpFileName argument to the function. Anyway, i had to changed the way of defining my port number. Currently i read text from *.txt file and save it as string variable e.g "COM3" or "COM10". The main problem is to convert string to LPCSTR properly. I found good solution but finally it doesn't seem working properly. My code looks like this:
string temp;
\\code that fill temp\\
wstring ws;
ws.assign(temp.begin(),temp.end());
I thought that conversion went correctly, maybe it did and I don't get it because when i print few things it makes me to wonder why it doesn't work as i want:
cout temp_cstr(): COM3
cout LCOM3: 0x40e586
cout ws.c_str(): 0x8b49b2c
Why LCOM3 and ws.c_str() doesn't contain the same? When i pass lpFileName = ws.c_str() to my function, it works uncorretly. On the other hand, passing lpFileName = L"COM3" gives success.
I code using cpp, and IDE is QtCreator
Eventually, I managed with the pitfall using conversion-function s2ws() and doing few operations. I place my soultion here for people who will have similar troubles with converting string. In my first post i wrote that i needed to convert string to LPCTSTR and finally it turned out that argument in my function is not, LPCTSTR but LPCWSTR that is const wchar_t*.
So, soulution:
string = "COM3";
wstring stemp;
LPCWSTR result_port;
stemp = s2ws(port_nr);
result_port = stemp.c_str(); // now passing result_port to my function i am getting success
declaration of s2ws:
wstring s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
Try to use wostringstream:
string temp;
\\code that fill temp\\
wostringstream ost;
ost << temp.c_str();
wstring ws = ost.str();
I have struggled with this for quite a while. After quite a bit of digging I found this works the best; you could try this.
std::string t = "xyz";
CA2T wt (t.c_str());

C++ WCHAR manipulations

I'm developing a tiny Win32 app in C++.
I've studied C++ fundamentals long time ago, so now I completely confused because of character strings in C++. There were no WCHAR or TCHAR only char and String.
After a little investigation I've decided not to use TCHAR.
My issue is very simple I think, but I can't find clear guide how to manipulate strings in C++. Affected by PHP coding last few years I've expected something simple with strings manipulations and was wrong!
Simply, all I need is to put new data to a character string.
WCHAR* cs = L"\0";
swprintf( cs, "NEW DATA" );
This was my first attempt. When debugging my app I've investigated that swprintf puts only first 2 chars to my cs var. I've resolved my problem this way:
WCHAR cs[1000];
swprintf( cs, "NEW DATA" );
But generally this trick could fail, because in my case new data is not constant value but another variable, that could potentialy be wider, than 1000 chars long. And my code is looks like this:
WCHAR cs[1000];
WCHAR* nd1;
WCHAR* nd2;
wcscpy(nd1, L"Some value");
wcscpy(nd2, L"Another value"); // Actually these vars stores the path for user selected folder
swprintf( cs, "The paths are %s and %s", nd1, nd2);
In this case there is possibility than nd1 and nd2 total character count could be greater than 1000 chars so critical data will be lost.
The question is how can I copy all data I need to WCHAR string declared this way WCHAR* wchar_var; without losing anything?
P.S. Since I'm Russian the question may be unclear. Let me now about that, and I'll try to explain my issue more clear and complex.
In modern Windows programming, it's OK to just ignore TCHAR and instead use wchar_t (WCHAR) and Unicode UTF-16.
(TCHAR is a model of the past, when you wanted to have a single code base, and produce both ANSI/MBCS and Unicode builds changing some preprocessor switches like _UNICODE and UNICODE.)
In any case, you should use C++ and convenient string classes to simplify your code. You can use ATL::CString (which corresponds to CStringW in Unicode builds, which are the default since VS2005), or STL's std::wstring.
Using CString, you can do:
CString str1 = L"Some value";
CString str2 = L"Another value";
CString cs;
cs.Format(L"The paths are %s and %s", str1.GetString(), str2.GetString());
CString also provides proper overloads of operator+ to concatenate strings (so you don't have to calculate the total length of the resulting string, dynamically allocate a buffer for the destination string or check existing buffer size, call wcscpy, wcscat, don't forget to release the buffer, etc.)
And you can simply pass instances of CString to Win32 APIs expecting const wchar_t* (LPCWSTR/PCWSTR) parameters, since CString offers an implicit conversion operator to const wchar_t*.
When you're using a WCHAR*, you are invoking undefined behavior because you have a pointer but have not made it point to anything valid. You need to find out how long the resulting string will be and dynamically allocate space for the string. For example:
WCHAR* cs;
WCHAR* nd1;
WCHAR* nd2;
nd1 = new WCHAR[lstrlen(L"Some value") + 1]; // +1 for the null terminator
nd2 = new WCHAR[lstrlen(L"Another value") + 1];
cs = new WCHAR[lstrlen(L"The paths are and ") + lstrlen(nd1) + lstrlen(nd2) + 1];
wcscpy(nd1, L"Some value");
wcscpy(nd2, L"Another value"); // Actually these vars stores the path for user selected folder
swprintf( cs, L"The paths are %s and %s", nd1, nd2);
delete[] nd1;
delete[] nd2;
delete[] cs;
But this is very ugly and error-prone. As noted, you should be using std::wstring instead, something like this:
std::wstring cs;
std::wstring nd1;
std::wstring nd2;
nd1 = L"Some value";
nd2 = L"Another value";
cs = std::wstring(L"The paths are ") + nd1 + L" and " + nd2;
Suggest to use ATL CStringW class instead of raw WCHAR, it's much more handy. CString is wrapper for dynamically allocated C-string. It will manage string length & allocated memory buffer appropriately after each operation so you wouldn't care on it.
Typical usage:
#include <atlstr.h>
CStringW s;
s.Format(L"The paths are %s and %s", L"Some value", L"Another value");
const WCHAR* wstr = s.GetString(); // To pass to some API that need WCHAR
or
#include <atlstr.h>
CStringW s(L"The paths are ");
s += L"Some value";
s += L" and ";
s += L"Another value";
const WCHAR* wstr = s.GetString(); // To pass to some API that need WCHAR

Why is the following C++ code printing only the first character?

I am trying to convert a char string to a wchar string.
In more detail: I am trying to convert a char[] to a wchar[] first and then append " 1" to that string and the print it.
char src[256] = "c:\\user";
wchar_t temp_src[256];
mbtowc(temp_src, src, 256);
wchar_t path[256];
StringCbPrintf(path, 256, _T("%s 1"), temp_src);
wcout << path;
But it prints just c
Is this the right way to convert from char to wchar? I have come to know of another way since. But I'd like to know why the above code works the way it does?
mbtowc converts only a single character. Did you mean to use mbstowcs?
Typically you call this function twice; the first to obtain the required buffer size, and the second to actually convert it:
#include <cstdlib> // for mbstowcs
const char* mbs = "c:\\user";
size_t requiredSize = ::mbstowcs(NULL, mbs, 0);
wchar_t* wcs = new wchar_t[requiredSize + 1];
if(::mbstowcs(wcs, mbs, requiredSize + 1) != (size_t)(-1))
{
// Do what's needed with the wcs string
}
delete[] wcs;
If you rather use mbstowcs_s (because of deprecation warnings), then do this:
#include <cstdlib> // also for mbstowcs_s
const char* mbs = "c:\\user";
size_t requiredSize = 0;
::mbstowcs_s(&requiredSize, NULL, 0, mbs, 0);
wchar_t* wcs = new wchar_t[requiredSize + 1];
::mbstowcs_s(&requiredSize, wcs, requiredSize + 1, mbs, requiredSize);
if(requiredSize != 0)
{
// Do what's needed with the wcs string
}
delete[] wcs;
Make sure you take care of locale issues via setlocale() or using the versions of mbstowcs() (such as mbstowcs_l() or mbstowcs_s_l()) that takes a locale argument.
why are you using C code, and why not write it in a more portable way, for example what I would do here is use the STL!
std::string src = std::string("C:\\user") +
std::string(" 1");
std::wstring dne = std::wstring(src.begin(), src.end());
wcout << dne;
it's so simple it's easy :D
L"Hello World"
the prefix L in front of the string makes it a wide char string.