mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-30 02:50:05 +00:00
Files: Added support for creating and reading relative or special path symbolic link files
This commit is contained in:
parent
71f281257b
commit
06d20b20c0
6 changed files with 185 additions and 48 deletions
|
|
@ -941,7 +941,9 @@ File File::createTempFile (StringRef fileNameEnding)
|
|||
return tempFile;
|
||||
}
|
||||
|
||||
bool File::createSymbolicLink (const File& linkFileToCreate, bool overwriteExisting) const
|
||||
bool File::createSymbolicLink (const File& linkFileToCreate,
|
||||
const String& nativePathOfTarget,
|
||||
bool overwriteExisting)
|
||||
{
|
||||
if (linkFileToCreate.exists())
|
||||
{
|
||||
|
|
@ -959,7 +961,7 @@ bool File::createSymbolicLink (const File& linkFileToCreate, bool overwriteExist
|
|||
|
||||
#if JUCE_MAC || JUCE_LINUX
|
||||
// one common reason for getting an error here is that the file already exists
|
||||
if (symlink (fullPath.toRawUTF8(), linkFileToCreate.getFullPathName().toRawUTF8()) == -1)
|
||||
if (symlink (nativePathOfTarget.toRawUTF8(), linkFileToCreate.getFullPathName().toRawUTF8()) == -1)
|
||||
{
|
||||
jassertfalse;
|
||||
return false;
|
||||
|
|
@ -967,15 +969,32 @@ bool File::createSymbolicLink (const File& linkFileToCreate, bool overwriteExist
|
|||
|
||||
return true;
|
||||
#elif JUCE_MSVC
|
||||
File targetFile (linkFileToCreate.getSiblingFile (nativePathOfTarget));
|
||||
|
||||
return CreateSymbolicLink (linkFileToCreate.getFullPathName().toWideCharPointer(),
|
||||
fullPath.toWideCharPointer(),
|
||||
isDirectory() ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) != FALSE;
|
||||
nativePathOfTarget.toWideCharPointer(),
|
||||
targetFile.isDirectory() ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) != FALSE;
|
||||
#else
|
||||
jassertfalse; // symbolic links not supported on this platform!
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool File::createSymbolicLink (const File& linkFileToCreate, bool overwriteExisting) const
|
||||
{
|
||||
return createSymbolicLink (linkFileToCreate, getFullPathName(), overwriteExisting);
|
||||
}
|
||||
|
||||
#if ! JUCE_WINDOWS
|
||||
File File::getLinkedTarget() const
|
||||
{
|
||||
if (isSymbolicLink())
|
||||
return getSiblingFile (getNativeLinkedTarget());
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode, bool exclusive)
|
||||
: range (0, file.getSize())
|
||||
|
|
|
|||
|
|
@ -987,12 +987,27 @@ public:
|
|||
*/
|
||||
File getLinkedTarget() const;
|
||||
|
||||
/** Create a symbolic link to a native path and return a boolean to indicate success.
|
||||
|
||||
Use this method if you want to create a link to a relative path or a special native
|
||||
file path (such as a device file on Windows).
|
||||
*/
|
||||
static bool createSymbolicLink (const File& linkFileToCreate,
|
||||
const String& nativePathOfTarget,
|
||||
bool overwriteExisting);
|
||||
|
||||
/** This returns the native path that the symbolic link points to. The returned path
|
||||
is a native path of the current OS and can be a relative, absolute or special path. */
|
||||
String getNativeLinkedTarget() const;
|
||||
|
||||
#if JUCE_WINDOWS || DOXYGEN
|
||||
/** Windows ONLY - Creates a win32 .LNK shortcut file that links to this file. */
|
||||
bool createShortcut (const String& description, const File& linkFileToCreate) const;
|
||||
|
||||
/** Windows ONLY - Returns true if this is a win32 .LNK file. */
|
||||
bool isShortcut() const;
|
||||
#else
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@
|
|||
#include <shlobj.h>
|
||||
#include <shlwapi.h>
|
||||
#include <mmsystem.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#if JUCE_MINGW
|
||||
#include <basetyps.h>
|
||||
|
|
|
|||
|
|
@ -55,26 +55,16 @@ bool File::isHidden() const
|
|||
return getFileName().startsWithChar ('.');
|
||||
}
|
||||
|
||||
static String getLinkedFile (const String& file)
|
||||
{
|
||||
HeapBlock<char> buffer (8194);
|
||||
const int numBytes = (int) readlink (file.toRawUTF8(), buffer, 8192);
|
||||
return String::fromUTF8 (buffer, jmax (0, numBytes));
|
||||
}
|
||||
|
||||
bool File::isSymbolicLink() const
|
||||
{
|
||||
return getLinkedFile (getFullPathName()).isNotEmpty();
|
||||
return getNativeLinkedTarget().isNotEmpty();
|
||||
}
|
||||
|
||||
File File::getLinkedTarget() const
|
||||
String File::getNativeLinkedTarget() const
|
||||
{
|
||||
String f (getLinkedFile (getFullPathName()));
|
||||
|
||||
if (f.isNotEmpty())
|
||||
return getSiblingFile (f);
|
||||
|
||||
return *this;
|
||||
HeapBlock<char> buffer (8194);
|
||||
const int numBytes = (int) readlink (getFullPathName().toRawUTF8(), buffer, 8192);
|
||||
return String::fromUTF8 (buffer, jmax (0, numBytes));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -281,12 +281,12 @@ bool File::isSymbolicLink() const
|
|||
return getFileLink (fullPath) != nil;
|
||||
}
|
||||
|
||||
File File::getLinkedTarget() const
|
||||
String File::getNativeLinkedTarget() const
|
||||
{
|
||||
if (NSString* dest = getFileLink (fullPath))
|
||||
return getSiblingFile (nsStringToJuce (dest));
|
||||
return nsStringToJuce (dest);
|
||||
|
||||
return *this;
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -30,6 +30,36 @@ namespace juce
|
|||
//==============================================================================
|
||||
namespace WindowsFileHelpers
|
||||
{
|
||||
//==============================================================================
|
||||
#if JUCE_WINDOWS
|
||||
typedef struct _REPARSE_DATA_BUFFER {
|
||||
ULONG ReparseTag;
|
||||
USHORT ReparseDataLength;
|
||||
USHORT Reserved;
|
||||
union {
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
ULONG Flags;
|
||||
WCHAR PathBuffer[1];
|
||||
} SymbolicLinkReparseBuffer;
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
WCHAR PathBuffer[1];
|
||||
} MountPointReparseBuffer;
|
||||
struct {
|
||||
UCHAR DataBuffer[1];
|
||||
} GenericReparseBuffer;
|
||||
} DUMMYUNIONNAME;
|
||||
} *PREPARSE_DATA_BUFFER, REPARSE_DATA_BUFFER;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
DWORD getAtts (const String& path) noexcept
|
||||
{
|
||||
return GetFileAttributes (path.toWideCharPointer());
|
||||
|
|
@ -666,9 +696,104 @@ bool File::isShortcut() const
|
|||
return hasFileExtension (".lnk");
|
||||
}
|
||||
|
||||
File File::getLinkedTarget() const
|
||||
static String readWindowsLnkFile (File lnkFile, bool wantsAbsolutePath)
|
||||
{
|
||||
if (! lnkFile.exists())
|
||||
lnkFile = File (lnkFile.getFullPathName() + ".lnk");
|
||||
|
||||
if (lnkFile.exists())
|
||||
{
|
||||
ComSmartPtr<IShellLink> shellLink;
|
||||
ComSmartPtr<IPersistFile> persistFile;
|
||||
|
||||
if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink))
|
||||
&& SUCCEEDED (shellLink.QueryInterface (persistFile))
|
||||
&& SUCCEEDED (persistFile->Load (lnkFile.getFullPathName().toWideCharPointer(), STGM_READ))
|
||||
&& (! wantsAbsolutePath || SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI))))
|
||||
{
|
||||
WIN32_FIND_DATA winFindData;
|
||||
WCHAR resolvedPath [MAX_PATH];
|
||||
|
||||
DWORD flags = SLGP_UNCPRIORITY;
|
||||
|
||||
if (! wantsAbsolutePath)
|
||||
flags |= SLGP_RAWPATH;
|
||||
|
||||
if (SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, &winFindData, flags)))
|
||||
return resolvedPath;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static String readWindowsShortcutOrLink (const File& shortcut, bool wantsAbsolutePath)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
if (! wantsAbsolutePath)
|
||||
{
|
||||
HANDLE h = CreateFile (shortcut.getFullPathName().toWideCharPointer(),
|
||||
GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
0);
|
||||
|
||||
if (h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
HeapBlock<WindowsFileHelpers::REPARSE_DATA_BUFFER> reparseData;
|
||||
|
||||
reparseData.calloc (1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
||||
DWORD bytesReturned = 0;
|
||||
|
||||
bool success = DeviceIoControl (h, FSCTL_GET_REPARSE_POINT, nullptr, 0,
|
||||
reparseData.getData(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
|
||||
&bytesReturned, nullptr) != 0;
|
||||
CloseHandle (h);
|
||||
|
||||
if (success)
|
||||
{
|
||||
if (IsReparseTagMicrosoft (reparseData->ReparseTag))
|
||||
{
|
||||
String targetPath;
|
||||
|
||||
switch (reparseData->ReparseTag)
|
||||
{
|
||||
case IO_REPARSE_TAG_SYMLINK:
|
||||
{
|
||||
auto& symlinkData = reparseData->SymbolicLinkReparseBuffer;
|
||||
targetPath = {symlinkData.PathBuffer + (symlinkData.SubstituteNameOffset / sizeof (WCHAR)),
|
||||
symlinkData.SubstituteNameLength / sizeof (WCHAR)};
|
||||
}
|
||||
break;
|
||||
|
||||
case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
{
|
||||
auto& mountData = reparseData->MountPointReparseBuffer;
|
||||
targetPath = {mountData.PathBuffer + (mountData.SubstituteNameOffset / sizeof (WCHAR)),
|
||||
mountData.SubstituteNameLength / sizeof (WCHAR)};
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (targetPath.isNotEmpty())
|
||||
{
|
||||
const StringRef prefix ("\\??\\");
|
||||
|
||||
if (targetPath.startsWith (prefix))
|
||||
targetPath = targetPath.substring (prefix.length());
|
||||
|
||||
return targetPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! wantsAbsolutePath)
|
||||
return readWindowsLnkFile (shortcut, false);
|
||||
|
||||
typedef DWORD (WINAPI* GetFinalPathNameByHandleFunc) (HANDLE, LPTSTR, DWORD, DWORD);
|
||||
|
||||
static GetFinalPathNameByHandleFunc getFinalPathNameByHandle
|
||||
|
|
@ -676,7 +801,7 @@ File File::getLinkedTarget() const
|
|||
|
||||
if (getFinalPathNameByHandle != nullptr)
|
||||
{
|
||||
HANDLE h = CreateFile (getFullPathName().toWideCharPointer(),
|
||||
HANDLE h = CreateFile (shortcut.getFullPathName().toWideCharPointer(),
|
||||
GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
|
||||
|
|
@ -695,8 +820,7 @@ File File::getLinkedTarget() const
|
|||
|
||||
// It turns out that GetFinalPathNameByHandleW prepends \\?\ to the path.
|
||||
// This is not a bug, it's feature. See MSDN for more information.
|
||||
return File (path.startsWith (prefix) ? path.substring (prefix.length())
|
||||
: path);
|
||||
return path.startsWith (prefix) ? path.substring (prefix.length()) : path;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -705,30 +829,18 @@ File File::getLinkedTarget() const
|
|||
}
|
||||
#endif
|
||||
|
||||
File result (*this);
|
||||
String p (getFullPathName());
|
||||
// as last resort try the resolve method of the ShellLink
|
||||
return readWindowsLnkFile (shortcut, true);
|
||||
}
|
||||
|
||||
if (! exists())
|
||||
p += ".lnk";
|
||||
else if (! hasFileExtension (".lnk"))
|
||||
return result;
|
||||
String File::getNativeLinkedTarget() const
|
||||
{
|
||||
return readWindowsShortcutOrLink (*this, false);
|
||||
}
|
||||
|
||||
ComSmartPtr<IShellLink> shellLink;
|
||||
ComSmartPtr<IPersistFile> persistFile;
|
||||
|
||||
if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink))
|
||||
&& SUCCEEDED (shellLink.QueryInterface (persistFile))
|
||||
&& SUCCEEDED (persistFile->Load (p.toWideCharPointer(), 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;
|
||||
File File::getLinkedTarget() const
|
||||
{
|
||||
return readWindowsShortcutOrLink (*this, true);
|
||||
}
|
||||
|
||||
bool File::createShortcut (const String& description, const File& linkFileToCreate) const
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue