This question already has answers here:
Getting user temporary folder path in Windows
(9 answers)
Closed 1 year ago.
I am trying to create a simple console app, that just cleans %temp% files.
So, how can I find the username and system disk to input it in directory of temp files?
This is a string variable, where I plan to store the %temp% directory:
string tempDir = sysDisk << ":/Users/" << userName << "AppData/Local/Temp/"
You are going about this all wrong. You don't need to retrieve the system disk or username at all. Especially since the location of the %temp% folder is user-defined, so there is no guarantee that it is even located at the path you are trying to create. And also that the location of Windows' USERS and TEMP folders have changed over time, and so may change again in the future.
The correct solution is to ask the OS exactly where the user's actual %temp% folder is currently located.
For instance, by using either:
std::getenv()
#include <cstdlib>
std::string tempDir = std::getenv("TEMP");
std::filesystem::temp_directory_path()
#include <filesystem>
std::string tempDir = std::filesystem::temp_directory_path().string();
Or, using Win32 API functions:
GetTempPath()
#include <windows.h>
char path[MAX_PATH] = {};
DWORD len = GetTempPath(path, MAX_PATH);
std::string tempDir(path, len);
GetEnvironmentVariable()
#include <windows.h>
char path[MAX_PATH] = {};
DWORD len = GetEnvironmentVariable("TEMP", path, MAX_PATH);
std::string tempDir(path, len);
ExpandEnvironmentStrings()
#include <windows.h>
char path[MAX_PATH] = {};
DWORD len = ExpandEnvironmentStrings("%TEMP%", path, MAX_PATH);
std::string tempDir(path, len-1);
// This would be useful when dealing with individual filenames, eg:
// ExpandEnvironmentStrings("%TEMP%\filename.ext", path, MAX_PATH);
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 12 months ago.
Improve this question
I'm trying to write a function which copies the current running program's .exe file to the Windows's Startup folder. I'm completely new to this, so don't be mad at me :D
I have this piece of code:
void startup()
{
std::string str = GetClipboardText();
wchar_t buffer[MAX_PATH];
GetModuleFileName(NULL, buffer, MAX_PATH);
std::wstring stemp = std::wstring(str.begin(), str.end());
LPCWSTR sw = stemp.c_str();
int a;
if (CopyFile(buffer, sw, true))
a = 1;
else a = 0;
}
Here I get the paths and save them in buffer (like D:\source\Project2\Debug\Project2.exe, I checked if that's the right path, if I paste what buffer contains in File Explorer, it runs the right .exe file) and sw (like C:\Users\user\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup). But it doesn't copy, and the simple if check shows a=0.
I thought it might not has the permission to copy files into the Startup folder, but it doesn't work with other "test" folders as well.
CopyFile() copies from one file to another file. So both lpExistingFileName and lpNewFileName parameters must be given file paths, but you are passing in a folder path for the lpNewFileName parameter. That will not work.
Had you followed what the documentation says:
If the function fails, the return value is zero. To get extended error information, call GetLastError.
GetLastError() would have told you that your inputs where unacceptable.
So, to fix this, you will have to extract and concatenate the source filename onto your destination folder path, eg:
std::wstring GetClipboardUnicodeText()
{
// get clipboard text using CF_UNICODETEXT, return as std::wstring...
}
void startup()
{
std::wstring str = GetClipboardUnicodeText();
WCHAR src[MAX_PATH] = {};
GetModuleFileNameW(NULL, src, MAX_PATH);
WCHAR dest[MAX_PATH] = {};
PathCombineW(dest, str.c_str(), PathFindFileNameW(src));
int a = CopyFileW(src, dest, TRUE);
}
Otherwise, to copy a file into a folder without specifying a target filename, use SHFileOperation() instead, eg:
void startup()
{
// note the extra null terminator!
std::wstring dest = GetClipboardUnicodeText() + L'\0';
// note +1 for the extra null terminator!
WCHAR src[MAX_PATH+1] = {};
GetModuleFileNameW(NULL, src, MAX_PATH);
SHFILEOPSTRUCTW op = {};
op.wFunc = FO_COPY;
op.pFrom = src;
op.pTo = dest.c_str();
op.fFlags = FOF_FILESONLY;
int a = SHFileOperationW(&op);
}
I want to delete all the files which begin with sub string.
CString Formatter = _T("C:\\logs\\test\\test_12-12-2018_1*.*");
DeleteFile(Formatter);
I intend to delete following files with above code
C:\logs\test\test_12-12-2018_1_G1.txt
C:\logs\test\test_12-12-2018_1_G2.txt
C:\logs\test\test_12-12-2018_1_G3.txt
C:\logs\test\test_12-12-2018_1_G4.txt
When I check error from GetLastError, I get ERROR_INVALID_NAME.
Any idea how to fix this?
DeleteFile doesn't take wildcards. It looks like what you need is a FindFirstFile/FindNextFile/FindClose loop to turn your wildcard into a list of full file names.
#include <windows.h>
#include <pathcch.h>
#pragma comment(lib, "pathcch.lib")
// (In a function now)
WIN32_FIND_DATAW wfd;
WCHAR wszPattern[MAX_PATH];
HANDLE hFind;
INT nDeleted = 0;
PathCchCombine(wszPattern, MAX_PATH, L"C:\\Logs\\Test", L"test_12-12-2018_1*.*");
SetCurrentDirectoryW(L"C:\\Logs\\Test");
hFind = FindFirstFileW(wszPattern, &wfd);
if(hFind == INVALID_HANDLE_VALUE)
{
// Handle error & exit
}
do
{
DeleteFileW(wfd.cFileName);
nDeleted++;
}
while (FindNextFileW(hFind, &wfd));
FindClose(hFind);
wprintf(L"Deleted %d files.\n", nDeleted);
Note that PathCchCombine, FindFirstFileW, and DeleteFileW can all fail, and robust code would check their return values and handle failures appropriately. Also, if FindNextFileW returns 0 and the last error code is not ERROR_NO_MORE_FILES, then it failed because of an actual error (not because there was nothing left to find), and that needs to be handled as well.
Also, if speed is a concern of yours (your example in your post about deleting four files in the same directory doesn't seem like it needs it), replace the line hFind = FindFirstFileW(...) with:
hFind = FindFirstFileExW(wszPattern, FindExInfoBasic, (LPVOID)&wfd, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH);
Although you can search for the file names, and then call DeleteFile individually for each, my advice would be to use one of the Windows shell functions to do the job instead.
For example, you could use code something like this:
#define _WIN32_IE 0x500
#include <windows.h>
#include <shellapi.h>
#include <shlobj.h>
#include <iostream>
#include <string>
static char const *full_path(std::string const &p) {
static char path[MAX_PATH+2] = {0};
char *ignore;
GetFullPathName(p.c_str(), sizeof(path), path, &ignore);
return path;
}
static int shell_delete(std::string const &name) {
SHFILEOPSTRUCT op = { 0 };
op.wFunc = FO_DELETE;
op.pFrom = full_path(name);
op.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_WANTNUKEWARNING | FOF_NOCONFIRMATION;
return !SHFileOperation(&op);
}
int main(int argc, char **argv) {
if ( argc < 2) {
fprintf(stderr, "Usage: delete <filename> [filename ...]");
return 1;
}
for (int i=1; i<argc; i++)
shell_delete(argv[i]);
}
One obvious advantage to this is that you can pass the FOF_ALLOWUNDO flag (as I have in the code above), which moves the files to the recycle bin instead of removing it permanently. Of course, you can omit that flag if you want to the files nuked.
Depending on what you're doing, there are a few other flags that might be handy, such as FOF_FILESONLY, to delete only files, not directories that might match the wildcard you specify, and FOF_NORECURSION to have it not recurse into subdirectories at all.
Microsoft considers SHFileOperation obsolescent, and has (in Windows Vista, if memory serves) "replaced" it with IFileOperation. IFileOperation is a COM interface though, so unless you're using COM elsewhere in your code, chances are pretty good that using it will add a fair amount of extra work for (at least in this case) little or no real advantage. Especially you're already using COM, however, this might be worth considering.
I am trying get path of the exe file at the same folder where this program will been. but i couldnt figure out how to do, i did something like this but it only gets the current programs path, and i dont know how to replace filenames between my program and the program i want to get path.
so simply can you help me about get the path of an exe (i know the name of that exe ) at the same folder where this program will been...
char fullp[MAX_PATH];
char selfp[MAX_PATH] = "..//myprogram.exe";
char otherprogram[MAX_PATH] = "//test.exe";
DWORD szPath;
szPath = GetModuleFileName(NULL, selfp, sizeof(selfp));
The Win32 API has a whole bunch of Path Handling functions available.
For instance, once you have obtained the calling process's full path from GetModuleFileName(), you can use PathRemoveFileSpec() to remove the filename leaving just the folder path:
char selfdir[MAX_PATH] = {0};
GetModuleFileNameA(NULL, selfdir, MAX_PATH);
PathRemoveFileSpecA(selfdir);
And then use either PathAppend() or PathCombine() to append a different filename to that path:
char otherprogram[MAX_PATH] = {0};
lstrcpyA(otherprogram, selfdir);
PathAppendA(otherprogram, "test.exe");
char otherprogram[MAX_PATH] = {0};
PathCombineA(otherprogram, selfdir, "test.exe");
OP is most of the way there. Here is an example of how to get the rest of the way.
To simplify the solution, I'm leaving char arrays as far behind as possible and using std::string.
#include <iostream>
#include <string>
#include <windows.h>
int main()
{
char selfp[MAX_PATH];
std::string otherprogram = "Failed to get path";
DWORD szPath;
szPath = GetModuleFileName(NULL, selfp, MAX_PATH);
if (szPath != 0) // successfully got path of current program
{
// helper string to make life much, much easier
std::string helper = selfp;
//find last backslash in current program path
size_t pos = helper.find_last_of( "\\" );
if (pos != std::string::npos) // found last backslash
{
// remove everything after last backslash. This should remove
// the current program's name.
otherprogram = helper.substr( 0, pos+1);
// append new program name
otherprogram += "test.exe";
}
}
std::cout << otherprogram << std::endl;
}
Currently I'm using windows 8.1....
in C++ when I'm trying to create a file on desktop with these codes ...
#include "stdafx.h"
#include <fstream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
ofstream myfile("C:/Users/%USERPROFILE%/Desktop/myfile.anything");
//ofstream myfile("C:/users/myfile.anything"); //Works fine with run As Administrator
return 0;
}
so the problems are completely clear 1.the userprofile don't know why? and 2.i should run the program as administrator but in here there is no need for run as....
i wanted to know if there is a little more simple way ....
Thanks
As the comments point out, you're trying to use an environment variable in your filepath, and the standard iostreams don't do environment variable expansion. You'll have to do that part yourself with platform-specific code, or simply use "normal" filepaths.
For C++ on Windows, the function to do this is GetEnvironmentVariable. It's one of those functions that takes a fixed size buffer, so using it is finicky enough that there's already a stackoverflow question all about how to call it correctly.
P.S. As the comments also pointed out, in places that do perform environment variable expansion (such as shell scripts or Windows Explorer), it's actually %USERPROFILE%, not &USERPROFILE&.
The comments to the other question were correct. Here's a basic way of fixing this (using http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188%28v=vs.85%29.aspx)
#include <fstream>
#include <Windows.h>
#include <string>
using namespace std;
int main() {
WCHAR *buffer = new WCHAR[260];
const WCHAR name[12] = "USERPROFILE";
DWORD result = GetEnvironmentVariable(name, buffer, 260);
if (result > 260) {
delete[] buffer; buffer = new WCHAR[result];
GetEnvironmentVariable(name, buffer, result);
}
wstring s("C:/Users/");
s += buffer;
s += "/Desktop/myfile.anything";
ofstream myfile(s.c_str());
// do things here
delete[] buffer;
return 0;
}
You have many ways to get user profile directory :
via the environment variable USERPROFILE :
#include <cstdlib>
...
string profile = getenv("USERPROFILE");
via Windows API, but it is bit harder :
#include <windows.h>
#include <userenv.h>
...
HANDLE processToken = ::GetCurrentProcess();
HANDLE user;
BOOL cr = ::OpenProcessToken(processToken, TOKEN_ALL_ACCESS, &user);
DWORD size = 2;
char * buff = new char[size];
cr = ::GetUserProfileDirectoryA(user, buff, &size); // find necessary size
delete[] buff;
buff = new char[size];
cr = ::GetUserProfileDirectoryA(user, buff, &size);
string profile = buff;
delete[] buff;
and you have to link with userenv.lib - the tests for return codes are left as an exercise :-)
via ExpandEnvironmentString :
size = ::ExpandEnvironmentStringsA("%USERPROFILE%\\Desktop\\myfile.anything",
NULL, 2);
buff = new char[size];
size = ::ExpandEnvironmentStringsA("%USERPROFILE%\\Desktop\\myfile.anything",
buff, size);
string profile = buff;
delete[] buff;
With third way you have directly your string, with first and second you only get profile directory and still have to concatenate it with relevant path.
But in fact, if you want you program to be language independant, you should really use SHGetSpecialFolderPath API function :
#include <shlobj.h>
...
buff = new char[255];
SHGetSpecialFolderPathA(HWND_DESKTOP, buff, CSIDL_DESKTOPDIRECTORY, FALSE);
string desktop = buff;
delete[] buff;
Because on my old XP box in french, Desktop is actually Bureau ...
I am writing a program in C++ which is going to process a large number (thousands) of PPM images all stored in the same directory. However, I first need to read in the pixel values. Is their a built in function in the standard namespace or a library I could import that would allow me to read in all the files one at a time without assuming I already know the file name using some sort of loop structure? In case it makes a difference I am writing the program on a Mac.
In order to avoid including boost::filesystem and the required dependencies, I ended up implementing this function:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <stdexcept>
//include headers required for directory traversal
#if defined(_WIN32)
//disable useless stuff before adding windows.h
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include "dirent.h"
#endif
/**
Traverses the provided directory (non-recursively) and extracts the absolute paths to all the files in it.
Doesn't support non-ASCII file names.
#param directory the absolute path to the directory
#return a vector of file names (including extension)
*/
std::vector<std::string> Filesystem::GetFilesInDirectory(const std::string &directory)
{
std::vector<std::string> output;
#if defined(_WIN32)
//select all files
std::string tempDirectory = directory + "*";
//initialize the WIN32_FIND_DATA structure
WIN32_FIND_DATA directoryHandle = {0};
//set the directory
std::wstring wideString = std::wstring(tempDirectory.begin(), tempDirectory.end());
LPCWSTR directoryPath = wideString.c_str();
//iterate over all files
HANDLE handle = FindFirstFile(directoryPath, &directoryHandle);
while(INVALID_HANDLE_VALUE != handle)
{
//skip non-files
if (!(directoryHandle.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
//convert from WCHAR to std::string
size_t size = wcslen(directoryHandle.cFileName);
std::vector<char> buffer;
buffer.resize(2 * size + 2);
size_t convertedCharacters = 0;
wcstombs_s(&convertedCharacters, buffer.data(), 2 * size + 2, directoryHandle.cFileName, _TRUNCATE);
//trim the null characters (ASCII characters won't fill the vector, since they require fewer bytes)
//convertedCharacters includes the null character, which we want to discard
std::string file(buffer.begin(), buffer.begin() + convertedCharacters - 1);
//add the absolute file path
output.emplace_back(file);
}
if(false == FindNextFile(handle, &directoryHandle)) break;
}
//close the handle
FindClose(handle);
#else
DIR *directoryHandle = opendir(directory.c_str());
if (NULL != directoryHandle)
{
dirent *entry = readdir(directoryHandle);
while (NULL != entry)
{
//skip directories and select only files (hopefully)
//if ((DT_DIR != entry->d_type) && (DT_UNKNOWN == entry->d_type))
if (DT_REG == entry->d_type)
{
output.emplace_back(entry->d_name);
}
//go to next entry
entry = readdir(directoryHandle);
}
closedir(directoryHandle);
}
#endif
return output;
}
I'm not very proud of the above unflexible / mostly useless / platform dependent code and I would definitely go for BOOST if possible. By the way, it's not tested on a MAC, so please let me know if it does the trick.