Exemplo n.º 1
0
        private static unsafe void DoQueuedCompletionWork(
            ISwatcherConfig config, IWindowsFacade facade, SafeLocalMemHandle completionPortHandle,
            ISubject <SwatcherCreatedEventArgs> createdSubject, ISubject <SwatcherEventArgs> deletedSubject,
            ISubject <SwatcherEventArgs> changedSubject, ISubject <RenamedInfo> renamedSubject)
        {
            //this is a blocking call...
            var completionStatus = WaitForCompletionStatus(facade, completionPortHandle);

            if (completionStatus == null)
            {
                return;
            }

            var overlapped    = Overlapped.Unpack(completionStatus.Value.OverlappedPointer);
            var asyncResult   = (SwatcherAsyncResult)overlapped.AsyncResult;
            var currentOffset = 0;
            // ReSharper disable once TooWideLocalVariableScope
            var nextOffset = 0;

            try
            {
                do
                {
                    nextOffset = DeserializeMessage(
                        config, asyncResult, ref currentOffset, createdSubject,
                        deletedSubject, changedSubject, renamedSubject);
                } while (nextOffset != 0);
            }
            finally
            {
                //*always* free up the overlapped. otherwise, we will have a ~65kb memory leak after each callback.
                Overlapped.Free(completionStatus.Value.OverlappedPointer);
            }
        }
Exemplo n.º 2
0
        private IObservable <SwatcherRenamedEventArgs> CreatePublicRenamedStream(ISwatcherConfig config)
        {
            return(_renamedSubject.AsObservable()
                   .CombineWithPrevious((previous, current) =>
            {
                if ((previous?.Event == FileSystemItemEvent.RenamedOldName) &&
                    (current.Event == FileSystemItemEvent.RenamedNewName))
                {
                    return new SwatcherRenamedEventArgs(config, current.Name, previous.Name);
                }

                return null;
            })
                   .WhereNotNull()
                   .Do(x =>
            {
                if (!Config.LoggingEnabled)
                {
                    return;
                }
                Logger.Debug(
                    $"[Renamed] OldName: {x.OldName}, Name: {x.Name}, OccurredAt:{x.TimeOccurred.ToLocalTime()}");
            })
                   .Publish()
                   .RefCount());
        }
Exemplo n.º 3
0
        private static unsafe int DeserializeMessage(
            ISwatcherConfig config, SwatcherAsyncResult asyncResult, ref int currentOffset,
            ISubject <SwatcherCreatedEventArgs> createdSubject, ISubject <SwatcherEventArgs> deletedSubject,
            ISubject <SwatcherEventArgs> changedSubject, ISubject <RenamedInfo> renamedSubject)
        {
            int    nextOffset;
            string name;
            int    @event;

            fixed(byte *bufferPointer = asyncResult.Buffer)
            {
                //buffer pointer was the address where we started.
                //add current offset to get the address of our the next offset.
                //4 bytes in an integer ;-).
                nextOffset = *(int *)(bufferPointer + currentOffset);
                // the next integer contains the action.
                @event = *(int *)(bufferPointer + currentOffset + 4);
                //next int pointer has the address that contains the length of the name
                //of the item that was created,changed,renamed or deleted.
                var nameLength = *(int *)(bufferPointer + currentOffset + 8);

                //finally, retrieve the string via char* using the name length from above.
                //we divide the length by 2 because a char is 2 bytes.
                name = new string((char *)(bufferPointer + currentOffset + 12), 0, nameLength / 2);
            }

            switch ((FileSystemItemEvent)@event)
            {
            case FileSystemItemEvent.Created:
                OnItemCreatedInternal(config, createdSubject, name);
                break;

            case FileSystemItemEvent.Deleted:
                OnItemDeletedInternal(config, deletedSubject, name);
                break;

            case FileSystemItemEvent.Changed:
                OnItemChangedInternal(config, changedSubject, name);
                break;

            case FileSystemItemEvent.RenamedOldName:
                OnItemRenamedInternal(config, renamedSubject, name, FileSystemItemEvent.RenamedOldName);
                break;

            case FileSystemItemEvent.RenamedNewName:
                OnItemRenamedInternal(config, renamedSubject, name, FileSystemItemEvent.RenamedNewName);
                break;

            default:
                if (config.LoggingEnabled)
                {
                    Logger.Trace($"[Skipped] An event was skipped because it didn't map to a Swatcher FileSystemEvent. Value={@event}, Name={name ?? "null"}.");
                }
                break;
            }

            currentOffset += nextOffset;
            return(nextOffset);
        }
Exemplo n.º 4
0
 private static void OnItemDeletedInternal(ISwatcherConfig config, ISubject <SwatcherEventArgs> deletedSubject,
                                           string name)
 {
     if (config.ChangeTypes.HasFlag(WatcherChangeTypes.Deleted))
     {
         deletedSubject.OnNext(
             new SwatcherEventArgs(config, WatcherChangeTypes.Deleted, name));
     }
 }
Exemplo n.º 5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="SwatcherRenamedEventArgs"/> class.
 /// </summary>
 /// <param name="config">Configuration for the <see cref="ISwatcher"/> that created this <see cref="SwatcherRenamedEventArgs"/> instance.</param>
 /// <param name="name">The <b>new</b> name of the item that changed.</param>
 /// <param name="oldName">The <b>old</b> name of the item changed.</param>
 public SwatcherRenamedEventArgs(ISwatcherConfig config, string name, string oldName)
 {
     SwatcherId   = config.Id;
     OldName      = oldName;
     FullPath     = config.PathToWatch;
     Name         = name;
     EventId      = Guid.NewGuid();
     TimeOccurred = DateTime.UtcNow;
 }
Exemplo n.º 6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="SwatcherEventArgs" /> class.
 /// </summary>
 /// <param name="config">The configuration for the <see cref="Swatcher"/>that created this instance.</param>
 /// <param name="changeType">Type of the change.</param>
 /// <param name="name">The name.</param>
 public SwatcherEventArgs(ISwatcherConfig config, WatcherChangeTypes changeType, string name)
 {
     SwatcherId   = config.Id;
     ChangeType   = changeType;
     Name         = name;
     TimeOccurred = DateTime.UtcNow;
     EventId      = Guid.NewGuid();
     FullPath     = config.PathToWatch + name;
 }
Exemplo n.º 7
0
 private static void OnItemCreatedInternal(ISwatcherConfig config,
                                           ISubject <SwatcherCreatedEventArgs> createdSubject,
                                           string name)
 {
     if (config.ChangeTypes.HasFlag(WatcherChangeTypes.Created))
     {
         createdSubject.OnNext(
             new SwatcherCreatedEventArgs(config, name));
     }
 }
Exemplo n.º 8
0
        // ReSharper disable once SuggestVarOrType_Elsewhere
        private static unsafe void WatchFolderForChanges(
            object wrapper, ISwatcherConfig config,
            IWindowsFacade windowsFacade, SafeFileHandle directoryHandle)
        {
            var result = new SwatcherAsyncResult {
                Buffer = new byte[DefaultBufferSize]
            };
            var overlapped = new Overlapped {
                AsyncResult = result
            };

            //the first parameter is null because we're not using IO completion callbacks; they're too slow.
            //we're taking the byte array from our empty byte array and passing that as user data to the overlapped.
            var overlappedPointer = overlapped.UnsafePack(null, result.Buffer);

            var success = false;

            try
            {
                //now we wrap this section in a fixed block to pin it to the original address.
                //we cannot have it move because the OS will write information about the changes
                //into this byte array.
                fixed(byte *bufferPointer = result.Buffer)
                {
                    var bytesReturned = 0;
                    var bufferHandle  = new HandleRef(result, (IntPtr)bufferPointer);
                    var isRecursive   = Convert.ToInt32(config.IsRecursive);

                    //because we're using IO completion ports, we pass our overlapped pointer into this unmanaged
                    //call. when a change has been received, the OS will callback via GetQueuedCompletionStatus
                    //passing the overlapped pointer (which has our IAsyncResult/byte array) back to us.
                    success = windowsFacade.ReadDirectoryChangesW(
                        directoryHandle, bufferHandle, DefaultBufferSize, isRecursive,
                        (int)config.NotificationTypes, bytesReturned, overlappedPointer, SafeLocalMemHandle.Empty);

                    //in this usage of ReadDirectoryChangesW, we should *always* get 0 bytes returned.
                    if (bytesReturned != 0)
                    {
                        Debugger.Break();
                    }
                }
            }
            finally
            {
                //if success is false, our directory handle has likely become invalid. attempt to re-establish it.
                if (!success)
                {
                    Debugger.Break();
                    //before doing anything else, cleanup here to prevent memory leaks.
                    Overlapped.Free(overlappedPointer);
                }
            }
        }
Exemplo n.º 9
0
 internal static IObservable <T> SelectConfiguredItemTypes <T>(
     this IObservable <T> @event, ISwatcherConfig config) where T : SwatcherEventArgs
 {
     return(@event
            .Select(x => new
     {
         ItemType = GetItemType(x.FullPath),
         Model = x
     })
            .Where(x => config.ItemTypes.HasFlag(x.ItemType))
            .Select(x => x.Model));
 }
Exemplo n.º 10
0
        private static void OnItemRenamedInternal(ISwatcherConfig config,
                                                  ISubject <RenamedInfo> renamedSubject, string name, FileSystemItemEvent @event)
        {
            if (!config.ChangeTypes.HasFlag(WatcherChangeTypes.Renamed))
            {
                return;
            }

            renamedSubject.OnNext(new RenamedInfo
            {
                Name  = name,
                Event = @event
            });
        }
Exemplo n.º 11
0
        internal Swatcher(ISwatcherConfig config, IWindowsFacade windowsFacade)
        {
            Config        = config;
            WindowsFacade = windowsFacade;

            var createdEventStream         = CreateCreatedEventStream();
            var finishedCreatedEventStream = CreateFinishedCreatedEventStream(createdEventStream);

            finishedCreatedEventStream
            .Delay(TimeSpan.FromSeconds(2.5))
            .Subscribe(x => CreatedEventsInProgress.Remove(x.FullPath));

            var createdEventWindows         = CreateCreatedEventWindowStream(createdEventStream);
            var finishedCreatedEventWindows = CreateFinishedCreatedEventWindows(finishedCreatedEventStream);

            var changedEventStream  = CreateChangedEventStream();
            var changedEventWindows = CreatedChangedEventWindows(changedEventStream);

            Renamed = CreatePublicRenamedStream(Config);
            Deleted = CreatePublicDeletedStream();
            Created = CreatePublicCreatedStream(finishedCreatedEventStream);
            Changed = CreatePublicChangedStream(changedEventStream,
                                                changedEventWindows, createdEventWindows, finishedCreatedEventWindows);

            ChangedEventPatternSource =
                Changed.Select(x => new EventPattern <SwatcherEventArgs>(this, x)).ToEventPattern();
            ChangedEventPatternSource.OnNext += OnItemChanged;

            DeletedEventPatternSource =
                Deleted.Select(x => new EventPattern <SwatcherEventArgs>(this, x)).ToEventPattern();
            DeletedEventPatternSource.OnNext += OnItemDeleted;

            CreatedEventPatternSource =
                Created.Select(x => new EventPattern <SwatcherCreatedEventArgs>(this, x)).ToEventPattern();
            CreatedEventPatternSource.OnNext += OnItemCreated;

            RenamedEventPatternSource =
                Renamed.Select(x => new EventPattern <SwatcherRenamedEventArgs>(this, x)).ToEventPattern();
            RenamedEventPatternSource.OnNext += OnItemRenamed;
        }
Exemplo n.º 12
0
 /// <summary>
 /// Initializes a new instance of the <see cref="SwatcherCreatedEventArgs"/> class.
 /// </summary>
 /// <param name="config">The configuration for the <see cref="Swatcher"/>that created this instance.</param>
 /// <param name="name">The name of the file system item that was created.</param>
 /// <param name="isCompleted">Indicated whether or not the <see cref="TimeCompleted"/> and <see cref="Duration"/> properties should be populated.</param>
 public SwatcherCreatedEventArgs(ISwatcherConfig config, string name)
     : base(config, WatcherChangeTypes.Created, name)
 {
 }
Exemplo n.º 13
0
        private bool IsWatchedItemType(SwatcherEventArgs e, ISwatcherConfig config)
        {
            var itemType = GetItemType(e.FullPath);

            return(config.ItemTypes.HasFlag(itemType));
        }
Exemplo n.º 14
0
 public Swatcher(ISwatcherConfig config) : this(config, new WindowsFacade())
 {
 }