I am writing a C++ program that makes use of named pipes on Windows. I can create and work with them quite fine. The only piece missing to the puzzle is a function to check for a pipe's existence.
Coming from the Unix world I originally tried std::filesystem::exists("\\\\.\\pipe\\myPipe") but this is not reliable and often errors with ERROR_PIPE_BUSY.
While searching for an alternative way to check for a pipe's existence, I stumbled upon this issue on GitHub (Boost process) and from there I take it that Boos process circumvents the problem by using a special naming scheme and a counter and then keeping track of that internally (only seems to work for pipes created via Boost process though).
Furthermore according to How can I get a list of all open named pipes in Windows? it seems that there are ways to list the existing named pipes. These solutions are not using C++ though and I did not find a way to port that over.
After having read the documentation of CreateNamedPipe, I now assembled the following solution to my problem:
bool NamedPipe::exists(const std::filesystem::path &pipePath) {
if (pipePath.parent_path() != "\\\\.\\pipe") {
// This can't be a pipe, so it also can't exist
return false;
}
// Attempt to create a pipe with FILE_FLAG_FIRST_INSTANCE so that the creation will fail
// if the pipe already exists
HANDLE pipeHandle = CreateNamedPipe(pipePath.string().c_str(),
PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_BYTE | PIPE_WAIT,
1, // # of allowed pipe instances
0, // Size of outbound buffer
0, // Size of inbound buffer
0, // Use default wait time
NULL // Use default security attributes
);
if (pipeHandle == INVALID_HANDLE_VALUE) {
// Creation has failed
// It has failed (most likely) due to there alredy existing a pipe with
// that name
return true;
} else {
// The creation has succeeded
if(!CloseHandle(pipeHandle)) {
throw PipeException< DWORD >(GetLastError(), "CheckExistance");
}
return false;
}
}
However attempting to create a named pipe only to check whether there already exists one with that name already seems like a lot of unnecessary overhead. Furthermore I am unsure of whether this solution is universally applicable or only works if the pipe tested for was also created with FILE_FLAG_FIRST_PIPE_INSTANCE.
Therefore my question is: Is there a better way to check whether a named pipe with the given name already exists in Windows?
std::filesystem::exists("\\\\.\\pipe\\myPipe") returning ERROR_PIPE_BUSY means it is using CreateFile() to actually connect to the pipe. It is not unreasonable for an exists() implementation to attempt to open the requested file to check its existance.
Per the CreateFile() documentation:
If there is at least one active pipe instance but there are no available listener pipes on the server, which means all pipe instances are currently connected, CreateFile fails with ERROR_PIPE_BUSY.
Which means the pipe does technically exist, it is not ready to receive a new client at that moment.
In the link you provided, many of the solutions provided suggest using .NET's System.IO.Directory.GetFiles() method to iterate though the contents of "\\.\pipe\". This answer shows how that call translates into Win32 API calls using FindFirstFile() and FindNextFile(). You can easily do the same API calls in C++, eg:
bool NamedPipe::exists(const std::filesystem::path &pipePath)
{
std::string pipeName = pipePath.string();
if ((pipeName.size() < 10) ||
(pipeName.compare(0, 9, "\\\\.\\pipe\\") != 0) ||
(pipeName.find('\\', 9) != std::string::npos))
{
// This can't be a pipe, so it also can't exist
return false;
}
pipeName.erase(0, 9);
WIN32_FIND_DATA fd;
DWORD dwErrCode;
HANDLE hFind = FindFirstFileA("\\\\.\\pipe\\*", &fd);
if (hFind == INVALID_HANDLE_VALUE)
{
dwErrCode = GetLastError();
}
else
{
do
{
if (pipeName == fd.cFileName)
{
FindClose(hFind);
return true;
}
}
while (FindNextFileA(hFind, &fd));
dwErrCode = GetLastError();
FindClose(hFind);
}
if ((dwErrCode != ERROR_FILE_NOT_FOUND) &&
(dwErrCode != ERROR_NO_MORE_FILES))
{
throw PipeException< DWORD >(dwErrCode, "CheckExistance");
}
return false;
}
UPDATE: or, using std::wstring with Unicode APIs instead, since the filesystem is natively Unicode on Windows:
bool NamedPipe::exists(const std::filesystem::path &pipePath)
{
std::wstring pipeName = pipePath;
if ((pipeName.size() < 10) ||
(pipeName.compare(0, 9, L"\\\\.\\pipe\\") != 0) ||
(pipeName.find(L'\\', 9) != std::string::npos))
{
// This can't be a pipe, so it also can't exist
return false;
}
pipeName.erase(0, 9);
WIN32_FIND_DATAW fd;
DWORD dwErrCode;
HANDLE hFind = FindFirstFileW(L"\\\\.\\pipe\\*", &fd);
if (hFind == INVALID_HANDLE_VALUE)
{
dwErrCode = GetLastError();
}
else
{
do
{
if (pipeName == fd.cFileName)
{
FindClose(hFind);
return true;
}
}
while (FindNextFileW(hFind, &fd));
dwErrCode = GetLastError();
FindClose(hFind);
}
if ((dwErrCode != ERROR_FILE_NOT_FOUND) &&
(dwErrCode != ERROR_NO_MORE_FILES))
{
throw PipeException< DWORD >(dwErrCode, "CheckExistance");
}
return false;
}
Related
I tried creating a mutant in my process that closes the process when it is created more than once with this code:
HANDLE m;
OBJECT_ATTRIBUTES attr;
attr.Length = sizeof(OBJECT_ATTRIBUTES);
UNICODE_STRING str;
RtlInitUnicodeString(&str, L"123");
attr.ObjectName = &str;
long stats;
if (!(stats = NtCreateMutant(&m, MUTANT_ALL_ACCESS, &attr, false))) {
if (0xC0000035 == stats) {
return false;
}
return true;
}
All of the functions have the correct address
When I remove the code attr.ObjectName = &str; the mutant handle is not null, but the second process does not exit since the NTSTATUS is 0
When I keep the code, NTSTATUS returns STATUS_OBJECT_PATH_SYNTAX_BAD, which I have no idea what has to do with NtCreateMutant
I want to know if it is even possible to do this using NtCreateMutex in usermode and to fix these issues or if I should just stop trying and just use CreateMutex (I dont want to use it for no reason other than I wanted to try using NtCreateMutex)
I know it's 1 year late but considering how many questions are on stack overflow, better later than never.
I`m quite surprised why #Raymond Chen made that comment, because you can use undocumented API's just fine. They are everywhere in Windows, and they hardly changed for the last decade.
They are also used in literally every Windows kernel driver possible, so they are not really "undocumented" and "unsupported".
First of all, that's not how you initialise OBJECT_ATTRIBUTES; if you want to do it that way you would do:
OBJECT_ATTRIBUTES ObjectAttributes; = { sizeof(ObjectAttributes) };
The recommended and official way is to use the InitializeObjectAttributes macro.
Second, the mutex name cannot be "123" inside the NT namespace. That's why you get STATUS_OBJECT_PATH_SYNTAX_BAD.
Third, your code is just ugly written and the usage is bad. What you want to create (from what it sounds) is a "process barrier".
I will give you a working full sample code:
BOOL CreateProcessBarrier()
{
NTSTATUS Result;
UNICODE_STRING MutantName;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE MutantHandle;
WCHAR MutantNameNT[] = L"\\??\\40691290-71d5-45bc-b86a-e714496f4bf2";
WCHAR MutantNameWin[] = L"40691290-71d5-45bc-b86a-e714496f4bf2";
RtlInitUnicodeString(&MutantName, MutantNameNT);
InitializeObjectAttributes(&ObjectAttributes, &MutantName, 0, NULL, NULL);
// ALL OK
if (0 <= (Result = ZwCreateMutant(&MutantHandle, MUTANT_ALL_ACCESS, &ObjectAttributes, TRUE)))
{
// Banana
return TRUE;
}
// Already exists
else if (Result == STATUS_OBJECT_NAME_COLLISION || Result == STATUS_OBJECT_NAME_EXISTS)
{
// Que paso ?
return FALSE;
}
// Mutex creation failed, fallback to winapi
else
{
// Papaya
CreateMutexW(NULL, TRUE, MutantNameWin);
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
return FALSE;
}
}
return TRUE;
}
The function above has support to fallback to normal WinApi in case it fails.
Usage is simple as:
Process A:
CreateProcessBarrier();
Process B,C,D,etc:
if(!CreateProcessBarrier()) ... do whatever.
Enjoy.
I have some weird behaviour while using WIndows API function EnumProcesses()
I have a function to determine wether a process with a certain name is already running which delivery different results wether I open the .executable manually (doubleclick) or open it via shell.
When I open it via shell it detects its running only 1 time (itself) and all is fine. When I open it using doubleclick on the .exe file however the function is returning true (already running) because the loop lists me the same process twice.
For the following code-snipped it is to mention that:
this->thisExecutableFile
contains argv[0] (initialised from running the program) to get the own process-name as you can see here:
int main(int argc, char* argv[])
{
ClientUpdate* update = ClientUpdate::getInstance();
update->setThisExecutableFile(argv[0]);
if (update->clientUpdateProcessIsRunning() == false) {
...
My goal is to find out if another instance of this process is already running and in this case exit it.
Here is my code:
bool ClientUpdate::clientUpdateProcessIsRunning()
{
bool retVal = false;
uint16_t processCount = 0;
unsigned long aProcesses[1024], cbNeeded, cProcesses;
if(!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
return false;
cProcesses = cbNeeded / sizeof(unsigned long);
for(unsigned int i = 0; i < cProcesses; i++) {
if (aProcesses[i] == 0) {
continue;
}
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, aProcesses[i]);
wchar_t buffer[50];
GetModuleBaseNameW(hProcess, 0, buffer, 50);
CloseHandle(hProcess);
std::wstring tempBuffer(buffer);
std::string tempStringBuffer(tempBuffer.begin(), tempBuffer.end());
boost::filesystem::path p(this->thisExecutableFile);
if(_strcmpi(p.filename().string().c_str(), tempStringBuffer.c_str()) == 0) {
processCount++;
if(processCount > 1) {
retVal = true;
break;
}
}
}
return retVal;
}
I know that the base-path is different when using doubleclick on the file or calling it via shell. (shell produces only filename while doubleclick passes entire path + filename into argv[0]) but I fixed that issue using
boost::filesystem::path p(this->thisExecutableFile);
p.fileName()
Which returns the correct filename (without path) in both cases I checked using print.
I am pretty puzzled why EnumProcesses() returns me the same file twice when calling the file via doubleclick instead of shell. Its not spawning two processed and in taskmanager I dont see anything like this either.
Is this a bug or I need to know something about the method I couldnt find in docs?
Thanks to the hint by Richard Critten I was able to fix it. My method is much smaller now and easier. (Also probably also alot more performant then scanning entire process-stack.) :D
Here is the solution
bool ClientUpdate::clientUpdateProcessIsRunning()
{
HANDLE hMutex = CreateMutexA(NULL, TRUE, "client-updater-mtx");
DWORD dwErr = GetLastError();
return dwErr == ERROR_ALREADY_EXISTS;
}
Thanks!
I am trying to open a com port for reading and writing using C++ but I can't seem to pass the first stage of actually opening it. I get an INVALID_HANDLE_VALUE on the handle
with GetLastError FILE_NOT_FOUND. I have searched around the web for a couple of days I'm fresh out of ideas. I have searched through all the questions regarding COM on this website too.
I have scanned through the existing ports (or so I believe) to get the name of the port right.
I also tried combinations of _T("COM1") with the slashes, without the slashes, with colon, without colon and without the _T
I'm using windows 7 on 64 bit machine.
this is the code i got
I'll be glad for any input on this
void SendToCom(char* data, int len)
{
DWORD cbNeeded = 0;
DWORD dwPorts = 0;
EnumPorts(NULL, 1, NULL, 0, &cbNeeded, &dwPorts);
//What will be the return value
BOOL bSuccess = FALSE;
LPCSTR COM1 ;
BYTE* pPorts = static_cast<BYTE*>(malloc(cbNeeded));
bSuccess = EnumPorts(NULL, 1, pPorts, cbNeeded, &cbNeeded, &dwPorts);
if (bSuccess){
PORT_INFO_1* pPortInfo = reinterpret_cast<PORT_INFO_1*>(pPorts);
for (DWORD i=0; i<dwPorts; i++)
{
//If it looks like "COMX" then
size_t nLen = _tcslen(pPortInfo->pName);
if (nLen > 3)
{
if ((_tcsnicmp(pPortInfo->pName, _T("COM"), 3) == 0) ){
COM1 =pPortInfo->pName;
//COM1 ="\\\\.\\COM1";
HANDLE m_hCommPort = CreateFile( COM1 ,
GENERIC_READ|GENERIC_WRITE, // access ( read and write)
0, // (share) 0:cannot share the COM port
NULL, // security (None)
OPEN_EXISTING, // creation : open_existing
FILE_FLAG_OVERLAPPED, // we want overlapped operation
NULL // no templates file for COM port...
);
if (m_hCommPort==INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
if (err == ERROR_FILE_NOT_FOUND) {
MessageBox(hWnd,"ERROR_FILE_NOT_FOUND",NULL,MB_ABORTRETRYIGNORE);
}
else
if(err == ERROR_INVALID_NAME) {
MessageBox(hWnd,"ERROR_INVALID_NAME",NULL,MB_ABORTRETRYIGNORE);
}
else
{
MessageBox(hWnd,"unkown error",NULL,MB_ABORTRETRYIGNORE);
}
}
else{
WriteAndReadPort(m_hCommPort,data);
}
}
pPortInfo++;
}
}
}
}
The Solution is to use
The Problem is, if your port is Bigger then 9 then you have to use the Syntax
LPCWSTR szPortName = L"\\\\.\\COM11";.
If you are on Windows 10 - running all system updates might help !
I had the same issue that opening port "COM4" returned an error ERROR_FILE_NOT_FOUND. When running the program as "Administrator" it worked. Now after a updating to 1511 the program can open "COM4" even not running as "Administrator".
http://www.cplusplus.com/forum/windows/163855/
Use CreateFileA(...) instead of CreateFile(...)
ERROR_FILE_NOT_FOUND can be produced from CreateFile(L"\\\\.\\COM1", ...) and CreateFile(L"COM1:", ...) after using the Device Manager to change the assigned COM Port number. Disabling and re-enabling the device, or unplugging and reconnecting the USB adapter resolves the issue.
A useful test to confirm whether it is your program or the system is to send data to the port in command prompt. A successful test will show an empty line. A failed test will show an error message.
C:\drop>echo > \\.\COM1
The system cannot find the file specified.
C:\drop>echo > \\.\COM1
C:\drop>
Using Linux and C++, I would like a function that does the following:
string f(string s)
{
string r = system("foo < s");
return r;
}
Obviously the above doesn't work, but you get the idea. I have a string s that I would like to pass as the standard input of a child process execution of application "foo", and then I would like to record its standard output to string r and then return it.
What combination of Linux syscalls or POSIX functions should I use?
I'm using Linux 3.0 and do not need the solution to work with older systems.
The code provided by eerpini does not work as written. Note, for example, that the pipe ends that are closed in the parent are used afterwards. Look at
close(wpipefd[1]);
and the subsequent write to that closed descriptor. This is just transposition, but it shows this code has never been used. Below is a version that I have tested. Unfortunately, I changed the code style, so this was not accepted as an edit of eerpini's code.
The only structural change is that I only redirect the I/O in the child (note the dup2 calls are only in the child path.) This is very important, because otherwise the parent's I/O gets messed up. Thanks to eerpini for the initial answer, which I used in developing this one.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define PIPE_READ 0
#define PIPE_WRITE 1
int createChild(const char* szCommand, char* const aArguments[], char* const aEnvironment[], const char* szMessage) {
int aStdinPipe[2];
int aStdoutPipe[2];
int nChild;
char nChar;
int nResult;
if (pipe(aStdinPipe) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
if (pipe(aStdoutPipe) < 0) {
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
perror("allocating pipe for child output redirect");
return -1;
}
nChild = fork();
if (0 == nChild) {
// child continues here
// redirect stdin
if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1) {
exit(errno);
}
// redirect stdout
if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) {
exit(errno);
}
// redirect stderr
if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) {
exit(errno);
}
// all these are for use by parent only
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
// run child process image
// replace this with any exec* function find easier to use ("man exec")
nResult = execve(szCommand, aArguments, aEnvironment);
// if we get here at all, an error occurred, but we are in the child
// process, so just exit
exit(nResult);
} else if (nChild > 0) {
// parent continues here
// close unused file descriptors, these are for child only
close(aStdinPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
// Include error check here
if (NULL != szMessage) {
write(aStdinPipe[PIPE_WRITE], szMessage, strlen(szMessage));
}
// Just a char by char read here, you can change it accordingly
while (read(aStdoutPipe[PIPE_READ], &nChar, 1) == 1) {
write(STDOUT_FILENO, &nChar, 1);
}
// done with these in this example program, you would normally keep these
// open of course as long as you want to talk to the child
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
} else {
// failed to create child
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
}
return nChild;
}
Since you want bidirectional access to the process, you would have to do what popen does behind the scenes explicitly with pipes. I am not sure if any of this will change in C++, but here is a pure C example :
void piped(char *str){
int wpipefd[2];
int rpipefd[2];
int defout, defin;
defout = dup(stdout);
defin = dup (stdin);
if(pipe(wpipefd) < 0){
perror("Pipe");
exit(EXIT_FAILURE);
}
if(pipe(rpipefd) < 0){
perror("Pipe");
exit(EXIT_FAILURE);
}
if(dup2(wpipefd[0], 0) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(dup2(rpipefd[1], 1) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(fork() == 0){
close(defout);
close(defin);
close(wpipefd[0]);
close(wpipefd[1]);
close(rpipefd[0]);
close(rpipefd[1]);
//Call exec here. Use the exec* family of functions according to your need
}
else{
if(dup2(defin, 0) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(dup2(defout, 1) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
close(defout);
close(defin);
close(wpipefd[1]);
close(rpipefd[0]);
//Include error check here
write(wpipefd[1], str, strlen(str));
//Just a char by char read here, you can change it accordingly
while(read(rpipefd[0], &ch, 1) != -1){
write(stdout, &ch, 1);
}
}
}
Effectively you do this :
Create pipes and redirect the stdout and stdin to the ends of the two pipes (note that in linux, pipe() creates unidirectional pipes, so you need to use two pipes for your purpose).
Exec will now start a new process which has the ends of the pipes for stdin and stdout.
Close the unused descriptors, write the string to the pipe and then start reading whatever the process might dump to the other pipe.
dup() is used to create a duplicate entry in the file descriptor table. While dup2() changes what the descriptor points to.
Note : As mentioned by Ammo# in his solution, what I provided above is more or less a template, it will not run if you just tried to execute the code since clearly there is a exec* (family of functions) missing, so the child will terminate almost immediately after the fork().
Ammo's code has some error handling bugs. The child process is returning after dup failure instead of exiting. Perhaps the child dups can be replaced with:
if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1 ||
dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1 ||
dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1
)
{
exit(errno);
}
// all these are for use by parent only
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
I've written an application that uses the WIN32 api to create a temporarily directory hierarchy. Now, when wanting to delete the directories when shutting down the application I'm running into some problems.
So lets say I have a directory hierarchy: C:\temp\directory\subdirectory\
I'm using this recursive function:
bool Dir::deleteDirectory(std::string& directoryname, int flags)
{
if(directoryname.at(directoryname.size()-1) != '\\') directoryname += '\\';
if ((flags & CONTENTS) == CONTENTS)
{
WIN32_FIND_DATAA fdata;
HANDLE dhandle;
directoryname += "\\*";
dhandle = FindFirstFileA(directoryname.c_str(), &fdata);
// Loop through all the files in the main directory and delete files & make a list of directories
while(true)
{
if(FindNextFileA(dhandle, &fdata))
{
std::string filename = fdata.cFileName;
if(filename.compare("..") != 0)
{
std::string filelocation = directoryname.substr(0, directoryname.size()-2) + StringManip::reverseSlashes(filename);
// If we've encountered a directory then recall this function for that specific folder.
if(!isDirectory(filelocation)) DeleteFileA(filename.c_str());
else deleteDirectory(filelocation, DIRECTORY_AND_CONTENTS);
}
} else if(GetLastError() == ERROR_NO_MORE_FILES) break;
}
directoryname = directoryname.substr(0, directoryname.size()-2);
}
if ((flags & DIRECTORY) == DIRECTORY)
{
HANDLE DirectoryHandle;
DirectoryHandle = CreateFileA(directoryname.c_str(),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
bool DeletionResult = (RemoveDirectoryA(directoryname.c_str()) != 0)?true:false;
CloseHandle(DirectoryHandle);
return DeletionResult;
}
return true;
}
This function iterates over the directory contents of the temp directory; and for each directory in the temp directory it keeps recalling itself until it's at the lowest directory; subdirectory in the example.
There are also 3 flags defined
enum DirectoryDeletion
{
CONTENTS = 0x1,
DIRECTORY = 0x2,
DIRECTORY_AND_CONTENTS = (0x1 | 0x2)
};
When using this function, it only removes the lowest subdirectory and I can't remove the ones higher in hierarchy because it says that the directory is not empty. When I go and look to the directory 'subdirectory' is only removed after the application ends. However, when I try to encapsulate this in a non recursive simple main application I have no problems at all with deleting the directories.
There's a Windows API, SHFileOperation, that will do a recursive folder delete for you.
LONG DeleteDirectoryAndAllSubfolders(LPCWSTR wzDirectory)
{
WCHAR szDir[MAX_PATH+1]; // +1 for the double null terminate
SHFILEOPSTRUCTW fos = {0};
StringCchCopy(szDir, MAX_PATH, wzDirectory);
int len = lstrlenW(szDir);
szDir[len+1] = 0; // double null terminate for SHFileOperation
// delete the folder and everything inside
fos.wFunc = FO_DELETE;
fos.pFrom = szDir;
fos.fFlags = FOF_NO_UI;
return SHFileOperation( &fos );
}
You're not closing dhandle from all those FindFirstFile calls, so each directory has a reference to it when you try to delete it.
And, why do you need to create DirectoryHandle? It's not needed, and will probably also block the directory deletion.
When your app closes, those handles are forced close, and (I guess) the last attempted delete then succeeds.
SHFileOperations works great on Windows 7. In fact in the IFileOperation documentation says
IFileOperation can only be applied in a single-threaded apartment (STA) situation. It cannot be used for a multithreaded apartment (MTA) situation. For MTA, you still must use SHFileOperation.
However my issue with SHFileOperations is it doesn't seem to support paths longer than 260 characters, and does not support \?\ prefix for long filenames.
This is a real pain....but a recursive function is still needed if you want ability to handle paths longer than 260 characters (Which NTFS supports - but not Windows Explorer, command prompt commands etc)
Well, I found several bugs in this code.. here is what I found
bool Dir::deleteDirectory(std::string& directoryname, int flags)
{
if(directoryname.at(directoryname.size()-1) != '\\') directoryname += '\\';
if ((flags & CONTENTS) == CONTENTS)
{
WIN32_FIND_DATAA fdata;
HANDLE dhandle;
//BUG 1: Adding a extra \ to the directory name..
directoryname += "*";
dhandle = FindFirstFileA(directoryname.c_str(), &fdata);
//BUG 2: Not checking for invalid file handle return from FindFirstFileA
if( dhandle != INVALID_HANDLE_VALUE )
{
// Loop through all the files in the main directory and delete files & make a list of directories
while(true)
{
if(FindNextFileA(dhandle, &fdata))
{
std::string filename = fdata.cFileName;
if(filename.compare("..") != 0)
{
//BUG 3: caused by BUG 1 - Removing too many characters from string.. removing 1 instead of 2
std::string filelocation = directoryname.substr(0, directoryname.size()-1) + filename;
// If we've encountered a directory then recall this function for that specific folder.
//BUG 4: not really a bug, but spurious function call - we know its a directory from FindData already, use it.
if( (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
DeleteFileA(filelocation.c_str());
else
deleteDirectory(filelocation, DIRECTORY_AND_CONTENTS);
}
} else if(GetLastError() == ERROR_NO_MORE_FILES) break;
}
directoryname = directoryname.substr(0, directoryname.size()-2);
//BUG 5: Not closing the FileFind with FindClose - OS keeps handles to directory open. MAIN BUG
FindClose( dhandle );
}
}
if ((flags & DIRECTORY) == DIRECTORY)
{
HANDLE DirectoryHandle;
DirectoryHandle = CreateFileA(directoryname.c_str(),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
//BUG 6: Not checking CreateFileA for invalid handle return.
if( DirectoryHandle != INVALID_HANDLE_VALUE )
{
bool DeletionResult = (RemoveDirectoryA(directoryname.c_str()) != 0)?true:false;
CloseHandle(DirectoryHandle);
return DeletionResult;
}
else
{
return true;
}
}
return true;
}
Try calling FindClose to close handle returned by FindFileFileA.
I don't see a FindClose for your dhandle. An open handle means that the directory is still in use.
MSDN says: "When the search handle is no longer needed, close it by using the FindClose function, not CloseHandle."
(CloseHandle appears to be correct for your DirectoryHandle further down, but not for the dhandle used in the Find loop.)
The main issue has already been answered, but here's something I noticed. Your main while loop seems a bit fragile to me...
while(true)
{
if(FindNextFileA(dhandle, &fdata))
{
//...
} else if(GetLastError() == ERROR_NO_MORE_FILES) break;
}
This ends if FindNextFile ends because there are no more files in the directory. But what if it ever ends for some other reason? If something abnormal happens, it seems you could end up with an infinite loop.
I'd think if FindNextFile fails for any reason, then you'll want to stop the loop and start returning through the recursive calls. So I'd suggest simply removing the GetLastError test and just making it "else break;"
Actually, after a moment's thought I would probably just reduce it to:
while(FindNextFileA(dhandle, &fdata))
{
//...
}