1

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.

CraftedGaming
  • 499
  • 7
  • 21
  • 1
    Possible duplicate of [How to convert string to wstring in C++](https://stackoverflow.com/questions/25485268/how-to-convert-string-to-wstring-in-c) or you could use wide strings from the beginning and call `GetModuleFileNameW` instead of A version. – user7860670 Jul 17 '17 at 05:27
  • @VTT, it's not really a duplicate as one shouldn't need a conversion when calling the Unicode version of the API, as you mentioned yourself. – zett42 Jul 17 '17 at 10:04

1 Answers1

2

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.
zett42
  • 25,437
  • 3
  • 35
  • 72
  • I'm getting an error still on the second code. `E0167 argument of type "const wchar_t *" is incompatible with parameter of type "PVOID"` – CraftedGaming Jul 17 '17 at 08:23
  • 1
    @CraftedGaming, fixed in code. I didn't notice that `pvParam` parameter of `SystemParametersInfoW` expects a pointer to non-const data. – zett42 Jul 17 '17 at 10:00
  • A `const_cast` would work as well. For `SPI_SETxxx` actions, the *pvParam* is an `_In_` parameter. – IInspectable Jul 17 '17 at 14:46
  • @IInspectable, true but I try to avoid casts if possible, because people reading this post may apply that to other situations where `const_cast` would not be valid. – zett42 Jul 17 '17 at 17:28