I am writing an updater program on in C# using Visual Studio 2008 on Windows 7. I would like the user to insert a USB thumb drive and if the program finds the drive and updates on the drive then it automatically copies them over. I desire to check only once at startup and then execute a program that is unaware of the updates (updates really need to happen with the program shutdown).
My issue is that the update program is being run before the thumb drive is being mounted so the computer detects no thumb drive and no updates and moves on prematurely. I want to have everything running as fast as possible but I need to force any thumb drives to mount before detection. Everything must be automatic with no input from the user.
Is this possible in c#?
Edit with more detail:
I currently run a batch file at startup (actually as the Windows 7 shell, but I'm not sure that makes a difference). The batch file runs the update check then the actual program. If the user had the USB drive stuck in at boot then I would like the updater to look at the drive and copy over any new files.
The current coded looks like:
DriveInfo[] ListDrives = DriveInfo.GetDrives();
foreach (DriveInfo Drive in ListDrives)
{
if(Drive.DriveType == DriveType.Removable)
{
// double check it's valid and copy over stuff
}
}
but it currently finds no drives at boot. If I run it later then everything is fine. I am assuming that since I run the updater so early it just hasn't had a chance to mount, but I don't just want to wait N seconds if I don't have to because under normal circumstances that's just dead time.
If I can do this check easy up from it is much simpler than having to continually monitor for an event and then shut everything down and do an update.
I would suggest a solution like the following one:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
/// <summary>
/// Represents our program class which contains the entry point of our application.
/// </summary>
public class Program
{
/// <summary>
/// Represents the entry point of our application.
/// </summary>
/// <param name="args">Possibly spcified command line arguments.</param>
public static void Main(string[] args)
{
RemovableDriveWatcher rdw = new RemovableDriveWatcher(); // Create a new instance of the RemoveableDriveWatcher class.
rdw.NewDriveFound += NewDriveFound; // Connect to the "NewDriveFound" event.
rdw.DriveRemoved += DriveRemoved; // Connect to the "DriveRemoved" event.
rdw.Start(); // Start watching.
// Do something here...
Console.ReadLine();
rdw.Stop(); // Stop watching.
}
/// <summary>
/// Is executed when a new drive has been found.
/// </summary>
/// <param name="sender">The sender of this event.</param>
/// <param name="e">The event arguments containing the changed drive.</param>
private static void NewDriveFound(object sender, RemovableDriveWatcherEventArgs e)
{
Console.WriteLine(string.Format("Found a new drive, the name is: {0}", e.ChangedDrive.Name));
}
/// <summary>
/// Is executed when a drive has been removed.
/// </summary>
/// <param name="sender">The sender of this event.</param>
/// <param name="e">The event arguments containing the changed drive.</param>
private static void DriveRemoved(object sender, RemovableDriveWatcherEventArgs e)
{
Console.WriteLine(string.Format("The drive with the name {0} has been removed.", e.ChangedDrive.Name));
}
}
The RemoveableDriveWatcher class looks like this:
/// <summary>
/// Repesents a watcher class for removable drives.
/// </summary>
public class RemovableDriveWatcher
{
/// <summary>
/// Represents the watcher thread which watches for new drives.
/// </summary>
private Thread watcherThread;
/// <summary>
/// Continas all found logical drives of this system.
/// </summary>
private List<DriveInfo> foundDrives;
/// <summary>
/// Initializes a new instance of the <see cref="RemovableDriveWatcher"/> class.
/// </summary>
public RemovableDriveWatcher()
{
this.foundDrives = new List<DriveInfo>();
this.watcherThread = new Thread(new ThreadStart(ScanLogicalDrives));
this.WaitBetweenScansDelay = 1000;
}
/// <summary>
/// Is fired if a new drive has been detected.
/// </summary>
public event EventHandler<RemovableDriveWatcherEventArgs> NewDriveFound;
/// <summary>
/// Is fired if a drive has been removed.
/// </summary>
public event EventHandler<RemovableDriveWatcherEventArgs> DriveRemoved;
/// <summary>
/// Gets or sets the delay in ms between two scans.
/// </summary>
public int WaitBetweenScansDelay
{
get;
set;
}
/// <summary>
/// Starts the watcher.
/// </summary>
public void Start()
{
if (!this.watcherThread.IsAlive)
{
this.watcherThread.Start();
}
}
/// <summary>
/// Stops the watcher.
/// </summary>
public void Stop()
{
if (this.watcherThread.IsAlive)
{
this.watcherThread.Abort();
this.watcherThread.Join();
}
}
/// <summary>
/// Scans for logical drives and fires an event every time a new
/// drive has been found or a drive was removed.
/// </summary>
private void ScanLogicalDrives()
{
DriveInfo[] drives;
do
{
drives = DriveInfo.GetDrives();
// Check for new drives
foreach (DriveInfo drive in drives)
{
if (!(drive.DriveType == DriveType.Removable))
{
continue;
}
if (!drive.IsReady)
{
continue;
}
if (!this.foundDrives.ContainsWithName(drive))
{
this.foundDrives.Add(drive);
if (this.NewDriveFound != null)
{
this.NewDriveFound(this, new RemovableDriveWatcherEventArgs(drives, drive));
}
}
}
// Check for removed drives
for (int i = this.foundDrives.Count - 1; i >= 0; i--)
{
DriveInfo drive = this.foundDrives[i];
if (!drives.ContainsWithName(drive))
{
if (this.DriveRemoved != null)
{
this.DriveRemoved(this, new RemovableDriveWatcherEventArgs(drives, drive));
}
this.foundDrives.RemoveWithName(drive);
}
}
// Sleep
Thread.Sleep(this.WaitBetweenScansDelay);
}
while (true);
}
}
For everything to work you need the RemovableDriveWatcherEventArgs:
/// <summary>
/// Represents the RemovableDriveWatcherEventArgs
/// </summary>
public class RemovableDriveWatcherEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="RemovableDriveWatcherEventArgs"/> class.
/// </summary>
/// <param name="allDrives">All currently available logical drives in the system.</param>
/// <param name="changedDrive">The changed drive.</param>
public RemovableDriveWatcherEventArgs(DriveInfo[] allDrives, DriveInfo changedDrive)
{
this.Drives = allDrives;
this.ChangedDrive = changedDrive;
}
/// <summary>
/// Gets the changed logical drive that has either been detected or removed.
/// </summary>
public DriveInfo ChangedDrive { get; private set; }
/// <summary>
/// Gets all currently available logical drives.
/// </summary>
public DriveInfo[] Drives { get; private set; }
}
And of course the Extensions:
/// <summary>
/// Contains extensions used by the RemovableDriveWatcher class.
/// </summary>
public static class RemovableDriveWatcherExtensions
{
/// <summary>
/// Extends the DiveInfo[] by the ContainsWithName method.
/// </summary>
/// <param name="all">The array where we want to find the specified instance.</param>
/// <param name="search">The instance which we want to find in the array.</param>
/// <returns>TRUE if the specified instance was found, FALSE if the specified instance was not found.</returns>
public static bool ContainsWithName(this DriveInfo[] all, DriveInfo search)
{
for (int i = 0; i < all.Length; i++)
{
if (all[i].Name == search.Name)
{
return true;
}
}
return false;
}
/// <summary>
/// Extends the List<DriveInfo> by the ContainsWithName method.
/// </summary>
/// <param name="all">The array where we want to find the specified instance.</param>
/// <param name="search">The instance which we want to find in the list.</param>
/// <returns>TRUE if the specified instance was found, FALSE if the specified instance was not found.</returns>
public static bool ContainsWithName(this List<DriveInfo> all, DriveInfo search)
{
for (int i = 0; i < all.Count; i++)
{
if (all[i].Name == search.Name)
{
return true;
}
}
return false;
}
/// <summary>
/// Extends the List<DriveInfo> by the RemoveWithName method.
/// </summary>
/// <param name="all">The array where we want to removed the specified instance.</param>
/// <param name="search">The instance which we want to remove in the list.</param>
public static void RemoveWithName(this List<DriveInfo> all, DriveInfo search)
{
for (int i = 0; i < all.Count; i++)
{
if (all[i].Name == search.Name)
{
all.RemoveAt(i);
return;
}
}
}
}
I hope this helps a little bit.
You don't give much detail, but it seems likely you could call DriveInfo.GetDrives() which returns an array of type DriveInfo[]
DriveInfo has an IsReady() method. Presumably once you check that the drive is ready, you can look for a well-known file on the USB drive() to verify they have mounted the correct USB
You could poll in a loop till you find what you want, but if you don't find what you want in say 60 seconds, you will need to notify the user that you can't find the USB drive you need.
I do not see a ready check inside of the if statement. According to MSDN:
IsReady indicates whether a drive is ready. For example, it indicates
whether a CD is in a CD drive or whether a removable storage device is
ready for read/write operations. If you do not test whether a drive is
ready, and it is not ready, querying the drive using DriveInfo will
raise an IOException.
Are you checking for an IOException? I do not see an IsReady event so you may have to spinwait or hook into the lower level Windows API to find an event to indicate drive readiness. Here's an idea for the meantime:
try
{
DriveInfo[] ListDrives = DriveInfo.GetDrives();
foreach (DriveInfo Drive in ListDrives)
{
if(!Drive.IsReady)//spin
if(Drive.DriveType == DriveType.Removable)
{
// double check it's valid and copy over stuff
}
}
}
catch(IOException ex)//...
I don't have any way to test this right now. Please let me know how it works out for you or if there's more details I need to be aware of.
However, because you are starting this process on startup there's always the possibility that IsReady will not be sufficient and once again you may have to find something else (Windows API I imagine). I have not discovered any documentation that say's anything to the effect.
Related
I have a Powershell script that requires user interaction. I can call powershell.exe from C# using System.Diagnostics.Process and pass the scripts path as a parameter but I would like the script to be an embedded resource of the project. I tried creating a Runspace (see below) and running the script but because the script requires user interaction I receive an exception.
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "mynamespace.myscriptfile.ps1";
string result = "";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
result = reader.ReadToEnd();
Console.WriteLine(result);
}
//Create Powershell Runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
// Create pipeline and add commands
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(result);
// Execute Script
Collection<PSObject> results = new Collection<PSObject>();
try
{
results = pipeline.Invoke();
}
catch (Exception ex)
{
results.Add(new PSObject((object)ex.Message));
}
runspace.Close();
Console.ReadKey();
Is there a way to either pass the embedded resource to powershell.exe using System.Diagnostics.Process or is there a way to Invoke the script from C# where the user can interact?
UPDATE:
It seems to me that I may be able to use an implementation of the abstract class PSHost along with using the PSHostUserInterface property correctly, I may be able to create a Runspace that takes the PSHost implementation as a parameter to use the native Powershell console. I have been trying to test the idea but I'm not quite sure how to implement the abstract class.
Below is a sample of code that I obtained from Microsoft. I am confused with a couple of things. If it matters I will be creating the Runspace in a console application with a namespace called: WebRequirements in the Program class.
private Host01 program; (Would Host01 be Program?)
PSHostUserInterface (Is this where I would dictate that I want to use a native Powershell host and if so how would I do that?)
internal class MyHost : PSHost
{
///
/// A reference to the PSHost implementation.
///
private Host01 program;
/// <summary>
/// The culture information of the thread that created
/// this object.
/// </summary>
private CultureInfo originalCultureInfo =
System.Threading.Thread.CurrentThread.CurrentCulture;
/// <summary>
/// The UI culture information of the thread that created
/// this object.
/// </summary>
private CultureInfo originalUICultureInfo =
System.Threading.Thread.CurrentThread.CurrentUICulture;
/// <summary>
/// The identifier of this PSHost implementation.
/// </summary>
private Guid myId = Guid.NewGuid();
/// <summary>
/// Initializes a new instance of the MyHost class. Keep
/// a reference to the host application object so that it
/// can be informed of when to exit.
/// </summary>
/// <param name="program">
/// A reference to the host application object.
/// </param>
public MyHost(Host01 program)
{
this.program = program;
}
/// <summary>
/// Return the culture information to use. This implementation
/// returns a snapshot of the culture information of the thread
/// that created this object.
/// </summary>
public override System.Globalization.CultureInfo CurrentCulture
{
get { return this.originalCultureInfo; }
}
/// <summary>
/// Return the UI culture information to use. This implementation
/// returns a snapshot of the UI culture information of the thread
/// that created this object.
/// </summary>
public override System.Globalization.CultureInfo CurrentUICulture
{
get { return this.originalUICultureInfo; }
}
/// <summary>
/// This implementation always returns the GUID allocated at
/// instantiation time.
/// </summary>
public override Guid InstanceId
{
get { return this.myId; }
}
/// <summary>
/// Return a string that contains the name of the host implementation.
/// Keep in mind that this string may be used by script writers to
/// identify when your host is being used.
/// </summary>
public override string Name
{
get { return "MySampleConsoleHostImplementation"; }
}
/// <summary>
/// This sample does not implement a PSHostUserInterface component so
/// this property simply returns null.
/// </summary>
public override PSHostUserInterface UI
{
get { return null; }
}
/// <summary>
/// Return the version object for this application. Typically this
/// should match the version resource in the application.
/// </summary>
public override Version Version
{
get { return new Version(1, 0, 0, 0); }
}
/// <summary>
/// Not implemented by this example class. The call fails with
/// a NotImplementedException exception.
/// </summary>
public override void EnterNestedPrompt()
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// Not implemented by this example class. The call fails
/// with a NotImplementedException exception.
/// </summary>
public override void ExitNestedPrompt()
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This API is called before an external application process is
/// started. Typically it is used to save state so the parent can
/// restore state that has been modified by a child process (after
/// the child exits). In this example, this functionality is not
/// needed so the method returns nothing.
/// </summary>
public override void NotifyBeginApplication()
{
return;
}
/// <summary>
/// This API is called after an external application process finishes.
/// Typically it is used to restore state that a child process may
/// have altered. In this example, this functionality is not
/// needed so the method returns nothing.
/// </summary>
public override void NotifyEndApplication()
{
return;
}
/// <summary>
/// Indicate to the host application that exit has
/// been requested. Pass the exit code that the host
/// application should use when exiting the process.
/// </summary>
/// <param name="exitCode">The exit code to use.</param>
public override void SetShouldExit(int exitCode)
{
this.program.ShouldExit = true;
this.program.ExitCode = exitCode;
}
}
For a time-critical media presentation application, it is important that media files be presented right at the instance when the user selects it. Those files reside in a truly humongous directory structure, comprised of thousands of media files.
Clearly, caching the media files in a MemoryStream is the way to go; however, due to the sheer amount of files, it’s not feasible to cache each file entirely. Instead, my idea is to pre-cache a certain buffer of each file, and once the file is presented, play from that cache until the rest of the file is loaded from the hard disk.
What I don’t see is how to “concatenate” both the MemoryStream and the FileStream so as to provide a seamless playback experience. I’m not very strong in data streams (yet), and I see several problems:
How does one keep track of the current read position within the MemoryStream and provide that to the FileStream without the MemoryStream reading more than that?
How does one switch from one stream to the other without having both streams either partially overlap each other or creating a “playback break”?
If using a queue of streams (as suggested in How do I concatenate two System.Io.Stream instances into one?), how can I specify that the second stream has to be ready for read access instantaneously after the first stream is done? Here, in particular, I don’t see how the MemoryStream would help at all, since the FileStream, as second one in the queue, would only begin accessing the hard disk once it’s actually used.
Is it really a feasible approach to have literally hundreds, if not thousands of open streams at once?
Note that I don’t need write access—reading is fully sufficient for the problem at hand. Also, this question is similar to Composite Stream Wrapper providing partial MemoryStream and full original Stream, but the solution provided there is a bug fix for Windows Phone 8 that doesn’t apply in my case.
I’d very much like to widen my rather limited understanding of this, so any help is greatly appreciated.
I would suggest something like the following solution:
Inherit your own CachableFileStream from FileStream
Implement a very simple Cache which uses a data structure you prefer (like a Queue)
Allow Preloading data into the internal cache
Allow Reloading data into the internal cache
Modify the original Read behaviour in a way, that your cache is used
To give you an idea of my idea I would suggest some implementation like the following one:
The usage could be like that:
CachableFileStream cachedStream = new CachableFileStream(...)
{
PreloadSize = 8192,
ReloadSize = 4096,
};
// Force preloading data into the cache
cachedStream.Preload();
...
cachedStream.Read(buffer, 0, buffer.Length);
...
Warning: The code below is neither correctly tested nor ideal - this shall just give you an idea!
The CachableFileStream class:
using System;
using System.IO;
using System.Threading.Tasks;
/// <summary>
/// Represents a filestream with cache.
/// </summary>
public class CachableFileStream : FileStream
{
private Cache<byte> cache;
private int preloadSize;
private int reloadSize;
/// <summary>
/// Gets or sets the amount of bytes to be preloaded.
/// </summary>
public int PreloadSize
{
get
{
return this.preloadSize;
}
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value), "The specified preload size must not be smaller than or equal to zero.");
this.preloadSize = value;
}
}
/// <summary>
/// Gets or sets the amount of bytes to be reloaded.
/// </summary>
public int ReloadSize
{
get
{
return this.reloadSize;
}
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value), "The specified reload size must not be smaller than or equal to zero.");
this.reloadSize = value;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="CachableFileStream"/> class with the specified path and creation mode.
/// </summary>
/// <param name="path">A relative or absolute path for the file that the current CachableFileStream object will encapsulate</param>
/// <param name="mode">A constant that determines how to open or create the file.</param>
/// <exception cref="System.ArgumentException">
/// Path is an empty string (""), contains only white space, or contains one or more invalid characters.
/// -or- path refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in an NTFS environment.
/// </exception>
/// <exception cref="System.NotSupportedException">
/// Path refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in a non-NTFS environment.
/// </exception>
/// <exception cref="System.ArgumentNullException">
/// Path is null.
/// </exception>
/// <exception cref="System.Security.SecurityException">
/// The caller does not have the required permission.
/// </exception>
/// <exception cref="System.IO.FileNotFoundException">
/// The file cannot be found, such as when mode is FileMode.Truncate or FileMode.Open, and the file specified by path does not exist.
/// The file must already exist in these modes.
/// </exception>
/// <exception cref="System.IO.IOException">
/// An I/O error, such as specifying FileMode.CreateNew when the file specified by path already exists, occurred.-or-The stream has been closed.
/// </exception>
/// <exception cref="System.IO.DirectoryNotFoundException">
/// The specified path is invalid, such as being on an unmapped drive.
/// </exception>
/// <exception cref="System.IO.PathTooLongException">
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Mode contains an invalid value
/// </exception>
public CachableFileStream(string path, FileMode mode) : base(path, mode)
{
this.cache = new Cache<byte>();
this.cache.CacheIsRunningLow += CacheIsRunningLow;
}
/// <summary>
/// Reads a block of bytes from the stream and writes the data in a given buffer.
/// </summary>
/// <param name="array">
/// When this method returns, contains the specified byte array with the values between
/// offset and (offset + count - 1) replaced by the bytes read from the current source.
/// </param>
/// <param name="offset">The byte offset in array at which the read bytes will be placed.</param>
/// <param name="count">The maximum number of bytes to read.</param>
/// <returns>
/// The total number of bytes read into the buffer. This might be less than the number
/// of bytes requested if that number of bytes are not currently available, or zero
/// if the end of the stream is reached.
/// </returns>
/// <exception cref="System.ArgumentNullException">
/// Array is null.
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Offset or count is negative.
/// </exception>
/// <exception cref="System.NotSupportedException">
/// The stream does not support reading.
/// </exception>
/// <exception cref="System.IO.IOException">
/// An I/O error occurred.
/// </exception>
/// <exception cref="System.ArgumentException">
/// Offset and count describe an invalid range in array.
/// </exception>
/// <exception cref="System.ObjectDisposedException">
/// Methods were called after the stream was closed.
/// </exception>
public override int Read(byte[] array, int offset, int count)
{
int readBytesFromCache;
for (readBytesFromCache = 0; readBytesFromCache < count; readBytesFromCache++)
{
if (this.cache.Size == 0)
break;
array[offset + readBytesFromCache] = this.cache.Read();
}
if (readBytesFromCache < count)
readBytesFromCache += base.Read(array, offset + readBytesFromCache, count - readBytesFromCache);
return readBytesFromCache;
}
/// <summary>
/// Preload data into the cache.
/// </summary>
public void Preload()
{
this.LoadBytesFromStreamIntoCache(this.PreloadSize);
}
/// <summary>
/// Reload data into the cache.
/// </summary>
public void Reload()
{
this.LoadBytesFromStreamIntoCache(this.ReloadSize);
}
/// <summary>
/// Loads bytes from the stream into the cache.
/// </summary>
/// <param name="count">The number of bytes to read.</param>
private void LoadBytesFromStreamIntoCache(int count)
{
byte[] buffer = new byte[count];
int readBytes = base.Read(buffer, 0, buffer.Length);
this.cache.AddRange(buffer, 0, readBytes);
}
/// <summary>
/// Represents the event handler for the CacheIsRunningLow event.
/// </summary>
/// <param name="sender">The sender of the event.</param>
/// <param name="e">Event arguments.</param>
private void CacheIsRunningLow(object sender, EventArgs e)
{
this.cache.WarnIfRunningLow = false;
new Task(() =>
{
Reload();
this.cache.WarnIfRunningLow = true;
}).Start();
}
}
The Cache class:
using System;
using System.Collections.Concurrent;
/// <summary>
/// Represents a generic cache.
/// </summary>
/// <typeparam name="T">Defines the type of the items in the cache.</typeparam>
public class Cache<T>
{
private ConcurrentQueue<T> queue;
/// <summary>
/// Is executed when the number of items within the cache run below the
/// specified warning limit and WarnIfRunningLow is set.
/// </summary>
public event EventHandler CacheIsRunningLow;
/// <summary>
/// Gets or sets a value indicating whether the CacheIsRunningLow event shall be fired or not.
/// </summary>
public bool WarnIfRunningLow
{
get;
set;
}
/// <summary>
/// Gets or sets a value that represents the lower warning limit.
/// </summary>
public int LowerWarningLimit
{
get;
set;
}
/// <summary>
/// Gets the number of items currently stored in the cache.
/// </summary>
public int Size
{
get;
private set;
}
/// <summary>
/// Initializes a new instance of the <see cref="Cache{T}"/> class.
/// </summary>
public Cache()
{
this.queue = new ConcurrentQueue<T>();
this.Size = 0;
this.LowerWarningLimit = 1024;
this.WarnIfRunningLow = true;
}
/// <summary>
/// Adds an item into the cache.
/// </summary>
/// <param name="item">The item to be added to the cache.</param>
public void Add(T item)
{
this.queue.Enqueue(item);
this.Size++;
}
/// <summary>
/// Adds the items of the specified array to the end of the cache.
/// </summary>
/// <param name="items">The items to be added.</param>
public void AddRange(T[] items)
{
this.AddRange(items, 0, items.Length);
}
/// <summary>
/// Adds the specified count of items of the specified array starting
/// from offset to the end of the cache.
/// </summary>
/// <param name="items">The array that contains the items.</param>
/// <param name="offset">The offset that shall be used.</param>
/// <param name="count">The number of items that shall be added.</param>
public void AddRange(T[] items, int offset, int count)
{
for (int i = offset; i < count; i++)
this.Add(items[i]);
}
/// <summary>
/// Reads one item from the cache.
/// </summary>
/// <returns>The item that has been read from the cache.</returns>
/// <exception cref="System.InvalidOperationException">
/// The cache is empty.
/// </exception>
public T Read()
{
T item;
if (!this.queue.TryDequeue(out item))
throw new InvalidOperationException("The cache is empty.");
this.Size--;
if (this.WarnIfRunningLow &&
this.Size < this.LowerWarningLimit)
{
this.CacheIsRunningLow?.Invoke(this, EventArgs.Empty);
}
return item;
}
/// <summary>
/// Peeks the next item from cache.
/// </summary>
/// <returns>The item that has been read from the cache (without deletion).</returns>
/// <exception cref="System.InvalidOperationException">
/// The cache is empty.
/// </exception>
public T Peek()
{
T item;
if (!this.queue.TryPeek(out item))
throw new InvalidOperationException("The cache is empty.");
return item;
}
}
I hope this helps, have fun ;-)
I'm newer to coding, as a warning not an excuse. I currently have a quick timer class 'wrapper' I use. While most of the time I've used it, it worked as expected, I now have an issue.
The code the timer calls back does not always have a set or even accurate estimate of how long it will take to execute that chunk of code start to finish. This leaves me with abnormally long timer update intervals to account for it, or, alternatively stop the timer, and start it again each time it 'ticks' to prevent over-lap.
I've found the first method unacceptable in my case. The second one sounded perfect, but when I tried it it slowed down the code a lot and could have easily ticked much more often after I reviewed it. I'm not sure why it slows it down so much.
Should I look at another way at doing this, besides timers, like some kind of manual control over the flow of events? This is for a simple game-update loop class. Here is said class in full below.
public class TimedUpdater<T>
{
#region Public Delegates/Events
/// <summary>
/// An event that is raised repeatedly at the Interval[in milliseconds] for this Instance.
/// </summary>
public event EventHandler<T> OnUpdate;
#endregion
#region Fields, Private Properties
private Timer Timer { get; }
private TimerCallback Callback { get; }
private T State { get; }
#endregion
#region Constructors, Destructors
/// <summary>
/// Initializes a new instance of the <see cref="TimedUpdater{T}" /> class.
/// </summary>
/// <param name="state">The state object to use for updating data.</param>
/// <param name="name">The unique name representing this instance.</param>
/// <param name="updateRateMs">The rate the the <code>OnUpdate</code> event is raised in milliseconds.</param>
public TimedUpdater(T state, string name, int updateRateMs)
{
Name = name;
Interval = updateRateMs;
State = state;
Callback += Process;
Timer = new Timer(Callback, State, Timeout.Infinite, Timeout.Infinite);
}
/// <summary>
/// Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage
/// collection.
/// </summary>
~TimedUpdater()
{
Dispose();
}
#endregion
#region Public Properties, Indexers
/// <summary>
/// Gets or sets the interval in milliseconds.
/// </summary>
/// <value>The interval in milaseconds.</value>
public int Interval { get; set; }
/// <summary>
/// States if the updater is enabled.
/// </summary>
public bool IsEnabled { get; private set; }
/// <summary>
/// Gets the unique name that represents this instance.
/// </summary>
/// <value>The name.</value>
public string Name { get; }
#endregion
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Disable();
Timer.Dispose();
}
/// <summary>
/// Enables the updater.
/// </summary>
public void Enable()
{
Timer.Change(Interval, Interval);
IsEnabled = true;
}
/// <summary>
/// Disables the updater.
/// </summary>
public void Disable()
{
Timer.Change(int.MaxValue, Interval);
IsEnabled = false;
}
/// <summary>
/// Processes the specified object casted to the type.
/// </summary>
/// <param name="e">The e.</param>
protected virtual void Process(object e)
{
OnUpdate?.Invoke(this, (T) e);
}
}
I am coding a parallel clustering algorithm in C#. I have a class called Classification which currently uses a Dictionary to associate a String label with a set of N-dimensional points. Each label has one or more points in its cluster. Classification also has a reverse index that associates each point with its label, which is in a second Dictionary.
The most expensive method currently performs many merge operations. A merge takes all members of a source cluster and moves them to the same target cluster. This operation updates both Dictionaries.
I have multiple processes that identify points that belong together in the same cluster. This relationship is transitive: if A belongs with B and B with C, then A, B and C belong in the same cluster. However, there is no easy way to segment the problem such that each process works on a disjoint subset of points. The processes will be operating on the same points.
How do I implement the merge method to minimize lock contention and assure atomic operations? Here is a portion of my class definition. The final method, Merge, is the one I want to parallelize. That may require me to change my Dictionaries into parallel collections.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Cluster
{
/// <summary>
/// Represent points grouped into class partitions where each point is associated with a class label
/// and each labeled class has a set of points.
///
/// This does not perform the unassisted clustering of points.
/// </summary>
/// <typeparam name="TPoint">Type of the points to be classified.</typeparam>
/// <typeparam name="TLabel">Type of the label that can be associated with a set within the classification.</typeparam>
public class Classification<TPoint, TLabel> where TLabel : IEquatable<TLabel>
{
#region Properties (LabelToPoints, PointToLabel, NumPartitions, NumPoints)
/// <summary>
/// Associates a class label with the points belonging to that class.
/// </summary>
public Dictionary<TLabel, ISet<TPoint>> LabelToPoints { get; set; }
/// <summary>
/// Associates a point with the label for its class.
/// </summary>
private Dictionary<TPoint, TLabel> PointToLabel { get; set; }
/// <summary>
/// Total number of class partitions that points are divided among.
/// </summary>
public int NumPartitions { get { return LabelToPoints.Count; } }
/// <summary>
/// Total number of points among all partitions.
/// </summary>
public int NumPoints { get { return PointToLabel.Count; } }
#endregion
#region Constructors
public Classification()
{
LabelToPoints = new Dictionary<TLabel, ISet<TPoint>>();
PointToLabel = new Dictionary<TPoint, TLabel>();
}
public Classification(IEnumerable<TPoint> points, Func<TPoint,TLabel> startingLabel) : this()
{
foreach (var point in points)
{
Add(point, startingLabel(point));
}
}
/// <summary>
/// Create a new Classification by randomly selecting a subset of the points in the current Classification.
/// </summary>
/// <param name="sampleSize">Number of points to include in the new Classification.</param>
/// <returns>A new Classification that has the given number of points.</returns>
public Classification<TPoint, TLabel> Sample(int sampleSize)
{
var subset = new Classification<TPoint, TLabel>();
foreach (var point in Points().TakeRandom(sampleSize, NumPoints))
subset.Add(point, GetClassLabel(point));
return subset;
}
#endregion
#region Modify the Classification (Add, Remove, Merge)
/// <summary>
/// Add a point to the classification with the associated label.
///
/// If the point was already classified, its old classification is removed.
/// </summary>
/// <param name="p">Point to add.</param>
/// <param name="classLabel">Label for classification.</param>
public void Add(TPoint p, TLabel classLabel)
{
Remove(p);
PointToLabel[p] = classLabel;
EnsurePartition(classLabel).Add(p);
}
private ISet<TPoint> EnsurePartition(TLabel classLabel)
{
ISet<TPoint> partition;
if (LabelToPoints.TryGetValue(classLabel, out partition)) return partition;
partition = new HashSet<TPoint>();
LabelToPoints[classLabel] = partition;
return partition;
}
/// <summary>
/// Remove a point from its class.
/// </summary>
/// <param name="p">Point to remove.</param>
/// <returns>True if the point was removed, false if it was not previously a member of any class.</returns>
public Boolean Remove(TPoint p)
{
TLabel label;
if (!PointToLabel.TryGetValue(p, out label)) return false;
PointToLabel.Remove(p);
var oldPoints = LabelToPoints[label];
var didRemove = oldPoints.Remove(p);
if (oldPoints.Count == 0)
LabelToPoints.Remove(label);
return didRemove;
}
/// <summary>
/// Merge all the members of the partitions labeled by any of the labels in sourceLabels
/// into the partition indicated by targetLabel.
/// </summary>
/// <param name="targetLabel">Move members into this labeled partition.</param>
/// <param name="sourceLabels">Move members out of these labeled partitions.</param>
public void Merge(TLabel targetLabel, IEnumerable<TLabel> sourceLabels)
{
var targetPartition = EnsurePartition(targetLabel);
foreach (var sourceLabel in sourceLabels.Where(sLabel => !sLabel.Equals(targetLabel)))
{
ISet<TPoint> singleSourcePoints;
if (!LabelToPoints.TryGetValue(sourceLabel, out singleSourcePoints)) continue;
// Add to LabelToPoints under new targetLabel
targetPartition.UnionWith(singleSourcePoints);
// Remove from LabelToPoints under old sourceLabel.
LabelToPoints.Remove(sourceLabel);
foreach (var p in singleSourcePoints)
PointToLabel[p] = targetLabel;
}
}
#endregion
}
}
UPDATE 1:
I am sure that changing the Dictionaries to ConcurrentDictionary is not enough. I think I need to take out locks on the target label and all the source labels. If any other process is in the middle of performing a merge with any of those labels as source or target, then I need to wait until that other process is done. How do you do this without creating deadlocks? I can sort the labels alphabetically to try and ensure the same order, but is that enough?
UPDATE 2:
New approach. I realized that the merge operations are order independent. I will create a multiple producer, single consumer queue. The producers find opportunities to merge, while the consumer serializes access to the Classification object. Since performing the merges takes longer than finding the merge candidates, the consumer won't starve. A better design would get better throughput, but this will still be an improvement.
My friend and I have been working on a game and we have a trace listener set up, here's some of the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
namespace GuiGame {
/// <summary>
/// A type of Trace Listener that sends its output to a ListBox.
/// </summary>
public class ListBoxTraceListener : TraceListener {
private ListBox listBox; // A reference to the listbox that we're writing to.
private string stringToAddToListBox = "";
private const char NEW_LINE = '\n';
/// <summary>
/// Parameterless constructor.
/// Do not want the generic default constructor to be used
/// as there is no way to set the ListBoxTraceListener's data.
/// This replaces the compiler's generic default constructor.
/// Pre: none
/// Post: ALWAYS throws an ArgumentException.
/// </summary>
/// <remarks>NOT TO BE USED!</remarks>
public ListBoxTraceListener() {
throw new ArgumentException("Parameterless constructor invalid.");
} // end ListBoxTraceListener constructor
/// <summary>
/// Constructor with initialising parameters.
/// Pre: the existence of a ListBox on a GUI form.
/// Post: initialised object.
/// </summary>
/// <param name="listBox">The ListBox that we're writing to.</param>
public ListBoxTraceListener(ListBox listBox) {
this.listBox = listBox;
}
/// <summary>
/// Automatically collects the outputs from all Trace.WriteLine statements.
/// Pre: none.
/// Post: the string s is displayed in the listBox.
/// </summary>
/// <param name="s"></param>
public override void WriteLine(string s) {
Write(s + NEW_LINE);
} //end WriteLine
/// <summary>
/// Automatically collects the outputs from all Trace.Write statements.
/// Pre: none.
/// Post: the string s is displayed in the listBox, once we receive a NEW_LINE.
/// </summary>
/// <param name="s"></param>
public override void Write(string s) {
stringToAddToListBox += s;
// If we have one or more complete lines
if (stringToAddToListBox.Contains (NEW_LINE)) {
// Split the string into multiple lines.
// If NEW_LINE is found at the beginning or end of the string,
// then the corresponding array element contains an empty string.
string[] lines = stringToAddToListBox.Split(NEW_LINE);
// Add all the lines to the listbox, except for the last one.
// When stringToAddToListBox has a new-line at the end,
// the last element in lines[] will be an empty string.
int highestLineNumber = lines.Length - 1;
for (int i = 0; i < highestLineNumber; i++) {
AddToListBox(lines[i]);
}
// Reset stringToAddToListBox to what remains. (May be an empty string).
stringToAddToListBox = lines[highestLineNumber];
}
} // end Write
/// <summary>
/// Adds a complete output-line to the ListBox.
/// Pre: none.
/// Post: the string listBoxLine is displayed in the listBox .
/// </summary>
/// <param name="listBoxLine"></param>
private void AddToListBox(string listBoxLine) {
Debug.Assert(listBox != null, "listBox != null");
listBox.Items.Add(listBoxLine);
} // end AddToListBox
}
}
At this stage we are just trying to use the trace listener to output some text on in the ListBox so we know it is working, so we have an event handler setup:
private void RollDiceButton_Click(object sender, EventArgs e)
{
}
We haven't been able to get any output from the trace listener. The Add method as the trace listener is not set up for that. Can anyone provide some suggestions please? I think maybe we are doing something really stupid and obvious that we have missed.
The most probable cause of your issue is that your application is single threaded (as most simple Windows applications are). This means that although you are sending messages to the listview to append a new element to the list, the message is not yet handled just because your original handler hasn't returned yet (the RollDiceButton_Click).
To work this around, you should force the list to refresh itself from within the current handler:
private void AddToListBox(string listBoxLine) {
Debug.Assert(listBox != null, "listBox != null");
listBox.Items.Add(listBoxLine);
// this would help?
listBox.Refresh();
} // end
If this doesn't help, try temporarily switching to unconditional processing of all pending events with
private void AddToListBox(string listBoxLine) {
Debug.Assert(listBox != null, "listBox != null");
listBox.Items.Add(listBoxLine);
// this would help?
Application.DoEvents();
} // end
and report back.