VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_core / native / juce_win32_Files.cpp
blob7d93f26afbf7f1f05e271a8de97b3bc39cd7cf7d
1 /*
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
8 licensing.
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
18 DISCLAIMED.
20 ==============================================================================
23 namespace juce
26 #ifndef INVALID_FILE_ATTRIBUTES
27 #define INVALID_FILE_ATTRIBUTES ((DWORD) -1)
28 #endif
30 //==============================================================================
31 namespace WindowsFileHelpers
33 //==============================================================================
34 #if JUCE_WINDOWS
35 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnested-anon-types")
37 typedef struct _REPARSE_DATA_BUFFER {
38 ULONG ReparseTag;
39 USHORT ReparseDataLength;
40 USHORT Reserved;
41 union {
42 struct {
43 USHORT SubstituteNameOffset;
44 USHORT SubstituteNameLength;
45 USHORT PrintNameOffset;
46 USHORT PrintNameLength;
47 ULONG Flags;
48 WCHAR PathBuffer[1];
49 } SymbolicLinkReparseBuffer;
50 struct {
51 USHORT SubstituteNameOffset;
52 USHORT SubstituteNameLength;
53 USHORT PrintNameOffset;
54 USHORT PrintNameLength;
55 WCHAR PathBuffer[1];
56 } MountPointReparseBuffer;
57 struct {
58 UCHAR DataBuffer[1];
59 } GenericReparseBuffer;
60 } DUMMYUNIONNAME;
61 } *PREPARSE_DATA_BUFFER, REPARSE_DATA_BUFFER;
63 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
64 #endif
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)
77 return false;
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
95 if (time <= 0)
96 return nullptr;
98 reinterpret_cast<ULARGE_INTEGER*> (ft)->QuadPart = (ULONGLONG) (time * 10000 + 116444736000000000LL);
99 return ft;
102 static String getDriveFromPath (String path)
104 if (path.isNotEmpty() && path[1] == ':' && path[2] == 0)
105 path << '\\';
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);
115 return path;
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;
126 return 0;
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));
141 return {};
144 static File getModuleFileName (HINSTANCE moduleHandle)
146 WCHAR dest[MAX_PATH + 256];
147 dest[0] = 0;
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();
170 if (path.isEmpty())
171 return false;
173 struct PsecurityDescriptorGuard
175 ~PsecurityDescriptorGuard() { if (psecurityDescriptor != nullptr) LocalFree (psecurityDescriptor); }
176 PSECURITY_DESCRIPTOR psecurityDescriptor = nullptr;
179 PsecurityDescriptorGuard descriptorGuard;
181 if (GetNamedSecurityInfo (path.toWideCharPointer(),
182 SE_FILE_OBJECT,
183 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
184 nullptr,
185 nullptr,
186 nullptr,
187 nullptr,
188 &descriptorGuard.psecurityDescriptor) != ERROR_SUCCESS)
190 return false;
193 struct HandleGuard
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))
205 return false;
208 HandleGuard duplicatedTokenGuard;
210 if (! DuplicateToken (primaryTokenGuard.handle,
211 SecurityImpersonation,
212 &duplicatedTokenGuard.handle))
214 return false;
217 GENERIC_MAPPING mapping { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS };
219 MapGenericMask (&accessType, &mapping);
220 DWORD allowed = 0;
221 BOOL granted = false;
222 PRIVILEGE_SET set;
223 DWORD setSize = sizeof (set);
225 if (! AccessCheck (descriptorGuard.psecurityDescriptor,
226 duplicatedTokenGuard.handle,
227 accessType,
228 &mapping,
229 &set,
230 &setSize,
231 &allowed,
232 &granted))
234 return false;
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
251 #endif
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
279 if (exists())
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();
292 return false;
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?
310 return false;
313 bool File::isHidden() const
315 return (WindowsFileHelpers::getAtts (fullPath) & FILE_ATTRIBUTE_HIDDEN) != 0;
318 //==============================================================================
319 bool File::deleteFile() const
321 if (! exists())
322 return true;
324 return isDirectory() ? RemoveDirectory (fullPath.toWideCharPointer()) != 0
325 : DeleteFile (fullPath.toWideCharPointer()) != 0;
328 bool File::moveToTrash() const
330 if (! exists())
331 return true;
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)
377 LARGE_INTEGER li;
378 li.QuadPart = pos;
379 li.LowPart = SetFilePointer ((HANDLE) handle, (LONG) li.LowPart,
380 &li.HighPart, FILE_BEGIN); // (returns -1 if it fails)
381 return li.QuadPart;
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;
392 else
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)
405 DWORD actualNum = 0;
407 if (! ReadFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, nullptr))
408 status = WindowsFileHelpers::getResultForLastError();
410 return (size_t) actualNum;
413 return 0;
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)
425 LARGE_INTEGER li;
426 li.QuadPart = 0;
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;
433 return;
437 status = WindowsFileHelpers::getResultForLastError();
440 void FileOutputStream::closeHandle()
442 CloseHandle ((HANDLE) fileHandle);
445 ssize_t FileOutputStream::writeInternal (const void* bufferToWrite, size_t numBytes)
447 DWORD actualNum = 0;
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)
466 return status;
468 flush();
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;
539 return 0;
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);
553 else
555 creationTime = accessTime = modificationTime = 0;
559 bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 creationTime) const
561 using namespace WindowsFileHelpers;
563 bool ok = false;
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)
570 FILETIME m, a, c;
572 ok = SetFileTime (h,
573 timeToFileTime (creationTime, &c),
574 timeToFileTime (accessTime, &a),
575 timeToFileTime (modificationTime, &m)) != 0;
577 CloseHandle (h);
580 return ok;
583 //==============================================================================
584 void File::findFileSystemRoots (Array<File>& destArray)
586 TCHAR buffer[2048] = {};
587 GetLogicalDriveStrings (2048, buffer);
589 const TCHAR* n = buffer;
590 StringArray roots;
592 while (*n != 0)
594 roots.add (String (n));
596 while (*n++ != 0)
600 roots.sort (true);
602 for (int i = 0; i < roots.size(); ++i)
603 destArray.add (roots[i]);
606 //==============================================================================
607 String File::getVolumeLabel() const
609 TCHAR dest[64];
611 if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest,
612 (DWORD) numElementsInArray (dest), nullptr, nullptr, nullptr, nullptr, 0))
613 dest[0] = 0;
615 return dest;
618 int File::getVolumeSerialNumber() const
620 TCHAR dest[64];
621 DWORD serialNum;
623 if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest,
624 (DWORD) numElementsInArray (dest), &serialNum, nullptr, nullptr, nullptr, 0))
625 return 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
642 uint64 result = 0;
644 String path = getFullPathName();
646 if (isRoot())
647 path += "\\";
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;
656 zerostruct (info);
658 if (GetFileInformationByHandle (h, &info))
659 result = (((uint64) info.nFileIndexHigh) << 32) | info.nFileIndexLow;
661 CloseHandle (h);
664 return result;
667 //==============================================================================
668 bool File::isOnCDRomDrive() const
670 return WindowsFileHelpers::getWindowsDriveType (getFullPathName()) == DRIVE_CDROM;
673 bool File::isOnHardDisk() const
675 if (fullPath.isEmpty())
676 return false;
678 auto n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
680 return n != DRIVE_REMOVABLE
681 && n != DRIVE_CDROM
682 && n != DRIVE_REMOTE
683 && n != DRIVE_NO_ROOT_DIR;
686 bool File::isOnRemovableDrive() const
688 if (fullPath.isEmpty())
689 return false;
691 auto n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
693 return n == DRIVE_CDROM
694 || n == DRIVE_REMOTE
695 || n == DRIVE_REMOVABLE
696 || n == DRIVE_RAMDISK;
699 //==============================================================================
700 File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type)
702 int csidlType = 0;
704 switch (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;
719 case tempDirectory:
721 WCHAR dest[2048];
722 dest[0] = 0;
723 GetTempPath ((DWORD) numElementsInArray (dest), dest);
724 return File (String (dest));
727 case windowsSystemDirectory:
729 WCHAR dest[2048];
730 dest[0] = 0;
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);
743 default:
744 jassertfalse; // unknown type?
745 return {};
748 return WindowsFileHelpers::getSpecialFolderPath (csidlType);
751 //==============================================================================
752 File File::getCurrentWorkingDirectory()
754 WCHAR dest[MAX_PATH + 256];
755 dest[0] = 0;
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
768 String result;
770 DWORD handle = 0;
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;
778 UINT len = 0;
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);
789 return result;
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)))
828 return resolvedPath;
832 return {};
835 static String readWindowsShortcutOrLink (const File& shortcut, bool wantsAbsolutePath)
837 #if JUCE_WINDOWS
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,
843 nullptr);
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;
855 CloseHandle (h);
857 if (success)
859 if (IsReparseTagMicrosoft (reparseData->ReparseTag))
861 String targetPath;
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)};
871 break;
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)};
879 break;
881 default:
882 break;
885 if (targetPath.isNotEmpty())
887 const StringRef prefix ("\\??\\");
889 if (targetPath.startsWith (prefix))
890 targetPath = targetPath.substring (prefix.length());
892 return targetPath;
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)
921 CloseHandle (h);
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;
932 CloseHandle (h);
935 #endif
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);
953 return *this;
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
975 public:
976 Pimpl (const File& directory, const String& wildCardIn)
977 : directoryWithWildCard (directory.getFullPathName().isNotEmpty() ? File::addTrailingSeparator (directory.getFullPathName()) + wildCardIn : String()),
978 handle (INVALID_HANDLE_VALUE)
982 ~Pimpl()
984 if (handle != INVALID_HANDLE_VALUE)
985 FindClose (handle);
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)
1000 return false;
1002 else
1004 if (FindNextFile (handle, &findData) == 0)
1005 return false;
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));
1017 return true;
1020 private:
1021 const String directoryWithWildCard;
1022 HANDLE handle;
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
1073 public:
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)
1080 if (createPipe)
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)
1087 closePipeHandle();
1091 ~Pimpl()
1093 closePipeHandle();
1094 CloseHandle (cancelEvent);
1097 bool connect (const int timeOutMs)
1099 if (! ownsPipe)
1101 if (pipeH != INVALID_HANDLE_VALUE)
1102 return true;
1104 const Time timeOutEnd (Time::getCurrentTime() + RelativeTime::milliseconds (timeOutMs));
1106 for (;;)
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)
1118 return true;
1120 if (shouldStop || (timeOutMs >= 0 && Time::getCurrentTime() > timeOutEnd))
1121 return false;
1123 Thread::sleep (1);
1127 if (! connected)
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;
1138 default: break;
1143 return connected;
1146 void disconnectPipe()
1148 if (ownsPipe && connected)
1150 DisconnectNamedPipe (pipeH);
1151 connected = false;
1155 void closePipeHandle()
1157 if (pipeH != INVALID_HANDLE_VALUE)
1159 disconnectPipe();
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)
1170 return 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))
1181 return -1;
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))
1190 disconnectPipe();
1191 else
1192 break;
1195 return -1;
1198 int write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds)
1200 if (connect (timeOutMilliseconds))
1202 if (numBytesToWrite <= 0)
1203 return 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))
1214 return -1;
1216 if (GetOverlappedResult (pipeH, &over.over, &numWritten, FALSE))
1217 return (int) numWritten;
1219 if (GetLastError() == ERROR_BROKEN_PIPE && ownsPipe)
1220 disconnectPipe();
1224 return -1;
1227 const String filename;
1228 HANDLE pipeH, cancelEvent;
1229 bool connected, ownsPipe;
1230 std::atomic<bool> shouldStop;
1231 CriticalSection createFileLock;
1233 private:
1234 struct OverlappedEvent
1236 OverlappedEvent()
1238 zerostruct (over);
1239 over.hEvent = CreateEvent (nullptr, TRUE, FALSE, nullptr);
1242 ~OverlappedEvent()
1244 CloseHandle (over.hEvent);
1247 OVERLAPPED over;
1250 bool waitForIO (OverlappedEvent& over, int timeOutMilliseconds)
1252 if (shouldStop)
1254 CancelIo (pipeH);
1255 return false;
1258 HANDLE handles[] = { over.over.hEvent, cancelEvent };
1259 DWORD waitResult = WaitForMultipleObjects (numElementsInArray (handles), handles, FALSE,
1260 timeOutMilliseconds >= 0 ? (DWORD) timeOutMilliseconds
1261 : INFINITE);
1263 if (waitResult == WAIT_OBJECT_0)
1264 return true;
1266 CancelIo (pipeH);
1267 return false;
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);
1287 pimpl.reset();
1291 bool NamedPipe::openInternal (const String& pipeName, const bool createPipe, bool mustNotExist)
1293 auto newPimpl = std::make_unique<Pimpl> (pipeName, createPipe, mustNotExist);
1295 if (createPipe)
1297 if (newPimpl->pipeH == INVALID_HANDLE_VALUE)
1298 return false;
1300 else if (! newPimpl->connect (200))
1302 return false;
1305 pimpl = std::move (newPimpl);
1306 return true;
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;
1321 } // namespace juce