|
|
|
@@ -731,18 +731,16 @@ void File::revealToUser() const |
|
|
|
class NamedPipe::Pimpl
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Pimpl (const String& file, const bool createPipe)
|
|
|
|
: pipeH (INVALID_HANDLE_VALUE),
|
|
|
|
Pimpl (const String& pipeName, const bool createPipe)
|
|
|
|
: filename ("\\\\.\\pipe\\" + File::createLegalFileName (pipeName)),
|
|
|
|
pipeH (INVALID_HANDLE_VALUE),
|
|
|
|
cancelEvent (CreateEvent (0, FALSE, FALSE, 0)),
|
|
|
|
connected (false),
|
|
|
|
ownsPipe (createPipe)
|
|
|
|
connected (false), ownsPipe (createPipe), shouldStop (false)
|
|
|
|
{
|
|
|
|
pipeH = createPipe ? CreateNamedPipe (file.toWideCharPointer(),
|
|
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0,
|
|
|
|
PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, 0)
|
|
|
|
: CreateFile (file.toWideCharPointer(),
|
|
|
|
GENERIC_READ | GENERIC_WRITE, 0, 0,
|
|
|
|
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
|
|
|
if (createPipe)
|
|
|
|
pipeH = CreateNamedPipe (filename.toWideCharPointer(),
|
|
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0,
|
|
|
|
PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
~Pimpl()
|
|
|
|
@@ -758,32 +756,47 @@ public: |
|
|
|
bool connect (const int timeOutMs)
|
|
|
|
{
|
|
|
|
if (! ownsPipe)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (! connected)
|
|
|
|
{
|
|
|
|
OVERLAPPED over = { 0 };
|
|
|
|
over.hEvent = CreateEvent (0, TRUE, FALSE, 0);
|
|
|
|
if (pipeH != INVALID_HANDLE_VALUE)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (ConnectNamedPipe (pipeH, &over) == 0)
|
|
|
|
{
|
|
|
|
const DWORD err = GetLastError();
|
|
|
|
const Time timeOutEnd (Time::getCurrentTime() + RelativeTime::milliseconds (timeOutMs));
|
|
|
|
|
|
|
|
if (err == ERROR_IO_PENDING || err == ERROR_PIPE_LISTENING)
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
HANDLE handles[] = { over.hEvent, cancelEvent };
|
|
|
|
const ScopedLock sl (createFileLock);
|
|
|
|
|
|
|
|
if (WaitForMultipleObjects (2, handles, FALSE,
|
|
|
|
timeOutMs >= 0 ? timeOutMs : INFINITE) == WAIT_OBJECT_0)
|
|
|
|
connected = true;
|
|
|
|
if (pipeH == INVALID_HANDLE_VALUE)
|
|
|
|
pipeH = CreateFile (filename.toWideCharPointer(),
|
|
|
|
GENERIC_READ | GENERIC_WRITE, 0, 0,
|
|
|
|
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
|
|
|
}
|
|
|
|
else if (err == ERROR_PIPE_CONNECTED)
|
|
|
|
|
|
|
|
if (pipeH != INVALID_HANDLE_VALUE)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (shouldStop || (timeOutMs >= 0 && Time::getCurrentTime() > timeOutEnd))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Thread::sleep (1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! connected)
|
|
|
|
{
|
|
|
|
OverlappedEvent over;
|
|
|
|
|
|
|
|
if (ConnectNamedPipe (pipeH, &over.over) == 0)
|
|
|
|
{
|
|
|
|
switch (GetLastError())
|
|
|
|
{
|
|
|
|
connected = true;
|
|
|
|
case ERROR_PIPE_CONNECTED: connected = true; break;
|
|
|
|
case ERROR_IO_PENDING:
|
|
|
|
case ERROR_PIPE_LISTENING: connected = waitForIO (over, timeOutMs); break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CloseHandle (over.hEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
return connected;
|
|
|
|
@@ -791,179 +804,150 @@ public: |
|
|
|
|
|
|
|
void disconnectPipe()
|
|
|
|
{
|
|
|
|
if (connected)
|
|
|
|
if (ownsPipe && connected)
|
|
|
|
{
|
|
|
|
DisconnectNamedPipe (pipeH);
|
|
|
|
connected = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HANDLE pipeH, cancelEvent;
|
|
|
|
bool connected, ownsPipe;
|
|
|
|
};
|
|
|
|
|
|
|
|
NamedPipe::NamedPipe()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
int read (void* destBuffer, const int maxBytesToRead, const int timeOutMilliseconds)
|
|
|
|
{
|
|
|
|
while (connect (timeOutMilliseconds))
|
|
|
|
{
|
|
|
|
if (maxBytesToRead <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
NamedPipe::~NamedPipe()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
OverlappedEvent over;
|
|
|
|
unsigned long numRead;
|
|
|
|
|
|
|
|
bool NamedPipe::isOpen() const
|
|
|
|
{
|
|
|
|
return pimpl != nullptr;
|
|
|
|
}
|
|
|
|
if (ReadFile (pipeH, destBuffer, (DWORD) maxBytesToRead, &numRead, &over.over))
|
|
|
|
return (int) numRead;
|
|
|
|
|
|
|
|
void NamedPipe::cancelPendingReads()
|
|
|
|
{
|
|
|
|
if (pimpl != nullptr)
|
|
|
|
SetEvent (pimpl->cancelEvent);
|
|
|
|
}
|
|
|
|
const DWORD lastError = GetLastError();
|
|
|
|
|
|
|
|
void NamedPipe::close()
|
|
|
|
{
|
|
|
|
cancelPendingReads();
|
|
|
|
if (lastError == ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
if (! waitForIO (over, timeOutMilliseconds))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
const ScopedLock sl (lock);
|
|
|
|
ScopedPointer<Pimpl> deleter (pimpl); // (clears the pimpl member variable before deleting it)
|
|
|
|
}
|
|
|
|
if (GetOverlappedResult (pipeH, &over.over, &numRead, FALSE))
|
|
|
|
return (int) numRead;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool NamedPipe::openInternal (const String& pipeName, const bool createPipe)
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
if (ownsPipe && (GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_PIPE_NOT_CONNECTED))
|
|
|
|
disconnectPipe();
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pimpl = new Pimpl ("\\\\.\\pipe\\" + File::createLegalFileName (pipeName), createPipe);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pimpl->pipeH != INVALID_HANDLE_VALUE)
|
|
|
|
return true;
|
|
|
|
int write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds)
|
|
|
|
{
|
|
|
|
if (connect (timeOutMilliseconds))
|
|
|
|
{
|
|
|
|
if (numBytesToWrite <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pimpl = nullptr;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OverlappedEvent over;
|
|
|
|
unsigned long numWritten;
|
|
|
|
|
|
|
|
int NamedPipe::read (void* destBuffer, const int maxBytesToRead, const int timeOutMilliseconds)
|
|
|
|
{
|
|
|
|
const ScopedLock sl (lock);
|
|
|
|
int bytesRead = -1;
|
|
|
|
bool waitAgain = true;
|
|
|
|
if (WriteFile (pipeH, sourceBuffer, (DWORD) numBytesToWrite, &numWritten, &over.over))
|
|
|
|
return (int) numWritten;
|
|
|
|
|
|
|
|
while (waitAgain && pimpl != nullptr)
|
|
|
|
{
|
|
|
|
waitAgain = false;
|
|
|
|
if (GetLastError() == ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
if (! waitForIO (over, timeOutMilliseconds))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (! pimpl->connect (timeOutMilliseconds))
|
|
|
|
break;
|
|
|
|
if (GetOverlappedResult (pipeH, &over.over, &numWritten, FALSE))
|
|
|
|
return (int) numWritten;
|
|
|
|
|
|
|
|
if (maxBytesToRead <= 0)
|
|
|
|
return 0;
|
|
|
|
if (GetLastError() == ERROR_BROKEN_PIPE && ownsPipe)
|
|
|
|
disconnectPipe();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OVERLAPPED over = { 0 };
|
|
|
|
over.hEvent = CreateEvent (0, TRUE, FALSE, 0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long numRead;
|
|
|
|
const String filename;
|
|
|
|
HANDLE pipeH, cancelEvent;
|
|
|
|
bool connected, ownsPipe, shouldStop;
|
|
|
|
CriticalSection createFileLock;
|
|
|
|
|
|
|
|
if (ReadFile (pimpl->pipeH, destBuffer, (DWORD) maxBytesToRead, &numRead, &over))
|
|
|
|
private:
|
|
|
|
struct OverlappedEvent
|
|
|
|
{
|
|
|
|
OverlappedEvent()
|
|
|
|
{
|
|
|
|
bytesRead = (int) numRead;
|
|
|
|
zerostruct (over);
|
|
|
|
over.hEvent = CreateEvent (0, TRUE, FALSE, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
|
|
|
|
~OverlappedEvent()
|
|
|
|
{
|
|
|
|
const DWORD lastError = GetLastError();
|
|
|
|
CloseHandle (over.hEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lastError == ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
HANDLE handles[] = { over.hEvent, pimpl->cancelEvent };
|
|
|
|
DWORD waitResult = WaitForMultipleObjects (2, handles, FALSE,
|
|
|
|
timeOutMilliseconds >= 0 ? timeOutMilliseconds
|
|
|
|
: INFINITE);
|
|
|
|
if (waitResult != WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
// if the operation timed out, let's cancel it...
|
|
|
|
CancelIo (pimpl->pipeH);
|
|
|
|
WaitForSingleObject (over.hEvent, INFINITE); // makes sure cancel is complete
|
|
|
|
}
|
|
|
|
OVERLAPPED over;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GetOverlappedResult (pimpl->pipeH, &over, &numRead, FALSE))
|
|
|
|
{
|
|
|
|
bytesRead = (int) numRead;
|
|
|
|
}
|
|
|
|
else if ((GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_PIPE_NOT_CONNECTED) && pimpl->ownsPipe)
|
|
|
|
{
|
|
|
|
pimpl->disconnectPipe();
|
|
|
|
waitAgain = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pimpl->ownsPipe)
|
|
|
|
{
|
|
|
|
waitAgain = true;
|
|
|
|
bool waitForIO (OverlappedEvent& over, int timeOutMilliseconds)
|
|
|
|
{
|
|
|
|
if (shouldStop)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (lastError == ERROR_BROKEN_PIPE || lastError == ERROR_PIPE_NOT_CONNECTED)
|
|
|
|
pimpl->disconnectPipe();
|
|
|
|
else
|
|
|
|
Sleep (5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
HANDLE handles[] = { over.over.hEvent, cancelEvent };
|
|
|
|
DWORD waitResult = WaitForMultipleObjects (2, handles, FALSE,
|
|
|
|
timeOutMilliseconds >= 0 ? timeOutMilliseconds
|
|
|
|
: INFINITE);
|
|
|
|
|
|
|
|
if (waitResult == WAIT_OBJECT_0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
CloseHandle (over.hEvent);
|
|
|
|
CancelIo (pipeH);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bytesRead;
|
|
|
|
}
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl);
|
|
|
|
};
|
|
|
|
|
|
|
|
int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds)
|
|
|
|
void NamedPipe::close()
|
|
|
|
{
|
|
|
|
int bytesWritten = -1;
|
|
|
|
|
|
|
|
if (pimpl != nullptr)
|
|
|
|
{
|
|
|
|
if (! pimpl->connect (timeOutMilliseconds))
|
|
|
|
{
|
|
|
|
pimpl = nullptr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (numBytesToWrite <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
OVERLAPPED over = { 0 };
|
|
|
|
over.hEvent = CreateEvent (0, TRUE, FALSE, 0);
|
|
|
|
|
|
|
|
unsigned long numWritten;
|
|
|
|
pimpl->shouldStop = true;
|
|
|
|
SetEvent (pimpl->cancelEvent);
|
|
|
|
|
|
|
|
if (WriteFile (pimpl->pipeH, sourceBuffer, (DWORD) numBytesToWrite, &numWritten, &over))
|
|
|
|
{
|
|
|
|
bytesWritten = (int) numWritten;
|
|
|
|
}
|
|
|
|
else if (GetLastError() == ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
HANDLE handles[] = { over.hEvent, pimpl->cancelEvent };
|
|
|
|
DWORD waitResult;
|
|
|
|
ScopedWriteLock sl (lock);
|
|
|
|
pimpl = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
waitResult = WaitForMultipleObjects (2, handles, FALSE,
|
|
|
|
timeOutMilliseconds >= 0 ? timeOutMilliseconds
|
|
|
|
: INFINITE);
|
|
|
|
bool NamedPipe::openInternal (const String& pipeName, const bool createPipe)
|
|
|
|
{
|
|
|
|
pimpl = new Pimpl (pipeName, createPipe);
|
|
|
|
|
|
|
|
if (waitResult != WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
CancelIo (pimpl->pipeH);
|
|
|
|
WaitForSingleObject (over.hEvent, INFINITE);
|
|
|
|
}
|
|
|
|
if (createPipe && pimpl->pipeH == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
pimpl = nullptr;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetOverlappedResult (pimpl->pipeH, &over, &numWritten, FALSE))
|
|
|
|
{
|
|
|
|
bytesWritten = (int) numWritten;
|
|
|
|
}
|
|
|
|
else if (GetLastError() == ERROR_BROKEN_PIPE && pimpl->ownsPipe)
|
|
|
|
{
|
|
|
|
pimpl->disconnectPipe();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CloseHandle (over.hEvent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
|
|
|
|
{
|
|
|
|
ScopedReadLock sl (lock);
|
|
|
|
return pimpl != nullptr ? pimpl->read (destBuffer, maxBytesToRead, timeOutMilliseconds) : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bytesWritten;
|
|
|
|
int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds)
|
|
|
|
{
|
|
|
|
ScopedReadLock sl (lock);
|
|
|
|
return pimpl != nullptr ? pimpl->write (sourceBuffer, numBytesToWrite, timeOutMilliseconds) : -1;
|
|
|
|
}
|