mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-03 03:30:06 +00:00
873 lines
26 KiB
C++
873 lines
26 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
|
Copyright 2004-10 by Raw Material Software Ltd.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
JUCE can be redistributed and/or modified under the terms of the GNU General
|
|
Public License (Version 2), as published by the Free Software Foundation.
|
|
A copy of the license is included in the JUCE distribution, or can be found
|
|
online at www.gnu.org/licenses.
|
|
|
|
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
To release a closed-source product which uses JUCE, commercial licenses are
|
|
available: visit www.rawmaterialsoftware.com/juce for more information.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
// (This file gets included by juce_win32_NativeCode.cpp, rather than being
|
|
// compiled on its own).
|
|
#if JUCE_INCLUDED_FILE
|
|
|
|
|
|
//==============================================================================
|
|
#ifndef CSIDL_MYMUSIC
|
|
#define CSIDL_MYMUSIC 0x000d
|
|
#endif
|
|
|
|
#ifndef CSIDL_MYVIDEO
|
|
#define CSIDL_MYVIDEO 0x000e
|
|
#endif
|
|
|
|
#ifndef INVALID_FILE_ATTRIBUTES
|
|
#define INVALID_FILE_ATTRIBUTES ((DWORD) -1)
|
|
#endif
|
|
|
|
//==============================================================================
|
|
namespace WindowsFileHelpers
|
|
{
|
|
int64 fileTimeToTime (const FILETIME* const ft)
|
|
{
|
|
static_jassert (sizeof (ULARGE_INTEGER) == sizeof (FILETIME)); // tell me if this fails!
|
|
|
|
return (reinterpret_cast<const ULARGE_INTEGER*> (ft)->QuadPart - literal64bit (116444736000000000)) / 10000;
|
|
}
|
|
|
|
void timeToFileTime (const int64 time, FILETIME* const ft)
|
|
{
|
|
reinterpret_cast<ULARGE_INTEGER*> (ft)->QuadPart = time * 10000 + literal64bit (116444736000000000);
|
|
}
|
|
|
|
const String getDriveFromPath (String path)
|
|
{
|
|
// (mess with the string to make sure it's not sharing its internal storage)
|
|
path = (path + " ").dropLastCharacters(1);
|
|
WCHAR* p = const_cast <WCHAR*> (path.toUTF16().getAddress());
|
|
|
|
if (PathStripToRoot (p))
|
|
return String ((const WCHAR*) p);
|
|
|
|
return path;
|
|
}
|
|
|
|
int64 getDiskSpaceInfo (const String& path, const bool total)
|
|
{
|
|
ULARGE_INTEGER spc, tot, totFree;
|
|
|
|
if (GetDiskFreeSpaceEx (getDriveFromPath (path).toUTF16(), &spc, &tot, &totFree))
|
|
return total ? (int64) tot.QuadPart
|
|
: (int64) spc.QuadPart;
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned int getWindowsDriveType (const String& path)
|
|
{
|
|
return GetDriveType (getDriveFromPath (path).toUTF16());
|
|
}
|
|
|
|
const File getSpecialFolderPath (int type)
|
|
{
|
|
WCHAR path [MAX_PATH + 256];
|
|
|
|
if (SHGetSpecialFolderPath (0, path, type, FALSE))
|
|
return File (String (path));
|
|
|
|
return File::nonexistent;
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
const juce_wchar File::separator = '\\';
|
|
const String File::separatorString ("\\");
|
|
|
|
|
|
//==============================================================================
|
|
bool File::exists() const
|
|
{
|
|
return fullPath.isNotEmpty()
|
|
&& GetFileAttributes (fullPath.toUTF16()) != INVALID_FILE_ATTRIBUTES;
|
|
}
|
|
|
|
bool File::existsAsFile() const
|
|
{
|
|
return fullPath.isNotEmpty()
|
|
&& (GetFileAttributes (fullPath.toUTF16()) & FILE_ATTRIBUTE_DIRECTORY) == 0;
|
|
}
|
|
|
|
bool File::isDirectory() const
|
|
{
|
|
const DWORD attr = GetFileAttributes (fullPath.toUTF16());
|
|
return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) && (attr != INVALID_FILE_ATTRIBUTES);
|
|
}
|
|
|
|
bool File::hasWriteAccess() const
|
|
{
|
|
if (exists())
|
|
return (GetFileAttributes (fullPath.toUTF16()) & FILE_ATTRIBUTE_READONLY) == 0;
|
|
|
|
// on windows, it seems that even read-only directories can still be written into,
|
|
// so checking the parent directory's permissions would return the wrong result..
|
|
return true;
|
|
}
|
|
|
|
bool File::setFileReadOnlyInternal (const bool shouldBeReadOnly) const
|
|
{
|
|
DWORD attr = GetFileAttributes (fullPath.toUTF16());
|
|
|
|
if (attr == INVALID_FILE_ATTRIBUTES)
|
|
return false;
|
|
|
|
if (shouldBeReadOnly == ((attr & FILE_ATTRIBUTE_READONLY) != 0))
|
|
return true;
|
|
|
|
if (shouldBeReadOnly)
|
|
attr |= FILE_ATTRIBUTE_READONLY;
|
|
else
|
|
attr &= ~FILE_ATTRIBUTE_READONLY;
|
|
|
|
return SetFileAttributes (fullPath.toUTF16(), attr) != FALSE;
|
|
}
|
|
|
|
bool File::isHidden() const
|
|
{
|
|
return (GetFileAttributes (getFullPathName().toUTF16()) & FILE_ATTRIBUTE_HIDDEN) != 0;
|
|
}
|
|
|
|
//==============================================================================
|
|
bool File::deleteFile() const
|
|
{
|
|
if (! exists())
|
|
return true;
|
|
else if (isDirectory())
|
|
return RemoveDirectory (fullPath.toUTF16()) != 0;
|
|
else
|
|
return DeleteFile (fullPath.toUTF16()) != 0;
|
|
}
|
|
|
|
bool File::moveToTrash() const
|
|
{
|
|
if (! exists())
|
|
return true;
|
|
|
|
SHFILEOPSTRUCT fos;
|
|
zerostruct (fos);
|
|
|
|
// The string we pass in must be double null terminated..
|
|
String doubleNullTermPath (getFullPathName() + " ");
|
|
WCHAR* const p = const_cast <WCHAR*> (doubleNullTermPath.toUTF16().getAddress());
|
|
p [getFullPathName().length()] = 0;
|
|
|
|
fos.wFunc = FO_DELETE;
|
|
fos.pFrom = p;
|
|
fos.fFlags = FOF_ALLOWUNDO | FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION
|
|
| FOF_NOCONFIRMMKDIR | FOF_RENAMEONCOLLISION;
|
|
|
|
return SHFileOperation (&fos) == 0;
|
|
}
|
|
|
|
bool File::copyInternal (const File& dest) const
|
|
{
|
|
return CopyFile (fullPath.toUTF16(), dest.getFullPathName().toUTF16(), false) != 0;
|
|
}
|
|
|
|
bool File::moveInternal (const File& dest) const
|
|
{
|
|
return MoveFile (fullPath.toUTF16(), dest.getFullPathName().toUTF16()) != 0;
|
|
}
|
|
|
|
void File::createDirectoryInternal (const String& fileName) const
|
|
{
|
|
CreateDirectory (fileName.toUTF16(), 0);
|
|
}
|
|
|
|
//==============================================================================
|
|
int64 juce_fileSetPosition (void* handle, int64 pos)
|
|
{
|
|
LARGE_INTEGER li;
|
|
li.QuadPart = pos;
|
|
li.LowPart = SetFilePointer ((HANDLE) handle, li.LowPart, &li.HighPart, FILE_BEGIN); // (returns -1 if it fails)
|
|
return li.QuadPart;
|
|
}
|
|
|
|
void FileInputStream::openHandle()
|
|
{
|
|
totalSize = file.getSize();
|
|
|
|
HANDLE h = CreateFile (file.getFullPathName().toUTF16(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
fileHandle = (void*) h;
|
|
}
|
|
|
|
void FileInputStream::closeHandle()
|
|
{
|
|
CloseHandle ((HANDLE) fileHandle);
|
|
}
|
|
|
|
size_t FileInputStream::readInternal (void* buffer, size_t numBytes)
|
|
{
|
|
if (fileHandle != 0)
|
|
{
|
|
DWORD actualNum = 0;
|
|
ReadFile ((HANDLE) fileHandle, buffer, numBytes, &actualNum, 0);
|
|
return (size_t) actualNum;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//==============================================================================
|
|
void FileOutputStream::openHandle()
|
|
{
|
|
HANDLE h = CreateFile (file.getFullPathName().toUTF16(), GENERIC_WRITE, FILE_SHARE_READ, 0,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
LARGE_INTEGER li;
|
|
li.QuadPart = 0;
|
|
li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_END);
|
|
|
|
if (li.LowPart != INVALID_SET_FILE_POINTER)
|
|
{
|
|
fileHandle = (void*) h;
|
|
currentPosition = li.QuadPart;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileOutputStream::closeHandle()
|
|
{
|
|
CloseHandle ((HANDLE) fileHandle);
|
|
}
|
|
|
|
int FileOutputStream::writeInternal (const void* buffer, int numBytes)
|
|
{
|
|
if (fileHandle != 0)
|
|
{
|
|
DWORD actualNum = 0;
|
|
WriteFile ((HANDLE) fileHandle, buffer, numBytes, &actualNum, 0);
|
|
return (int) actualNum;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void FileOutputStream::flushInternal()
|
|
{
|
|
if (fileHandle != 0)
|
|
FlushFileBuffers ((HANDLE) fileHandle);
|
|
}
|
|
|
|
//==============================================================================
|
|
int64 File::getSize() const
|
|
{
|
|
WIN32_FILE_ATTRIBUTE_DATA attributes;
|
|
|
|
if (GetFileAttributesEx (fullPath.toUTF16(), GetFileExInfoStandard, &attributes))
|
|
return (((int64) attributes.nFileSizeHigh) << 32) | attributes.nFileSizeLow;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const
|
|
{
|
|
using namespace WindowsFileHelpers;
|
|
WIN32_FILE_ATTRIBUTE_DATA attributes;
|
|
|
|
if (GetFileAttributesEx (fullPath.toUTF16(), GetFileExInfoStandard, &attributes))
|
|
{
|
|
modificationTime = fileTimeToTime (&attributes.ftLastWriteTime);
|
|
creationTime = fileTimeToTime (&attributes.ftCreationTime);
|
|
accessTime = fileTimeToTime (&attributes.ftLastAccessTime);
|
|
}
|
|
else
|
|
{
|
|
creationTime = accessTime = modificationTime = 0;
|
|
}
|
|
}
|
|
|
|
bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 creationTime) const
|
|
{
|
|
using namespace WindowsFileHelpers;
|
|
|
|
bool ok = false;
|
|
HANDLE h = CreateFile (fullPath.toUTF16(), GENERIC_WRITE, FILE_SHARE_READ, 0,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
FILETIME m, a, c;
|
|
timeToFileTime (modificationTime, &m);
|
|
timeToFileTime (accessTime, &a);
|
|
timeToFileTime (creationTime, &c);
|
|
|
|
ok = SetFileTime (h,
|
|
creationTime > 0 ? &c : 0,
|
|
accessTime > 0 ? &a : 0,
|
|
modificationTime > 0 ? &m : 0) != 0;
|
|
|
|
CloseHandle (h);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
//==============================================================================
|
|
void File::findFileSystemRoots (Array<File>& destArray)
|
|
{
|
|
TCHAR buffer [2048];
|
|
buffer[0] = 0;
|
|
buffer[1] = 0;
|
|
GetLogicalDriveStrings (2048, buffer);
|
|
|
|
const TCHAR* n = buffer;
|
|
StringArray roots;
|
|
|
|
while (*n != 0)
|
|
{
|
|
roots.add (String (n));
|
|
|
|
while (*n++ != 0)
|
|
{}
|
|
}
|
|
|
|
roots.sort (true);
|
|
|
|
for (int i = 0; i < roots.size(); ++i)
|
|
destArray.add (roots [i]);
|
|
}
|
|
|
|
//==============================================================================
|
|
const String File::getVolumeLabel() const
|
|
{
|
|
TCHAR dest[64];
|
|
if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toUTF16(), dest,
|
|
numElementsInArray (dest), 0, 0, 0, 0, 0))
|
|
dest[0] = 0;
|
|
|
|
return dest;
|
|
}
|
|
|
|
int File::getVolumeSerialNumber() const
|
|
{
|
|
TCHAR dest[64];
|
|
DWORD serialNum;
|
|
|
|
if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toUTF16(), dest,
|
|
numElementsInArray (dest), &serialNum, 0, 0, 0, 0))
|
|
return 0;
|
|
|
|
return (int) serialNum;
|
|
}
|
|
|
|
int64 File::getBytesFreeOnVolume() const
|
|
{
|
|
return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), false);
|
|
}
|
|
|
|
int64 File::getVolumeTotalSize() const
|
|
{
|
|
return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), true);
|
|
}
|
|
|
|
//==============================================================================
|
|
bool File::isOnCDRomDrive() const
|
|
{
|
|
return WindowsFileHelpers::getWindowsDriveType (getFullPathName()) == DRIVE_CDROM;
|
|
}
|
|
|
|
bool File::isOnHardDisk() const
|
|
{
|
|
if (fullPath.isEmpty())
|
|
return false;
|
|
|
|
const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
|
|
|
|
if (fullPath.toLowerCase()[0] <= 'b' && fullPath[1] == ':')
|
|
return n != DRIVE_REMOVABLE;
|
|
else
|
|
return n != DRIVE_CDROM && n != DRIVE_REMOTE;
|
|
}
|
|
|
|
bool File::isOnRemovableDrive() const
|
|
{
|
|
if (fullPath.isEmpty())
|
|
return false;
|
|
|
|
const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
|
|
|
|
return n == DRIVE_CDROM
|
|
|| n == DRIVE_REMOTE
|
|
|| n == DRIVE_REMOVABLE
|
|
|| n == DRIVE_RAMDISK;
|
|
}
|
|
|
|
//==============================================================================
|
|
const File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type)
|
|
{
|
|
int csidlType = 0;
|
|
|
|
switch (type)
|
|
{
|
|
case userHomeDirectory: csidlType = CSIDL_PROFILE; break;
|
|
case userDocumentsDirectory: csidlType = CSIDL_PERSONAL; break;
|
|
case userDesktopDirectory: csidlType = CSIDL_DESKTOP; break;
|
|
case userApplicationDataDirectory: csidlType = CSIDL_APPDATA; break;
|
|
case commonApplicationDataDirectory: csidlType = CSIDL_COMMON_APPDATA; break;
|
|
case globalApplicationsDirectory: csidlType = CSIDL_PROGRAM_FILES; break;
|
|
case userMusicDirectory: csidlType = CSIDL_MYMUSIC; break;
|
|
case userMoviesDirectory: csidlType = CSIDL_MYVIDEO; break;
|
|
|
|
case tempDirectory:
|
|
{
|
|
WCHAR dest [2048];
|
|
dest[0] = 0;
|
|
GetTempPath (numElementsInArray (dest), dest);
|
|
return File (String (dest));
|
|
}
|
|
|
|
case invokedExecutableFile:
|
|
case currentExecutableFile:
|
|
case currentApplicationFile:
|
|
{
|
|
HINSTANCE moduleHandle = (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle();
|
|
|
|
WCHAR dest [MAX_PATH + 256];
|
|
dest[0] = 0;
|
|
GetModuleFileName (moduleHandle, dest, numElementsInArray (dest));
|
|
return File (String (dest));
|
|
}
|
|
|
|
case hostApplicationPath:
|
|
{
|
|
WCHAR dest [MAX_PATH + 256];
|
|
dest[0] = 0;
|
|
GetModuleFileName (0, dest, numElementsInArray (dest));
|
|
return File (String (dest));
|
|
}
|
|
|
|
default:
|
|
jassertfalse; // unknown type?
|
|
return File::nonexistent;
|
|
}
|
|
|
|
return WindowsFileHelpers::getSpecialFolderPath (csidlType);
|
|
}
|
|
|
|
//==============================================================================
|
|
const File File::getCurrentWorkingDirectory()
|
|
{
|
|
WCHAR dest [MAX_PATH + 256];
|
|
dest[0] = 0;
|
|
GetCurrentDirectory (numElementsInArray (dest), dest);
|
|
return File (String (dest));
|
|
}
|
|
|
|
bool File::setAsCurrentWorkingDirectory() const
|
|
{
|
|
return SetCurrentDirectory (getFullPathName().toUTF16()) != FALSE;
|
|
}
|
|
|
|
//==============================================================================
|
|
const String File::getVersion() const
|
|
{
|
|
String result;
|
|
|
|
DWORD handle = 0;
|
|
DWORD bufferSize = GetFileVersionInfoSize (getFullPathName().toUTF16(), &handle);
|
|
HeapBlock<char> buffer;
|
|
buffer.calloc (bufferSize);
|
|
|
|
if (GetFileVersionInfo (getFullPathName().toUTF16(), 0, bufferSize, buffer))
|
|
{
|
|
VS_FIXEDFILEINFO* vffi;
|
|
UINT len = 0;
|
|
|
|
if (VerQueryValue (buffer, (LPTSTR) _T("\\"), (LPVOID*) &vffi, &len))
|
|
{
|
|
result << (int) HIWORD (vffi->dwFileVersionMS) << '.'
|
|
<< (int) LOWORD (vffi->dwFileVersionMS) << '.'
|
|
<< (int) HIWORD (vffi->dwFileVersionLS) << '.'
|
|
<< (int) LOWORD (vffi->dwFileVersionLS);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//==============================================================================
|
|
const File File::getLinkedTarget() const
|
|
{
|
|
File result (*this);
|
|
String p (getFullPathName());
|
|
|
|
if (! exists())
|
|
p += ".lnk";
|
|
else if (getFileExtension() != ".lnk")
|
|
return result;
|
|
|
|
ComSmartPtr <IShellLink> shellLink;
|
|
if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)))
|
|
{
|
|
ComSmartPtr <IPersistFile> persistFile;
|
|
if (SUCCEEDED (shellLink.QueryInterface (IID_IPersistFile, persistFile)))
|
|
{
|
|
if (SUCCEEDED (persistFile->Load (p.toUTF16(), STGM_READ))
|
|
&& SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI)))
|
|
{
|
|
WIN32_FIND_DATA winFindData;
|
|
WCHAR resolvedPath [MAX_PATH];
|
|
|
|
if (SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, &winFindData, SLGP_UNCPRIORITY)))
|
|
result = File (resolvedPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
class DirectoryIterator::NativeIterator::Pimpl
|
|
{
|
|
public:
|
|
Pimpl (const File& directory, const String& wildCard)
|
|
: directoryWithWildCard (File::addTrailingSeparator (directory.getFullPathName()) + wildCard),
|
|
handle (INVALID_HANDLE_VALUE)
|
|
{
|
|
}
|
|
|
|
~Pimpl()
|
|
{
|
|
if (handle != INVALID_HANDLE_VALUE)
|
|
FindClose (handle);
|
|
}
|
|
|
|
bool next (String& filenameFound,
|
|
bool* const isDir, bool* const isHidden, int64* const fileSize,
|
|
Time* const modTime, Time* const creationTime, bool* const isReadOnly)
|
|
{
|
|
using namespace WindowsFileHelpers;
|
|
WIN32_FIND_DATA findData;
|
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
handle = FindFirstFile (directoryWithWildCard.toUTF16(), &findData);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (FindNextFile (handle, &findData) == 0)
|
|
return false;
|
|
}
|
|
|
|
filenameFound = findData.cFileName;
|
|
|
|
if (isDir != 0) *isDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
|
|
if (isHidden != 0) *isHidden = ((findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0);
|
|
if (fileSize != 0) *fileSize = findData.nFileSizeLow + (((int64) findData.nFileSizeHigh) << 32);
|
|
if (modTime != 0) *modTime = Time (fileTimeToTime (&findData.ftLastWriteTime));
|
|
if (creationTime != 0) *creationTime = Time (fileTimeToTime (&findData.ftCreationTime));
|
|
if (isReadOnly != 0) *isReadOnly = ((findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const String directoryWithWildCard;
|
|
HANDLE handle;
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl);
|
|
};
|
|
|
|
DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard)
|
|
: pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard))
|
|
{
|
|
}
|
|
|
|
DirectoryIterator::NativeIterator::~NativeIterator()
|
|
{
|
|
}
|
|
|
|
bool DirectoryIterator::NativeIterator::next (String& filenameFound,
|
|
bool* const isDir, bool* const isHidden, int64* const fileSize,
|
|
Time* const modTime, Time* const creationTime, bool* const isReadOnly)
|
|
{
|
|
return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly);
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
bool PlatformUtilities::openDocument (const String& fileName, const String& parameters)
|
|
{
|
|
HINSTANCE hInstance = 0;
|
|
|
|
JUCE_TRY
|
|
{
|
|
hInstance = ShellExecute (0, 0, fileName.toUTF16(), parameters.toUTF16(), 0, SW_SHOWDEFAULT);
|
|
}
|
|
JUCE_CATCH_ALL
|
|
|
|
return hInstance > (HINSTANCE) 32;
|
|
}
|
|
|
|
void File::revealToUser() const
|
|
{
|
|
if (isDirectory())
|
|
startAsProcess();
|
|
else if (getParentDirectory().exists())
|
|
getParentDirectory().startAsProcess();
|
|
}
|
|
|
|
//==============================================================================
|
|
class NamedPipeInternal
|
|
{
|
|
public:
|
|
NamedPipeInternal (const String& file, const bool isPipe_)
|
|
: pipeH (0),
|
|
cancelEvent (0),
|
|
connected (false),
|
|
isPipe (isPipe_)
|
|
{
|
|
cancelEvent = CreateEvent (0, FALSE, FALSE, 0);
|
|
|
|
pipeH = isPipe ? CreateNamedPipe (file.toUTF16(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0,
|
|
PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, 0)
|
|
: CreateFile (file.toUTF16(), GENERIC_READ | GENERIC_WRITE, 0, 0,
|
|
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
|
}
|
|
|
|
~NamedPipeInternal()
|
|
{
|
|
disconnectPipe();
|
|
|
|
if (pipeH != 0)
|
|
CloseHandle (pipeH);
|
|
|
|
CloseHandle (cancelEvent);
|
|
}
|
|
|
|
bool connect (const int timeOutMs)
|
|
{
|
|
if (! isPipe)
|
|
return true;
|
|
|
|
if (! connected)
|
|
{
|
|
OVERLAPPED over;
|
|
zerostruct (over);
|
|
|
|
over.hEvent = CreateEvent (0, TRUE, FALSE, 0);
|
|
|
|
if (ConnectNamedPipe (pipeH, &over))
|
|
{
|
|
connected = false; // yes, you read that right. In overlapped mode it should always return 0.
|
|
}
|
|
else
|
|
{
|
|
const int err = GetLastError();
|
|
|
|
if (err == ERROR_IO_PENDING || err == ERROR_PIPE_LISTENING)
|
|
{
|
|
HANDLE handles[] = { over.hEvent, cancelEvent };
|
|
|
|
if (WaitForMultipleObjects (2, handles, FALSE,
|
|
timeOutMs >= 0 ? timeOutMs : INFINITE) == WAIT_OBJECT_0)
|
|
connected = true;
|
|
}
|
|
else if (err == ERROR_PIPE_CONNECTED)
|
|
{
|
|
connected = true;
|
|
}
|
|
}
|
|
|
|
CloseHandle (over.hEvent);
|
|
}
|
|
|
|
return connected;
|
|
}
|
|
|
|
void disconnectPipe()
|
|
{
|
|
if (connected)
|
|
{
|
|
DisconnectNamedPipe (pipeH);
|
|
connected = false;
|
|
}
|
|
}
|
|
|
|
HANDLE pipeH;
|
|
HANDLE cancelEvent;
|
|
bool connected, isPipe;
|
|
};
|
|
|
|
void NamedPipe::close()
|
|
{
|
|
cancelPendingReads();
|
|
|
|
const ScopedLock sl (lock);
|
|
delete static_cast<NamedPipeInternal*> (internal);
|
|
internal = 0;
|
|
}
|
|
|
|
bool NamedPipe::openInternal (const String& pipeName, const bool createPipe)
|
|
{
|
|
close();
|
|
|
|
ScopedPointer<NamedPipeInternal> intern (new NamedPipeInternal ("\\\\.\\pipe\\" + pipeName, createPipe));
|
|
|
|
if (intern->pipeH != INVALID_HANDLE_VALUE)
|
|
{
|
|
internal = intern.release();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
|
|
{
|
|
const ScopedLock sl (lock);
|
|
int bytesRead = -1;
|
|
bool waitAgain = true;
|
|
|
|
while (waitAgain && internal != 0)
|
|
{
|
|
NamedPipeInternal* const intern = static_cast<NamedPipeInternal*> (internal);
|
|
waitAgain = false;
|
|
|
|
if (! intern->connect (timeOutMilliseconds))
|
|
break;
|
|
|
|
if (maxBytesToRead <= 0)
|
|
return 0;
|
|
|
|
OVERLAPPED over;
|
|
zerostruct (over);
|
|
over.hEvent = CreateEvent (0, TRUE, FALSE, 0);
|
|
|
|
unsigned long numRead;
|
|
|
|
if (ReadFile (intern->pipeH, destBuffer, maxBytesToRead, &numRead, &over))
|
|
{
|
|
bytesRead = (int) numRead;
|
|
}
|
|
else if (GetLastError() == ERROR_IO_PENDING)
|
|
{
|
|
HANDLE handles[] = { over.hEvent, intern->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 (intern->pipeH);
|
|
WaitForSingleObject (over.hEvent, INFINITE); // makes sure cancel is complete
|
|
}
|
|
|
|
if (GetOverlappedResult (intern->pipeH, &over, &numRead, FALSE))
|
|
{
|
|
bytesRead = (int) numRead;
|
|
}
|
|
else if (GetLastError() == ERROR_BROKEN_PIPE && intern->isPipe)
|
|
{
|
|
intern->disconnectPipe();
|
|
waitAgain = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
waitAgain = internal != 0;
|
|
Sleep (5);
|
|
}
|
|
|
|
CloseHandle (over.hEvent);
|
|
}
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds)
|
|
{
|
|
int bytesWritten = -1;
|
|
NamedPipeInternal* const intern = static_cast<NamedPipeInternal*> (internal);
|
|
|
|
if (intern != 0 && intern->connect (timeOutMilliseconds))
|
|
{
|
|
if (numBytesToWrite <= 0)
|
|
return 0;
|
|
|
|
OVERLAPPED over;
|
|
zerostruct (over);
|
|
|
|
over.hEvent = CreateEvent (0, TRUE, FALSE, 0);
|
|
|
|
unsigned long numWritten;
|
|
|
|
if (WriteFile (intern->pipeH, sourceBuffer, numBytesToWrite, &numWritten, &over))
|
|
{
|
|
bytesWritten = (int) numWritten;
|
|
}
|
|
else if (GetLastError() == ERROR_IO_PENDING)
|
|
{
|
|
HANDLE handles[] = { over.hEvent, intern->cancelEvent };
|
|
DWORD waitResult;
|
|
|
|
waitResult = WaitForMultipleObjects (2, handles, FALSE,
|
|
timeOutMilliseconds >= 0 ? timeOutMilliseconds
|
|
: INFINITE);
|
|
|
|
if (waitResult != WAIT_OBJECT_0)
|
|
{
|
|
CancelIo (intern->pipeH);
|
|
WaitForSingleObject (over.hEvent, INFINITE);
|
|
}
|
|
|
|
if (GetOverlappedResult (intern->pipeH, &over, &numWritten, FALSE))
|
|
{
|
|
bytesWritten = (int) numWritten;
|
|
}
|
|
else if (GetLastError() == ERROR_BROKEN_PIPE && intern->isPipe)
|
|
{
|
|
intern->disconnectPipe();
|
|
}
|
|
}
|
|
|
|
CloseHandle (over.hEvent);
|
|
}
|
|
|
|
return bytesWritten;
|
|
}
|
|
|
|
void NamedPipe::cancelPendingReads()
|
|
{
|
|
if (internal != 0)
|
|
SetEvent (static_cast<NamedPipeInternal*> (internal)->cancelEvent);
|
|
}
|
|
|
|
|
|
#endif
|