Problem joining char* to filesystem::path.filename() or char[260] - c++

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

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.

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

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

Related

How to pass variable to windows.h function text()?

First off, I googled the hell out of this then searched the forums. In my ignorance of how the TEXT() function operates I cannot find an efficient way to search for an answer.
I writing a piece of code to search for a file that relies on inputting the directory you want to search. However, when I pass anything but a literal value to the function, like displayContent(_TEXT("c:\")), the software does not execute properly. It does not search for anything. Inserting breakpoints doesn't tell much, as the software closes anyways.
I would like to pass a variable to the the displayContent function by placing TEXT(variable) inside its argument like displayContent(_TEXT(*ptrDir)) but that is not compiling. Furthermore, when I simply place ptrDir inside of the argument of displayContent the software compiles but does not execute properly, as it asks for the directory to search but does not actually search it.
What's happening here? There has to be a way to pass a variable to displayContent that includes a string that's recieved from the user.
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <tchar.h>
#include "Strsafe.h"
#include <string>
using namespace std;
typedef wchar_t WCHAR;
#define CONST const
typedef CONST WCHAR* LPCWSTR;
int displayContent(LPCWSTR lpszPath, int level = 0) {
wcout << lpszPath << endl;
getchar();
getchar();
WIN32_FIND_DATA ptrFileData;
HANDLE hFile = NULL;
BOOL bGetNext = TRUE;
wchar_t lpszNewPath[MAX_PATH];
if (lstrlen(lpszPath) > MAX_PATH)
return -1;
StringCchCopy(lpszNewPath, MAX_PATH, lpszPath);
StringCchCat(lpszNewPath, MAX_PATH, _TEXT("*.*"));
hFile = FindFirstFile(lpszNewPath, &ptrFileData);
while (bGetNext)
{
for (int i = 0; i < level; i++)
wcout << "-";
if (ptrFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY
&& lstrlen(ptrFileData.cFileName) > 2)
{
wchar_t lpszFirstTimePath[MAX_PATH];
StringCchCopy(lpszFirstTimePath, MAX_PATH, lpszPath);
StringCchCat(lpszFirstTimePath, MAX_PATH, ptrFileData.cFileName);
StringCchCat(lpszFirstTimePath, MAX_PATH, _TEXT("\\"));
wcout << ">" << ptrFileData.cFileName << endl;
displayContent(lpszFirstTimePath, level + 2);
}
else
{
wcout << ">" << ptrFileData.cFileName << endl;
}
bGetNext = FindNextFile(hFile, &ptrFileData);
}
FindClose(hFile);
return 0;
}
int main(int argc, char* argv[])
{
WCHAR directory;
LPCWSTR ptrDir;
ptrDir = &directory;
cout << "Enter directory you wish to search: " << endl;
//cin >> directory;
directory = 'c:\\' ;
ptrDir = &directory;
displayContent(_TEXT(*ptrDir));
getchar();
getchar();
return 0;
}
The _TEXT (and equivalently, _T) macro is strictly for literals (string literals or character literals). It expands to L for a Unicode build, and to nothing for a narrow-character build. So, for a string like (say) "hello", you'll get L"hello" for a Unicode build and "hello" for a narrow-character build. This gives you a wide literal in a Unicode build and a narrow literal otherwise.
If you have a string in a variable, you can convert between wide and narrow characters with the MultiByteToWideChar and WideCharToMultibyte functions.
In this case, doing a conversion on the contents of a variable isn't really needed though. After eliminating some unnecessary complexity, and using a few standard library types where they make sense, I end up with code something like this:
#include <iostream>
#include <tchar.h>
#include <string>
#define UNICODE
#include <windows.h>
int displayContent(std::wstring const &path, int level = 0) {
WIN32_FIND_DATA FileData;
if (path.length() > MAX_PATH)
return -1;
std::wstring new_path = path + L"\\*.*";
HANDLE hFile = FindFirstFile(new_path.c_str(), &FileData);
do {
if ((FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (FileData.cFileName[0] == L'.'))
continue;
std::wcout << std::wstring(level, L'-') << L">" << FileData.cFileName << L"\n";
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
displayContent(path + L"\\" + FileData.cFileName, level + 2);
} while (FindNextFile(hFile, &FileData));
FindClose(hFile);
return 0;
}
int main(int argc, char* argv[]) {
wchar_t current_dir[MAX_PATH];
GetCurrentDirectory(sizeof(current_dir), current_dir);
displayContent(current_dir);
return 0;
}
[Note: I've also changed it to start from the current directory instead of always starting at the root of the C drive, but if you want to change it back, that's pretty trivial--in fact, it simplifies the code a bit more).

c++ FindFirstFile Cannot convert const char to basic_string

FindFirstFile function somehow doesn't accept my wstring (nor string) to be passed as a parameter.
I get a compiler error
Cannot convert const char[9] to std::basic_string
#include "stdafx.h"
#include <string>
#include <iostream>
#include <stdio.h>
#include <Windows.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
wstring path = "C:\\*.dmp";
WIN32_FIND_DATA dataFile;
HANDLE hFind;
hFind = FindFirstFile (path.c_str(), &dataFile);
cout << "The name of the first found file is %s \n" dataFile.cFileName << endl;
FindClose hFind;
getchar();
return 0;
}
I get a compiler error
Cannot convert const char[9] to std::basic_string
You need a wide char literal to initialize a std::wstring properly:
wstring path = L"C:\\*.dmp";
// ^
Also you have missed to put another <<
cout << "The name of the first found file is " << dataFile.cFileName << endl;`
// ^^
Also note that output formatting with std::ostream is different from printf() format string styles. Note I removed the %s from the above sample.
Change
const char path[] = "C:\\*.dmp"; // C-style string
hFind = FindFirstFile(path, &dataFile); // Pass the string directly

Full path of executable file in Windows, c++ [duplicate]

This question already has answers here:
Wide character output result [closed]
(2 answers)
Closed 8 years ago.
I just want to get full path of my executable file written on console, but the variable path just stores numbers how can i convert it to string (I know this code only outputs memory location of path)?
#include <iostream>
#include <windows.h>
#include <string>
using namespace std;
int main() {
WCHAR path[MAX_PATH];
GetModuleFileName(NULL, path, 500);
cout << "File path is: " << path << endl;
}
There are a couple of problems with your code:
You're passing the wrong size parameter to GetModuleFilename - the buffer size is supposed to be the size in TCHARs, which would be MAX_PATH in your case, not 500. That's a buffer overflow waiting to happen as 500 > MAX_PATH
You're using the narrow stream for your output, that can't print a wide string so you're seeing the address instead. To print wide characters, you need to use std::wcout instead.
As already noted in the comments, you may want to use wcout to print a WCHAR string.
You may want to consider a function like this to wrap the GetModuleFileName() call in a convenient C++ way:
#include <stdexcept> // For std::runtime_error
#include <string> // For std::wstring
#include <Windows.h> // For Win32 API
// Represents an error in a call to a Win32 API.
class win32_error : public std::runtime_error
{
public:
win32_error(const char * msg, DWORD error)
: std::runtime_error(msg)
, _error(error)
{ }
DWORD error() const
{
return _error;
}
private:
DWORD _error;
};
// Returns the full path of current EXE
std::wstring GetPathOfExe()
{
// Get filename with full path for current process EXE
wchar_t filename[MAX_PATH];
DWORD result = ::GetModuleFileName(
nullptr, // retrieve path of current process .EXE
filename,
_countof(filename)
);
if (result == 0)
{
// Error
const DWORD error = ::GetLastError();
throw win32_error("Error in getting module filename.",
error);
}
return filename;
}
Note that if you want the size in WCHARs of a raw string buffer, you may want to use _countof().
wchar are 16bit and you are using cout and that output stream expect 8bit char that's why you get weird number on the output
when you have to use wchar use wcout and take care of the kind of string you use!
#include <iostream>
#include <string>
#include <windows.h>
std::wstring app_path() {
std::wstring path;
path.resize(MAX_PATH, 0);
auto path_size(GetModuleFileName(nullptr, &path.front(), MAX_PATH));
path.resize(path_size);
return path;
}
int main() {
auto path(app_path());
std::wcout << L"File path is: " << path << std::endl;
}
Try this one!

wstring::find() doesn't work with non-latin symbols?

I have an wide-character string (std::wstring) in my code, and I need to search wide character in it.
I use find() function for it:
wcin >> str;
wcout << ((str.find(L'ф') != wstring::npos)? L"EXIST":L"NONE");
L'ф' is a Cyrillic letter.
But find() in same call always returns npos. In a case with Latin letters find() works correctly.
It is a problem of this function?
Or I incorrectly do something?
UPD
I use MinGW and save source in UTF-8.
I also set locale with setlocale(LC_ALL, "");.
Code same wcout << L'ф'; works coorectly.
But same
wchar_t w;
wcin >> w;
wcout << w;
works incorrectly.
It is strange. Earlier I had no problems with the encoding, using setlocale ().
The encoding of your source file and the execution environment's encoding may be wildly different. C++ makes no guarantees about any of this. You can check this by outputting the hexadecimal value of your string literal:
std::wcout << std::hex << L"ф";
Before C++11, you could use non-ASCII characters in source code by using their hex values:
"\x05" "five"
C++11 adds the ability to specify their Unicode value, which in your case would be
L"\u03A6"
If you're going full C++11 (and your environment ensures these are encoded in UTF-*), you can use any of char, char16_t, or char32_t, and do:
const char* phi_utf8 = "\u03A6";
const char16_t* phi_utf16 = u"\u03A6";
const char32_t* phi_utf16 = U"\u03A6";
You must set the encoding of the console.
This works:
#include <iostream>
#include <string>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
using namespace std;
int main()
{
_setmode(_fileno(stdout), _O_U16TEXT);
_setmode(_fileno(stdin), _O_U16TEXT);
wstring str;
wcin >> str;
wcout << ((str.find(L'ф') != wstring::npos)? L"EXIST":L"NONE");
system("pause");
return 0;
}
std::wstring::find() works fine. But you have to read the input string correctly.
The following code runs fine on Windows console (the input Unicode string is read using ReadConsoleW() Win32 API):
#include <exception>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <windows.h>
using namespace std;
class Win32Error : public runtime_error
{
public:
Win32Error(const char* message, DWORD error)
: runtime_error(message)
, m_error(error)
{}
DWORD Error() const
{
return m_error;
}
private:
DWORD m_error;
};
void ThrowLastWin32(const char* message)
{
const DWORD error = GetLastError();
throw Win32Error(message, error);
}
void Test()
{
const HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (hStdIn == INVALID_HANDLE_VALUE)
ThrowLastWin32("GetStdHandle failed.");
static const int kBufferLen = 200;
wchar_t buffer[kBufferLen];
DWORD numRead = 0;
if (! ReadConsoleW(hStdIn, buffer, kBufferLen, &numRead, nullptr))
ThrowLastWin32("ReadConsoleW failed.");
const wstring str(buffer, numRead - 2);
static const wchar_t kEf = 0x0444;
wcout << ((str.find(kEf) != wstring::npos) ? L"EXIST" : L"NONE");
}
int main()
{
static const int kExitOk = 0;
static const int kExitError = 1;
try
{
Test();
return kExitOk;
}
catch(const Win32Error& e)
{
cerr << "\n*** ERROR: " << e.what() << '\n';
cerr << " (GetLastError returned " << e.Error() << ")\n";
return kExitError;
}
catch(const exception& e)
{
cerr << "\n*** ERROR: " << e.what() << '\n';
return kExitError;
}
}
Output:
C:\TEMP>test.exe
abc
NONE
C:\TEMP>test.exe
abcфabc
EXIST
That's probably an encoding issue. wcin works with an encoding different from your compiler's/source code's. Try entering the ф in the console/wcin -- it will work. Try printing the ф via wcout -- it will show a different character or no character at all.
There is no platform independent way to circumvent this, but if you are on windows, you can manually change the console encoding, either with the chchp commandline command or programmatically with SetConsoleCP() (input) and SetConsoleOutputCP() (output).
You could also change your source file's/compiler's encoding. How this is done depends on your editor/compiler. If you are using MSVC, this answer might help you: https://stackoverflow.com/a/1660901/2128694

Problems formatting arrays and strings in C++

I'm having a simple problem that's been driving me nuts all day. I am trying to open a file on the current user's Desktop without knowing the current user's name.
The idea is that I would use the GetCurrentUser call to the API to get the user name. Then format a string to give the full path directory, and pass that variable into fopen to open the file. Here is the code I'm working on, I get no compiler errors and it compiles fine but nothing writes to the file.
int main() {
char pathName[200]; // declaring arrays
char userName[100];
DWORD userNameSize = sizeof(userName); // storage for user name
if (!GetUserName(userName, &userNameSize)) { cout << "user not found"; }
else { cout "hello" << userName;} // error checking
// format for Windows 7 desktop
sprintf(pathName, "\"C:\\Users\\%s\\Desktop\\text.txt\"", userName);
cout << pathName << "\n"; // confirms correct location
const char* fileLocation = pathName; // pointer to full path to pass into fputs
const char* test = "test"; // test information to write to file to confirm
FILE *f = fopen(fileLocation,"a+"); // open file in append mode
fputs(test, f); // write to file
fclose(f); // flush and exit
return 0;
}
Maybe I need to use a different call to format the string? Or declare fileLocation as a different variable type?
I'm fairly new to C++ and would appreciate any tips that would help me to be able to open a file on the current user's Desktop. Thanks.
EDIT IN RESPONSE TO JERRY'S ADVICE:
This is what my latest comment was referring to:
#include <iostream>
#include <cstring>
#include <string>
#include <conio.h>
using namespace std;
string location ("C:\\Users\\testuser\\Desktop\\log.dat");
char cstr = char* [location.size()]; //This is a problematic line
strcpy (cstr, location.c_str());
void write(const char* c)
{
const char* fileLocation = cstr;
//const char* fileLocation = g_pathName;
FILE *f = fopen(fileLocation,"a+"); // This is the problematic line right here.
if(f!=NULL)
{
fputs(c,f); // append to end of file
fclose(f); // save so no entries are lost without being flushed
}
}
int main ()
{
write("test");
cout << "done";
_getch();
return 0;
}
You have a missing semicolon at line 9 where it says:
...{ cout << "user not found" }...
Semicolons are not optional in C++, you need them for a working program. Also, as stated in the comments, you do not need quotes around the name of the file.
I'd use SHGetSpecialFolderPath from shlobj.h:
const char *szFileName = "text.txt";
const char *szContent = "test string";
char szPath[_MAX_PATH];
SHGetSpecialFolderPath(NULL, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
strcat(szPath, "\\");
strcat(szPath, szFileName);
FILE *pFile = fopen(szPath, "a+");
if(pFile != NULL)
{
fputs(szContent, pFile);
fclose(pFile);
}
I would use SHGetKnownFolderPath with FOLDERID_Desktop to get the path to the desktop, then add a file name to the end. You also almost certainly want to do the manipulation on std::strings, then when you've created the full name, use the .c_str member function to retrieve the name as a C-style string. Unless you have a really specific reason to do otherwise, you're probably better off using a std::ofstream instead of a C-style FILE * as well (and in that case if your compiler is current, you can probably pass the std::string object directly as the name).
Edit: some quick demo code creating and writing to a file on the user's desktop:
#include <windows.h>
#include <Shlobj.h>
#include <objbase.h>
#include <string>
#include <fstream>
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "shell32.lib")
std::string GetKnownFolderPath(REFKNOWNFOLDERID f) {
PWSTR sys_path;
SHGetKnownFolderPath(f, 0, NULL, &sys_path);
DWORD size = WideCharToMultiByte(CP_ACP, 0, sys_path, -1, 0, 0, NULL, NULL);
std::string path(size, ' ');
WideCharToMultiByte(CP_ACP, 0, sys_path, -1, &path[0], size, NULL, NULL);
// We're finished with the string the system allocated:
CoTaskMemFree(sys_path);
// WideCharToMultiByte leaves space for a NUL terminator we don't need
path.resize(path.size()-1);
return path;
}
int main() {
std::string path(GetKnownFolderPath(FOLDERID_Desktop));
path += "\\test.txt";
std::ofstream test(path.c_str());
test << "This is a test";
return 0;
}