How to remove directories using c++ in windows - c++

I'm trying to remove directories using this code, but this wouldn't work. And, I couldn't found the issue. Maybe lack of privileges on the deletion operation on Windows?
Here is the code that I'm using:
#define _CRT_SECURE_NO_DEPRECATE
#include <string>
#include <iostream>
#include <windows.h>
#include <conio.h>
int DeleteDirectory(const std::string &refcstrRootDirectory,
bool bDeleteSubdirectories = true)
{
bool bSubdirectory = false; // Flag, indicating whether
// subdirectories have been found
HANDLE hFile; // Handle to directory
std::string strFilePath; // Filepath
std::string strPattern; // Pattern
WIN32_FIND_DATA FileInformation; // File information
strPattern = refcstrRootDirectory + "\\*.*";
hFile = ::FindFirstFile((LPCWSTR)strPattern.c_str(), &FileInformation);
if (hFile != INVALID_HANDLE_VALUE)
{
do
{
if (FileInformation.cFileName[0] != '.')
{
strFilePath.erase();
strFilePath = refcstrRootDirectory + "\\" + (char*)FileInformation.cFileName;
if (FileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (bDeleteSubdirectories)
{
// Delete subdirectory
int iRC = DeleteDirectory(strFilePath, bDeleteSubdirectories);
if (iRC)
return iRC;
}
else
bSubdirectory = true;
}
else
{
// Set file attributes
if (::SetFileAttributes((LPCWSTR)strFilePath.c_str(),
FILE_ATTRIBUTE_NORMAL) == FALSE)
return ::GetLastError();
// Delete file
if (::DeleteFile((LPCTSTR)strFilePath.c_str()) == FALSE)
return ::GetLastError();
}
}
} while (::FindNextFile(hFile, &FileInformation) == TRUE);
// Close handle
::FindClose(hFile);
DWORD dwError = ::GetLastError();
if (dwError != ERROR_NO_MORE_FILES)
return dwError;
else
{
if (!bSubdirectory)
{
// Set directory attributes
if (::SetFileAttributes((LPCWSTR)refcstrRootDirectory.c_str(),
FILE_ATTRIBUTE_NORMAL) == FALSE)
return ::GetLastError();
// Delete directory
if (::RemoveDirectory((LPCWSTR)refcstrRootDirectory.c_str()) == FALSE)
return ::GetLastError();
}
}
}
return 0;
}
int main()
{
int iRC = 0;
std::string strDirectoryToDelete = "C:\\Users\\AbysCo\\Desktop\\del";
// Delete 'c:\mydir' without deleting the subdirectories
iRC = DeleteDirectory(strDirectoryToDelete, false);
if (iRC)
{
std::cout << "Error " << iRC << std::endl;
return -1;
}
// Delete 'c:\mydir' and its subdirectories
iRC = DeleteDirectory(strDirectoryToDelete);
if (iRC)
{
std::cout << "Error " << iRC << std::endl;
return -1;
}
// Wait for keystroke
_getch();
return 0;
}
OS: Windows 7 SP1.
Any brilliant idea, please?

The Windows API already comes with a function to delete entire directories. That is SHFileOperation. And on Vista or later you use IFileOperation for the same purpose.
Not only does this make the process trivial, not much more than a one liner, but it has other benefits:
You can put the deleted directory into the recycle bin if you choose.
You can show the standard system progress dialog if you choose.

Related

Is there a way to find out if a file deleted using system() got deleted?

I'm trying to make a program that deletes a list of files one by one, using system().
The reason I'm using system() instead of remove() is because remove() doesn't support environment variables.
I've tried checking the return value, but my code seems to just always output that it's been deleted, even when it hasn't.
Code:
void fileLoop() {
std::vector<std::string> fileList = { "\"%userprofile%\\test.txt\"" };
while (!inter) {
for (int count = 0; count <= 0; count++) {
std::string moddedstring = "del /F " + fileList[count];
DWORD checker = GetFileAttributes((LPCWSTR)fileList[count].c_str());
if (checker == INVALID_FILE_ATTRIBUTES) {
goto next;
}
else {
system(moddedstring.c_str());
MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
}
next:
std::cout << "\n";
}
Sleep(500);
}
}
I thought there is some easy way to find this out. I haven't found a way like that yet.
I will be adding more paths later.
Update:
I've tried using OpenProcessToken() with ExpandEnvironmentStringsForUserW() to add ENVs
But it complains that my buffer is of type LPCSTR* even when I set it to LPCSTR
Thanks!
A better way to get the user's profile path is to simply ask the OS directly, via SHGetFolderPath(CSIDL_PROFILE) or SHGetKnownFolderPath(FOLDERID_Profile).
Also, you should use DeleteFileA() instead of system("del"). There is no need to spawn an external console process when you can do the deletion directly. Also, because you are interested in error checking, which is much easier if you use the Win32 API directly.
Also, (LPCWSTR)fileList[count].c_str() will not work like you think. You can't convert a const char* to a const wchar_t* using a simple typecast. Drop the typecast and use GetFileAttributesA() instead.
Or, simply don't bother checking attributes at all. You can call DeleteFileA() unconditionally and let it tell you if it actually succeeded or failed.
With that said, try something more like this:
#include <shlobj.h>
std::string getUserProfilePath() {
CHAR szPath[MAX_PATH];
if (FAILED(SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, szPath))) {
// error handling...
throw ...;
}
int len = lstrlenA(szPath);
szPath[len] = '\\';
return std::string(szPath, len + 1);
/*
PWSTR pszPath;
if (FAILED(SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pszPath))) {
// error handling...
throw ...;
}
int wlen = lstrlenW(pszPath);
int len = WideCharToMultiByte(0, 0, pszPath, wlen, NULL, 0, NULL, NULL);
if (len == 0) {
// error handling...
throw ...;
}
std::vector<CHAR> buffer(len + 1);
len = WideCharToMultiByte(CP_ACP, 0, pszPath, wlen, buffer.data(), len, NULL, NULL);
if (len == 0) {
// error handling...
throw ...;
}
buffer[len] = '\\';
CoTaskMemFree(pszPath);
return std::wstring(buffer.data(), buffer.size());
*/
}
void fileLoop() {
std::vector<std::string> fileList = { getUserProfilePath() + "test.txt" };
while (!inter) {
for (size_t count = 0; count < fileList.size(); ++count) {
if (DeleteFileA(fileList[count].c_str())) {
MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
} else {
// error handling...
}
std::cout << "\n";
}
Sleep(500);
}
}
Alternatively, using Unicode strings instead (which you really should use when interacting with the filesystem):
#include <shlobj.h>
std::wstring getUserProfilePath() {
WCHAR szPath[MAX_PATH];
if (FAILED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, szPath))) {
// error handling...
throw ...;
}
int len = lstrlenW(szPath);
szPath[len] = '\\';
return std::wstring(szPath, len + 1);
/*
PWSTR pszPath;
if (FAILED(SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pszPath))) {
// error handling...
throw ...;
}
int len = lstrlenW(pszPath);
std::wstring sPath(len + 1, '\0');
std::copy(pszPath, pszPath + len, sPath.begin());
sPath[len] = '\\';
CoTaskMemFree(pszPath);
return sPath;
*/
}
void fileLoop() {
std::vector<std::wstring> fileList = { getUserProfilePath() + L"test.txt" };
while (!inter) {
for (size_t count = 0; count < fileList.size(); ++count) {
if (DeleteFileW(fileList[count].c_str())) {
MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
} else {
// error handling...
}
std::cout << "\n";
}
Sleep(500);
}
}
Even better, if you are using C++17 or later, consider using the <filesystem> library instead:
#include <shlobj.h>
#include <filesystem>
using fs = std::filesystem;
fs::path getUserProfilePath() {
WCHAR szPath[MAX_PATH];
if (FAILED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, szPath))) {
// error handling...
throw ...;
}
return szPath;
/*
PWSTR pszPath;
if (FAILED(SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pszPath))) {
// error handling...
throw ...;
}
fs::path pth(pszPath);
CoTaskMemFree(pszPath);
return pth;
*/
}
void fileLoop() {
std::vector<fs::path> fileList = { getUserProfilePath() / L"test.txt" };
while (!inter) {
for (auto &pth : fileList) {
std::error_code ec;
if (fs::remove(pth, ec)) {
MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
} else {
// error handling...
}
std::cout << "\n";
}
Sleep(500);
}
}

C++ read new files added to a directory

I have a C++ program that when starts, gets the list of all files in a directory and does some processing with them (packetizes them for transmission over network). The current version is static and the program only sees the files at the time the program starts. Now I want to change it to a dynamic version where it can continuously see the new files added in the directory (let's assume they have a meaningful incremented name; for instance f1, f2, f3, ... so next one coming would be f4. This way I can use some index).
Right now this is the piece of code (static reading of all files):
DIR *dp;
struct dirent *dirp;
char * dir = new char[inData->dir.length() + 1];
strcpy(dir, inData->dir.c_str());
dp = opendir(dir);
int file_index = 0;
int total_files = DataUtil::count_files(dp);
if (readdir(dp) == NULL)
rewinddir(dp);
//for each file in the directory
while ((dirp = readdir( dp ))) {
string f_name = dirp->d_name;
if (f_name != "." && f_name != "..") { // except . and ..
//Do some processing
my_function(f_name, file_index);
file_index++;
}
}
closedir(dp);
I modified the code using the rewinddir(dp); function and file_index to notice new files. But it doesn't work. Where is the problem?
DIR *dp;
struct dirent *dirp;
char * dir = new char[inData->dir.length() + 1];
strcpy(dir, inData->dir.c_str());
dp = opendir(dir);
int file_index = 0;
int total_files = MyDataUtil::count_files(dp);
if (readdir(dp) == NULL)
rewinddir(dp);
//for each file in the directory
while ((dirp = readdir( dp ))) {
string f_name = dirp->d_name;
if (f_name != "." && f_name != "..") {
//skip files already read
for (int var = 0; var < file_index; var++) {
readdir(dp);
}
//Do some processing
my_function(f_name, file_index);
file_index++;
//reset dir stream, and update total_files
rewinddir(dp);
total_files = MyDataUtil::count_files(dp);
}
}
closedir(dp);
The total_files function:
int MyDataUtil::count_files(DIR *dp){
struct dirent *dirp;
int num = 0;
while ((dirp = readdir( dp ))) {
string f_name = dirp->d_name;
if (f_name != "." && f_name != "..")
num++;
}
return num;
}
Here is the rewinddir documentation. It says ' might or might not be returned'!
Description:
The rewinddir function is used to reinitialize the directory stream dirstream, so that if you call readdir
it returns information about the first entry in the directory again. This function also notices if files
have been added or removed to the directory since it was opened with opendir. (Entries for these files
might or might not be returned by readdir if they were added or removed since you last called opendir
or rewinddir.)
Use inotify on Linux to get asynchronous notifications when files are added / removed / changed in a directory of your choice
http://man7.org/linux/man-pages/man7/inotify.7.html
Here is another way, maybe you can try it.
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <list>
#include <Locale.h>
using namespace std;
void ListFile(std::list<std::wstring> &list_Steady)
{
setlocale(LC_ALL, "chs");
WIN32_FIND_DATAW FindFileData;
HANDLE hFind;
LPCWSTR s = L"C:\\Test_Monitor\\*.*";//The path of Directory you want to Monitor
hFind = FindFirstFileW(s, &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
printf ("FindFirstFile failed (%d)\n", GetLastError());
return ;
}
else
{
wcout << "The first file found is " << FindFileData.cFileName << endl;
list_Steady.push_back(FindFileData.cFileName);
}
while(FindNextFile(hFind, &FindFileData))
{
wcout << "The next file found is " << FindFileData.cFileName << endl;
list_Steady.push_back(FindFileData.cFileName);
}
FindClose(hFind);
hFind = NULL;
}
void CompareFile(std::list<std::wstring> &new_list , std::list<std::wstring> &steady_list)
{
bool bFound = false;
for (std::list<std::wstring>::iterator it = new_list.begin(); it != new_list.end(); ++it)
{
bFound = false;
for (std::list<std::wstring>::iterator steady_it = steady_list.begin(); steady_it != steady_list.end(); ++steady_it)
{
//Compare to origin list, if file still exist will return true
if(it->compare(steady_it->c_str()) == 0)
{
bFound = true;
}
}
//else this is a new file
if(!bFound)
{
wcout << it->c_str() << L" new file detected" << endl;
}
}
bool bfound2 = false;
for (std::list<std::wstring>::iterator it = steady_list.begin(); it != steady_list.end(); ++it)
{
bfound2 = false;
for (std::list<std::wstring>::iterator new_it = new_list.begin(); new_it != new_list.end(); ++new_it)
{
//Compare to origin list, if file still exist will return true
if(it->compare(new_it->c_str()) == 0)
{
bfound2 = true;
}
}
//else this file has been deleted
if(!bfound2)
{
wcout<< it->c_str() << L" This file has disappeared" << endl;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
list<wstring> Steady_Directory;
list<wstring> New_Directory;
DWORD WaitStatus = 0;
Steady_Directory.clear(); New_Directory.clear(); //be sure that list is clean
ListFile(Steady_Directory);
HANDLE FileChange = INVALID_HANDLE_VALUE;
FileChange = ::FindFirstChangeNotification(L"C:\\Test_Monitor",FALSE,FILE_NOTIFY_CHANGE_FILE_NAME);
if(FileChange == INVALID_HANDLE_VALUE)
{
cout << "LOG : FAIL to get File Change, With error code: " << ::GetLastError() << endl;
return -1;
}
WaitStatus = ::WaitForSingleObject(FileChange, INFINITE);
if (WaitStatus == WAIT_OBJECT_0)
{
ListFile(New_Directory);
CompareFile(New_Directory, Steady_Directory);
}
system("PAUSE");
return 0;
}
I use two list, one stand for old state, the other is for the next state, and use ::FindFirstChangeNotification to subscribe specific directory's state.
Any file change in the directory will set the HANDLE Filechange, then will call the function CompareFile to make sure which file has been added or deleted.
You can make a for/while loop to make this monitor work constantly

Recursively find files with a certain extension using FindFirstFile and FindNextFile

I have a working code that returns all files in all sub-directories given a directory with a wild card. For example: "C://*". This works perfectly fine. Now, I was wondering if it is possible to recursively iterate over all files using a certain file extension. For example: "C://*.png", without altering the code. I was looking for sth like: "C://*/../*.png", but I could not find a solution. Are there any wildcard tricks I could use?
And here is an example using FindFirstFile and FindNextFile that can recursively find files having a given extension.
#include "stdafx.h"
#include <Windows.h>
#include <atlpath.h>
#include <list>
#include <iostream>
#ifdef _UNICODE
#define cout wcout
#endif
void FindFiles(
const CString& strRootPath,
const CString& strExt,
std::list<CString>& listFiles,
bool bRecursive = true)
{
CString strFileToFind = strRootPath;
ATLPath::Append(CStrBuf(strFileToFind, MAX_PATH), _T("*.*"));
WIN32_FIND_DATA findData = { 0 };
HANDLE hFileFind = ::FindFirstFile(strFileToFind, &findData);
if (INVALID_HANDLE_VALUE != hFileFind)
{
do
{
CString strFileName = findData.cFileName;
if ((strFileName == _T(".")) || (strFileName == _T("..")))
continue;
CString strFilePath = strRootPath;
ATLPath::Append(CStrBuf(strFilePath, MAX_PATH), strFileName);
if (bRecursive && (ATLPath::IsDirectory(strFilePath)))
{
FindFiles(strFilePath, strExt, listFiles);
}
else
{
CString strFoundExt = ATLPath::FindExtension(strFilePath);
if (! strExt.CompareNoCase(strFoundExt))
istFiles.push_back(strFilePath);
}
} while (::FindNextFile(hFileFind, &findData));
::FindClose(hFileFind);
}
}
int main()
{
std::list<CString> listFiles;
FindFiles(_T("e:\\tests"), _T(".cpp"), listFiles);
for (const auto& strFile : listFiles)
std::cout << strFile.GetString() << std::endl;
return 0;
}
Note: to make things easier, I've used ATL stuff like ATL::CString and ATLPath functions. There is no problem to use them in a Win32 or Console application.
You need to search recursively for each subdirectory. I happen to have some code to do this, the following code might help.
#include <functional>
#include <io.h>
enum enumflags {
ENUM_FILE = 1,
ENUM_DIR,
ENUM_BOTH
};
//return value:
// False means that the searching has been aborted by the callback function.
// It will return true otherwise.
bool enumsubfiles(
const std::wstring &dir_with_back_slant, //for example: L"C:\\", L"E:\\test\\"
const std::wstring &filename, //for example: L"123.txt", L"*.exe", L"123.???"
unsigned int maxdepth, //0 means not searching subdirectories, 1 means maximum depth of subdirectories is 1,
// pass -1 to search all the subdirectories.
enumflags flags, //search files, directories, or both.
std::function<bool(const std::wstring &dir_with_back_slant, _wfinddata_t &attrib)> callback
)
{
_wfinddata_t dat;
size_t hfile;
std::wstring fullname = dir_with_back_slant + filename;
std::wstring tmp;
bool ret = true;
hfile = _wfindfirst(fullname.c_str(), &dat);
if (hfile == -1) goto a;
do {
if (!(wcscmp(L".", dat.name) && wcscmp(L"..", dat.name))) continue;
if (((dat.attrib&_A_SUBDIR) && (!(flags&ENUM_DIR))) || ((!(dat.attrib&_A_SUBDIR)) && (!(flags&ENUM_FILE)))) continue;
ret = callback(dir_with_back_slant, dat);
if (!ret) {
_findclose(hfile);
return ret;
}
} while (_wfindnext(hfile, &dat) == 0);
_findclose(hfile);
a:
if (!maxdepth) return ret;
tmp = dir_with_back_slant + L"*";
hfile = _wfindfirst(tmp.c_str(), &dat);
if (hfile == -1) return ret;
do {
if (!(wcscmp(L".", dat.name) && wcscmp(L"..", dat.name))) continue;
if (!(dat.attrib&_A_SUBDIR)) continue;
tmp = dir_with_back_slant + dat.name + L"\\";
ret = enumsubfiles(tmp, filename, maxdepth - 1, flags, callback);
if (!ret) {
_findclose(hfile);
return ret;
}
} while (_wfindnext(hfile, &dat) == 0);
_findclose(hfile);
return ret;
}
Here is an example of the usage of the function above:
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
//the default code page of my console window is 936
setlocale(CP_ACP, ".936");
enumsubfiles(L"C:\\", L"*.exe", 1, ENUM_FILE, [](const std::wstring &dir_with_back_slant, _wfinddata_t &attrib)->bool
{
std::wcout << dir_with_back_slant << attrib.name << '\n';
return true; //return true to continue, return false to abort searching.
});
return 0;
}
And you will get the following output:
C:\OpenSSL-Win64\unins000.exe
C:\putty\PAGEANT.EXE
C:\putty\PLINK.EXE
C:\putty\PSCP.EXE
C:\putty\PSFTP.EXE
C:\putty\PUTTY.EXE
C:\putty\PUTTYGEN.EXE
C:\Windows\ampa.exe
C:\Windows\bfsvc.exe
C:\Windows\explorer.exe
C:\Windows\HelpPane.exe
C:\Windows\hh.exe
C:\Windows\notepad.exe
C:\Windows\regedit.exe
C:\Windows\RtCRU64.exe
C:\Windows\SECOH-QAD.exe
C:\Windows\splwow64.exe
C:\Windows\winhlp32.exe
C:\Windows\write.exe
C:\测试\测试.exe
The maxdepth I passed to the function is 1. Pass -1 to search all the subdirectories.

go to other logical drives and continues to search for file

i have a program that search for files of a particular extention(.apk) in a particular logical drive(C:). my system has 3 more partitions :- D: E: F: and these also contains apk files. now i want that my program will also search in these logical drives for the apk's files. how i can do this. please anybody have some suggestion then help me, am trying this since morning. here is my code.....
int SearchDirectory(std::vector<std::string> &refvecFiles,
const std::string &refcstrRootDirectory,
const std::string &refcstrExtension,
bool bSearchSubdirectories = true)
{
std::string strFilePath; // Filepath
std::string strPattern; // Pattern
std::string strExtension; // Extension
HANDLE hFile; // Handle to file
WIN32_FIND_DATA FileInformation; // File information
strPattern = refcstrRootDirectory + "\\*.*";
hFile = FindFirstFile(strPattern.c_str(), &FileInformation);
if(hFile != INVALID_HANDLE_VALUE)
{
do
{
if(FileInformation.cFileName[0] != '.')
{
strFilePath.erase();
strFilePath = refcstrRootDirectory + "\\" + FileInformation.cFileName;
if(FileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if(bSearchSubdirectories)
{
// Search subdirectory
int iRC = SearchDirectory(refvecFiles,
strFilePath,
refcstrExtension,
bSearchSubdirectories);
if(iRC)
return iRC;
}
}
else
{
// Check extension
strExtension = FileInformation.cFileName;
strExtension = strExtension.substr(strExtension.rfind(".") + 1);
if(strExtension == refcstrExtension)
{
// Save filename
refvecFiles.push_back(strFilePath);
}
}
}
} while(FindNextFile(hFile, &FileInformation) == TRUE);
// Close handle
FindClose(hFile);
DWORD dwError = GetLastError();
if(dwError != ERROR_NO_MORE_FILES)
return dwError;
}
return 0;
}
int main()
{
int iRC = 0;
std::vector<std::string> vecAPKFiles;
//std::vector<std::string> vecTxtFiles;
// Search 'c:' for '.apk' files including subdirectories
iRC = SearchDirectory(vecAPKFiles, "c:", "apk");
if(iRC)
{
std::cout << "Error " << iRC << std::endl;
return -1;
}
// Print results
for(std::vector<std::string>::iterator iterAvi = vecAPKFiles.begin();
iterAvi != vecAPKFiles.end();
++iterAvi)
std::cout << *iterAvi << std::endl;
TCHAR szDrive[] = (" A:");
DWORD uDriveMask = GetLogicalDrives();
while(uDriveMask)
{
// Use the bitwise AND, 1â€"available, 0-not available
if(uDriveMask & 1)
printf("%s ", (const char *)szDrive);
// increment, check next drive
++szDrive[1];
// shift the bitmask binary right
uDriveMask >>= 1;
}
printf("\n ");
// Wait for keystroke
_getch();
return 0;
}
You've got a bit weird drive string char szDrive[] = " A:"; (Even with the half-baked TCHAR stuff removed). I'd use char szDrive[] = "A:\"; instead, and increment ++szDrive[0];. You can then pass szDrive to SearchDirectory()

Win32 equivalent of getuid()

I'm in the process of porting a C++ library from Linux to Windows, and am having problems with getuid(), which is not supported in Windows.
Any ideas what I can use in its place?
The Windows equivilent is actually the user's SID. You can get this by using the "GetTokenInformation" call and querying for the TokenUser information class.
To call GetTokenInformation, you need a handle to the users token, which you can get by calling OpenProcessToken (or OpenThreadToken if you're impersonating someone).
You can retrieves the name of the user associated with the current thread with GetUserName :
// ANSI version
string GetWindowsUserNameA()
{
char buffer[UNLEN + 1] = {0};
DWORD buffer_len = UNLEN + 1;
if (!::GetUserNameA(buffer, & buffer_len))
{
// error handling
}
return string(buffer);
}
Windows' closest equivalent of a UID is (probably) a SID. GetUserName followed by LookupAccountName should get you the user's SID.
This is what I came up with.
#include <stdint.h>
#include <stdlib.h>
#include <Windows.h>
#include <sddl.h>
#include <iostream>
#include <iomanip>
#include <memory>
struct heap_delete
{
typedef LPVOID pointer;
void operator()(LPVOID p)
{
::HeapFree(::GetProcessHeap(), 0, p);
}
};
typedef std::unique_ptr<LPVOID, heap_delete> heap_unique_ptr;
struct handle_delete
{
typedef HANDLE pointer;
void operator()(HANDLE p)
{
::CloseHandle(p);
}
};
typedef std::unique_ptr<HANDLE, handle_delete> handle_unique_ptr;
typedef uint32_t uid_t;
BOOL GetUserSID(HANDLE token, PSID* sid)
{
if (
token == nullptr || token == INVALID_HANDLE_VALUE
|| sid == nullptr
)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
DWORD tokenInformationLength = 0;
::GetTokenInformation(
token, TokenUser, nullptr, 0, &tokenInformationLength);
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
return FALSE;
}
heap_unique_ptr data(
::HeapAlloc(
::GetProcessHeap(), HEAP_ZERO_MEMORY,
tokenInformationLength));
if (data.get() == nullptr)
{
return FALSE;
}
BOOL getTokenInfo = ::GetTokenInformation(
token, TokenUser, data.get(),
tokenInformationLength, &tokenInformationLength);
if (! getTokenInfo)
{
return FALSE;
}
PTOKEN_USER pTokenUser = (PTOKEN_USER)(data.get());
DWORD sidLength = ::GetLengthSid(pTokenUser->User.Sid);
heap_unique_ptr sidPtr(
::HeapAlloc(
GetProcessHeap(), HEAP_ZERO_MEMORY, sidLength));
PSID sidL = (PSID)(sidPtr.get());
if (sidL == nullptr)
{
return FALSE;
}
BOOL copySid = ::CopySid(sidLength, sidL, pTokenUser->User.Sid);
if (! copySid)
{
return FALSE;
}
if (!IsValidSid(sidL))
{
return FALSE;
}
*sid = sidL;
sidPtr.release();
return TRUE;
}
uid_t GetUID(HANDLE token)
{
PSID sid = nullptr;
BOOL getSID = GetUserSID(token, &sid);
if (! getSID || ! sid)
{
return -1;
}
heap_unique_ptr sidPtr((LPVOID)(sid));
LPWSTR stringSid = nullptr;
BOOL convertSid = ::ConvertSidToStringSidW(
sid, &stringSid);
if (! convertSid)
{
return -1;
}
uid_t ret = -1;
LPCWSTR p = ::wcsrchr(stringSid, L'-');
if (p && ::iswdigit(p[1]))
{
++p;
ret = ::_wtoi(p);
}
::LocalFree(stringSid);
return ret;
}
uid_t getuid()
{
HANDLE process = ::GetCurrentProcess();
handle_unique_ptr processPtr(process);
HANDLE token = nullptr;
BOOL openToken = ::OpenProcessToken(
process, TOKEN_READ|TOKEN_QUERY_SOURCE, &token);
if (! openToken)
{
return -1;
}
handle_unique_ptr tokenPtr(token);
uid_t ret = GetUID(token);
return ret;
}
uid_t geteuid()
{
HANDLE process = ::GetCurrentProcess();
HANDLE thread = ::GetCurrentThread();
HANDLE token = nullptr;
BOOL openToken = ::OpenThreadToken(
thread, TOKEN_READ|TOKEN_QUERY_SOURCE, FALSE, &token);
if (! openToken && ::GetLastError() == ERROR_NO_TOKEN)
{
openToken = ::OpenThreadToken(
thread, TOKEN_READ|TOKEN_QUERY_SOURCE, TRUE, &token);
if (! openToken && ::GetLastError() == ERROR_NO_TOKEN)
{
openToken = ::OpenProcessToken(
process, TOKEN_READ|TOKEN_QUERY_SOURCE, &token);
}
}
if (! openToken)
{
return -1;
}
handle_unique_ptr tokenPtr(token);
uid_t ret = GetUID(token);
return ret;
}
int main()
{
uid_t uid = getuid();
uid_t euid = geteuid();
std::cout
<< "uid: " << std::setbase(10) << uid << std::endl
<< "euid: " << std::setbase(10) << euid << std::endl
<< std::endl;
return EXIT_SUCCESS;
}
Note that the answer given by Larry Osterman was very helpful. It got me started in the correct direction.
Check out Microsoft's recommendations on porting with the Interix (also known as Services for UNIX 3.0) library. Overkill for what you want though.
in DotNet - Environment.UserName
The right api is SHGetUID(), exported from Shell