CraftedGaming CraftedGaming - 18 minutes ago 2
C++ Question

How to convert a dynamic string to wchar_t to change background

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);



  1. Error (active) E0040 expected an identifier

  2. Error (active) E0020 identifier "L" is undefined

  3. Error (active) E0254 type name is not allowed

  4. 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


  1. 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.

Answer

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.