'When trying to get notifications about file system changes on Windows, getting "Attempted to read or write protected memory" error

I have an application that monitors file and folder changes. I get an error when a new file is created, deleted or modified. I have the following code and have no idea why the Marshal.PtrToStringAuto throw "Attempted to read or write protected memory. This is often an indication that other memory is corrupt.". Why am I getting this error when I try to get the filename?

Can someone please help me on what to look for to resolve this error...

error image

[DllImport("kernel32.dll", EntryPoint = "FindFirstChangeNotification")]
    static extern System.IntPtr FindFirstChangeNotification(string lpPathName, bool bWatchSubtree, uint dwNotifyFilter);

    [DllImport("kernel32.dll", EntryPoint = "FindNextChangeNotification")]
    static extern bool FindNextChangeNotification(IntPtr hChangedHandle);

    [DllImport("kernel32.dll", EntryPoint = "FindCloseChangeNotification")]
    static extern bool FindCloseChangeNotification(IntPtr hChangedHandle);

    [DllImport("kernel32.dll", EntryPoint = "WaitForSingleObject")]
    static extern uint WaitForSingleObject(IntPtr handle, uint dwMilliseconds);

    [DllImport("kernel32.dll", EntryPoint = "ReadDirectoryChangesW")]
    static extern bool ReadDirectoryChangesW(IntPtr hDirectory, IntPtr lpBuffer, uint nBufferLength, bool bWatchSubtree, uint dwNotifyFilter, out uint lpBytesReturned, uint lpOverlapped, uint lpCompletionRoutine);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    public static extern int ReadDirectoryChangesW(uint hDirectory, out FILE_NOTIFY_INFORMATION finfo, uint nBufferLength,uint bWatchSubtree, uint dwNotifyFilter, out uint lpbytesReturned,uint PassZero1, uint PassZero2);

    [DllImport("kernel32.dll", EntryPoint = "CreateFile")]
    public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr SecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

    private const int MaxChanges = 4096;
    public async Task ReadChangesAsyncNew()
    {
        var token = _cancellationTokenSource.Token;
        await Task.Run(() =>
        {
            unsafe
            {
                var directoryHandle = CreateFile(_path, 0x80000000, 0x00000007, IntPtr.Zero, 3, 0x02000000, IntPtr.Zero);
                var fileCreatedDeletedOrUpdated = FileSystemNotifications.FileNameChanged | FileSystemNotifications.FileModified;
                var waitable = FindFirstChangeNotification(_path, true, (uint)fileCreatedDeletedOrUpdated);
                var notifySize = Marshal.SizeOf(typeof(FileNotifyInformation));

                do
                {
                    var changes = new FileNotifyInformation[MaxChanges];
                    var pinnedArray = GCHandle.Alloc(changes, GCHandleType.Pinned);
                    var buffer = pinnedArray.AddrOfPinnedObject();
                    uint bytesReturned = 0;

                    if (!ReadDirectoryChangesW(directoryHandle, buffer, (uint)(notifySize * MaxChanges), true, (uint)fileCreatedDeletedOrUpdated, out bytesReturned, 0, 0))
                        throw Win32Error.GetLastError().GetException();

                    var result = new List<FileEvent>();

                    for (var i = 0; i < bytesReturned / notifySize; i += 1)
                    {
                        var change = Marshal.PtrToStructure<FileNotifyInformation>(new IntPtr(buffer.ToInt64() + i * notifySize));

                        if (((int)change.Action) == (int)FileActions.FileAdded)
                        {
                            result.Add(new FileEvent(Marshal.PtrToStringAuto(change.FileName, change.FileNameLength), FileEventType.FileAdded));
                        }
                        else if (((int)change.Action) == (int)FileActions.FileRemoved)
                        {
                            result.Add(new FileEvent(Marshal.PtrToStringAuto(change.FileName, change.FileNameLength), FileEventType.FileDeleted));
                        }
                        else if (((int)change.Action) == (int)FileActions.FileModified)
                        {
                            result.Add(new FileEvent(Marshal.PtrToStringAuto(change.FileName, change.FileNameLength), FileEventType.FileChanged));
                        }                           
                        else if (((int)change.Action) == (int)FileActions.FileRenamedNew)
                        {
                            result.Add(new FileEvent(Marshal.PtrToStringAuto(change.FileName, change.FileNameLength), FileEventType.FileRenamed));
                        }
                    }

                    pinnedArray.Free();
                } while (FindNextChangeNotification(waitable));

                FindCloseChangeNotification(waitable);
            }
        }, token);
    }


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source