1

I Know similar questions have been asked before but none of them Helped in my case.

Basically I want dstPath = %AppData% + "CURRENT EXE NAME"

but problem is with different string types and string concantation

SIMPLIFIED CODE :-

#include <stdio.h>
#include <string>
#include <filesystem>

#include <Shlwapi.h>
#include <Windows.h>

using namespace std;

int main()
{
    TCHAR selfPath[MAX_PATH];
    TCHAR dstPath[MAX_PATH];
    
    if (GetModuleFileName(NULL, selfPath, MAX_PATH) == 0)       // Getting exe File Location
        printf("Error : %ul\n", GetLastError());
    
    filesystem::path p(selfPath);
    
    dstPath = strcat(getenv("APPDATA"), p.filename().string().c_str());     // Here Comes The Error
    
    printf("Src : %s\n", selfPath);
    printf("Dst : %s\n", dstPath);
    
    return 0;
}

COMPILER COMMAND :-

g++ -Os -s -o ./builds/gcc-rat-x64.exe ./source/rat.cpp -std=c++17 -m64 -lshlwapi

COMPILER ERROR :-

error: incompatible types in assignment of 'char*' to 'TCHAR [260]' {aka 'char [260]'}
   80 |  dstPath = strcat(getenv("APPDATA"), p.filename().string().c_str());
0xB00B
  • 1,598
  • 8
  • 26
  • You're not allowed to modify the string returned by [`std::getenv`](https://en.cppreference.com/w/cpp/utility/program/getenv). Attempting to do so, like you do with the `strcat` call, leads to *undefined behavior*. – Some programmer dude Apr 14 '21 at 15:25
  • 1
    As for the error itself, you can't assign to an array, only copy to it. If you're programming in C++, why don't you use `std::filesystem::path` for *all* paths? And `std::string` for *all* strings? – Some programmer dude Apr 14 '21 at 15:25
  • @Some programmer dude can't use `std::string` because functions don't accept it. This code is just a sample out of full code. – 0xB00B Apr 14 '21 at 15:29
  • `std::string selfPath(MAX_PATH, 0); GetModuleFileNameA(NULL, selfPath.data(), selfPath.length());` Also try to use `std::cout` instead of `printf`. I recommend [a decent book or two](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) to help you learn C++ better. – Some programmer dude Apr 14 '21 at 15:30
  • I use `` beacuse it produces much smaller executable after compiling than `` – 0xB00B Apr 14 '21 at 15:38

3 Answers3

2

You cannot assign to arrays. You should use strcpy() to copy C-style strings.

strcpy(dstPath, getenv("APPDATA"));
strcat(dstPath, p.filename().string().c_str());

Or the concatination can be done in one line via snprintf():

snprintf(dstPath, sizeof(dstPath), "%s%s", getenv("APPDATA"), p.filename().string().c_str());

Finally, TCHAR and GetModuleFileName can refer to UNICODE version of the API, according to the compilation option. Using ANSI version (char and GetModuleFileNameA) explicitly is safer to work with std::string and other APIs that require strings consists of char.

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
  • Note that these solutions assume `getenv("APPDATA")` always outputs a trailing ```\``` character, which is not guaranteed. `PathCombine()` or related Win32 API would be better. `std::filesystem::path::operator/=` would be even better. – Remy Lebeau Apr 14 '21 at 17:41
0

You are trying to use strcat to concatenate two strings and store the result in another one, but it does not work that way. The call strcat (str1, str2) adds the content of str2 at the end of str1. It also returns a pointer to str1 but I don't normally use it.

What you are trying to do should be done in three steps:

  • Make sure that dstPath contains an empty string
  • Concatenate to dstPath the value of the environment variable APPDATA
  • Concatenate to dstPath the value of filename

Something like this:

dstPath[0] = '\0';
strcat(dstPath, getenv("APPDATA"));
strcat(dstPath, p.filename().string().c_str());

You should also add checks not to overflow dstPath...

Ion Larrañaga
  • 464
  • 2
  • 5
  • In fact, the answer by @MikeCAT is probably better. I wanted to stick to `strcat` but in the real world you would substitute the first two steps for `strcpy` as he suggests. – Ion Larrañaga Apr 14 '21 at 15:36
  • I still get the same error with your solution – 0xB00B Apr 14 '21 at 15:42
  • in second line you should use strcpy() instead of strcat() for it to work – 0xB00B Apr 14 '21 at 15:44
  • and wouldn't the null terminator get in the beginning this way ? – 0xB00B Apr 14 '21 at 15:45
  • No, the null terminator at the beginning converts `dstPath` into an empty string at first, and `strcat` takes care of moving the terminator to the end of the string after concatenating each new string. It's strange that you get the same error, I just tried it just in case and it works perfectly for me. I assume that you have removed the `dstPath = ` part before the `strcat`, didn't you? – Ion Larrañaga Apr 14 '21 at 15:54
  • it was saying dstPath not defined so i changed it to `TCHAR dstPath[MAX_PATH]; dstPath = '\0'`, then i get the same old error as mentioned in question. – 0xB00B Apr 14 '21 at 16:02
  • @TanishqBanyal you can't assign a single `TCHAR` to a `TCHAR[]` array like that. You meant to use `dstPath[0] = _T('\0');` instead to set the 1st `TCHAR` in the array. – Remy Lebeau Apr 14 '21 at 18:00
0

First off, you are mixing TCHAR and char APIs in a way you should not be. You really should not be using TCHAR at all in modern code. But, if you are going to use TCHAR, then at least use TCHAR- based functions/macros, like _tprintf() instead of printf(), _tcscat() instead of strcat(), etc.

The compiler error is because you are trying to assign the char* pointer returned by strcat() to your dstPath TCHAR[] array. You can't assign a pointer to an array like that. You should strcpy() the result of getenv() into dstPath first, and then strcat() your filename onto the end of it, eg:

#include <string>
#include <filesystem>

#include <Windows.h>
#include <Shlwapi.h>
#include <stdio.h>
#include <tchar.h>

TCHAR* _tgetenv(const TCHAR *varname)
{
    #ifdef _UNICODE
    return _wgetenv(varname);
    #else
    return getenv(varname);
    #endif
}

std::basic_string<TCHAR> path2TStr(const std::filesystem::path &p)
{
    #ifdef _UNICODE
    return p.wstring();
    #else
    return p.string();
    #endif
}

int main()
{
    TCHAR selfPath[MAX_PATH];
    TCHAR dstPath[MAX_PATH];
    
    if (GetModuleFileName(NULL, selfPath, MAX_PATH) == 0)       // Getting exe File Location
    {
        printf("Error : %ul\n", GetLastError());
        return 0;
    }

    std::filesystem::path p(selfPath);
    
    _tcscpy(dstPath, _tgetenv(_T("APPDATA")));
    _tcscat(dstPath, path2TStr(p.filename()).c_str());
    
    _tprintf(_T("Src : %s\n"), selfPath);
    _tprintf(_T("Dst : %s\n"), dstPath);
    
    return 0;
}

However, you really should be using SHGetFolderPath(CSIDL_APPDATA) or SHGetKnownFolderPath(FOLDERID_RoamingAppData) instead of using getenv("APPDATA").

And since you are using the <filesystem> library anyway, you really should just use std::filesystem::path for all of your path handling. It has operator/= and operator/ to concatenate path segments, and an operator<< for printing paths to a std::ostream, like std::cout. Don't use strcat() for concatenating path segments, it won't handle directory separators correctly, at least.

Try this instead:

#include <iostream>
#include <string>
#include <filesystem>
#include <stdexcept>

#include <Windows.h>
#include <Shlobj.h>

std::filesystem::path getSelfPath()
{
    WCHAR wPath[MAX_PATH] = {};
    
    if (!GetModuleFileNameW(NULL, wPath, MAX_PATH))       // Getting exe File Location
    {
        DWORD err = GetLastError();
        throw std::runtime_error("Error : " << std::to_string(err));
    }

    return wPath;
}

std::filesystem::path getAppDataPath()
{
    WCHAR wPath[MAX_PATH] = {};
    
    HRESULT hRes = SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wPath);       // Getting APPDATA Folder Location
    if (hRes != S_OK)
        throw std::runtime_error("Error : " << std::to_string(hRes));

    return wPath;
}

int main()
{
    try
    {
        auto selfPath = getSelfPath();
        auto dstPath = getAppDataPath() / selfPath.filename();
    
        std::cout << "Src : " << selfPath << "\n";
        std::cout << "Dst : " << dstPath << "\n";
    }
    catch (const std::exception &e)
    {
        std::cerr << e.what() << "\n";
    }
    
    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • yes i have already switched to filesystem for this problem(and i was thinking of adding a answer here), still thanks. – 0xB00B Apr 15 '21 at 16:49