I am using MoveFileEx() API to move an existing file to a new file. I'm executing below program multiple times via a script.
Below program creates a unique file on every execution, and moves it to some file, say FinalCAFile.txt.
But, for some process execution, I'm getting an "Access is denied" error.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <string>
#include <iostream>
#include <fstream>
#include <process.h>
using namespace std;
#define BUFSIZE 1024
void PrintError(LPCTSTR errDesc,string process_id);
int main(int argc, TCHAR* argv[])
{
BOOL fSuccess = FALSE;
//Get process id to create unique file
std::string process_id = std::to_string(_getpid());
std::string tempCaFile = "C://Users//Administrator//source//repos//MyProgram//Debug//tempCAFile" + process_id + ".txt";;
std::string finalCaFile = "C://Users//Administrator//source//repos//MyProgram//Debug//FinalCAFile.txt";
//Create unique temp CA file
std::ofstream file(tempCaFile);
std::string my_string = "Hello from process " + process_id;
file << my_string;
std::wstring sourceTempCAFile = std::wstring(tempCaFile.begin(), tempCaFile.end());
std::wstring finalCAFile = std::wstring(finalCaFile.begin(), finalCaFile.end());
file.close();
//move temporary created file to FinalCAFile.txt
fSuccess = MoveFileEx(sourceTempCAFile.c_str(),
finalCAFile.c_str(),
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
if (!fSuccess)
{
cout<<endl<<"\nMoveFileEx failed for"<<process_id;
file.close();
PrintError(TEXT("MoveFileEx failed"),process_id);
return (9);
}
else
{
cout << endl<<"\nMoveFileEx Success for " << process_id;
std::string passedFileName = "C://Users//Administrator//source//repos//MyProgram//Debug//PassedFile" + process_id + ".txt";
std::ofstream passedFile(passedFileName);
std::string my_string = "Passed for process id: " + process_id;
passedFile << my_string;
passedFile.close();
}
file.close();
return (0);
}
// ErrorMessage support function.
// Retrieves the system error message for the GetLastError() code.
// Note: caller must use LocalFree() on the returned LPCTSTR buffer.
LPCTSTR ErrorMessage(DWORD error, string process_id)
{
LPVOID lpMsgBuf;
//error = 5;
printf("\nDWORD=%d", (unsigned int)error);
std::string failedFileName = "C://Users//Administrator//source//repos//MyProgram//Debug//FailedFile" + process_id + ".txt";
std::ofstream failedFile(failedFileName);
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL);
std::string my_string = "Failed for process id: " + process_id + " due to " + std::to_string((unsigned int)error) ;
failedFile << my_string;
return((LPCTSTR)lpMsgBuf);
}
// PrintError support function.
// Simple wrapper function for error output.
void PrintError(LPCTSTR errDesc, string process_id)
{
LPCTSTR errMsg = ErrorMessage(GetLastError(), process_id);
_tprintf(TEXT("\n** ERROR ** %s: %s\n"), errDesc, errMsg);
Sleep(10000);
LocalFree((LPVOID)errMsg);
}
MyScript.bat
FOR /L %%i IN (1,1,50) DO (
start C:\Users\Administrator\source\repos\ConsoleApplication4\Debug\MyProgram.exe
)
As per my understanding, we get the "Access is denied" error if there is a write permission issue.
But here, in my case, I'm executing the same program multiple times, so I don't know how a permission issue comes into the picture.
So, there seems to be some synchronization issue.
Also, there may be the possibility that MoveFileEx() doesn't support a proper locking mechanism internally.
I doubt if there is a synchronization issue with MoveFileEx(), then others might also get this issue.
Any suggestion to avoid this issue?
This above script starts executing MyProgram.exe 50 times, and on some executions few processes are giving the "Access is denied" error.
The result is inconsistent, I don't get the error on every script run, but after 2-3 runs I'm able to get the error.
Related
This question already has an answer here:
OpenFileMapping issues, can't find filemap
(1 answer)
Closed last year.
I'm trying to use WinApi to CreateFileMapping, MapViewOfFile and CopyMemory. It's not showhing me errors and buffor is being filed with my PID
int write_pid_to_memory(const char *t_pid)
{
_tprintf(TEXT("[write_pid_to_memory] t_pid: (%s).\n"), t_pid);
HANDLE h_map_file;
LPCTSTR p_buf;
h_map_file = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
BUF_SIZE, // maximum object size (low-order DWORD)
t_name); // name of mapping object
if (h_map_file == NULL)
{
_tprintf(TEXT("[write_pid_to_memory] Could not create file mapping object (%d).\n"),
GetLastError());
return 1;
}
p_buf = (LPTSTR)MapViewOfFile(
h_map_file, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (p_buf == NULL)
{
_tprintf(TEXT("[write_pid_to_memory] Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(h_map_file);
return 1;
}
std::cout << "[write_pid_to_memory] strlen(t_pid) * sizeof(char) " << strlen(t_pid) * sizeof(char) << std::endl;
CopyMemory((PVOID)p_buf, t_pid, (strlen(t_pid) * sizeof(char)));
_getch();
std::cout << "p_buf " << p_buf << std::endl;
UnmapViewOfFile(p_buf);
CloseHandle(h_map_file);
return 0;
}
... but then there is reading from memmory
int access_pid_from_memory()
{
HANDLE h_map_file;
LPCTSTR p_buf;
h_map_file = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write access
FALSE, // do not inherit the name
t_name); // name of mapping object
if (h_map_file == NULL)
{
_tprintf(TEXT("[access_pid_from_memory] Could not open file mapping object (%d).\n"),
GetLastError());
return 1;
}
p_buf = (LPTSTR)MapViewOfFile(
h_map_file, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (p_buf == NULL)
{
_tprintf(TEXT("[access_pid_from_memory] Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(h_map_file);
return 1;
}
MessageBox(NULL, p_buf, TEXT("[access_pid_from_memory] Process2"), MB_OK);
UnmapViewOfFile(p_buf);
CloseHandle(h_map_file);
return 0;
}
where I get System Error (2) while trying to open Mapping.
My PID: 19516
[access_pid_from_memory] Could not open file mapping object (2).
[write_pid_to_memory] t_pid: (19516).
[write_pid_to_memory] strlen(t_pid) * sizeof(char) 5
p_buf 19516
Envariamental variable = NEW
Env var value length = 3
Env var value compare resault = 0
Mutex created sucesfully
Code of those functions is from
https://learn.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory
and only thing I've changed is
CopyMemory((PVOID)p_buf, t_pid, (strlen(t_pid) * sizeof(char)));
Instead of
CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
Where t_pid is just a const char *, becouse I was tired of Windows TCHAR types and I had no clue how to convert DWORD ProcessID to TCHAR to pass it to memcopy.
Well, I'm clueless why I'm unable to open Mapping. Windows is probably beyond me and I have no idea
how
TCHAR t_name[] = TEXT("Global\\MyFileMappingObject");
is supposed to be recognised by system to find memory from which I want to read a message.
Whole programm is supposed to lock execution for only one process and if there is a System variable named "SO2" of value "NEW", new process should stop execution of previous process and continoue locking program for himself.
Locking mechanism is with mutex and to find previous porcess ID, I wanted my current process ID to be saved in memory, for next process to read it form, to close it when sys var will be "NEW".
Nothing crazy. All of this in Linux I've done in one day, but Windows is killing me.
Please help
There is main if someone would be intrested:
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <windows.h>
#include <string>
#include <conio.h>
#include <tchar.h>
#define BUFFER_SIZE 2048
#define ENV_KEY "SO2"
#define ENV_VAL "NEW"
#define BUF_SIZE 256
TCHAR t_name[] = TEXT("Global\\MyFileMappingObject");
HANDLE h_mutex;
int write_pid_to_memory(const char *dw_pid);
int access_pid_from_memory();
int main(int argc, char **argv)
{
DWORD dw_pid = GetCurrentProcessId();
std::stringstream stream;
stream << dw_pid;
const char *t_pid = stream.str().c_str();
// int legnth = s_pid.length()
// const char *t_pid = (char*)malloc( * sizeof(char));
// const char t_pid = (char)malloc(strlen(dw_pid) * sizeof(char));
std::cout << "My PID: " << dw_pid << std::endl;
access_pid_from_memory();
write_pid_to_memory(t_pid);
std::string env_val(ENV_VAL);
char c_buffer[BUFFER_SIZE];
LPCSTR lp_name = ENV_KEY;
LPSTR lp_buffer = c_buffer;
DWORD dw_size = BUFFER_SIZE;
DWORD get_env_var;
//Write to memory your pid for other process to access it and close you
get_env_var = GetEnvironmentVariable(
lp_name,
lp_buffer,
dw_size);
if (GetLastError() == ERROR_ENVVAR_NOT_FOUND)
{
std::cout << "Couldn't find envariamental variable \"SO2\"." << std::endl;
}
if (BUFFER_SIZE == get_env_var)
{
std::cout << "Buffer for function [GetEnvironmentVariable] is too small. Function failed." << std::endl;
}
std::cout << "Envariamental variable = " << lp_buffer << std::endl;
std::string str_buffer(lp_buffer);
std::cout << "Env var value length = " << str_buffer.length() << std::endl;
std::cout << "Env var value compare resault = " << str_buffer.compare(env_val) << std::endl;
HANDLE h_mutex = NULL;
LPCSTR str = ENV_KEY;
h_mutex = OpenMutex(
MUTEX_ALL_ACCESS,
TRUE,
str);
if (NULL != h_mutex)
{
if (str_buffer.compare(env_val) == 0)
{
//Realease mutex3
ReleaseMutex(h_mutex);
//Close previous process
}
else
{
throw std::runtime_error("Instance of a program is already running");
}
}
h_mutex = CreateMutex(
NULL,
FALSE,
str);
if (h_mutex == NULL)
{
std::cout << "Failed to create mutex: error - " << GetLastError() << std::endl;
return 1;
}
std::cout << "Mutex created sucesfully" << std::endl;
DWORD dw_wait_res;
dw_wait_res = WaitForSingleObject(
h_mutex, // handle to mutex
INFINITE); // no time-out interval
for (;;)
{
Sleep(100);
}
CloseHandle(h_mutex);
system("PAUSE");
return 0;
}
Your logging clearly shows [access_pid_from_memory] occurs before [write_pid_to_memory].
And we can clearly see in your main() function that it calls access_pid_from_memory() first (which attempts to open the memory map and then closes it), and then afterwards calls write_pid_to_memory() (which creates the memory map and then closes it). Since there is no active handle referring to the mapping, it gets destroyed as soon as write_pid_to_memory() exits.
So, you are doing operations out of order, which is why OpenFileMapping() is failing. At no point does the mapping actually exist when access_pid_from_memory() is trying to open it.
You need to do the following instead:
in one process, create the mapping first, and leave it open.
THEN, in another process, open the mapping while it is still open in the previous process.
THEN, use the mapped memory as needed.
THEN, close the mapping in both processes.
There are other issues with your code, too:
converting the process ID to a string just to share it. You can share the ID as a binary DWORD instead.
access_pid_from_memory() doesn't actually read anything from the mapping (if it were able to open it at all).
main() is assigning t_pid to point at dangling memory. The call to stream.str() produces a temporary std::string that is destroyed as soon as c_str() exits.
I don't even know what you are attempting to do with your environment variable and mutex, and what that has to do with sharing memory. For purposes of this post, you should remove that code until you have your shared memory working properly.
So basically, I'm listing all of the files in my temp directory, and then deleting them. Obviously, some of the files are in use and the program itself is using the files that it's deleting. I tried fiddling around with SHFileOperation with no success. (tbh I got no idea how to use it). How do I make it check if the file is in use by another program before deleting it? Thanks!
This is giving me the error: fs::remove_all(entry.path());
Code:
#include <iostream>
#include <Windows.h>
#include <filesystem>
#include <lmcons.h>
#include <fileapi.h>
#include "cColors.h"
using namespace std;
namespace fs = filesystem;
char type;
int main()
{
SetConsoleTextAttribute(h, 15);
while (true)
{
cout << "[~] Are you sure you want to run Windows Cleaner? [Y/N]";
cin >> type;
if (type == 'n')
{
break;
exit(0);
}
else if (type == 'y')
{
cout << "[#] Cleaning temp directory\n";
for (const auto& entry : fs::directory_iterator(fs::temp_directory_path()))
{
cout << "[#] Deleting " << entry.path();
fs::remove_all(entry.path()); //This is giving me the error
}
}
else
{
break;
exit(0);
}
}
}
Here's what I came up with. A recursive delete function which uses CreateFile. My original comment
Maybe you could use CreateFile with desired access 0 and share mode OPEN_EXISTING. Then you have to check the failing reason. If it doesn't fail; close and delete.
wasn't 100% correct. dwDesiredAccess should be 0, dwShareMode should be FILE_SHARE_DELETE, dwCreationDisposition should be OPEN_EXISTING and dwFlagsAndAttributes should be FILE_FLAG_DELETE_ON_CLOSE. If then a valid handle is received the last CloseHandle on the file will lead to deletion (see here).
Here's an example:
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <tchar.h>
#include <iostream>
#include <filesystem>
#ifdef _UNICODE
auto& cout = std::wcout;
#else
using std::cout;
#endif // _UNICODE
using std::endl;
namespace fs=std::filesystem;
void deleteRecursive(const fs::path& path);
void tryDeleteFile(const fs::path& path);
int main(int argc, char* argv[])
{
TCHAR tempDir[255];
GetEnvironmentVariable(_T("TEMP"), tempDir, 255);
deleteRecursive(fs::path(tempDir));
return 0;
}
void deleteRecursive(const fs::path& path)
{
fs::directory_iterator dirs(path);
for (const auto& entry : dirs)
{
const auto& path = entry.path();
if (entry.is_directory())
{
deleteRecursive(path);
if (fs::is_empty(path))
{
if (!RemoveDirectory(path.c_str()))
{
cout << _T("Can't delete dir: ") << path << endl;
}
}
}
else
{
tryDeleteFile(path);
}
}
}
void tryDeleteFile(const fs::path& path)
{
const auto file = path.c_str();
HANDLE fileHandle = CreateFile(
file, // lpFileName,
0, // dwDesiredAccess,
FILE_SHARE_DELETE, // dwShareMode,
NULL, // lpSecurityAttributes,
OPEN_EXISTING, // dwCreationDisposition,
FILE_FLAG_DELETE_ON_CLOSE, // dwFlagsAndAttributes,
NULL // hTemplateFile
);
if (fileHandle == INVALID_HANDLE_VALUE)
{
DWORD lastErr = GetLastError();
if (lastErr != ERROR_FILE_NOT_FOUND) // gone in the mean time
{
cout << _T("Can't delete file: ") << file << endl;
}
}
else
{
CloseHandle(fileHandle);
}
}
tryDeleteFile contains the crucial part.
Catch the exception Just ignore the error and continue. Or use the second form and pass an error_code argument. Than you get no exception and you can check the reason why it failed.
If the file is in use you get an error. So you can't delete it. If you have no rights you can't delete it too.
Checking the usage first is a race condition. After the check, the file might get closed and you may be able to delete it safely. There is no difference in checking first and than delete it or trying to delete it and it fails.
I am trying to create a simple word highlighter for browsers (Chrome and Firefox) and I would like my program to use the process name (chrome.exe or firefox.exe) and then get their process ID.
I've found code that lets me get the process ID, but it requires a user to type the process name manually:
#include "pch.h"
#include <iostream>
#include <string>
#include <windows.h>
#include <tlhelp32.h>
DWORD FindProcessId(const std::wstring& processName);
int main()
{
std::wstring processName;
std::wcout << "Enter the process name: ";
std::getline(std::wcin, processName);
DWORD processID = FindProcessId(processName);
if (processID == 0)
std::wcout << "Could not find " << processName.c_str() << std::endl;
else
std::wcout << "Process ID is " << processID << std::endl;
system("PAUSE");
return 0;
}
DWORD FindProcessId(const std::wstring& processName)
{
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (processesSnapshot == INVALID_HANDLE_VALUE)
return 0;
Process32First(processesSnapshot, &processInfo);
if (!processName.compare(processInfo.szExeFile))
{
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
while (Process32Next(processesSnapshot, &processInfo))
{
if (!processName.compare(processInfo.szExeFile))
{
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
}
CloseHandle(processesSnapshot);
return 0;
}
Now, is there a way to manipulate this code for it to get the process ID automatically by checking whether the user is running firefox.exe or chrome.exe?
And after getting the process ID, how do I make my program understand that it needs to focus on said ID?
Now, is there a way to manipulate this code for it to get the process ID automatically by checking whether the user is running firefox.exe or chrome.exe?
#include <iostream>
#include <string>
#include <windows.h>
#include <tlhelp32.h>
DWORD FindProcessId(const std::wstring& processName);
int main()
{
std::wstring fifi = L"firefox.exe";
std::wstring gogo = L"chrome.exe";
auto fifi_proc_id = FindProcessId(fifi);
auto gogo_proc_id = FindProcessId(gogo);
if(fifi_proc_id && gogo_proc_id) {
// both runnin O.O what now?
}
else if(fifi_proc_id) {
// firefox running ... do stuff
}
else if(gogo_proc_id) {
// chrome running ... do stuff
}
else {
// none of both :(
}
}
And after getting the process ID, how do I make my program understand that it needs to focus on said ID?
I am sorry, but I don't know what you mean by "make my program understand that it needs to focus on said ID".
I would like to copy all files from test1 into test2. The code compiles but nothing happens.
#include <iostream>
#include <stdlib.h>
#include <windows.h>
using namespace std;
int main()
{
string input1 = "C:\\test1\\";
string input2 = "C:\\test2\\";
MoveFile(input1.c_str(), input2.c_str());
}
I was considering xcopy but it would not accept a pre defined string. Is there a work around?
std::string GetLastErrorAsString()
{
//Get the error message, if any.
DWORD errorMessageID = ::GetLastError();
if (errorMessageID == 0)
return std::string(); //No error message has been recorded
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
std::string message(messageBuffer, size);
//Free the buffer.
LocalFree(messageBuffer);
return message;
}
int main()
{
string input1 = "C:\\test1\\";
string input2 = "C:\\test2\\";
if (!MoveFile(input1.c_str(), input2.c_str()))
{
string msg = GetLastErrorAsString();
cout << "fail: " << msg << endl;
}
else {
cout << "ok" << endl;
}
system("pause");
}
Your code works for me, you may have to set the character set to use multi-byte character set in your project properties.
If not, provide us with the error.
Check if you have the write rights on C:.
Check if there already is a test2 folder in C: (or if there is not a test1 folder in C:).
I resovled the issue by removing the \\ from test2. Folder test 2 doesn't exist. Thank you for the replies and the test code. I think SHFileOperation will be a better option as I have to transfer files from a floppy to my C drive. string input1 = "C:\\test1\\";
string input2 = "C:\\test2";
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;
}