I'm a newbie to WMI and I need to implement RegistryValueChangeEvent in a C# service.
I need an event handler that gets triggered each time any one of a set of registry values is changed. I want behavior similar to the FileSystemWatcher class's Changed event, but for registry values.
If there's some other technique I could use to accomplish the same task, I'd appreciate that as well. My minimum requirement is that it be a better solution than what I have now: polling every 20 seconds and comparing the registry value with the last result.
Please provide example code in your answer. If I can get an example for watching just one registry value, that would be fine.
I need a solution in .Net 2.0
Thanks.
WMI can sometimes be interesting to work with...I think I understand your question, so take a look at the code snippet below and let me know if it's what you're looking for.
// ---------------------------------------------------------------------------------------------------------------------
// <copyright file="Program.cs" company="">
//
// </copyright>
// <summary>
// Defines the WmiChangeEventTester type.
// </summary>
// ---------------------------------------------------------------------------------------------------------------------
namespace WmiExample
{
using System;
using System.Management;
/// <summary>
/// </summary>
public class WmiChangeEventTester
{
/// <summary>
/// Initializes a new instance of the <see cref="WmiChangeEventTester"/> class.
/// </summary>
public WmiChangeEventTester()
{
try
{
// Your query goes below; "KeyPath" is the key in the registry that you
// want to monitor for changes. Make sure you escape the \ character.
WqlEventQuery query = new WqlEventQuery(
"SELECT * FROM RegistryValueChangeEvent WHERE " +
"Hive = 'HKEY_LOCAL_MACHINE'" +
#"AND KeyPath = 'SOFTWARE\\Microsoft\\.NETFramework' AND ValueName='InstallRoot'");
ManagementEventWatcher watcher = new ManagementEventWatcher(query);
Console.WriteLine("Waiting for an event...");
// Set up the delegate that will handle the change event.
watcher.EventArrived += new EventArrivedEventHandler(HandleEvent);
// Start listening for events.
watcher.Start();
// Do something while waiting for events. In your application,
// this would just be continuing business as usual.
System.Threading.Thread.Sleep(100000000);
// Stop listening for events.
watcher.Stop();
}
catch (ManagementException managementException)
{
Console.WriteLine("An error occurred: " + managementException.Message);
}
}
/// <summary>
/// </summary>
/// <param name="sender">
/// The sender.
/// </param>
/// <param name="e">
/// The e.
/// </param>
private void HandleEvent(object sender, EventArrivedEventArgs e)
{
Console.WriteLine("Received an event.");
// RegistryKeyChangeEvent occurs here; do something.
}
/// <summary>
/// </summary>
public static void Main()
{
// Just calls the class above to check for events...
WmiChangeEventTester receiveEvent = new WmiChangeEventTester();
}
}
}
You really don't need WMI, as others have pointed out. I too have used RegistryMonitor with no problems.
If you need an example, there's already example code for the RegistryMonitor on the page itself. Did you scroll down to this bit on the code project:
public class MonitorSample
{
static void Main()
{
RegistryMonitor monitor = new
RegistryMonitor(RegistryHive.CurrentUser, "Environment");
monitor.RegChanged += new EventHandler(OnRegChanged);
monitor.Start();
while(true);
monitor.Stop();
}
private void OnRegChanged(object sender, EventArgs e)
{
Console.WriteLine("registry key has changed");
}
}
Are you limited to WMI?
If not you can use RegNotifyChangeKeyValue wrappers like RegistryMonitor
You'll need to utilize WMI for it. See http://msdn.microsoft.com/en-us/library/aa393035.aspx
Related
I recently created an interface with MVVM making sure there was no code behind. Our development team in India reused that interface with some modifications. When I went to use the new UI I found that these modifications seemed to break some of the bindings I had made between the view and viewmodel. I then found this new code below in the view. I have only included part of the code. You can see that the viewmodel is referenced multiple times in the view. I though it was a highly discouraged. I know that having a setter to the viewmodel in the view is something they are always doing with their Prism implementation, but I have never seen actual calls to the viewmodel from the view before. I don't want to bring this up with their supervisor unless I am sure of my position with regards to good coding practice.
[Import]
public ProfileLimitsViewModel ViewModel
{
get
{
return DataContext as ProfileLimitsViewModel;
}
set
{
DataContext = value;
_performSelection = true;
ViewModel.OnSelectedProfilesChanged -= setSelection;
ViewModel.OnSelectedProfilesChanged += setSelection;
}
}
/// <summary>
/// On Profile selection changed in UI
/// </summary>
private void ProfileList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
int count = 0;
ViewModel.SelectedProfiles.Clear();
//if multiple profile is selected
if (ProfileList.SelectedItems.Count > 0)
{
foreach (var profile in ProfileList.SelectedItems)
{
ViewModel.SelectedProfiles.Add((profile as ProfileNS).ProfileID);
//the profile that is selected
if (count++ == 0)
ViewModel.SelectedProfile = profile as ProfileNS;
}
}
if (_performSelection)
{
ViewModel.SelectedFormationId = ViewModel.SelectedProfile.Layers[0].BedId;
ViewModel.ProfileSelectionChanged();
}
}
catch(Exception ex)
{
Diagnostics.LogHandledException(ex);
}
}
/// <summary>
/// on closed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ProfileEditor_Closed(object sender, RoutedEventArgs e)
{
//Event to update ProfileEditor checkbox in ContextualTab
ServiceLocator.Current.GetInstance<IEventAggregatorService>().Publish
<ProfileEditorSelectedEvent, MCWDEditorMode>(MCWDEditorMode.Close);
ViewModel.ModuleService.UpdateMcwdEditorViewModelsHash(ViewModel.WellLandingId, null);
}
/// <summary>
/// On formation selection changed in UI
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ProfileLayersDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
if (e.AddedItems != null && e.AddedItems.Count > 0 && _performSelection)
ViewModel.FormationSelectionChanged(e.AddedItems[0]);
}
catch (Exception ex)
{
Diagnostics.LogHandledException(ex);
}
}
An event handler in C# is a sure sign you are not doing MVVM properly. I'm looking at you, ProfileList_SelectionChange. Reference for you/your boss: https://msdn.microsoft.com/en-us/library/hh848246.aspx Prism only gives you the tools to do MVVM properly; it does not prevent you from doing things in WinForms style.
Todd Sprang is right, at least partly. There may be legit reasons to use EventHandlers, but the one pointed out by Todd (ProfileList_SelectionChange) is definitively not belonging there, neither is RoutedEventArgs, as they are both defined in the Presentation.dll.
There are pretty easy ways to enforce this, by splitting your MVVM layers into multiple assemblies.
MyApp.ViewModels.dll assembly and put only ViewModels there
Put your view related stuff in a MyApp.UI.dll assembly (Views)
Application related goes into MyApp.Desktop.dll or MyApp.WinPhone.dll etc. Plattform dependend stuff, WPF, XAML, Silverlight, ASP.NET)
MyViewModels.dll should never have a reference to UI/Desktop assembly and NEVER reference Presentation.dll. If a developer needs to use anything from Presentation.dll you automatically know, it violates MVVM, because you will have a reference to Platform or View specific type in your code
I recently have released a small application with a trial to the Windows Phone Marketplace, but my application is not working as expected. I have followed http://code.msdn.microsoft.com/Trial-Experience-Sample-c58f21af when making my trial, so that I can call the current 'LicenseInformation' state and block a feature or not depending on the current application's license state. According to the sample application, The LicenseMode property returns a value from the LicenseModes enum (Full, MissingOrRevoked, or Trial) so that your app code needs to check only a single value. There’s also a convenient Boolean IsFull property. Whenever the license mode has changed, or it is likely to have changed, TrialExperienceHelper raises its LicenseChanged event and your app code can handle that event to query LicenseMode or IsFull again. Then, your app can control the availability of features, ads, and your Buy UI as needed.
In my application I have a click event in which I would like to perform an action based on the current LicenseInformation state and upon a count (the count being the number of times an image is saved with particular aspects applied).
Settings.SavedCount.Value records the number of times the save button is clicked, and if the count is above 100 and the application is in trial mode I would like to ask the user if they would like to upgrade, otherwise if the count is less than 100 while the application is in trial mode or if the license is in full mode then the user is allowed to continue with the save process (hopefully that makes logical sense).
void saveButton_Click(object sender, EventArgs e)
{
Settings.SavedCount.Value += 1;
if (TrialViewModel.LicenseModeString == "Trial" && Settings.SavedCount.Value > 100)
{
MessageBoxResult result = MessageBox.Show("You have saved over 100 items! Would you like to continue?", "Congratulations!", MeesageBoxButton.OKCancel);
switch (result)
{
case MessageBoxResult.OK:
//A command takes a parameter so pass null
TrialViewModel.BuyCommand.Execute(null);
break;
case MessageBoxResult.Cancel:
editPagePivotControl.SelectedIndex = 0;
break;
}
}
else if ((TrialViewModel.LicenseModeString == "Trial" && Settings.SavedCount.Value <= 100) || (TrialViewModel.LicenseModeString == "Full")
{
PerformSaveAsync();
}
}
}
When testing in Debug mode and with the sample implementation from the msdn website, the Trial and Full implementations worked properly, and then when in Release mode the license was listed as MissingOrRevoked which I assumed would be called correctly in the marketplace. What is ACTUALLY occuring when i have downloaded the app in the marketplace under both trial and full modes is that the PerformSaveAsync() method is never being called (which ultimately saves the new image and disables the button) and I can use the new image elsewhere. I am having trouble figuring out what the issue may be?
EDIT** In researching I came across http://msdn.microsoft.com/en-us/library/aa691310(v=vs.71).aspx which states that The operation x && y corresponds to the operation x & y, except that y is evaluated only if x is true. and `•The operation x || y corresponds to the operation x | y, except that y is evaluated only if x is false/' . Would this be the cause of the issues? If so, how should they be fixed?
Edit 2** Addtion of TrialViewModel and TrialExperienceHelper.cs for additional info
TrialViewModel
TrialViewModel
#region fields
private RelayCommand buyCommand;
#endregion fields
#region constructors
public TrialViewModel()
{
// Subscribe to the helper class's static LicenseChanged event so that we can re-query its LicenseMode property when it changes.
TrialExperienceHelper.LicenseChanged += TrialExperienceHelper_LicenseChanged;
}
#endregion constructors
#region properties
/// <summary>
/// You can bind the Command property of a Button to BuyCommand. When the Button is clicked, BuyCommand will be
/// invoked. The Button will be enabled as long as BuyCommand can execute.
/// </summary>
public RelayCommand BuyCommand
{
get
{
if (this.buyCommand == null)
{
// The RelayCommand is constructed with two parameters - the action to perform on invocation,
// and the condition under which the command can execute. It's important to call RaiseCanExecuteChanged
// on a command whenever its can-execute condition might have changed. Here, we do that in the TrialExperienceHelper_LicenseChanged
// event handler.
this.buyCommand = new RelayCommand(
param => TrialExperienceHelper.Buy(),
param => TrialExperienceHelper.LicenseMode == TrialExperienceHelper.LicenseModes.Trial);
}
return this.buyCommand;
}
}
public string LicenseModeString
{
get
{
return TrialExperienceHelper.LicenseMode.ToString()/* + ' ' + AppResources.ModeString*/;
}
}
#endregion properties
#region event handlers
// Handle TrialExperienceHelper's LicenseChanged event by raising property changed notifications on the
// properties and commands that
internal void TrialExperienceHelper_LicenseChanged()
{
this.RaisePropertyChanged("LicenseModeString");
this.BuyCommand.RaiseCanExecuteChanged();
}
#endregion event handlers
TrialExperienceHelper.cs
#region enums
/// <summary>
/// The LicenseModes enumeration describes the mode of a license.
/// </summary>
public enum LicenseModes
{
Full,
MissingOrRevoked,
Trial
}
#endregion enums
#region fields
#if DEBUG
// Determines how a debug build behaves on launch. This field is set to LicenseModes.Full after simulating a purchase.
// Calling the Buy method (or navigating away from the app and back) will simulate a purchase.
internal static LicenseModes simulatedLicMode = LicenseModes.Trial;
#endif // DEBUG
private static bool isActiveCache;
private static bool isTrialCache;
#endregion fields
#region constructors
// The static constructor effectively initializes the cache of the state of the license when the app is launched. It also attaches
// a handler so that we can refresh the cache whenever the license has (potentially) changed.
static TrialExperienceHelper()
{
TrialExperienceHelper.RefreshCache();
PhoneApplicationService.Current.Activated += (object sender, ActivatedEventArgs e) => TrialExperienceHelper.
#if DEBUG
// In debug configuration, when the user returns to the application we will simulate a purchase.
OnSimulatedPurchase();
#else // DEBUG
// In release configuration, when the user returns to the application we will refresh the cache.
RefreshCache();
#endif // DEBUG
}
#endregion constructors
#region properties
/// <summary>
/// The LicenseMode property combines the active and trial states of the license into a single
/// enumerated value. In debug configuration, the simulated value is returned. In release configuration,
/// if the license is active then it is either trial or full. If the license is not active then
/// it is either missing or revoked.
/// </summary>
public static LicenseModes LicenseMode
{
get
{
#if DEBUG
return simulatedLicMode;
#else // DEBUG
if (TrialExperienceHelper.isActiveCache)
{
return TrialExperienceHelper.isTrialCache ? LicenseModes.Trial : LicenseModes.Full;
}
else // License is inactive.
{
return LicenseModes.MissingOrRevoked;
}
#endif // DEBUG
}
}
/// <summary>
/// The IsFull property provides a convenient way of checking whether the license is full or not.
/// </summary>
public static bool IsFull
{
get
{
return (TrialExperienceHelper.LicenseMode == LicenseModes.Full);
}
}
#endregion properties
#region methods
/// <summary>
/// The Buy method can be called when the license state is trial. the user is given the opportunity
/// to buy the app after which, in all configurations, the Activated event is raised, which we handle.
/// </summary>
public static void Buy()
{
MarketplaceDetailTask marketplaceDetailTask = new MarketplaceDetailTask();
marketplaceDetailTask.ContentType = MarketplaceContentType.Applications;
marketplaceDetailTask.Show();
}
/// <summary>
/// This method can be called at any time to refresh the values stored in the cache. We re-query the application object
/// for the current state of the license and cache the fresh values. We also raise the LicenseChanged event.
/// </summary>
public static void RefreshCache()
{
TrialExperienceHelper.isActiveCache = CurrentApp.LicenseInformation.IsActive;
TrialExperienceHelper.isTrialCache = CurrentApp.LicenseInformation.IsTrial;
TrialExperienceHelper.RaiseLicenseChanged();
}
private static void RaiseLicenseChanged()
{
if (TrialExperienceHelper.LicenseChanged != null)
{
TrialExperienceHelper.LicenseChanged();
}
}
#if DEBUG
private static void OnSimulatedPurchase()
{
TrialExperienceHelper.simulatedLicMode = LicenseModes.Full;
TrialExperienceHelper.RaiseLicenseChanged();
}
#endif // DEBUG
#endregion methods
#region events
/// <summary>
/// The static LicenseChanged event is raised whenever the value of the LicenseMode property has (potentially) changed.
/// </summary>
public static event LicenseChangedEventHandler LicenseChanged;
#endregion events
If your dev builds work and the only difference is with the app being published via the store then I think it's very unlikely to be your logic.
When you submitted the app, are you sure you checked the option to be able to use the trial functionality in the app?
If you didn't check this then it won't work in a released app.
Regarding your edit, I don't see any problem with your condition, your quote is just that the operator is lazy, evaluating only what is needed to determine the result (for example when you do x&& y if x is false, x&& false=> false and x&& true==false which is the same result so it don't evaluate y).
Also like I said in your previous question even the windows phone 7 api still work on windows phone 8 so if you are creating code for both platform there is probably no need to use the new api specifically for wp8.
In this code I don't see any problem but why do you convert the LicenseModes enum to string, using the enum will add some type safety and prevent you to do some invalid comparison.
The only problem is where you set LicenseModeString or a problem inside PerformSaveAsync?
how to avoid the error of FileSystemWatcher in C#?
too many changes at once in directory
I have to detect all changes on a network share.
The InternalBufferSize is increased to 8192 * 128
There are two things you should do:
Set InternalBufferSize to the maximum supported value (65536). Your attempt to set it to "8192 * 128" is larger than the maximum supported value listed in the documentation, so you may not have increased the buffer size at all.
Queue events from the FileSystemWatcher onto a background thread for processing.
It's the second point here that isn't well understood, and really should be documented on MSDN. Internally, FileSystemWatcher is queuing change events into that internal buffer you set the size of above. Critically however, items are only removed from that buffer after your event handler returns. This means every cycle of overhead your event handlers introduce increases the possibility of the buffer filling up. What you should do is clear the limited queue of the FileSystemWatcher as quickly as possible, and move the events into your own infinite queue, to process at the rate you can handle, or discard if you care to do so, but with some intelligence around it.
Here's basically what I do in my code. First, I start my own dispatcher thread:
Dispatcher changeDispatcher = null;
ManualResetEvent changeDispatcherStarted = new ManualResetEvent(false);
Action changeThreadHandler = () =>
{
changeDispatcher = Dispatcher.CurrentDispatcher;
changeDispatcherStarted.Set();
Dispatcher.Run();
};
new Thread(() => changeThreadHandler()) { IsBackground = true }.Start();
changeDispatcherStarted.WaitOne();
Then I create the watcher. Note the buffer size being set. In my case, I only watch changes in the target directory, not subdirectories:
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.InternalBufferSize = 64 * 1024;
watcher.IncludeSubdirectories = false;
Now I attach my event handlers, but here I invoke them onto my dispatcher rather than running them synchronously in the watcher thread. Yes, the events will be processed in order by the dispatcher:
watcher.Changed += (sender, e) => changeDispatcher.BeginInvoke(new Action(() => OnChanged(sender, e)));
watcher.Created += (sender, e) => changeDispatcher.BeginInvoke(new Action(() => OnCreated(sender, e)));
watcher.Deleted += (sender, e) => changeDispatcher.BeginInvoke(new Action(() => OnDeleted(sender, e)));
watcher.Renamed += (sender, e) => changeDispatcher.BeginInvoke(new Action(() => OnRenamed(sender, e)));
And finally, after disposing of the FileSystemWatcher (you were doing that, right?), you need to shut down your dispatcher:
watcher.Dispose()
changeDispatcher.BeginInvokeShutdown(DispatcherPriority.Normal);
And that's it. I was getting this problem myself, both in network and local scenarios. After using this approach, I wasn't able to generate this error again, even when hammering out empty files to watched directories as fast as possible. If you did ever manage to somehow exhaust the buffer in this case (which I'm not sure is possible, the API upstream is probably slower), there's still further room for optimization here. As long as your dispatcher is over the "tipping point" though, where the sender can't post the events faster than you can dispatch them, you'll never get a backlog, and hence never blow the buffer. I believe this approach puts you into that safe area.
I think I might have found a pattern that can help improve the the usage of the buffer considerably.
The issue with this class is that until the delegates for the events finish running, it cannot release the memory it uses to hold that information.
For the life of me I don't know why the maximum InternalBufferSize was set to 64Kb, but with this idea you'll use that small buffer much more effectively.
If instead of doing the operations inside the delegates you simply queue them and defer their execution for a background worker, the amount of memory used by it will be considerably smaller.
In fact, I've got no idea why the class in itself doesn't implement something like this in the first place.
The code below is just a sample piece of code for the idea, and shouldn't be used in production environments, but it increased the number of files I could copy and keep track of drastically.
using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.IO;
using System.Threading;
using NUnit.Framework;
namespace Soundnet.Synchronisation.FileSystemWatcherTests
{
/// <summary>
///
/// </summary>
[TestFixture]
public class Tests
{
static readonly ConcurrentQueue<Change> ChangesQueue = new ConcurrentQueue<Change>();
const string Destination = #"c:\Destination";
const string Source = #"c:\Source";
/// <summary>
/// Tests this instance.
/// </summary>
[Test]
public void Test()
{
var changesBackgroundWorker = new BackgroundWorker();
changesBackgroundWorker.DoWork += ChangesBackgroundWorkerOnDoWork;
changesBackgroundWorker.RunWorkerAsync();
var fileSystemWatcher = new FileSystemWatcher
{
Path = Source,
EnableRaisingEvents = true,
IncludeSubdirectories = true,
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.LastAccess | NotifyFilters.CreationTime | NotifyFilters.FileName | NotifyFilters.DirectoryName,
InternalBufferSize = 65536
};
fileSystemWatcher.Created += FileSystemWatcherOnCreated;
fileSystemWatcher.Deleted += FileSystemWatcherOnDeleted;
fileSystemWatcher.Renamed += FileSystemWatcherOnRenamed;
fileSystemWatcher.Error += FileSystemWatcherOnError;
while (true)
Thread.Sleep(1000000);
}
/// <summary>
/// Changeses the background worker configuration document work.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="doWorkEventArgs">The <see cref="DoWorkEventArgs"/> instance containing the event data.</param>
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
private static void ChangesBackgroundWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
while (true)
{
Change change;
if (ChangesQueue.TryDequeue(out change))
{
var backgroundWorker = new BackgroundWorker();
switch (change.ChangeType)
{
case WatcherChangeTypes.Created:
backgroundWorker.DoWork += (o, args) =>
{
var fileSystemType = GetFileSystemType(change.FullPath);
var newItem = Path.Combine(Destination, change.Name);
while (true)
{
try
{
switch (fileSystemType)
{
case FileSystemType.File:
File.Copy(change.FullPath, newItem, true);
break;
case FileSystemType.Directory:
var directorySecurity =
Directory.GetAccessControl(change.FullPath);
Directory.CreateDirectory(newItem, directorySecurity);
break;
case FileSystemType.NotExistant:
break;
}
return;
}
catch (IOException exception)
{
Thread.Sleep(100);
Console.WriteLine(exception.Message);
}
}
};
break;
case WatcherChangeTypes.Deleted:
backgroundWorker.DoWork += (o, args) =>
{
var itemToDelete = Path.Combine(Destination, change.Name);
var fileSystemType = GetFileSystemType(itemToDelete);
switch (fileSystemType)
{
case FileSystemType.File:
File.Delete(itemToDelete);
break;
case FileSystemType.Directory:
Directory.Delete(itemToDelete, true);
break;
}
};
break;
case WatcherChangeTypes.Changed:
backgroundWorker.DoWork += (o, args) =>
{
var fileSystemType = GetFileSystemType(change.FullPath);
var newItem = Path.Combine(Destination, change.Name);
switch (fileSystemType)
{
case FileSystemType.File:
File.Copy(change.FullPath, newItem, true);
break;
}
};
break;
case WatcherChangeTypes.Renamed:
backgroundWorker.DoWork += (o, args) =>
{
var fileSystemType = GetFileSystemType(change.FullPath);
var oldItem = Path.Combine(Destination, change.OldName);
var newItem = Path.Combine(Destination, change.Name);
switch (fileSystemType)
{
case FileSystemType.File:
if (File.Exists(oldItem))
File.Move(oldItem, newItem);
break;
case FileSystemType.Directory:
if (Directory.Exists(oldItem))
Directory.Move(oldItem, newItem);
break;
}
};
break;
case WatcherChangeTypes.All:
break;
default:
throw new ArgumentOutOfRangeException();
}
backgroundWorker.RunWorkerAsync();
}
}
}
/// <summary>
/// Files the system watcher configuration created.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="fileSystemEventArgs">The <see cref="FileSystemEventArgs"/> instance containing the event data.</param>
private static void FileSystemWatcherOnCreated(object sender, FileSystemEventArgs fileSystemEventArgs)
{
ChangesQueue.Enqueue(new Change
{
ChangeType = WatcherChangeTypes.Created,
FullPath = fileSystemEventArgs.FullPath,
Name = fileSystemEventArgs.Name
});
}
/// <summary>
/// Files the system watcher configuration deleted.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="fileSystemEventArgs">The <see cref="FileSystemEventArgs"/> instance containing the event data.</param>
private static void FileSystemWatcherOnDeleted(object sender, FileSystemEventArgs fileSystemEventArgs)
{
ChangesQueue.Enqueue(new Change
{
ChangeType = WatcherChangeTypes.Deleted,
FullPath = fileSystemEventArgs.FullPath,
Name = fileSystemEventArgs.Name
});
}
/// <summary>
/// Files the system watcher configuration error.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="errorEventArgs">The <see cref="ErrorEventArgs"/> instance containing the event data.</param>
private static void FileSystemWatcherOnError(object sender, ErrorEventArgs errorEventArgs)
{
var exception = errorEventArgs.GetException();
Console.WriteLine(exception.Message);
}
/// <summary>
/// Files the system watcher configuration renamed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="fileSystemEventArgs">The <see cref="RenamedEventArgs"/> instance containing the event data.</param>
private static void FileSystemWatcherOnRenamed(object sender, RenamedEventArgs fileSystemEventArgs)
{
ChangesQueue.Enqueue(new Change
{
ChangeType = WatcherChangeTypes.Renamed,
FullPath = fileSystemEventArgs.FullPath,
Name = fileSystemEventArgs.Name,
OldFullPath = fileSystemEventArgs.OldFullPath,
OldName = fileSystemEventArgs.OldName
});
}
/// <summary>
/// Gets the type of the file system.
/// </summary>
/// <param name="fullPath">The full path.</param>
/// <returns></returns>
private static FileSystemType GetFileSystemType(string fullPath)
{
if (Directory.Exists(fullPath))
return FileSystemType.Directory;
if (File.Exists(fullPath))
return FileSystemType.File;
return FileSystemType.NotExistant;
}
}
/// <summary>
/// Type of file system object
/// </summary>
internal enum FileSystemType
{
/// <summary>
/// The file
/// </summary>
File,
/// <summary>
/// The directory
/// </summary>
Directory,
/// <summary>
/// The not existant
/// </summary>
NotExistant
}
/// <summary>
/// Change information
/// </summary>
public class Change
{
/// <summary>
/// Gets or sets the type of the change.
/// </summary>
/// <value>
/// The type of the change.
/// </value>
public WatcherChangeTypes ChangeType { get; set; }
/// <summary>
/// Gets or sets the full path.
/// </summary>
/// <value>
/// The full path.
/// </value>
public string FullPath { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the old full path.
/// </summary>
/// <value>
/// The old full path.
/// </value>
public string OldFullPath { get; set; }
/// <summary>
/// Gets or sets the old name.
/// </summary>
/// <value>
/// The old name.
/// </value>
public string OldName { get; set; }
}
}
From MSDN;
The Windows operating system notifies your component of file changes
in a buffer created by the FileSystemWatcher. If there are many
changes in a short time, the buffer can overflow. This causes the
component to lose track of changes in the directory, and it will only
provide blanket notification. Increasing the size of the buffer with
the InternalBufferSize property is expensive, as it comes from
non-paged memory that cannot be swapped out to disk, so keep the
buffer as small yet large enough to not miss any file change events.
To avoid a buffer overflow, use the NotifyFilter and
IncludeSubdirectories properties so you can filter out unwanted change
notifications.
I have the experience that FileSystemWatcher is not always the most reliable thing to use. You could specify the filter to narrow down the files you are watching (NotifyFilter), or increase the buffer size.
But depending on your requirements you may also want to do it another way, like polling every x seconds to get the list of files. But then you might need to tell us more about your business case.
SHChangeNotifyRegister can be used to get shell notifications.
It should be fixed if you increase the buffer size but its not a practical solution.
Because to make sure it always records everything you would need to make the buffer huge.
And that will effect the performance greatlly.
And i think performance issues could be fixed by implementing multi threading.
I'm trying to make a Setting flyout easily accessible in all pages. I looked at the Settings example at http://code.msdn.microsoft.com/windowsapps/App-settings-sample-1f762f49/sourcecode?fileId=50851&pathId=2033699455.
However there are about a dozen functions and it's just dumb to have to copy/paste/maintain an undetermined number of copies of the same code in each page. So I took the code for the popup and put it in a base class that inherits from LayoutAwarePage base class. My pages then inherit from this new class. Ex:
// Base class declaration that includes settings flyout code
public class SettingsFlyoutAwarePage : myApp.Common.LayoutAwarePage
{
// Settings flyout code here
}
// Page declaration
public sealed partial class GroupedItemsPage : myApp.Common.SettingsFlyoutAwarePage
{
// Standard working page stuff here
}
Here is the actual code to create popup that IS being called and executed in the SettingsFlyoutAwarePage base class, which is identical to the sample code, but nothing actually appears on screen:
void onSettingsCommand(IUICommand command)
{
// Create a Popup window which will contain our flyout.
settingsPopup = new Popup();
settingsPopup.Closed += OnPopupClosed;
Window.Current.Activated += OnWindowActivated;
settingsPopup.IsLightDismissEnabled = true;
settingsPopup.Width = settingsWidth;
settingsPopup.Height = windowBounds.Height;
// Add the proper animation for the panel.
settingsPopup.ChildTransitions = new TransitionCollection();
settingsPopup.ChildTransitions.Add(new PaneThemeTransition()
{
Edge = (SettingsPane.Edge == SettingsEdgeLocation.Right) ?
EdgeTransitionLocation.Right :
EdgeTransitionLocation.Left
});
// Create a SettingsFlyout the same dimenssions as the Popup.
SettingsFlyout mypane = new SettingsFlyout();
mypane.Width = settingsWidth;
mypane.Height = windowBounds.Height;
// Place the SettingsFlyout inside our Popup window.
settingsPopup.Child = mypane;
// Let's define the location of our Popup.
settingsPopup.SetValue(Canvas.LeftProperty, SettingsPane.Edge == SettingsEdgeLocation.Right ? (windowBounds.Width - settingsWidth) : 0);
settingsPopup.SetValue(Canvas.TopProperty, 0);
settingsPopup.IsOpen = true;
}
While I can step through all the code and it all appears to execute just fine, I never actually see the flyout appear. Is it at least theoretically possible to do this from an inherited base class?
Did you implement the onCommandsRequested function as described in demo?
void onCommandsRequested(SettingsPane settingsPane, SettingsPaneCommandsRequestedEventArgs eventArgs)
{
UICommandInvokedHandler handler = new UICommandInvokedHandler(onSettingsCommand);
SettingsCommand generalCommand = new SettingsCommand("DefaultsId", "Defaults", handler);
eventArgs.Request.ApplicationCommands.Add(generalCommand);
}
If this helps anyone, here is a Settings flyout-aware class that you can inherit your XAML pages from. Of course you still have to implement the popup itself (demo code link)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.ApplicationSettings;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;
namespace MyApp.Common
{
[Windows.Foundation.Metadata.WebHostHidden]
public class MyAppBasePage : MyApp.Common.LayoutAwarePage
{
public MyAppBasePage()
{
}
private bool isSettingCharmEventRegistered;
// Used to determine the correct height to ensure our custom UI fills the screen.
private Rect windowBounds;
// Desired width for the settings UI. UI guidelines specify this should be 346 or 646 depending on your needs.
private double settingsWidth = 646;
// This is the container that will hold our custom content.
private Popup settingsPopup;
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
// Added to make sure the event handler for CommandsRequested is cleaned up before other scenarios.
if (this.isSettingCharmEventRegistered)
{
SettingsPane.GetForCurrentView().CommandsRequested -= onCommandsRequested;
this.isSettingCharmEventRegistered = false;
}
// Unregister the event that listens for events when the window size is updated.
Window.Current.SizeChanged -= OnWindowSizeChanged;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
windowBounds = Window.Current.Bounds;
// Added to listen for events when the window size is updated.
Window.Current.SizeChanged += OnWindowSizeChanged;
// Added to make sure the event handler for CommandsRequested is cleaned up before other scenarios.
if (!this.isSettingCharmEventRegistered)
{
SettingsPane.GetForCurrentView().CommandsRequested += onCommandsRequested;
this.isSettingCharmEventRegistered = true;
}
}
/// <summary>
/// Invoked when the window size is updated.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the event.</param>
void OnWindowSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
windowBounds = Window.Current.Bounds;
}
/// <summary>
/// We use the window's activated event to force closing the Popup since a user maybe interacted with
/// something that didn't normally trigger an obvious dismiss.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the event.</param>
private void OnWindowActivated(object sender, Windows.UI.Core.WindowActivatedEventArgs e)
{
if (e.WindowActivationState == Windows.UI.Core.CoreWindowActivationState.Deactivated)
{
settingsPopup.IsOpen = false;
}
}
/// <summary>
/// When the Popup closes we no longer need to monitor activation changes.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the event.</param>
void OnPopupClosed(object sender, object e)
{
Window.Current.Activated -= OnWindowActivated;
}
/// <summary>
/// This event is generated when the user opens the settings pane. During this event, append your
/// SettingsCommand objects to the available ApplicationCommands vector to make them available to the
/// SettingsPange UI.
/// </summary>
/// <param name="settingsPane">Instance that triggered the event.</param>
/// <param name="eventArgs">Event data describing the conditions that led to the event.</param>
void onCommandsRequested(SettingsPane settingsPane, SettingsPaneCommandsRequestedEventArgs eventArgs)
{
UICommandInvokedHandler handler = new UICommandInvokedHandler(onSettingsCommand);
SettingsCommand generalCommand = new SettingsCommand("DefaultsId", "Defaults", handler);
eventArgs.Request.ApplicationCommands.Add(generalCommand);
}
/// <summary>
/// This the event handler for the "Defaults" button added to the settings charm. This method
/// is responsible for creating the Popup window will use as the container for our settings Flyout.
/// The reason we use a Popup is that it gives us the "light dismiss" behavior that when a user clicks away
/// from our custom UI it just dismisses. This is a principle in the Settings experience and you see the
/// same behavior in other experiences like AppBar.
/// </summary>
/// <param name="command"></param>
void onSettingsCommand(IUICommand command)
{
// Create a Popup window which will contain our flyout.
settingsPopup = new Popup();
settingsPopup.Closed += OnPopupClosed;
Window.Current.Activated += OnWindowActivated;
settingsPopup.IsLightDismissEnabled = true;
settingsPopup.Width = settingsWidth;
settingsPopup.Height = windowBounds.Height;
// Add the proper animation for the panel.
settingsPopup.ChildTransitions = new TransitionCollection();
settingsPopup.ChildTransitions.Add(new PaneThemeTransition()
{
Edge = (SettingsPane.Edge == SettingsEdgeLocation.Right) ?
EdgeTransitionLocation.Right :
EdgeTransitionLocation.Left
});
// Create a SettingsFlyout the same dimenssions as the Popup.
SettingsFlyout mypane = new SettingsFlyout();
mypane.Width = settingsWidth;
mypane.Height = windowBounds.Height;
// Place the SettingsFlyout inside our Popup window.
settingsPopup.Child = mypane;
// Let's define the location of our Popup.
settingsPopup.SetValue(Canvas.LeftProperty, SettingsPane.Edge == SettingsEdgeLocation.Right ? (windowBounds.Width - settingsWidth) : 0);
settingsPopup.SetValue(Canvas.TopProperty, 0);
settingsPopup.IsOpen = true;
}
}
}
In a web application, I want to sprinkle events in places and then let others, who want to extend the functionality, add subscribers to those events.
So something like:
Users.PreUserUpdated();
Users.Update(user);
Users.OnUserUpdated();
If someone now wanted to subscribe to the pre or on user updated events, where would they do so? global.asax before application startup?
I would recommend subclassing System.Web.UI.Page and expose your events there. Then people can inherit from your class and get access to your events if needed.
Take a look at BlogEngine.net and see how they do it. Basically, the model classes have static events that "plugins" can attach to do do processing on them. In the application start event, the plugins in the bin directory are enumerated and a specific method is called on each one which wires up the necessary event handlers.
As an example, here's an event from the Post class that extensions can hook into to manipulate the post content before it is rendered to the page:
/// <summary>
/// Occurs when the post is being served to the output stream.
/// </summary>
public static event EventHandler<ServingEventArgs> Serving;
/// <summary>
/// Raises the event in a safe way
/// </summary>
public static void OnServing(Post post, ServingEventArgs arg)
{
if (Serving != null)
{
Serving(post, arg);
}
}
And one of the extensions that hooks into it:
[Extension("Converts ASCII smilies into real smilies in the comments", "1.3", "BlogEngine.NET")]
public class Smilies
{
static Smilies()
{
Comment.Serving += new EventHandler<ServingEventArgs>(Post_CommentServing);
}
private const string LINK = "<img src=\"{0}editors/tiny_mce3/plugins/emotions/img/smiley-{1}.gif\" class=\"flag\" alt=\"{2}\" />";
/// <summary>
/// The event handler that is triggered every time a comment is served to a client.
/// </summary>
private static void Post_CommentServing(object sender, ServingEventArgs e)
{
if (!string.IsNullOrEmpty(e.Body))
{
e.Body = e.Body.Replace("(H)", Convert("cool", "Cool"));
e.Body = e.Body.Replace(":'(", Convert("cry", "Cry"));
e.Body = e.Body.Replace(":$", Convert("embarassed", "Embarassed"));
e.Body = e.Body.Replace(":|", Convert("foot-in-mouth", "Foot"));
e.Body = e.Body.Replace(":(", Convert("frown", "Frown"));
e.Body = e.Body.Replace("(A)", Convert("innocent", "Innocent"));
e.Body = e.Body.Replace("(K)", Convert("kiss", "Kiss"));
e.Body = e.Body.Replace(":D", Convert("laughing", "Laughing"));
e.Body = e.Body.Replace("($)", Convert("money-mouth", "Money"));
e.Body = e.Body.Replace(":-#", Convert("sealed", "Sealed"));
e.Body = e.Body.Replace(":)", Convert("smile", "Smile"));
e.Body = e.Body.Replace(":-)", Convert("smile", "Smile"));
e.Body = e.Body.Replace(":-O", Convert("surprised", "Surprised"));
e.Body = e.Body.Replace(":P", Convert("tongue-out", "Tong"));
e.Body = e.Body.Replace("*-)", Convert("undecided", "Undecided"));
e.Body = e.Body.Replace(";-)", Convert("wink", "Wink"));
e.Body = e.Body.Replace("8o|", Convert("yell", "Yell"));
}
}
/// <summary>
/// Formats the anchor and inserts the right smiley image.
/// </summary>
private static string Convert(string name, string alt)
{
return string.Format(LINK, Utils.RelativeWebRoot, name, alt);
}
}