2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
20 ==============================================================================
26 #ifndef INVALID_FILE_ATTRIBUTES
27 #define INVALID_FILE_ATTRIBUTES ((DWORD) -1)
30 //==============================================================================
31 namespace WindowsFileHelpers
33 //==============================================================================
35 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnested-anon-types")
37 typedef struct _REPARSE_DATA_BUFFER
{
39 USHORT ReparseDataLength
;
43 USHORT SubstituteNameOffset
;
44 USHORT SubstituteNameLength
;
45 USHORT PrintNameOffset
;
46 USHORT PrintNameLength
;
49 } SymbolicLinkReparseBuffer
;
51 USHORT SubstituteNameOffset
;
52 USHORT SubstituteNameLength
;
53 USHORT PrintNameOffset
;
54 USHORT PrintNameLength
;
56 } MountPointReparseBuffer
;
59 } GenericReparseBuffer
;
61 } *PREPARSE_DATA_BUFFER
, REPARSE_DATA_BUFFER
;
63 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
66 //==============================================================================
67 static DWORD
getAtts (const String
& path
) noexcept
69 return GetFileAttributes (path
.toWideCharPointer());
72 static bool changeAtts (const String
& path
, DWORD bitsToSet
, DWORD bitsToClear
) noexcept
74 auto oldAtts
= getAtts (path
);
76 if (oldAtts
== INVALID_FILE_ATTRIBUTES
)
79 auto newAtts
= ((oldAtts
| bitsToSet
) & ~bitsToClear
);
81 return newAtts
== oldAtts
82 || SetFileAttributes (path
.toWideCharPointer(), newAtts
) != FALSE
;
85 static int64
fileTimeToTime (const FILETIME
* const ft
) noexcept
87 static_assert (sizeof (ULARGE_INTEGER
) == sizeof (FILETIME
),
88 "ULARGE_INTEGER is too small to hold FILETIME: please report!");
90 return (int64
) ((reinterpret_cast<const ULARGE_INTEGER
*> (ft
)->QuadPart
- 116444736000000000LL) / 10000);
93 static FILETIME
* timeToFileTime (const int64 time
, FILETIME
* const ft
) noexcept
98 reinterpret_cast<ULARGE_INTEGER
*> (ft
)->QuadPart
= (ULONGLONG
) (time
* 10000 + 116444736000000000LL);
102 static String
getDriveFromPath (String path
)
104 if (path
.isNotEmpty() && path
[1] == ':' && path
[2] == 0)
107 const size_t numBytes
= CharPointer_UTF16::getBytesRequiredFor (path
.getCharPointer()) + 4;
108 HeapBlock
<WCHAR
> pathCopy
;
109 pathCopy
.calloc (numBytes
, 1);
110 path
.copyToUTF16 (pathCopy
, numBytes
);
112 if (PathStripToRoot (pathCopy
))
113 path
= static_cast<const WCHAR
*> (pathCopy
);
118 static int64
getDiskSpaceInfo (const String
& path
, const bool total
)
120 ULARGE_INTEGER spc
, tot
, totFree
;
122 if (GetDiskFreeSpaceEx (getDriveFromPath (path
).toWideCharPointer(), &spc
, &tot
, &totFree
))
123 return total
? (int64
) tot
.QuadPart
124 : (int64
) spc
.QuadPart
;
129 static unsigned int getWindowsDriveType (const String
& path
)
131 return GetDriveType (getDriveFromPath (path
).toWideCharPointer());
134 static File
getSpecialFolderPath (int type
)
136 WCHAR path
[MAX_PATH
+ 256];
138 if (SHGetSpecialFolderPath (nullptr, path
, type
, FALSE
))
139 return File (String (path
));
144 static File
getModuleFileName (HINSTANCE moduleHandle
)
146 WCHAR dest
[MAX_PATH
+ 256];
148 GetModuleFileName (moduleHandle
, dest
, (DWORD
) numElementsInArray (dest
));
149 return File (String (dest
));
152 static Result
getResultForLastError()
154 TCHAR messageBuffer
[256] = {};
156 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
,
157 nullptr, GetLastError(), MAKELANGID (LANG_NEUTRAL
, SUBLANG_DEFAULT
),
158 messageBuffer
, (DWORD
) numElementsInArray (messageBuffer
) - 1, nullptr);
160 return Result::fail (String (messageBuffer
));
163 // The docs for the Windows security API aren't very clear. Some parts of the following
164 // function (the flags passed to GetNamedSecurityInfo, duplicating the primary access token)
165 // were guided by the example at https://blog.aaronballman.com/2011/08/how-to-check-access-rights/
166 static bool hasFileAccess (const File
& file
, DWORD accessType
)
168 const auto& path
= file
.getFullPathName();
173 struct PsecurityDescriptorGuard
175 ~PsecurityDescriptorGuard() { if (psecurityDescriptor
!= nullptr) LocalFree (psecurityDescriptor
); }
176 PSECURITY_DESCRIPTOR psecurityDescriptor
= nullptr;
179 PsecurityDescriptorGuard descriptorGuard
;
181 if (GetNamedSecurityInfo (path
.toWideCharPointer(),
183 OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION
,
188 &descriptorGuard
.psecurityDescriptor
) != ERROR_SUCCESS
)
195 ~HandleGuard() { if (handle
!= INVALID_HANDLE_VALUE
) CloseHandle (handle
); }
196 HANDLE handle
= nullptr;
199 HandleGuard primaryTokenGuard
;
201 if (! OpenProcessToken (GetCurrentProcess(),
202 TOKEN_IMPERSONATE
| TOKEN_DUPLICATE
| TOKEN_QUERY
| STANDARD_RIGHTS_READ
,
203 &primaryTokenGuard
.handle
))
208 HandleGuard duplicatedTokenGuard
;
210 if (! DuplicateToken (primaryTokenGuard
.handle
,
211 SecurityImpersonation
,
212 &duplicatedTokenGuard
.handle
))
217 GENERIC_MAPPING mapping
{ FILE_GENERIC_READ
, FILE_GENERIC_WRITE
, FILE_GENERIC_EXECUTE
, FILE_ALL_ACCESS
};
219 MapGenericMask (&accessType
, &mapping
);
221 BOOL granted
= false;
223 DWORD setSize
= sizeof (set
);
225 if (! AccessCheck (descriptorGuard
.psecurityDescriptor
,
226 duplicatedTokenGuard
.handle
,
237 return granted
!= FALSE
;
239 } // namespace WindowsFileHelpers
241 //==============================================================================
242 #if JUCE_ALLOW_STATIC_NULL_VARIABLES
244 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
246 const juce_wchar
File::separator
= '\\';
247 const StringRef
File::separatorString ("\\");
249 JUCE_END_IGNORE_WARNINGS_MSVC
253 juce_wchar
File::getSeparatorChar() { return '\\'; }
254 StringRef
File::getSeparatorString() { return "\\"; }
256 void* getUser32Function (const char*);
258 //==============================================================================
259 bool File::exists() const
261 return fullPath
.isNotEmpty()
262 && WindowsFileHelpers::getAtts (fullPath
) != INVALID_FILE_ATTRIBUTES
;
265 bool File::existsAsFile() const
267 return fullPath
.isNotEmpty()
268 && (WindowsFileHelpers::getAtts (fullPath
) & FILE_ATTRIBUTE_DIRECTORY
) == 0;
271 bool File::isDirectory() const
273 auto attr
= WindowsFileHelpers::getAtts (fullPath
);
274 return (attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0 && attr
!= INVALID_FILE_ATTRIBUTES
;
277 bool File::hasWriteAccess() const
281 const auto attr
= WindowsFileHelpers::getAtts (fullPath
);
283 return WindowsFileHelpers::hasFileAccess (*this, GENERIC_WRITE
)
284 && (attr
== INVALID_FILE_ATTRIBUTES
285 || (attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0
286 || (attr
& FILE_ATTRIBUTE_READONLY
) == 0);
289 if ((! isDirectory()) && fullPath
.containsChar (getSeparatorChar()))
290 return getParentDirectory().hasWriteAccess();
295 bool File::hasReadAccess() const
297 return WindowsFileHelpers::hasFileAccess (*this, GENERIC_READ
);
300 bool File::setFileReadOnlyInternal (bool shouldBeReadOnly
) const
302 return WindowsFileHelpers::changeAtts (fullPath
,
303 shouldBeReadOnly
? FILE_ATTRIBUTE_READONLY
: 0,
304 shouldBeReadOnly
? 0 : FILE_ATTRIBUTE_READONLY
);
307 bool File::setFileExecutableInternal (bool /*shouldBeExecutable*/) const
309 // XXX is this possible?
313 bool File::isHidden() const
315 return (WindowsFileHelpers::getAtts (fullPath
) & FILE_ATTRIBUTE_HIDDEN
) != 0;
318 //==============================================================================
319 bool File::deleteFile() const
324 return isDirectory() ? RemoveDirectory (fullPath
.toWideCharPointer()) != 0
325 : DeleteFile (fullPath
.toWideCharPointer()) != 0;
328 bool File::moveToTrash() const
333 // The string we pass in must be double null terminated..
334 const size_t numBytes
= CharPointer_UTF16::getBytesRequiredFor (fullPath
.getCharPointer()) + 8;
335 HeapBlock
<WCHAR
> doubleNullTermPath
;
336 doubleNullTermPath
.calloc (numBytes
, 1);
337 fullPath
.copyToUTF16 (doubleNullTermPath
, numBytes
);
339 SHFILEOPSTRUCT fos
= {};
340 fos
.wFunc
= FO_DELETE
;
341 fos
.pFrom
= doubleNullTermPath
;
342 fos
.fFlags
= FOF_ALLOWUNDO
| FOF_NOERRORUI
| FOF_SILENT
| FOF_NOCONFIRMATION
343 | FOF_NOCONFIRMMKDIR
| FOF_RENAMEONCOLLISION
;
345 return SHFileOperation (&fos
) == 0;
348 bool File::copyInternal (const File
& dest
) const
350 return CopyFile (fullPath
.toWideCharPointer(),
351 dest
.getFullPathName().toWideCharPointer(), false) != 0;
354 bool File::moveInternal (const File
& dest
) const
356 return MoveFile (fullPath
.toWideCharPointer(),
357 dest
.getFullPathName().toWideCharPointer()) != 0;
360 bool File::replaceInternal (const File
& dest
) const
362 return ReplaceFile (dest
.getFullPathName().toWideCharPointer(),
363 fullPath
.toWideCharPointer(),
364 nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS
| 4 /*REPLACEFILE_IGNORE_ACL_ERRORS*/,
365 nullptr, nullptr) != 0;
368 Result
File::createDirectoryInternal (const String
& fileName
) const
370 return CreateDirectory (fileName
.toWideCharPointer(), nullptr) ? Result::ok()
371 : WindowsFileHelpers::getResultForLastError();
374 //==============================================================================
375 int64
juce_fileSetPosition (void* handle
, int64 pos
)
379 li
.LowPart
= SetFilePointer ((HANDLE
) handle
, (LONG
) li
.LowPart
,
380 &li
.HighPart
, FILE_BEGIN
); // (returns -1 if it fails)
384 void FileInputStream::openHandle()
386 auto h
= CreateFile (file
.getFullPathName().toWideCharPointer(),
387 GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, nullptr,
388 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_SEQUENTIAL_SCAN
, nullptr);
390 if (h
!= INVALID_HANDLE_VALUE
)
391 fileHandle
= (void*) h
;
393 status
= WindowsFileHelpers::getResultForLastError();
396 FileInputStream::~FileInputStream()
398 CloseHandle ((HANDLE
) fileHandle
);
401 size_t FileInputStream::readInternal (void* buffer
, size_t numBytes
)
403 if (fileHandle
!= nullptr)
407 if (! ReadFile ((HANDLE
) fileHandle
, buffer
, (DWORD
) numBytes
, &actualNum
, nullptr))
408 status
= WindowsFileHelpers::getResultForLastError();
410 return (size_t) actualNum
;
416 //==============================================================================
417 void FileOutputStream::openHandle()
419 auto h
= CreateFile (file
.getFullPathName().toWideCharPointer(),
420 GENERIC_WRITE
, FILE_SHARE_READ
, nullptr,
421 OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, nullptr);
423 if (h
!= INVALID_HANDLE_VALUE
)
427 li
.LowPart
= SetFilePointer (h
, 0, &li
.HighPart
, FILE_END
);
429 if (li
.LowPart
!= INVALID_SET_FILE_POINTER
)
431 fileHandle
= (void*) h
;
432 currentPosition
= li
.QuadPart
;
437 status
= WindowsFileHelpers::getResultForLastError();
440 void FileOutputStream::closeHandle()
442 CloseHandle ((HANDLE
) fileHandle
);
445 ssize_t
FileOutputStream::writeInternal (const void* bufferToWrite
, size_t numBytes
)
449 if (fileHandle
!= nullptr)
450 if (! WriteFile ((HANDLE
) fileHandle
, bufferToWrite
, (DWORD
) numBytes
, &actualNum
, nullptr))
451 status
= WindowsFileHelpers::getResultForLastError();
453 return (ssize_t
) actualNum
;
456 void FileOutputStream::flushInternal()
458 if (fileHandle
!= nullptr)
459 if (! FlushFileBuffers ((HANDLE
) fileHandle
))
460 status
= WindowsFileHelpers::getResultForLastError();
463 Result
FileOutputStream::truncate()
465 if (fileHandle
== nullptr)
469 return SetEndOfFile ((HANDLE
) fileHandle
) ? Result::ok()
470 : WindowsFileHelpers::getResultForLastError();
473 //==============================================================================
474 void MemoryMappedFile::openInternal (const File
& file
, AccessMode mode
, bool exclusive
)
476 jassert (mode
== readOnly
|| mode
== readWrite
);
478 if (range
.getStart() > 0)
480 SYSTEM_INFO systemInfo
;
481 GetNativeSystemInfo (&systemInfo
);
483 range
.setStart (range
.getStart() - (range
.getStart() % systemInfo
.dwAllocationGranularity
));
486 DWORD accessMode
= GENERIC_READ
, createType
= OPEN_EXISTING
;
487 DWORD protect
= PAGE_READONLY
, access
= FILE_MAP_READ
;
489 if (mode
== readWrite
)
491 accessMode
= GENERIC_READ
| GENERIC_WRITE
;
492 createType
= OPEN_ALWAYS
;
493 protect
= PAGE_READWRITE
;
494 access
= FILE_MAP_ALL_ACCESS
;
497 auto h
= CreateFile (file
.getFullPathName().toWideCharPointer(), accessMode
,
498 exclusive
? 0 : (FILE_SHARE_READ
| FILE_SHARE_DELETE
| (mode
== readWrite
? FILE_SHARE_WRITE
: 0)), nullptr,
499 createType
, FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_SEQUENTIAL_SCAN
, nullptr);
501 if (h
!= INVALID_HANDLE_VALUE
)
503 fileHandle
= (void*) h
;
505 auto mappingHandle
= CreateFileMapping (h
, nullptr, protect
,
506 (DWORD
) (range
.getEnd() >> 32),
507 (DWORD
) range
.getEnd(), nullptr);
509 if (mappingHandle
!= nullptr)
511 address
= MapViewOfFile (mappingHandle
, access
, (DWORD
) (range
.getStart() >> 32),
512 (DWORD
) range
.getStart(), (SIZE_T
) range
.getLength());
514 if (address
== nullptr)
515 range
= Range
<int64
>();
517 CloseHandle (mappingHandle
);
522 MemoryMappedFile::~MemoryMappedFile()
524 if (address
!= nullptr)
525 UnmapViewOfFile (address
);
527 if (fileHandle
!= nullptr)
528 CloseHandle ((HANDLE
) fileHandle
);
531 //==============================================================================
532 int64
File::getSize() const
534 WIN32_FILE_ATTRIBUTE_DATA attributes
;
536 if (GetFileAttributesEx (fullPath
.toWideCharPointer(), GetFileExInfoStandard
, &attributes
))
537 return (((int64
) attributes
.nFileSizeHigh
) << 32) | attributes
.nFileSizeLow
;
542 void File::getFileTimesInternal (int64
& modificationTime
, int64
& accessTime
, int64
& creationTime
) const
544 using namespace WindowsFileHelpers
;
545 WIN32_FILE_ATTRIBUTE_DATA attributes
;
547 if (GetFileAttributesEx (fullPath
.toWideCharPointer(), GetFileExInfoStandard
, &attributes
))
549 modificationTime
= fileTimeToTime (&attributes
.ftLastWriteTime
);
550 creationTime
= fileTimeToTime (&attributes
.ftCreationTime
);
551 accessTime
= fileTimeToTime (&attributes
.ftLastAccessTime
);
555 creationTime
= accessTime
= modificationTime
= 0;
559 bool File::setFileTimesInternal (int64 modificationTime
, int64 accessTime
, int64 creationTime
) const
561 using namespace WindowsFileHelpers
;
564 auto h
= CreateFile (fullPath
.toWideCharPointer(),
565 GENERIC_WRITE
, FILE_SHARE_READ
, nullptr,
566 OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, nullptr);
568 if (h
!= INVALID_HANDLE_VALUE
)
573 timeToFileTime (creationTime
, &c
),
574 timeToFileTime (accessTime
, &a
),
575 timeToFileTime (modificationTime
, &m
)) != 0;
583 //==============================================================================
584 void File::findFileSystemRoots (Array
<File
>& destArray
)
586 TCHAR buffer
[2048] = {};
587 GetLogicalDriveStrings (2048, buffer
);
589 const TCHAR
* n
= buffer
;
594 roots
.add (String (n
));
602 for (int i
= 0; i
< roots
.size(); ++i
)
603 destArray
.add (roots
[i
]);
606 //==============================================================================
607 String
File::getVolumeLabel() const
611 if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest
,
612 (DWORD
) numElementsInArray (dest
), nullptr, nullptr, nullptr, nullptr, 0))
618 int File::getVolumeSerialNumber() const
623 if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest
,
624 (DWORD
) numElementsInArray (dest
), &serialNum
, nullptr, nullptr, nullptr, 0))
627 return (int) serialNum
;
630 int64
File::getBytesFreeOnVolume() const
632 return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), false);
635 int64
File::getVolumeTotalSize() const
637 return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), true);
640 uint64
File::getFileIdentifier() const
644 String path
= getFullPathName();
649 auto h
= CreateFile (path
.toWideCharPointer(),
650 GENERIC_READ
, FILE_SHARE_READ
, nullptr,
651 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, nullptr);
653 if (h
!= INVALID_HANDLE_VALUE
)
655 BY_HANDLE_FILE_INFORMATION info
;
658 if (GetFileInformationByHandle (h
, &info
))
659 result
= (((uint64
) info
.nFileIndexHigh
) << 32) | info
.nFileIndexLow
;
667 //==============================================================================
668 bool File::isOnCDRomDrive() const
670 return WindowsFileHelpers::getWindowsDriveType (getFullPathName()) == DRIVE_CDROM
;
673 bool File::isOnHardDisk() const
675 if (fullPath
.isEmpty())
678 auto n
= WindowsFileHelpers::getWindowsDriveType (getFullPathName());
680 return n
!= DRIVE_REMOVABLE
683 && n
!= DRIVE_NO_ROOT_DIR
;
686 bool File::isOnRemovableDrive() const
688 if (fullPath
.isEmpty())
691 auto n
= WindowsFileHelpers::getWindowsDriveType (getFullPathName());
693 return n
== DRIVE_CDROM
695 || n
== DRIVE_REMOVABLE
696 || n
== DRIVE_RAMDISK
;
699 //==============================================================================
700 File JUCE_CALLTYPE
File::getSpecialLocation (const SpecialLocationType type
)
706 case userHomeDirectory
: csidlType
= CSIDL_PROFILE
; break;
707 case userDocumentsDirectory
: csidlType
= CSIDL_PERSONAL
; break;
708 case userDesktopDirectory
: csidlType
= CSIDL_DESKTOP
; break;
709 case userApplicationDataDirectory
: csidlType
= CSIDL_APPDATA
; break;
710 case commonApplicationDataDirectory
: csidlType
= CSIDL_COMMON_APPDATA
; break;
711 case commonDocumentsDirectory
: csidlType
= CSIDL_COMMON_DOCUMENTS
; break;
712 case globalApplicationsDirectory
: csidlType
= CSIDL_PROGRAM_FILES
; break;
713 case globalApplicationsDirectoryX86
: csidlType
= CSIDL_PROGRAM_FILESX86
; break;
714 case windowsLocalAppData
: csidlType
= CSIDL_LOCAL_APPDATA
; break;
715 case userMusicDirectory
: csidlType
= 0x0d; /*CSIDL_MYMUSIC*/ break;
716 case userMoviesDirectory
: csidlType
= 0x0e; /*CSIDL_MYVIDEO*/ break;
717 case userPicturesDirectory
: csidlType
= 0x27; /*CSIDL_MYPICTURES*/ break;
723 GetTempPath ((DWORD
) numElementsInArray (dest
), dest
);
724 return File (String (dest
));
727 case windowsSystemDirectory
:
731 GetSystemDirectoryW (dest
, (UINT
) numElementsInArray (dest
));
732 return File (String (dest
));
735 case invokedExecutableFile
:
736 case currentExecutableFile
:
737 case currentApplicationFile
:
738 return WindowsFileHelpers::getModuleFileName ((HINSTANCE
) Process::getCurrentModuleInstanceHandle());
740 case hostApplicationPath
:
741 return WindowsFileHelpers::getModuleFileName (nullptr);
744 jassertfalse
; // unknown type?
748 return WindowsFileHelpers::getSpecialFolderPath (csidlType
);
751 //==============================================================================
752 File
File::getCurrentWorkingDirectory()
754 WCHAR dest
[MAX_PATH
+ 256];
756 GetCurrentDirectory ((DWORD
) numElementsInArray (dest
), dest
);
757 return File (String (dest
));
760 bool File::setAsCurrentWorkingDirectory() const
762 return SetCurrentDirectory (getFullPathName().toWideCharPointer()) != FALSE
;
765 //==============================================================================
766 String
File::getVersion() const
771 DWORD bufferSize
= GetFileVersionInfoSize (getFullPathName().toWideCharPointer(), &handle
);
772 HeapBlock
<char> buffer
;
773 buffer
.calloc (bufferSize
);
775 if (GetFileVersionInfo (getFullPathName().toWideCharPointer(), 0, bufferSize
, buffer
))
777 VS_FIXEDFILEINFO
* vffi
;
780 if (VerQueryValue (buffer
, (LPTSTR
) _T("\\"), (LPVOID
*) &vffi
, &len
))
782 result
<< (int) HIWORD (vffi
->dwFileVersionMS
) << '.'
783 << (int) LOWORD (vffi
->dwFileVersionMS
) << '.'
784 << (int) HIWORD (vffi
->dwFileVersionLS
) << '.'
785 << (int) LOWORD (vffi
->dwFileVersionLS
);
792 //==============================================================================
793 bool File::isSymbolicLink() const
795 const auto attributes
= WindowsFileHelpers::getAtts (fullPath
);
796 return (attributes
!= INVALID_FILE_ATTRIBUTES
&& (attributes
& FILE_ATTRIBUTE_REPARSE_POINT
) != 0);
799 bool File::isShortcut() const
801 return hasFileExtension (".lnk");
804 static String
readWindowsLnkFile (File lnkFile
, bool wantsAbsolutePath
)
806 if (! lnkFile
.exists())
807 lnkFile
= File (lnkFile
.getFullPathName() + ".lnk");
809 if (lnkFile
.exists())
811 ComSmartPtr
<IShellLink
> shellLink
;
812 ComSmartPtr
<IPersistFile
> persistFile
;
814 if (SUCCEEDED (shellLink
.CoCreateInstance (CLSID_ShellLink
))
815 && SUCCEEDED (shellLink
.QueryInterface (persistFile
))
816 && SUCCEEDED (persistFile
->Load (lnkFile
.getFullPathName().toWideCharPointer(), STGM_READ
))
817 && (! wantsAbsolutePath
|| SUCCEEDED (shellLink
->Resolve (nullptr, SLR_ANY_MATCH
| SLR_NO_UI
))))
819 WIN32_FIND_DATA winFindData
= {};
820 WCHAR resolvedPath
[MAX_PATH
];
822 DWORD flags
= SLGP_UNCPRIORITY
;
824 if (! wantsAbsolutePath
)
825 flags
|= SLGP_RAWPATH
;
827 if (SUCCEEDED (shellLink
->GetPath (resolvedPath
, MAX_PATH
, &winFindData
, flags
)))
835 static String
readWindowsShortcutOrLink (const File
& shortcut
, bool wantsAbsolutePath
)
838 if (! wantsAbsolutePath
)
840 HANDLE h
= CreateFile (shortcut
.getFullPathName().toWideCharPointer(),
841 GENERIC_READ
, FILE_SHARE_READ
, nullptr, OPEN_EXISTING
,
842 FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
,
845 if (h
!= INVALID_HANDLE_VALUE
)
847 HeapBlock
<WindowsFileHelpers::REPARSE_DATA_BUFFER
> reparseData
;
849 reparseData
.calloc (1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
850 DWORD bytesReturned
= 0;
852 bool success
= DeviceIoControl (h
, FSCTL_GET_REPARSE_POINT
, nullptr, 0,
853 reparseData
.getData(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE
,
854 &bytesReturned
, nullptr) != 0;
859 if (IsReparseTagMicrosoft (reparseData
->ReparseTag
))
863 switch (reparseData
->ReparseTag
)
865 case IO_REPARSE_TAG_SYMLINK
:
867 auto& symlinkData
= reparseData
->SymbolicLinkReparseBuffer
;
868 targetPath
= {symlinkData
.PathBuffer
+ (symlinkData
.SubstituteNameOffset
/ sizeof (WCHAR
)),
869 symlinkData
.SubstituteNameLength
/ sizeof (WCHAR
)};
873 case IO_REPARSE_TAG_MOUNT_POINT
:
875 auto& mountData
= reparseData
->MountPointReparseBuffer
;
876 targetPath
= {mountData
.PathBuffer
+ (mountData
.SubstituteNameOffset
/ sizeof (WCHAR
)),
877 mountData
.SubstituteNameLength
/ sizeof (WCHAR
)};
885 if (targetPath
.isNotEmpty())
887 const StringRef
prefix ("\\??\\");
889 if (targetPath
.startsWith (prefix
))
890 targetPath
= targetPath
.substring (prefix
.length());
899 if (! wantsAbsolutePath
)
900 return readWindowsLnkFile (shortcut
, false);
902 typedef DWORD (WINAPI
* GetFinalPathNameByHandleFunc
) (HANDLE
, LPTSTR
, DWORD
, DWORD
);
904 static GetFinalPathNameByHandleFunc getFinalPathNameByHandle
905 = (GetFinalPathNameByHandleFunc
) getUser32Function ("GetFinalPathNameByHandle");
907 if (getFinalPathNameByHandle
!= nullptr)
909 HANDLE h
= CreateFile (shortcut
.getFullPathName().toWideCharPointer(),
910 GENERIC_READ
, FILE_SHARE_READ
, nullptr,
911 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, nullptr);
913 if (h
!= INVALID_HANDLE_VALUE
)
915 if (DWORD requiredSize
= getFinalPathNameByHandle (h
, nullptr, 0, 0 /* FILE_NAME_NORMALIZED */))
917 HeapBlock
<WCHAR
> buffer (requiredSize
+ 2, true);
919 if (getFinalPathNameByHandle (h
, buffer
, requiredSize
, 0 /* FILE_NAME_NORMALIZED */) > 0)
923 const StringRef
prefix ("\\\\?\\");
924 const String
path (buffer
.get());
926 // It turns out that GetFinalPathNameByHandleW prepends \\?\ to the path.
927 // This is not a bug, it's feature. See MSDN for more information.
928 return path
.startsWith (prefix
) ? path
.substring (prefix
.length()) : path
;
937 // as last resort try the resolve method of the ShellLink
938 return readWindowsLnkFile (shortcut
, true);
941 String
File::getNativeLinkedTarget() const
943 return readWindowsShortcutOrLink (*this, false);
946 File
File::getLinkedTarget() const
948 auto target
= readWindowsShortcutOrLink (*this, true);
950 if (target
.isNotEmpty() && File::isAbsolutePath (target
))
951 return File (target
);
956 bool File::createShortcut (const String
& description
, const File
& linkFileToCreate
) const
958 linkFileToCreate
.deleteFile();
960 ComSmartPtr
<IShellLink
> shellLink
;
961 ComSmartPtr
<IPersistFile
> persistFile
;
963 ignoreUnused (CoInitialize (nullptr));
965 return SUCCEEDED (shellLink
.CoCreateInstance (CLSID_ShellLink
))
966 && SUCCEEDED (shellLink
->SetPath (getFullPathName().toWideCharPointer()))
967 && SUCCEEDED (shellLink
->SetDescription (description
.toWideCharPointer()))
968 && SUCCEEDED (shellLink
.QueryInterface (persistFile
))
969 && SUCCEEDED (persistFile
->Save (linkFileToCreate
.getFullPathName().toWideCharPointer(), TRUE
));
972 //==============================================================================
973 class DirectoryIterator::NativeIterator::Pimpl
976 Pimpl (const File
& directory
, const String
& wildCardIn
)
977 : directoryWithWildCard (directory
.getFullPathName().isNotEmpty() ? File::addTrailingSeparator (directory
.getFullPathName()) + wildCardIn
: String()),
978 handle (INVALID_HANDLE_VALUE
)
984 if (handle
!= INVALID_HANDLE_VALUE
)
988 bool next (String
& filenameFound
,
989 bool* const isDir
, bool* const isHidden
, int64
* const fileSize
,
990 Time
* const modTime
, Time
* const creationTime
, bool* const isReadOnly
)
992 using namespace WindowsFileHelpers
;
993 WIN32_FIND_DATA findData
;
995 if (handle
== INVALID_HANDLE_VALUE
)
997 handle
= FindFirstFile (directoryWithWildCard
.toWideCharPointer(), &findData
);
999 if (handle
== INVALID_HANDLE_VALUE
)
1004 if (FindNextFile (handle
, &findData
) == 0)
1008 filenameFound
= findData
.cFileName
;
1010 if (isDir
!= nullptr) *isDir
= ((findData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) != 0);
1011 if (isHidden
!= nullptr) *isHidden
= ((findData
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
) != 0);
1012 if (isReadOnly
!= nullptr) *isReadOnly
= ((findData
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
) != 0);
1013 if (fileSize
!= nullptr) *fileSize
= findData
.nFileSizeLow
+ (((int64
) findData
.nFileSizeHigh
) << 32);
1014 if (modTime
!= nullptr) *modTime
= Time (fileTimeToTime (&findData
.ftLastWriteTime
));
1015 if (creationTime
!= nullptr) *creationTime
= Time (fileTimeToTime (&findData
.ftCreationTime
));
1021 const String directoryWithWildCard
;
1024 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl
)
1027 DirectoryIterator::NativeIterator::NativeIterator (const File
& directory
, const String
& wildCardIn
)
1028 : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory
, wildCardIn
))
1032 DirectoryIterator::NativeIterator::~NativeIterator()
1036 bool DirectoryIterator::NativeIterator::next (String
& filenameFound
,
1037 bool* const isDir
, bool* const isHidden
, int64
* const fileSize
,
1038 Time
* const modTime
, Time
* const creationTime
, bool* const isReadOnly
)
1040 return pimpl
->next (filenameFound
, isDir
, isHidden
, fileSize
, modTime
, creationTime
, isReadOnly
);
1044 //==============================================================================
1045 bool JUCE_CALLTYPE
Process::openDocument (const String
& fileName
, const String
& parameters
)
1047 HINSTANCE hInstance
= ShellExecute (nullptr, nullptr, fileName
.toWideCharPointer(),
1048 parameters
.toWideCharPointer(), nullptr, SW_SHOWDEFAULT
);
1050 return hInstance
> (HINSTANCE
) 32;
1053 void File::revealToUser() const
1055 DynamicLibrary
dll ("Shell32.dll");
1056 JUCE_LOAD_WINAPI_FUNCTION (dll
, ILCreateFromPathW
, ilCreateFromPathW
, ITEMIDLIST
*, (LPCWSTR
))
1057 JUCE_LOAD_WINAPI_FUNCTION (dll
, ILFree
, ilFree
, void, (ITEMIDLIST
*))
1058 JUCE_LOAD_WINAPI_FUNCTION (dll
, SHOpenFolderAndSelectItems
, shOpenFolderAndSelectItems
, HRESULT
, (ITEMIDLIST
*, UINT
, void*, DWORD
))
1060 if (ilCreateFromPathW
!= nullptr && shOpenFolderAndSelectItems
!= nullptr && ilFree
!= nullptr)
1062 if (ITEMIDLIST
* const itemIDList
= ilCreateFromPathW (fullPath
.toWideCharPointer()))
1064 shOpenFolderAndSelectItems (itemIDList
, 0, nullptr, 0);
1065 ilFree (itemIDList
);
1070 //==============================================================================
1071 class NamedPipe::Pimpl
1074 Pimpl (const String
& pipeName
, const bool createPipe
, bool mustNotExist
)
1075 : filename ("\\\\.\\pipe\\" + File::createLegalFileName (pipeName
)),
1076 pipeH (INVALID_HANDLE_VALUE
),
1077 cancelEvent (CreateEvent (nullptr, TRUE
, FALSE
, nullptr)),
1078 connected (false), ownsPipe (createPipe
), shouldStop (false)
1082 pipeH
= CreateNamedPipe (filename
.toWideCharPointer(),
1083 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
, 0,
1084 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, nullptr);
1086 if (mustNotExist
&& GetLastError() == ERROR_ALREADY_EXISTS
)
1094 CloseHandle (cancelEvent
);
1097 bool connect (const int timeOutMs
)
1101 if (pipeH
!= INVALID_HANDLE_VALUE
)
1104 const Time
timeOutEnd (Time::getCurrentTime() + RelativeTime::milliseconds (timeOutMs
));
1109 const ScopedLock
sl (createFileLock
);
1111 if (pipeH
== INVALID_HANDLE_VALUE
)
1112 pipeH
= CreateFile (filename
.toWideCharPointer(),
1113 GENERIC_READ
| GENERIC_WRITE
, 0, nullptr,
1114 OPEN_EXISTING
, FILE_FLAG_OVERLAPPED
, nullptr);
1117 if (pipeH
!= INVALID_HANDLE_VALUE
)
1120 if (shouldStop
|| (timeOutMs
>= 0 && Time::getCurrentTime() > timeOutEnd
))
1129 OverlappedEvent over
;
1131 if (ConnectNamedPipe (pipeH
, &over
.over
) == 0)
1133 switch (GetLastError())
1135 case ERROR_PIPE_CONNECTED
: connected
= true; break;
1136 case ERROR_IO_PENDING
:
1137 case ERROR_PIPE_LISTENING
: connected
= waitForIO (over
, timeOutMs
); break;
1146 void disconnectPipe()
1148 if (ownsPipe
&& connected
)
1150 DisconnectNamedPipe (pipeH
);
1155 void closePipeHandle()
1157 if (pipeH
!= INVALID_HANDLE_VALUE
)
1160 CloseHandle (pipeH
);
1161 pipeH
= INVALID_HANDLE_VALUE
;
1165 int read (void* destBuffer
, const int maxBytesToRead
, const int timeOutMilliseconds
)
1167 while (connect (timeOutMilliseconds
))
1169 if (maxBytesToRead
<= 0)
1172 OverlappedEvent over
;
1173 unsigned long numRead
= 0;
1175 if (ReadFile (pipeH
, destBuffer
, (DWORD
) maxBytesToRead
, &numRead
, &over
.over
))
1176 return (int) numRead
;
1178 if (GetLastError() == ERROR_IO_PENDING
)
1180 if (! waitForIO (over
, timeOutMilliseconds
))
1183 if (GetOverlappedResult (pipeH
, &over
.over
, &numRead
, FALSE
))
1184 return (int) numRead
;
1187 const auto lastError
= GetLastError();
1189 if (ownsPipe
&& (lastError
== ERROR_BROKEN_PIPE
|| lastError
== ERROR_PIPE_NOT_CONNECTED
))
1198 int write (const void* sourceBuffer
, int numBytesToWrite
, int timeOutMilliseconds
)
1200 if (connect (timeOutMilliseconds
))
1202 if (numBytesToWrite
<= 0)
1205 OverlappedEvent over
;
1206 unsigned long numWritten
;
1208 if (WriteFile (pipeH
, sourceBuffer
, (DWORD
) numBytesToWrite
, &numWritten
, &over
.over
))
1209 return (int) numWritten
;
1211 if (GetLastError() == ERROR_IO_PENDING
)
1213 if (! waitForIO (over
, timeOutMilliseconds
))
1216 if (GetOverlappedResult (pipeH
, &over
.over
, &numWritten
, FALSE
))
1217 return (int) numWritten
;
1219 if (GetLastError() == ERROR_BROKEN_PIPE
&& ownsPipe
)
1227 const String filename
;
1228 HANDLE pipeH
, cancelEvent
;
1229 bool connected
, ownsPipe
;
1230 std::atomic
<bool> shouldStop
;
1231 CriticalSection createFileLock
;
1234 struct OverlappedEvent
1239 over
.hEvent
= CreateEvent (nullptr, TRUE
, FALSE
, nullptr);
1244 CloseHandle (over
.hEvent
);
1250 bool waitForIO (OverlappedEvent
& over
, int timeOutMilliseconds
)
1258 HANDLE handles
[] = { over
.over
.hEvent
, cancelEvent
};
1259 DWORD waitResult
= WaitForMultipleObjects (numElementsInArray (handles
), handles
, FALSE
,
1260 timeOutMilliseconds
>= 0 ? (DWORD
) timeOutMilliseconds
1263 if (waitResult
== WAIT_OBJECT_0
)
1270 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl
)
1273 void NamedPipe::close()
1276 ScopedReadLock
sl (lock
);
1278 if (pimpl
!= nullptr)
1280 pimpl
->shouldStop
= true;
1281 SetEvent (pimpl
->cancelEvent
);
1286 ScopedWriteLock
sl (lock
);
1291 bool NamedPipe::openInternal (const String
& pipeName
, const bool createPipe
, bool mustNotExist
)
1293 auto newPimpl
= std::make_unique
<Pimpl
> (pipeName
, createPipe
, mustNotExist
);
1297 if (newPimpl
->pipeH
== INVALID_HANDLE_VALUE
)
1300 else if (! newPimpl
->connect (200))
1305 pimpl
= std::move (newPimpl
);
1309 int NamedPipe::read (void* destBuffer
, int maxBytesToRead
, int timeOutMilliseconds
)
1311 ScopedReadLock
sl (lock
);
1312 return pimpl
!= nullptr ? pimpl
->read (destBuffer
, maxBytesToRead
, timeOutMilliseconds
) : -1;
1315 int NamedPipe::write (const void* sourceBuffer
, int numBytesToWrite
, int timeOutMilliseconds
)
1317 ScopedReadLock
sl (lock
);
1318 return pimpl
!= nullptr ? pimpl
->write (sourceBuffer
, numBytesToWrite
, timeOutMilliseconds
) : -1;