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

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.

You could P/Invoke into this function:

It would probably be the easiest way to do it.

enum MoveExFlags
{
    MOVEFILE_REPLACE_EXISTING = 0x1
    MOVEFILE_COPY_ALLOWED = 0x2
    MOVEFILE_DELAY_UNTIL_REBOOT = 0x4
    MOVEFILE_WRITE_THROUGH = 0x8
    MOVEFILE_CREATE_HARDLINK = 0x10
    MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20
}

[DllImport("api-ms-win-core-file-l2-1-1.dll")]
static extern bool MoveFileExW([MarshalAs(UnmanagedType.LPWStr)] string existingFileName,[MarshalAs(UnmanagedType.LPWStr)] string newFileName, MoveExFlags flags);
1 Like

Wow, it worked, thanks! I wonder what other API’s we can tap into with this technique?

A lot. You can call any win32 API that’s compatible with Windows Store (see the page I linked):

1 Like

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.

Yeah.

        enum GenericAccessRights : uint
        {
            GENERIC_WRITE = 0x40000000,
            GENERIC_READ  = 0x80000000,
        }

        enum CreationDisposition
        {
            CREATE_NEW = 1,
            CREATE_ALWAYS = 2,
            OPEN_EXISTING = 3,
            OPEN_ALWAYS = 4,
            TRUNCATE_EXISTING = 5,
        }

        [DllImport("api-ms-win-core-file-l1-2-1.dll", SetLastError = true)]
        static extern IntPtr CreateFile2(
            [MarshalAs(UnmanagedType.LPWStr)]string fileName,
            GenericAccessRights accessRights,
            FileShare shareMode,
            CreationDisposition creationDisposition,
            IntPtr optExParams);

        [DllImport("api-ms-win-core-file-l1-2-1.dll", SetLastError = true)]
        static extern bool ReadFile(
            IntPtr fileHandle,
            [Out][MarshalAs(UnmanagedType.LPArray)] byte[] buffer,
            uint numberOfBytesToRead,
            out uint numberOfBytesRead,
            IntPtr pOverlapped);

        [DllImport("api-ms-win-core-file-l1-2-1.dll", SetLastError = true)]
        static extern bool GetFileSizeEx(IntPtr fileHandle, out long fileSize);

        [DllImport("api-ms-win-core-handle-l1-1-0.dll", SetLastError = true)]
        static extern bool CloseHandle(IntPtr handle);

        static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

        struct FileHandleHolder : IDisposable
        {
            public FileHandleHolder(IntPtr handle)
            {
                _handle = handle;
            }

            public void Dispose()
            {
                if (_handle != INVALID_HANDLE_VALUE)
                {
                    CloseHandle(_handle);
                    _handle = INVALID_HANDLE_VALUE;
                }
            }

            public static implicit operator IntPtr(FileHandleHolder _this)
            {
                return _this._handle;
            }

            private IntPtr _handle;
        }

        public static string ReadAllText(string path, Encoding encoding)
        {
            var rawFileHandle = CreateFile2(path, GenericAccessRights.GENERIC_READ, FileShare.Read, CreationDisposition.OPEN_EXISTING, IntPtr.Zero);
            if (rawFileHandle == INVALID_HANDLE_VALUE)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            using (var fileHandle = new FileHandleHolder(rawFileHandle))
            {
                long fileSize;
                if (!GetFileSizeEx(fileHandle, out fileSize))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                if (fileSize > int.MaxValue)
                    throw new OverflowException("Can't read contents of files that are larger than 2 GB all at once.");

                var bytes = new byte[(int)fileSize];
                uint bytesRead;

                if (!ReadFile(fileHandle, bytes, (uint)fileSize, out bytesRead, IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                if (bytesRead != fileSize)
                    throw new IOException("For some reason we read less bytes than we expected to read.");

                return encoding.GetString(bytes);
            }
        }

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.

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?

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.

I am targeting Windows 8.1, could that have something to do with the exception not being there?

Yup, seems like it doesn’t exist when targeting Windows 8.1. As I said, just change it to some other exception type.

1 Like