Search Unity

'Directory' does not contain a definition for 'Move'

Discussion in 'Windows' started by User340, Apr 15, 2017.

  1. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    On Windows 8.1 WSA, my Directory.Move() code does not work. Is there an alternative to Move? I check the UnityEngine Windows Directory class but it didn't have a Move method.
     
  2. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    You could P/Invoke into this function:

    https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx

    It would probably be the easiest way to do it.

    Code (csharp):
    1. enum MoveExFlags
    2. {
    3.     MOVEFILE_REPLACE_EXISTING = 0x1
    4.     MOVEFILE_COPY_ALLOWED = 0x2
    5.     MOVEFILE_DELAY_UNTIL_REBOOT = 0x4
    6.     MOVEFILE_WRITE_THROUGH = 0x8
    7.     MOVEFILE_CREATE_HARDLINK = 0x10
    8.     MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20
    9. }
    10.  
    11. [DllImport("api-ms-win-core-file-l2-1-1.dll")]
    12. static extern bool MoveFileExW([MarshalAs(UnmanagedType.LPWStr)] string existingFileName,[MarshalAs(UnmanagedType.LPWStr)] string newFileName, MoveExFlags flags);
     
    User340 likes this.
  3. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Wow, it worked, thanks! I wonder what other API's we can tap into with this technique?
     
  4. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    A lot. You can call any win32 API that's compatible with Windows Store (see the page I linked):

    upload_2017-4-15_15-51-38.png
     
    User340 likes this.
  5. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Cool. Do you know if that's possible for the File.ReadAllText() method? I'm trying to read files from the Desktop, and File.ReadAllText() is generating IOExceptions. It's strange because File.ReadAllText() works just fine when accessing files in the app's specific folder. Also, I need this not to be asynchronous, FileIO only seems to have asynchronous methods.
     
  6. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Yeah.
    Code (csharp):
    1.         enum GenericAccessRights : uint
    2.         {
    3.             GENERIC_WRITE = 0x40000000,
    4.             GENERIC_READ  = 0x80000000,
    5.         }
    6.  
    7.         enum CreationDisposition
    8.         {
    9.             CREATE_NEW = 1,
    10.             CREATE_ALWAYS = 2,
    11.             OPEN_EXISTING = 3,
    12.             OPEN_ALWAYS = 4,
    13.             TRUNCATE_EXISTING = 5,
    14.         }
    15.  
    16.         [DllImport("api-ms-win-core-file-l1-2-1.dll", SetLastError = true)]
    17.         static extern IntPtr CreateFile2(
    18.             [MarshalAs(UnmanagedType.LPWStr)]string fileName,
    19.             GenericAccessRights accessRights,
    20.             FileShare shareMode,
    21.             CreationDisposition creationDisposition,
    22.             IntPtr optExParams);
    23.  
    24.         [DllImport("api-ms-win-core-file-l1-2-1.dll", SetLastError = true)]
    25.         static extern bool ReadFile(
    26.             IntPtr fileHandle,
    27.             [Out][MarshalAs(UnmanagedType.LPArray)] byte[] buffer,
    28.             uint numberOfBytesToRead,
    29.             out uint numberOfBytesRead,
    30.             IntPtr pOverlapped);
    31.  
    32.         [DllImport("api-ms-win-core-file-l1-2-1.dll", SetLastError = true)]
    33.         static extern bool GetFileSizeEx(IntPtr fileHandle, out long fileSize);
    34.  
    35.         [DllImport("api-ms-win-core-handle-l1-1-0.dll", SetLastError = true)]
    36.         static extern bool CloseHandle(IntPtr handle);
    37.  
    38.         static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
    39.  
    40.         struct FileHandleHolder : IDisposable
    41.         {
    42.             public FileHandleHolder(IntPtr handle)
    43.             {
    44.                 _handle = handle;
    45.             }
    46.  
    47.             public void Dispose()
    48.             {
    49.                 if (_handle != INVALID_HANDLE_VALUE)
    50.                 {
    51.                     CloseHandle(_handle);
    52.                     _handle = INVALID_HANDLE_VALUE;
    53.                 }
    54.             }
    55.  
    56.             public static implicit operator IntPtr(FileHandleHolder _this)
    57.             {
    58.                 return _this._handle;
    59.             }
    60.  
    61.             private IntPtr _handle;
    62.         }
    63.  
    64.         public static string ReadAllText(string path, Encoding encoding)
    65.         {
    66.             var rawFileHandle = CreateFile2(path, GenericAccessRights.GENERIC_READ, FileShare.Read, CreationDisposition.OPEN_EXISTING, IntPtr.Zero);
    67.             if (rawFileHandle == INVALID_HANDLE_VALUE)
    68.                 throw new Win32Exception(Marshal.GetLastWin32Error());
    69.  
    70.             using (var fileHandle = new FileHandleHolder(rawFileHandle))
    71.             {
    72.                 long fileSize;
    73.                 if (!GetFileSizeEx(fileHandle, out fileSize))
    74.                     throw new Win32Exception(Marshal.GetLastWin32Error());
    75.  
    76.                 if (fileSize > int.MaxValue)
    77.                     throw new OverflowException("Can't read contents of files that are larger than 2 GB all at once.");
    78.  
    79.                 var bytes = new byte[(int)fileSize];
    80.                 uint bytesRead;
    81.  
    82.                 if (!ReadFile(fileHandle, bytes, (uint)fileSize, out bytesRead, IntPtr.Zero))
    83.                     throw new Win32Exception(Marshal.GetLastWin32Error());
    84.  
    85.                 if (bytesRead != fileSize)
    86.                     throw new IOException("For some reason we read less bytes than we expected to read.");
    87.  
    88.                 return encoding.GetString(bytes);
    89.             }
    90.         }
    But keep in mind that it will still not let you freely access files on the file system as windows store applications are sandboxed. If you need to access files outside of the sandbox, you can use file pickers (to have the user explicitly select the file you want) and then convert StorageFile to a handle (which then can be fed to functions like GetFileSizeEx or ReadFile) through IStorageFolderHandleAccess::Create method.
     
  7. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Right, specifically, I am using a File Picker, then adding the selected file to the StorageApplicationPermissions.FutureAccessList. I can't get your code to compile, can't find type Win32Exception even after I add using System.ComponentModel, it doesn't help. But I'm trying to understand why File.ReadAllText() works perfectly when referencing files in the app's data folder. Yet throws an exception when referencing files selected by the File Picker. Any insight as to why this might be?
     
  8. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Weird, no idea why that code doesn't compile. You could change the exception type to something else. Worked fine for me when targeting windows 10, though.

    You can't access files through their path when they're picked through a file picker. That's just how Microsoft designed the sandbox... you need to access them through brokered APIs - that means StorageFile and friends.
     
  9. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    I am targeting Windows 8.1, could that have something to do with the exception not being there?
     
  10. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Yup, seems like it doesn't exist when targeting Windows 8.1. As I said, just change it to some other exception type.
     
    User340 likes this.