29 Nov 2012 @ 3:01 PM 

One of the big reasons I’ve found myself preferring C# Over Java is that C# is about a million times easier to refactor. By this, I mean that often times you can make rather large changes to a codebase without changing very much other code. This is possible because C# has features such as Properties (which allow you to encapsulate logic on what appears to be a variable access), operator overloads, and casting overloads (though those sort of count towards the previous point). Java doesn’t have these features, so I’ve found changes to the core logic of a program can cause reverberations throughout the entire codebase as every piece of logic that referred to the previous structure is made to refer to the new stuff. If you suddenly decide that a field needs logic, you need to create an Accessor method for it, and then you need to change all the references to the aforementioned field. This is easiest to accomplish by renaming the original field name so your compiler errors point to where you used it and changing each of those. C#, in contrast, let’s you change a field into a Property with very little impact on the code within that codebase. it does, of course, have the effect of requiring any dependent assemblies be recompiled and naturally any reflection that uses the aforementioned field will need to instead look for a property, but it will require very little actual code changes.

In a Previous post I discussed and provided a class that allowed the relatively transparent replacement of core fields in a class definition with a generic type that made the in-memory representation of that type obfuscated. This allowed a lot of logic to be executed transparently from code that used to simply access the field. Naturally this did have performance implications, but the fact that it’s possible to make such class definitions and their uses transparent aside from changing the declaration is one of my favourite features of C#. Combine this with the fact that changing, modifying, and refactoring existing code comprises a large portion of our work as developers and you have a recipe for making those tasks far less of a chore.

.NET 4.0 added quite a number of helpful little classes that “wrap” another type. It’s a very useful capability of the language. Since they don’t usually meverage .NET 4.0 or 4.5 features, you can often backport the class functionality. Arguably the only reason they are even in the library now is because they became something of a fundamental class in numerous available libraries.

One of these such classes is the ThreadLocal<T> type (ThreadLocal(Of T) for VB.NET, naturally). The purpose of this is to have a variable who’s value is separate on different threads. This can be useful for any number of different reasons. .NET 4.0 adds this class to the System.Threading namespace. The functionality, of course, can be useful in previous Frameworks, so it can be necessary to reconstruct the class.

That is what I’ll be doing here. The ThreadLocal&ltT&gt type has a relatively simple interface; it implements IDisposable and contains only a few methods, so it’s ripe for duplication for use in previous framework versions for which the Base Class Library lacks it. I will call my implementation “ThreadVariable”, but it could simply be called ThreadLocal instead, allowing code written using the variable for use in the BCL to work in previous framework versions (other framework differences notwithstanding, of course).

  1.  
  2.     /// <summary>
  3.     /// Class used to duplicate the functionality provided by the .NET Framework 4.0 and 4.5′s System.Threading.ThreadLocal wrapper type.
  4.     /// This implements instance and static types that are thread local and refer to different instances across threads, even though the actual
  5.     /// instance type that the class is a aggregate of might be the same.
  6.     /// </summary>
  7.     /// <typeparam name="T">Variable type to wrap in thread selection logic</typeparam>
  8.     class ThreadVariable<T> : IDisposable
  9.     {
  10.         //private dictionary indexing the values used in different threads.
  11.         private Dictionary<Thread, T> ThreadData = new Dictionary<Thread, T>();
  12.         //factory routine used to initialize the value when used initially in a new thread.
  13.         private Func<T> FactoryRoutine = () => default(T);
  14.  
  15.  
  16.         public ThreadVariable()
  17.         {
  18.  
  19.  
  20.  
  21.         }
  22.         public bool isValueCreated()
  23.         {
  24.             return ThreadData.ContainsKey(Thread.CurrentThread);
  25.  
  26.            
  27.         }
  28.         public ThreadVariable(Func<T> factory)
  29.         {
  30.             FactoryRoutine = factory;
  31.         }
  32.         public T Value {
  33.             get
  34.             {
  35.                 lock (ThreadData) {
  36.                 if (!ThreadData.ContainsKey(Thread.CurrentThread))
  37.                     ThreadData.Add(Thread.CurrentThread, FactoryRoutine());
  38.  
  39.                 return ThreadData [Thread.CurrentThread] ;
  40.             }
  41.             }
  42.  
  43.  
  44.  
  45.             set
  46.             {
  47.                 lock (ThreadData)
  48.                 {
  49.                     ThreadData [Thread.CurrentThread]  = value;
  50.                 }
  51.  
  52.             }
  53.         }
  54.  
  55.  
  56.         public static implicit operator T(ThreadVariable<T> source)
  57.         {
  58.  
  59.             return source.Value;
  60.  
  61.         }
  62.  
  63.  
  64.         public void Dispose()
  65.         {
  66.             ThreadData = null;
  67.         }
  68.     }
  69.  

242 total views, 1 views today

Posted By: BC_Programming
Last Edit: 29 Nov 2012 @ 03:02 PM

EmailPermalinkComments (0)
Tags
 03 May 2012 @ 3:58 PM 

Naturally, as we write programs we create a small set of useful functions. BASeBlock has been no exception. I’ve created quite a few functions that offer generic functionality that could be used elsewhere. Here, I share some of them.

Value Clamping

Clamping values is a very common activity. It started to get on my nerves, repeating code to make sure a given value was within a range. As a result I conceived of a generic function that could be used for any IComparable.

  1.  
  2.  
  3.  public static T ClampValue<T> (T Value, T min, T max)  where T:IComparable
  4.     {
  5.             //cast to IComparable
  6.         IComparable cvalue = (IComparable)Value;
  7.         IComparable cmin = (IComparable)min;
  8.         IComparable cmax = (IComparable)max;
  9.             //return (T)(cvalue.CompareTo(cmin)< 0 ?cmin:cvalue.CompareTo(cmax)>0?max:Value);
  10.         if (cvalue.CompareTo(cmin) < 0)
  11.         {
  12.             return min;
  13.         }
  14.        else if (cvalue.CompareTo(cmax) > 0)
  15.         {
  16.             return max;
  17.         }
  18.             return Value;
  19.     }
  20.  
  21.  

The basic idea is fairly simple. First, in order to clamp the value, we will need to be able to compare them, so we constrain the function to accepting only type T’s that implement that interface. The first step is casting each value to an IComparable; then we use those variables to compare and return the appropriate value. if the value is larger than max, max is returned; if it is smaller than min, min is returned. otherwise, value is returned unchanged. This function is most useful for numbers, but it can also have interesting implications and usage cases for other classes that are comparable, even strings.

choosing N items from a Enumerable list of S

This also came up quite a lot- some parts of the game needed to randomly choose some set of values from a larger set of values. Naturally this gave birth to another generic routine for the purpose:

  1.  
  2.  
  3. public static T []  Choose<T>(IEnumerable<T> ChooseArray, int numselect)
  4.  
  5. {
  6.     Random rgen = new Random();
  7.     T []  returnarray = new T [numselect] ;
  8.     SortedList<double , T> sorttest = new SortedList<double, T>();
  9.     foreach (T loopvalue in ChooseArray)
  10.          sorttest.Add(rgen.NextDouble(), loopvalue);
  11.        
  12.  
  13.     var usearray = sorttest.ToArray();
  14.    for (int i = 0; i < numselect; i++)
  15.     {
  16.         returnarray [i]  = usearray [i] .Value;
  17.     }
  18.  
  19.     return returnarray;
  20.  
  21.  
  22.  
  23.  
  24.  
  25. }
  26.  
  27.  

The idea is simple- make a SortedList that sorts the given listing using a random value as a key, then take the top numselect items off the top. This code will not work properly if numselect is larger than the size of the enumeration, but using count to clamp the size of the array would enumerate twice.

It’s probably possible to make this faster- possibly much faster- since we only need numselect elements. The core idea here is to shuffle the input array and choose two elements. One flawed approach for shuffling an array is to choose a random index and swap it with another random index, but this has myriad problems since it doesn’t really guarantee that everything is shuffled, and the result could very well have runs of the original card order.

Now, what if we had three objects we wanted to randomly choose from, and we wanted one of them to be chosen more frequently? One way of doing this is to use the above choose function and add duplicate entries. However, this could be tricky if you had odd requirements. This is where the Select<T> function would come in. this function is designed to accept an array and a corresponding array of probability weightings; if all the weightings are equal, than the result should be similar to what we get from Choose. Select accomplishes this by keeping it simple. each array element is essentially assigned a given range within the total, and a random number is generated from the complete total of all weightings. For example, if we had the following elements:

# Name Weight
1 Billy 15
2 Thomas 35
3 Jack 70
4 Selmac 40
5 Patrick 80

We can see that if we generate a value between 0 and 240, than if it is between 0 and 15, we choose Billy, if it is between 15 and 50, we choose Thomas, etc.

Here is the code for the Select Function:

  1.  
  2.  
  3.         public static T Select<T>(T []  items, float []  Probabilities)
  4.        {
  5.     return Select(items,Probabilities,new Random());
  6.        }
  7.  
  8.         public static T Select<T>(T []  items, float []  Probabilities,Random rgen)
  9.         {
  10.             //first, sum all the probabilities.
  11.             //we do this manually because we will also build a corresponding list of the sums up to that element.
  12.             float getsum = 0;
  13.             float []  sumulations = new float [Probabilities.Length + 1] ;
  14.             for (int i = 0; i < Probabilities.Length; i++)
  15.             {
  16.                 sumulations [i]  = getsum;
  17.                 getsum += Probabilities [i] ;
  18.             }
  19.  
  20.  
  21.  
  22.             sumulations [sumulations.Length-1]  = getsum; //add this last value in…
  23.             //get a percentage using nextDouble. we use doubles, just in case the probabilities array uses rather large numbers to attempt to prevent
  24.             //abberations as a result of floating point errors.
  25.             double usepercentage = rgen.NextDouble();
  26.             //convert this percentage into a value we can use, that corresponds to the sum of float values:
  27.             float searchtotal = (float)(usepercentage * getsum);
  28.             //now find the corresponding index and return the corresponding value in the items array.
  29.             for (int i = 0; i < Probabilities.Length; i++)
  30.             {
  31.                 if (searchtotal > sumulations [i]  && searchtotal < sumulations [i + 1] )
  32.                     return items [i] ;
  33.             }
  34.             return default(T);
  35.         }
  36.  

A short test Main() routine that can be used to… test it:

  1.  
  2.  
  3.         static void Main(string []  args)
  4.  
  5.         {
  6.  
  7.             int totalcount = 50000;
  8.  
  9.             String []  names = new string []  { "Bill", "Tom", "Dick" };
  10.  
  11.             float []  probability = new float []  { 50, 20, 30 };
  12.  
  13.             Dictionary <string , int>  countrunner = new Dictionary </string>  <string , int> ();
  14.  
  15.             Console.WriteLine("running simulations…");
  16.  
  17.             for (int i = 0; i > totalcount; i++)
  18.  
  19.             {
  20.  
  21.                 String Selected = Select(names, probability);
  22.  
  23.                 if (!countrunner.ContainsKey(Selected)) countrunner.Add(Selected, 0);
  24.  
  25.                 countrunner [Selected] ++;
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.             }
  34.  
  35.             Console.WriteLine("Completed. Results:");
  36.  
  37.             foreach (var iterate in countrunner)
  38.  
  39.             {
  40.  
  41.                 Console.WriteLine(iterate.Key + "\t\t\t" + iterate.Value.ToString() + " " + ((float)iterate.Value)/(float)totalcount);
  42.  
  43.  
  44.  
  45.  
  46.  
  47.             }
  48.  
  49.  
  50.  
  51.             Console.ReadKey();
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.         }
  64.  
  65.  </string>  

The test is created in such a way that the values can be thought of directly as percentages. The resulting output shows that after a run the occurences of each lie around the percentages as given; Bill appears 50 percent of the time on average, Tom 20 percent, Dick 30 percent, etc. Obviously there is no requirement that the values add up to 100, but the values end up as percentages anyway. (choosing the values 100,40, and 60 for the probability array results in similar results).

This particular method has a bit of a “problem”; what if we run it repeatedly on the same array? Then we are constantly recreating the sumulations array and calculating the totals. How can we cache it? Easy- we use a Dictionary and keep weak references to the given array.

But all is not that simple! This is a generic method and the type T could easily change between calls- so what do we do? Well, it is possible to create a static object that contains a Dictionary indexed by a Type that has a value that is a KeyValuePair<weakreference ,List<T>>, and then inspect the Dictionary for the appropriate values, make sure the cache doesn’t get to big, dispose ofthe WeakReferences that point to arrays that have since been destroyed, blah blah, tricky business. We don’t want that, because for one thing it will be a pain to write- and for another, it will probably be slower overall to begin with. Instead, how about absolving the method itself from the responsibility, and having a ref parameter that can accept a calculated sum array.

For me, the above testing Main() routine took 200ms to execute, on average (I placed calls to System.Diagnostics.Stopwatch members before and after the loop). There was no appreciable difference in speed. Thnakfully, however, the extra logic did not slow it down, either.

The speed improvement can be seen when we have a lot more members. With 5000 members in the probability and Values arrays, and executing a Select on them 50,000 times, the average time was around 5-6 seconds. When the Main function instead gave a float [] ref as the third parameter, the average time dropped to one second. The code for the revised version of the procedure. This also adds some overloads:

  1.  
  2.         public static T Select<T>(T []  items, float []  Probabilities)
  3.  
  4.         {
  5.  
  6.             return Select(items, Probabilities, new Random());
  7.  
  8.  
  9.  
  10.         }
  11.  
  12.         public static T Select<T>(T []  items, float []  Probabilities, Random rgen)
  13.  
  14.         {
  15.  
  16.             float []  sumulator = null;
  17.  
  18.             return Select(items, Probabilities, rgen, ref sumulator);
  19.  
  20.  
  21.  
  22.         }
  23.  
  24.         public static T Select<T>(T []  items, float []  Probabilities, ref float []  sumulations)
  25.  
  26.         {
  27.  
  28.             return Select(items, Probabilities, new Random(), ref sumulations);
  29.  
  30.  
  31.  
  32.         }
  33.  
  34.         public static T Select<T>(T []  items, float []  Probabilities,Random rgen, ref float []  sumulations)
  35.  
  36.         {
  37.  
  38.            
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.             //first, sum all the probabilities; unless a cached value is being given to us.
  47.  
  48.             //we do this manually because we will also build a corresponding list of the sums up to that element.
  49.  
  50.             float getsum = 0;
  51.  
  52.             if(sumulations ==null)
  53.  
  54.             {
  55.  
  56.                 sumulations = new float [Probabilities.Length + 1] ;
  57.  
  58.                 for (int i = 0; i < Probabilities.Length; i++)
  59.  
  60.                 {
  61.  
  62.  
  63.  
  64.                     sumulations [i]  = getsum;
  65.  
  66.                     getsum += Probabilities [i] ;
  67.  
  68.                 }
  69.  
  70.  
  71.  
  72.                 sumulations [sumulations.Length-1]  = getsum; //add this last value in…
  73.  
  74.             }
  75.  
  76.             else
  77.  
  78.             {
  79.  
  80.                 getsum = sumulations [sumulations.Length-1] ;
  81.  
  82.             }
  83.  
  84.  
  85.  
  86.             //get a percentage using nextDouble. we use doubles, just in case the probabilities array uses rather large numbers to attempt to prevent
  87.  
  88.             //abberations as a result of floating point errors.
  89.  
  90.             double usepercentage = rgen.NextDouble();
  91.  
  92.  
  93.  
  94.             //convert this percentage into a value we can use, that corresponds to the sum of float values:
  95.  
  96.             float searchtotal = (float)(usepercentage * getsum);
  97.  
  98.  
  99.  
  100.             //now find the corresponding index and return the corresponding value in the items array.
  101.  
  102.             for (int i = 0; i < Probabilities.Length; i++)
  103.  
  104.             {
  105.  
  106.  
  107.  
  108.                 if (searchtotal > sumulations [i]  && searchtotal < sumulations [i + 1] )
  109.  
  110.                     return items [i] ;
  111.  
  112.  
  113.  
  114.             }
  115.  
  116.             return default(T);
  117.  
  118.         }
  119.  
  120.  

A lot of the above could even be implemented as Extension methods to the appropriate classes, making it seamless.

Since I am working on a game, dealing with vectors and speeds and whatnot is common. One frequent requirement is for items to move at a random angle at a random speed within a given range. The obvious base case here is creating a Vector given a angle and a magnitude:

  1.  
  2.  
  3.  public static PointF GetVelocity(double speed, double angle)
  4.  
  5.  {
  6.  
  7.        return new PointF((float)(Math.Cos(angle) * speed), (float)(Math.Sin(angle) * speed));
  8.  
  9.  
  10.  
  11.  }
  12.  
  13.  

This uses standard trigonometry to calculate what would be the X and Y axes of a fictitious triangle with the given direction as it’s hypoteneuse. Extending from this, we simply create a few extra routines that perform the randomizations:

  1.  
  2.  
  3. public static PointF GetRandomVelocity(double minspeed, double maxspeed, double angle)
  4.  
  5. {
  6.  
  7.     return GetRandomVelocity(minspeed,maxspeed,angle,new Random());
  8.  
  9.  
  10.  
  11. }
  12.  
  13. public static PointF GetRandomVelocity(double minspeed, double maxspeed, double angle, Random rgen)
  14.  
  15. {
  16.  
  17.     return GetVelocity(minspeed + (rgen.NextDouble() * (maxspeed-minspeed)),angle);
  18.  
  19.  
  20.  
  21. }
  22.  
  23. public static PointF GetRandomVelocity(double minspeed, double maxspeed, Random rgen)
  24.  
  25. {
  26.  
  27.    return GetRandomVelocity(minspeed,maxspeed, Math.PI*2*rgen.NextDouble(),rgen);
  28.  
  29.  
  30.  
  31. }
  32.  
  33.  

easy as 3.1415926!

Another interesting endeavour is simplifying the otherwise messy world of Object serialization, such as easily converting a Serializable Object to and from a Stream:

[code]

public static void ObjectToStream<T>(T saveme, Stream outstream) where T : ISerializable

{

BinaryFormatter bf = getFormatter();

using(GZipStream gz = new GZipStream(outstream,CompressionMode.Compress))

{

bf.Serialize(gz, saveme);

}

}

public static T StreamToObject<t>(Stream instream) where T : ISerializable

{

BinaryFormatter bf = getFormatter();

using(GZipStream gz = new GZipStream(instream,CompressionMode.Decompress))

{

return (T)bf.Deserialize(gz);

}

}

[/code]

This also plonks in a bit of compression through the use of a GZipStream.

Anyway, that’s a quick sampling of a few snippets of possible usefulness :)

452 total views, no views today

Posted By: BC_Programming
Last Edit: 05 May 2012 @ 10:35 PM

EmailPermalinkComments (0)
Tags
 12 Apr 2012 @ 7:52 PM 

BASeBlock – Music Manipulation Lessons

As with most games, BASeBlock has music. Originally, I implemented a naive approach to have “Multiple music” playing; a simple stack. The multiple music idea is sort of like
how games might change the music to a boss music when a boss appears, and change it back when they die, or how a certain powerup might change the music while you have it.

This implementation sat for a while. it used a stack based approach- the Sound manager had PushMusic and PopMusic methods.

However, several critical flaws in this approach became clear after I added an invulnerability powerup. Everything seemed to work fine, (get the powerup, you’re invulnerable and while you are there is different music) however, the problem became clear when I, while still invulnerable, released a boss. The boss music would start playing; however, while the boss was alive, the invincible power would run out; it would “PopMusic” which would revert the music from the boss music to the starman music, and then only when the boss died would it go back to normal. This is obviously not intended. The ideal case would be:

  1. Player starts level. Level Music is playing.
  2. Player gets invulnerability powerup; invincible powerup music plays.
  3. While invincible, the player, or something causes a boss to spawn.
  4. when the boss is spawned, the invulnerable music can continue until the power runs out; at which point it plays the boss music, or, the boss music can replace the invincible music. The former is probably overall a better idea.
  5. Either way: the music must fit. Invincible music should only play while the player has said powerup; and boss music should only play when there is a boss.

Obviously, my approach failed miserably; it worked fine, but I had only had a single “active” piece of music at a time; how do you manage multiples?

After some thought, I considered the idea of “reference counting” or keeping track of how many times a given piece of music was requested to play. a boss spawning would increment the boss music by one, a second one with the same music would make it two; each time this happens, the sound manager could re-evaluate which piece of music to play based on finding the maximum reference count.

With this idea, I rearchitected some of the code within the SoundManager. The SoundManager (technically cNewSoundManager, since it was a rewrite of a strongly coupled version I had before) is essentially a class that, well, manages sound and music. I have a interface class that allows for different actual implementations of the details of playing sound (“Driver” classes, of you will) The Manager class itself merely deals with the details based on that basic functionality, which exposes a few critical events, such as music stopping and whatnot. The original “PushMusic” and “PopMusic” stack based approach used a small data class, shown here:

  1.  
  2.  
  3.  private class ActiveMusicData
  4.  
  5.         {
  6.  
  7.             public string Name;
  8.  
  9.             public iActiveSoundObject ActiveSound{get;set;}
  10.  
  11.             public iSoundSourceObject Source { get; set; }
  12.  
  13.             public ActiveMusicData(String pName,iActiveSoundObject pActiveSound, iSoundSourceObject pSource)
  14.  
  15.             {
  16.  
  17.                 ActiveSound = pActiveSound;
  18.  
  19.                 Source = pSource;
  20.  
  21.                 Name = pName;
  22.  
  23.  
  24.  
  25.             }
  26.  
  27.  
  28.  
  29.         }
  30.  
  31.  

A minor explanation may be necessary; iActiveSoundObject is an interface class that is implemented by the “driver”; same for iSoundSourceObject; the details of how they work isn’t important, just that their interface methods do what the interface definition says. A Active Sound object is something that is “active” usually, this means it is playing, but it could also be paused. A Sound Source object can be used to “spawn” Active Sound Objects; in order to actually play music or sound, a iActiveSoundSource object is required. Rather than discard this class I extended from it. Arguably, I could have simply changed the actual class itself but that could always be done later:

  1.  
  2.  
  3.         private class TemporaryMusicData:ActiveMusicData,IComparable<TemporaryMusicData>
  4.  
  5.         {
  6.  
  7.             public TemporaryMusicData(String pName, iActiveSoundObject pActiveSound, iSoundSourceObject pSource):base(pName,pActiveSound,pSource)
  8.  
  9.             {
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.             }
  18.  
  19.             public int Occurences; //reference count; we add one to this when a "temporary" music is played. and subtract one when it is "stopped".
  20.  
  21.             //we play the music with the highest "reference count"; items are removed when their "reference count" is zero.
  22.  
  23.  
  24.  
  25.  
  26.  
  27.          
  28.  
  29.             public int CompareTo(TemporaryMusicData other)
  30.  
  31.             {
  32.  
  33.                 return Occurences.CompareTo(other.Occurences);
  34.  
  35.             }
  36.  
  37.         }
  38.  
  39.  

Again, another private class. The Implementation of IComparable is vestigial from when I was flailing around trying to shoehorn the old stack-based approach into the new reference counted method using a SortedDictionary. Then I realized it was stupid and just made the data structure a normal dictionary.

Dictionary<String,TemporaryMusicData> to be precise; This indexes the TemporaryMusicData instances by Name (Key); the Name/Key is used by the sound Manager to index Sound sources, so getting the appropriate source is easy given a name, and it’s guaranteed to be unique since the listing is taken from the file system itself, and the loading routine has other considerations to prevent duplicate entries (and error handling for duplicate key Exceptions if they do occur). The Occurences field is basically the entire purpose here; when “Temporary” music is told to play, it merely increments the field for the appropriate entry at the Named Index; then both the Stop and Play routines will call another routine that Ensures that the item with the maximum occurences is playing. The implementation for the relevant routines:

  1.  
  2.  
  3.  private String OriginalSoundName; //allocated when TemporaryMusicData is empty when music is pushed.
  4.  
  5.         private iSoundSourceObject OriginalSoundObject = null;
  6.  
  7.         //private SortedList<TemporaryMusicData, TemporaryMusicData> TempMusicData = new SortedList<TemporaryMusicData, TemporaryMusicData>();
  8.  
  9.         private Dictionary<String, TemporaryMusicData> TempMusicData = new Dictionary<string, TemporaryMusicData>();
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.         /// <summary>
  18.  
  19.         /// stops music played with the PlayTemporaryMusic function.
  20.  
  21.         /// </summary>
  22.  
  23.         /// <param name="MusicName"></param>
  24.  
  25.         public iActiveSoundObject StopTemporaryMusic(String MusicName)
  26.  
  27.         {
  28.  
  29.             MusicName = MusicName.ToUpper();
  30.  
  31.             if (!TempMusicData.Any()) return mPlayingMusic; //no elements in Temporary Music Dictionary, so nothing to stop.
  32.  
  33.  
  34.  
  35.  
  36.  
  37.             TemporaryMusicData incrementData = getTemporaryMusicData(MusicName);
  38.  
  39.             incrementData.Occurences–; //add one to occurences.
  40.  
  41.  
  42.  
  43.             if (incrementData.Occurences == 0) TempMusicData.Remove(MusicName);
  44.  
  45.             return PlayMax(1.0f, true);
  46.  
  47.  
  48.  
  49.  
  50.  
  51.         }
  52.  
  53.  
  54.  
  55.         /// <summary>
  56.  
  57.         /// Plays "temporary" music; for example the music from a power up.
  58.  
  59.         /// </summary>
  60.  
  61.         /// <param name="MusicName"></param>
  62.  
  63.         public iActiveSoundObject PlayTemporaryMusic(String MusicName,float volume, bool loop)
  64.  
  65.         {
  66.  
  67.             MusicName = MusicName.ToUpper();
  68.  
  69.             //the idea is simple, we want to allow for the following:
  70.  
  71.             //Player starts game. Music playing is standard level music or something.
  72.  
  73.             //player get’s a power up, which has it’s own music. this powerup is timed.
  74.  
  75.             //player causes something else to have different music; for example, they could spawn a boss or something.
  76.  
  77.             //when the powerup runs out, the music will revert to the normal game music; and when the boss is killed, it will go back to the
  78.  
  79.             //music that was playing from the powerup.
  80.  
  81.  
  82.  
  83.  
  84.  
  85.             //This attempts to mitigate this behaviour.
  86.  
  87.  
  88.  
  89.             //step one: if the list is empty…
  90.  
  91.             if (!TempMusicData.Any())
  92.  
  93.             {
  94.  
  95.                 //empty list. Initialize OriginalSoundName…
  96.  
  97.                 OriginalSoundObject = mPlayingMusicSource;
  98.  
  99.                 OriginalSoundName = scurrentPlayingMusic;
  100.  
  101.  
  102.  
  103.  
  104.  
  105.             }
  106.  
  107.             //step two: is there an element in the sorted list for MusicName?
  108.  
  109.             TemporaryMusicData incrementData = getTemporaryMusicData(MusicName);
  110.  
  111.             incrementData.Occurences++; //add one to occurences.
  112.  
  113.  
  114.  
  115.  
  116.  
  117.             return PlayMax(volume, loop);
  118.  
  119.         }
  120.  
  121.  
  122.  
  123.         private TemporaryMusicData getTemporaryMusicData(string MusicName)
  124.  
  125.         {
  126.  
  127.             MusicName=MusicName.ToUpper();
  128.  
  129.             TemporaryMusicData incrementData;
  130.  
  131.             if (TempMusicData.ContainsKey(MusicName))
  132.  
  133.             {
  134.  
  135.                 incrementData = TempMusicData [MusicName] ;
  136.  
  137.             }
  138.  
  139.             else
  140.  
  141.             {
  142.  
  143.                 incrementData = new TemporaryMusicData(MusicName, null, mSoundSources [MusicName] );
  144.  
  145.                 TempMusicData.Add(MusicName, incrementData);
  146.  
  147.             }
  148.  
  149.             return incrementData;
  150.  
  151.         }
  152.  
  153.  
  154.  
  155.         private iActiveSoundObject PlayMax(float volume, bool loop)
  156.  
  157.         {
  158.  
  159. //now, find the one with the maximum occurences.
  160.  
  161.            
  162.  
  163.  
  164.  
  165.             if (!TempMusicData.Any())
  166.  
  167.             {
  168.  
  169.                 mPlayingMusic.Stop();
  170.  
  171.                 //play default.
  172.  
  173.                 scurrentPlayingMusic = OriginalSoundName;
  174.  
  175.                 mPlayingMusic = OriginalSoundObject.Play(loop,volume);
  176.  
  177.  
  178.  
  179.                 return mPlayingMusic;
  180.  
  181.  
  182.  
  183.  
  184.  
  185.             }
  186.  
  187.  
  188.  
  189.             int currentmax = int.MinValue;
  190.  
  191.             TemporaryMusicData tmduse = null;
  192.  
  193.             foreach (var iterate in TempMusicData)
  194.  
  195.             {
  196.  
  197.                 if (iterate.Value.Occurences > currentmax)
  198.  
  199.                 {
  200.  
  201.                     currentmax = iterate.Value.Occurences;
  202.  
  203.                     tmduse = iterate.Value;
  204.  
  205.                 }
  206.  
  207.             }
  208.  
  209.             if (scurrentPlayingMusic != tmduse.Name)
  210.  
  211.             {
  212.  
  213.                 mPlayingMusic.Stop();
  214.  
  215.                 scurrentPlayingMusic = tmduse.Name;
  216.  
  217.                 mPlayingMusic = tmduse.Source.Play(loop, volume);
  218.  
  219.                 return mPlayingMusic;
  220.  
  221.             }
  222.  
  223.  
  224.  
  225.             return mPlayingMusic;
  226.  
  227.         }
  228.  
  229.  
  230.  
  231.  

So far, this has worked well.

However, more recently I found that I also need the same sort of “reference count” management for other things related to powerups, such as the “DrawAttributes” of various objects. But it would be foolish to clutter up that code with this sort of thing. Surely there is some way that I can add the feature with little to no changes to existing code? Turns out, that leveraging a few C# features, this is relatively easily accomplished.

Consider the Nullable<T> class. Any struct or value type can be made “Nullable” using it; there is even a shortcut in the language syntax for type definitions to use it, by appending a question mark, (Nullable is equivalent to int?). What we want is a way to- generically- make a class “reference counted” so that only the value that has the highest reference count “is” the value. The Nullable<T> type can be implicitly cast to type T in most contexts; so you can change a T to a Nullable<T> type with few code changes, which is what we are after.

Enter ReferenceCounted<T>

ReferenceCounted<T> is the name of the class that I created (or, as I write this, am creating) for this purpose. My original idea was to use implicit cast operators to make it a simple type change; assignments to the object of the “old type” (type T) would “automatically” be added to the reference list; going the other way, the ReferenceCounted<T> Type would be implicitly cast to T by way of taking the T value it currently has with the highest reference Count. This hit a snag, however; the second cast, thankfully, would work fine, but the first would not have the proper information; the cast operator is a static routine and wouldn’t have access to the ReferenceCounted<T> Object that is being assigned.

somewhat miffed but not surprised (it would be silly to provide for overloading of the assignment operator, but in this case I wish there was an exception), I didn’t give up; I just thought about it a little. And it hit me- I don’t need to overload the assignment operator to overload assignment; I could overload the addition operator and implement the “assignment” code there; this is what the Event classes do for event hooking; and I could use -= to remove “references”. Arguably, this would take more code and wouldn’t be quite as clean as I was hoping, but for the most part the actual reference counting logic would be out of the way, handled mostly by the implicit cast to T.

After some effort… it was made. Here is the source code:

  1.  
  2.  
  3. using System;
  4.  
  5. using System.Collections.Generic;
  6.  
  7. using System.Diagnostics;
  8.  
  9. using System.Drawing.Imaging;
  10.  
  11. using System.Linq;
  12.  
  13. using System.Text;
  14.  
  15.  
  16.  
  17. namespace System.Collections.Generic
  18.  
  19. {
  20.  
  21.  
  22.  
  23.  
  24.  
  25.    
  26.  
  27.  
  28.  
  29.     /// <summary>
  30.  
  31.     /// Implements a reference counted list of T, allowing for implicit conversion to type T.
  32.  
  33.     /// A "Reference counted list" keeps track of both the items in the list as well as a reference count for each.
  34.  
  35.     /// When an item is added, it’s reference count is incremented. If the item already exists, the existing item has it’s reference count incremented.
  36.  
  37.     /// Equality is determined by default using the Equals Method, but a predicate can be assigned.
  38.  
  39.     /// </summary>
  40.  
  41.     /// <typeparam name="T"></typeparam>
  42.  
  43.     class ReferenceCounted<T>
  44.  
  45.     {
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.         private T lastitem = default(T);
  54.  
  55.         private Dictionary<T, int> ReferenceDictionary= new Dictionary<T, int>();
  56.  
  57.         /// <summary>
  58.  
  59.         /// Returns the Dictionary holding the Elements.
  60.  
  61.         /// </summary>
  62.  
  63.         /// <returns></returns>
  64.  
  65.         public Dictionary<T, int> getReferenceDictionary() { return ReferenceDictionary; }
  66.  
  67.         /// <summary>
  68.  
  69.         /// creates a new ReferenceCounted List Object.
  70.  
  71.         /// </summary>
  72.  
  73.         public ReferenceCounted()
  74.  
  75.         {
  76.  
  77.  
  78.  
  79.  
  80.  
  81.  
  82.  
  83.         }
  84.  
  85.         /// <summary>
  86.  
  87.         /// creates a new ReferenceCounted List Object, with the given IEqualityComparer as the comparison predicate.
  88.  
  89.         /// </summary>
  90.  
  91.         /// <param name="compareobject"></param>
  92.  
  93.         public ReferenceCounted(IEqualityComparer<T> compareobject)
  94.  
  95.         {
  96.  
  97.             //initialize Dictionary with given IEqualityComparer interface.
  98.  
  99.             ReferenceDictionary = new Dictionary<T, int>(compareobject);
  100.  
  101.            
  102.  
  103.  
  104.  
  105.         }
  106.  
  107.         /// <summary>
  108.  
  109.         /// adds the given element to this list; which will either add an nonexistent item to the list and set it’s reference count to 1 or
  110.  
  111.         /// increment the value of the existing element for the given value, as determined by any IEqualityComparer predicate given in the constructor.
  112.  
  113.         /// </summary>
  114.  
  115.         /// <param name="value">Value to add or increment in the list.</param>
  116.  
  117.         public void AddElement(T value)
  118.  
  119.         {
  120.  
  121.             //adds an element to this ReferenceCounted object.
  122.  
  123.             //remove the previous item… or dereference it, rather.
  124.  
  125.  
  126.  
  127.             if (!ReferenceDictionary.Comparer.Equals(lastitem, default(T)))
  128.  
  129.             {
  130.  
  131.                 RemoveElement(lastitem);
  132.  
  133.                 lastitem = default(T);
  134.  
  135.             }
  136.  
  137.             //first, does the given item exist in our dictionary already?
  138.  
  139.             if (!ReferenceDictionary.ContainsKey(value))
  140.  
  141.             {
  142.  
  143.                 //if not, we add it. Add it with a value 0, since it will be incremented after this if.
  144.  
  145.                 ReferenceDictionary.Add(value, 0);
  146.  
  147.  
  148.  
  149.                
  150.  
  151.                
  152.  
  153.             }
  154.  
  155.             //due to the above condition we know the element exists; increment it’s reference count.
  156.  
  157.             ReferenceDictionary [value] ++;
  158.  
  159.             _Dirty = true;
  160.  
  161.  
  162.  
  163.         }
  164.  
  165.         /// <summary>
  166.  
  167.         /// "removes" the given element from this list. This is accomplished by decrementing the appropriate keyed item in the dictionary.
  168.  
  169.         /// If the element is not in this dictionary, this method has no effect.
  170.  
  171.         /// </summary>
  172.  
  173.         /// <param name="value"></param>
  174.  
  175.         public void RemoveElement(T value)
  176.  
  177.         {
  178.  
  179.             if (!ReferenceDictionary.ContainsKey(value))
  180.  
  181.                 return; //nothing to do if no item.
  182.  
  183.  
  184.  
  185.             ReferenceDictionary [value] –;
  186.  
  187.             //if reference count is zero: remove it.
  188.  
  189.             if (ReferenceDictionary [value]  == 0) ReferenceDictionary.Remove(value);
  190.  
  191.             _Dirty = true;
  192.  
  193.  
  194.  
  195.         }
  196.  
  197.         T _CurrentMaximum=default(T);
  198.  
  199.         bool _Dirty = true; //whether the max is out of date.
  200.  
  201.         /// <summary>
  202.  
  203.         /// retrieves the item in this list with the maximum reference count.
  204.  
  205.         /// If multiple items have the maximum, only the first one encountered will be returned.
  206.  
  207.         /// This method also caches the result; calls only calculate a new maximum if necessary. (if the list was changed since the last one was cached).
  208.  
  209.         ///
  210.  
  211.         /// </summary>
  212.  
  213.         /// <returns></returns>
  214.  
  215.         public T getMaxReferenced()
  216.  
  217.         {
  218.  
  219.             //find the item with the maximum value and return it’s key.
  220.  
  221.  
  222.  
  223.             if (!_Dirty) return _CurrentMaximum;
  224.  
  225.  
  226.  
  227.  
  228.  
  229.             //otherwise, we’re "dirty" and need to re-find the maximum again.
  230.  
  231.             int maxfound_int = int.MinValue;
  232.  
  233.             T maxfound_T = default(T);
  234.  
  235.             foreach (var iterate in ReferenceDictionary)
  236.  
  237.             {
  238.  
  239.                 if (iterate.Value > maxfound_int)
  240.  
  241.                 {
  242.  
  243.                     maxfound_int = iterate.Value;
  244.  
  245.                     maxfound_T = iterate.Key;
  246.  
  247.  
  248.  
  249.                 }
  250.  
  251.  
  252.  
  253.  
  254.  
  255.  
  256.  
  257.  
  258.  
  259.             }
  260.  
  261.             _CurrentMaximum = maxfound_T;
  262.  
  263.             _Dirty = false;
  264.  
  265.             return _CurrentMaximum;
  266.  
  267.  
  268.  
  269.  
  270.  
  271.         }
  272.  
  273.         //Operator+; designed to be used like events- rather than code using = to "assign" a ReferenceCounted<T> value,
  274.  
  275.         //it will use +=
  276.  
  277.     public static ReferenceCounted<T> operator+(ReferenceCounted<T> firstvalue, T secondvalue)
  278.  
  279.     {
  280.  
  281.         firstvalue.AddElement(secondvalue);
  282.  
  283.        
  284.  
  285.         return firstvalue; //return the ReferenceCounted<T> object.
  286.  
  287.        
  288.  
  289.     }
  290.  
  291.         /// <summary>
  292.  
  293.         /// Operator that calls RemoveElement. The result will be the ReferenceCounted object itself.
  294.  
  295.         /// </summary>
  296.  
  297.         /// <param name="firstvalue"></param>
  298.  
  299.         /// <param name="secondvalue"></param>
  300.  
  301.         /// <returns></returns>
  302.  
  303.     public static ReferenceCounted<T> operator -(ReferenceCounted<T> firstvalue, T secondvalue)
  304.  
  305.     {
  306.  
  307.         firstvalue.RemoveElement(secondvalue);
  308.  
  309.        
  310.  
  311.         return firstvalue;
  312.  
  313.  
  314.  
  315.     }
  316.  
  317.         /// <summary>
  318.  
  319.         /// implicitly converts this list to the type of it’s component.
  320.  
  321.         /// </summary>
  322.  
  323.         /// <param name="value"></param>
  324.  
  325.         /// <returns></returns>
  326.  
  327.     public static implicit operator T(ReferenceCounted<T> value)
  328.  
  329.     {
  330.  
  331.  
  332.  
  333.         return value.getMaxReferenced();
  334.  
  335.  
  336.  
  337.  
  338.  
  339.     }
  340.  
  341.  
  342.  
  343.  
  344.  
  345.      
  346.  
  347.  
  348.  
  349.  
  350.  
  351.  
  352.  
  353.  
  354.  
  355.     }
  356.  
  357. }
  358.  
  359.  
  360.  
  361.  

Aftermath

Pleased I had created a nice implementation, I set about creating the Comparison routine. Unfortunately, to my horror and surprise, the class which I wanted to use in conjunction with this class in one instance, ImageAttributes, had no way of getting it’s ColorMatrix. This presented an issue since I didn’t want added ColorMatrix values to mess about with the image, and the results could be less than extraordinary unless I cached each ImageAttributes.

And that was the entire purpose. However I decided to consider how else to acheive my goal; the goal here was to prevent powerups from changing the state of GameObject’s appearance in a manner that prevented them from undoing it. So, for example, powerups might have a limited duration, and the results from a overlap of two powerups could result in a confusing ending state for the object. The idea was to replace the GameObject class’s “DrawAttributes” field with a ReferenceCounted ; then in it’s draw routine, it would assign use that in the appropriate method which would implicitly cast it to the maximum referenced item in the list. All I needed was a way to compare DrawAttributes aside from as references; but the ImageAttributes class, sadly, does not provide this functionality.

So how do I address this?

I considered possibilities, and the problem, a bit more thoughtfully. Evidently, the ReferenceCounted<T> class would be very useful for it, but what would I use it for.

I’ve decided- though not yet attempted to implement- that I would use the ReferenceCounted class to keep track of The powerups themselves rather than a few fields of the gameobject. Since the powerup classes are what would result in the unwanted behaviour, it makes sense. So how does it work? Well, the framework basically allows a GameCharacter to have a list of GameCharacterAbilities; the GameCharacter calls the draw function of each when it draws, and it calls a frame function when it’s own frame function is called. My idea is to change that to a ReferenceCounted<GameCharacterAbility>. The code could then be changed to only call PerformFrame and Draw for the one with the highest reference count, or something similar.

The other possibility is to change the GameObject’s DrawAttributes field to a read-only property that is created “on the fly” from another new ColorMatrix field; the ColorMatrix item could be a ReferenceCounted object and therefore the use of that object in the property would use the implicit conversion operator. I’m trying to avoid this, even though I cannot foresee a circumstance where the ImageAttributes class provides something that I can’t do with a ColorMatrix (oh it does, but nothing I know how to do) I prefer to keep all my roads open, so to speak. If there was a way to compare the innards of the ImageAttributes, I could just change the DrawAttributes field to a ReferenceCounted object and make a new comparer, but it’s unfortunately not that simple.

264 total views, 1 views today

Posted By: BC_Programming
Last Edit: 12 Apr 2012 @ 07:52 PM

EmailPermalinkComments (0)
Tags
 12 Jan 2012 @ 3:11 PM 

In some of my recent posts, I’ve covered the topic of accessing and parsing an INI file for configuration data in a C# Application.

Some may wonder why. After all; the “norm” for C# and .NET applications is to use XML files for configuration information, isn’t it? Well, yes. But to be honest, XML files are a fucking pain in the ass. They aren’t human readable to your average person the same way an INI file is, and getting/setting values is tedious. Primarily, the reason I use INI files is that they are:

  1. Human Readable: Anybody can understand the basic structure of the sections and Name=Value syntax.
  2. Accessible: You don’t need a special editor
  3. Portable: since the entire thing is interpreted using Managed code, it will act the same on any platform (Mono or the MS CLR).

Mostly, I feel that XML, and in many ways other configuration options, are more or less driven by fad. Another option for configuration settings on Windows is the Registry, which is in fact often the recommended method; but this is anything but accessible to the user. Would you rather guide a user to edit a INI file or to fiddle with registry settings?

With that said, INI Files do have their own issues. For example, their data is typically typeless; or, more precisely, the Values are all strings. Whereas using a .NET XML Serializer, for example, you could easily(relatively speaking) serialize and deserialize a special configuration class to and from an XML file and preserve it’s format, with my INI file class there will typically be some work to parse the values.

It was with the idea of turning my string-only INIFile configuration settings into something that can be used for nearly any type that I created the INItemValueExtensions class, which is nothing more than a static class that provides some extension methods for the INIDataItem class. I covered this in my previous post.

The prototypes for the two static functions are:

  1.  
  2. public static T GetValue<T>(this INIDataItem dataitem, T DefaultValue);
  3.  
  4. //and
  5.  
  6. public static void SetValue<T>(this INIDataItem dataitem, T newvalue);
  7.  
  8.  

How would one use these extension methods? Well, here’s an Example:

  1.  
  2. public static void main(String []  args)
  3. {
  4.     INIFile loadini = new INIFile("D:\\testini.ini");
  5.     loadini ["Dates"]  ["TestDate"] .SetValue(DateTime.Now);
  6.     DateTime readvalue = loadini ["Dates"]  ["TestDate"] .GetValue <datetime> ();
  7.    
  8. }
  9.  </datetime>  

Woah, hold the phone! What’s going on here? We’re loading DateTime values directly from the INI File? How does that work?

All the “magic” happens in the getValue generic extension method. The first thing the routine does is check to see if the Type Parameter has a static TryParse() method; if it implements ISerializable and have a TryParse method, than the routine will read the string from the INI file, decode it via Base64, and throw it in a MemoryStream, and then try to deserialize the Object Graph for a Type T using that stream.

If it does implement a TryParse() routine, (like, for example, DateTime) it doesn’t try quite as hard. It takes the string from the INI file and hands it to the Type’s TryParse() routine, and then returns what that gives back. Naturally, the inverse function (setValue) does something somewhat opposite; it checks the Base64 logic, and if so it sets the value of the item to the Base64 encoded value of the serialized object. Otherwise, it just uses toString().

This typically works, particularly with DateTime, because usually ToString() is the inverse of TryParse(). In the case of DateTime, this has a few edge cases with regards to locale, but usually it works quite well. And more importantly, the introduction of allowing any object that implements ISerializable to simply be thrown as an INI value via a Base64 encoded string is useful too, although with large objects it’s probably not a good idea for obvious reasons.

But… I still want to access other settings!

Of Course, an INIFile is only one of any number of ways to store/retrieve configuration settings. And while they don’t typically lend themselves to the same syntax provided by the INIFile class, it would be useful to have some sort of common denominator that can handle it all. That was the original intent of the relatively unassuming ISettingsStorage interface:

  1.  
  2.    public interface ISettingsStorage
  3.     {
  4.         void Save();
  5.         void Load();
  6.         void AddValue(String Category, String ValueName, String Value);
  7.         String GetValue(String Category, String ValueName);
  8.     }
  9.  

This uses a concept known as a “category” which is pretty much the same idea as an INI File section. What makes it different is that, for implementors that use other storage mechanisms, it could have additional meaning; for example, a fictitious XML implementation of ISettingsStorage could use the “Category” string as an XPath to an element; and the Value could be stored/retrieved as a Attribute. a Registry implementation might use it as a Registry path, and so on.

The problem is, even though the INIFile class implements this interface, it’s too basic, and doesn’t provide nearly the syntactic cleanliness that just using the INIFile does. Stemming from that, and because I wanted to try to get a way to store settings directly in a DB, I introduced two events to the INIFile class; one that fires when a Value is retrieved, and one when a value is saved. This way, the event could be hooked and the value saved elsewhere, If desired. Now, to be fair, this is mostly a shortcoming of my interface definition; as you can see above, there is no way to, for example, inspect category or Value names. I toyed with the idea of adding a “psuedo” category/value combination that would return a delimited string of category names, but that felt extremely silly. The creation of a generic interface- or abstract class- that provides all the conveniences I currently enjoy using my INIFile class but allowing me to also use XML, Registry, or nearly any other persistent storage for settings will be a long term goal. For now, I’m content with accessing INI files and having a unclean event to hack in my own behaviour.

My first test of the above feature- whereby it allows values to be TryParse’d and ToString’d back and forth from a given type on the fly- was the creation of a FormPositionSaver class.

The proper way to save and restore a window’s position on Windows is using the GetWindowPlacement() and SetWindowPlacement() API Functions. These use a structure, named, quite aptly, “WINDOWPLACEMENT” to retrieve and set the window position and various attributes. Therefore, our first task is to create the proper P/Invoke’s for these functions:

  1.  
  2.   [DllImport("user32.dll")]
  3.         private static extern int OffsetRect(ref RECT lpRect, int x, int y);
  4.  
  5.  [DllImport("user32.dll")]
  6.         private static extern int GetWindowPlacement(IntPtr hwnd, ref WINDOWPLACEMENT lpwndpl);
  7.  
  8.  [DllImport("user32.dll")]
  9.         private static extern int SetWindowPlacement(IntPtr hwnd, ref WINDOWPLACEMENT lpwndpl);
  10.  

I also include OffsetRect(), but I’ll get to that in a bit. Now the “big one” is the definition of the WINDOWPLACEMENT structure and it’s various aggregate structures. Why? well, in the interest of leveraging the INIFile’s static extensions, Why not define a static TryParse() and a toString() method on the structure that can set and retrieve the member values:

  1.  
  2.  
  3.          [StructLayout(LayoutKind.Sequential)]
  4.         private struct POINTAPI
  5.         {
  6.             internal int x;
  7.             internal int y;
  8.  
  9.  
  10.             public POINTAPI(int px, int py)
  11.             {
  12.                 x = px;
  13.                 y = py;
  14.             }
  15.  
  16.             public static void TryParse(String parseit, out POINTAPI result)
  17.             {
  18.                 //format: (X,Y)
  19.                 //strip out parens.
  20.                 String []  parsed = parseit.Replace("(", "").Replace(")", "").Split(new char []  {‘,’});
  21.                 int kx = int.Parse(parsed [0] );
  22.                 int ky = int.Parse(parsed [1] );
  23.  
  24.  
  25.                 result = new POINTAPI(kx, ky);
  26.             }
  27.  
  28.             public override string ToString()
  29.             {
  30.                 return "(" + x + "," + y + ")";
  31.             }
  32.         }
  33.  
  34.          [StructLayout(LayoutKind.Sequential)]
  35.         private struct RECT
  36.         {
  37.             internal int Left;
  38.             internal int Top;
  39.             internal int Right;
  40.             internal int Bottom;
  41.  
  42.             public override string ToString()
  43.             {
  44.                 return "{" + new POINTAPI(Left, Top).ToString() + "-" + new POINTAPI(Right, Bottom).ToString() + "}";
  45.             }
  46.  
  47.             public RECT(int pLeft, int pTop, int pRight, int pBottom)
  48.             {
  49.                 Left = pLeft;
  50.                 Top = pTop;
  51.                 Right = pRight;
  52.                 Bottom = pBottom;
  53.             }
  54.  
  55.             public static void TryParse(String parsestr, out RECT result)
  56.             {
  57.                 //strip out braces…
  58.                 parsestr = parsestr.Replace("{", "").Replace("}", "");
  59.                 //split at ")-("…
  60.                 String []  Pointstrings = parsestr.Split(new string []  {")-("}, StringSplitOptions.RemoveEmptyEntries);
  61.                 POINTAPI firstpoint, secondpoint;
  62.                 //parse the resulting values. re-add the parens that were removed by the split.
  63.                 POINTAPI.TryParse(Pointstrings [0]  + ")", out firstpoint);
  64.                 POINTAPI.TryParse("(" + Pointstrings [1] , out secondpoint);
  65.  
  66.  
  67.                 result = new RECT(firstpoint.y, firstpoint.y, secondpoint.x, secondpoint.y);
  68.             }
  69.         }
  70.  
  71.  
  72.   [StructLayout(LayoutKind.Sequential)]
  73.         private struct WINDOWPLACEMENT
  74.         {
  75.             internal int Length;
  76.             internal int flags;
  77.             internal int showCmd;
  78.             internal POINTAPI ptMinPosition;
  79.             internal POINTAPI ptMaxPosition;
  80.             internal RECT rcNormalPosition;
  81.  
  82.             public override string ToString()
  83.             {
  84.                 return String.Join(",", new string []
  85.                                             {
  86.                                                 flags.ToString(), showCmd.ToString(),
  87.                                                 ptMinPosition.x.ToString(), ptMinPosition.y.ToString(),
  88.                                                 ptMaxPosition.x.ToString(), ptMaxPosition.y.ToString(),
  89.                                                 rcNormalPosition.Left.ToString(), rcNormalPosition.Top.ToString(),
  90.                                                 rcNormalPosition.Right.ToString(), rcNormalPosition.Bottom.ToString()
  91.                                             });
  92.             }
  93.  
  94.             //parsed a string into a WINDOWPLACEMENT structure.
  95.             public static bool TryParse(String parseme, out WINDOWPLACEMENT result)
  96.             {
  97.                 try
  98.                 {
  99.                     String []  splitvalues = parseme.Split(‘,’);
  100.                     int []  parsedvalues = new int [splitvalues.Length] ;
  101.  
  102.  
  103.                     for (int i = 0; i < parsedvalues.Length; i++)
  104.                     {
  105.                         int.TryParse(splitvalues [i] , out parsedvalues [i] );
  106.                     }
  107.  
  108.                     result = new WINDOWPLACEMENT
  109.                                  {
  110.                                      flags = parsedvalues [0] ,
  111.                                      showCmd = parsedvalues [1] ,
  112.                                      ptMinPosition = new POINTAPI(parsedvalues [2] , parsedvalues [3] ),
  113.                                      ptMaxPosition = new POINTAPI(parsedvalues [4] , parsedvalues [5] ),
  114.                                      rcNormalPosition =
  115.                                          new RECT(parsedvalues [6] , parsedvalues [7] , parsedvalues [8] , parsedvalues [9] )
  116.                                  };
  117.  
  118.                     return true;
  119.                 }
  120.                 catch
  121.                 {
  122.                     result = new WINDOWPLACEMENT();
  123.                     return false;
  124.                 }
  125.             }
  126.         }
  127.  
  128.  

WHEW! that’s quite a bit of code for a structure definition, but we’ll make up for it with the brevity of the actual FormPositionSaver class itself. First, my design goal with this class was to make it basically do all the heavy lifting; it hooks both the Load and Unload event, and saves to and from a given INIFile Object in those events. Since the application I was working on at the time didn’t actually get a Valid INI object until during it’s main form’s Load event, and since there is no way to say “Invoke this event first no matter what” I also added a way for it to be told that hooking the load event would be pointless since it already occured, at which point it will not hook the event and instead set the form position immediately. Values are stored

  1.  
  2. public class FormPositionSaver
  3. {
  4.  
  5.   private Form FormObject = null;
  6.         private INIFile Configuration = null;
  7.         private static readonly String usesectionName = "WindowPositions";
  8.  
  9.  
  10.         ///  <summary>
  11.         /// Create the FormPositionSaver
  12.         ///  </summary>
  13.         ///  <param name="FormObj"> Form to deal with </param>
  14.         ///  <param name="configfile"> INIFile to load and save </param>
  15.         ///  <param name="alreadyloaded"> whether the Load event has fired. If true, will try to set the form position immediately. otherwise, it hooks the Load event and waits. </param>
  16.         public FormPositionSaver(Form FormObj, INIFile configfile, bool alreadyloaded)
  17.         {
  18.             Configuration = configfile;
  19.             FormObject = FormObj;
  20.  
  21.             FormObject.FormClosed += new FormClosedEventHandler(FormObject_FormClosed);
  22.  
  23.  
  24.             if (!alreadyloaded)
  25.                 FormObject.Load += new EventHandler(FormObject_Load);
  26.             else
  27.             {
  28.                 FormObject_Load(FormObject, new EventArgs());
  29.             }
  30.         }
  31.  
  32.         //save the placement...
  33.         private void FormObject_FormClosed(object sender, FormClosedEventArgs e)
  34.         {
  35.             //save placement.
  36.             //all the "tough work" is handled above, and by the INIDataItem Extension methods. Here we
  37.             //can simply use SetValue <>  and set the value. Nice and clean.
  38.             WINDOWPLACEMENT grabplacement = new WINDOWPLACEMENT();
  39.             GetWindowPlacement(FormObject.Handle, ref grabplacement);
  40.             Configuration [usesectionName]  [FormObject.Name] .SetValue(grabplacement);
  41.         }
  42.  
  43.         //Load event: load the form placement, if present, from the INI file we were given in our constructor.
  44.         private void FormObject_Load(object sender, EventArgs e)
  45.         {
  46.             WINDOWPLACEMENT currplacement = new WINDOWPLACEMENT();
  47.             GetWindowPlacement(FormObject.Handle, ref currplacement);
  48.                 //default is wherever it is now if there is a parse problem.
  49.  
  50.             WINDOWPLACEMENT getplacement =
  51.                 Configuration [usesectionName]  [FormObject.Name] .GetValue(currplacement);
  52.  
  53.             //check for previous instances, and offset if there are.
  54.             String thisproc = Process.GetCurrentProcess().ProcessName;
  55.             Process []  existing = Process.GetProcessesByName(thisproc);
  56.             if (existing.Length > 1)
  57.             {
  58.                 //more than one, so offset...
  59.                 OffsetRect(ref getplacement.rcNormalPosition, 16*existing.Length, 16*existing.Length);
  60.             }
  61.  
  62.  
  63.             SetWindowPlacement(FormObject.Handle, ref getplacement);
  64.  
  65.             //load placement...
  66.         }
  67.     }
  68.  

Alright, so maybe I lied a bit. It's not super short. Although a lot of it is comments. Some might note that I only sporadically add doc comments, even though I ought to be adding them everywhere. Well, sue me. I just add them when I feel like it. When I'm concentrating on function, I'm not one to give creedence to form.

This is where I explain OffsetRect(). Basically, if your application is run twice, and you load the form position twice, the second form will open over the first one, and the screen will look pretty much the same. So we detect previous instances and offset by an amount to make it's position different from any previous instances as necessary. That's pretty much the only purpose of OffsetRect.

I have packaged the current versions of cINIFile.cs and the new FormPositionSaver.cs in a zip file, it can be downloaded from here .

312 total views, no views today

Posted By: BC_Programming
Last Edit: 12 Jan 2012 @ 03:11 PM

EmailPermalinkComments (0)
Tags
 25 Oct 2011 @ 11:13 AM 

Serialization has always been a thorn in my side. In VB6. in Python, in C#. The biggest annoyance is that most applications consist of somewhat complex object relationships and heirarchy’s, different race conditions about values that need to be initialized first, and who knows what else, all meticulously built during the course of your application, which you eventually need to save to persistent storage.

All applications can benefit from some form of persistence, even if it is only in the form of settings. As a result most Object Oriented languages have a way to allow you to “serialize” an object to a stream. In .NET, this is provided by way of the [Serializable] Attribute and the ISerializable interface, which allows you to customize the serialization somewhat.

What is interesting, at least in the case of .NET object serialization, is that you can choose one of a number of “Formatters” which will take the data acquired by the Serialization support framework and format it to one of any number of formats. Personally, I have only used The BinaryFormatter class, which will allow to write or read a serialized stream to or from a stream.

So, what use is serialization? Well, aside from the obvious ability to save the stream to a file, you can also use it in other ways. For example, since you can send the data across any stream, you could send it through a network stream, and on the other end another instance of your application would be able to rebuild the exact same object. Another usage I’ve found is for usage on the system clipboard; serialize what is being copied, plop it on the clipboard with your own format specifier, and then when you want to see if you can paste check if that specifier exists and when pasting deserialize that data from the clipboard, and you’ve got the objects that need to be pasted, which will be “new” objects separate from those copied (if they are still present).

Copying a object graph to stream, however, has a bit of boilerplate; you need to create the BinaryFormatter(), serialize, and the opposite in the other direction.

However, with generics, this task can be made a tad easier:

  1.  
  2.     public static void ObjectToStream<T>(T saveme, Stream outstream) where T : ISerializable
  3.     {
  4.  
  5.         BinaryFormatter bf = new BinaryFormatter();
  6.         using(GZipStream gz = new GZipStream(outstream,CompressionMode.Compress))
  7.         {
  8.             bf.Serialize(gz, saveme);
  9.  
  10.  
  11.  
  12.         }
  13.    }
  14.  
  15.    public static T StreamToObject<T>(Stream instream) where T : ISerializable
  16.         {
  17.             BinaryFormatter bf = new BinaryFormatter();
  18.             using(GZipStream gz = new GZipStream(instream,CompressionMode.Decompress))
  19.             {
  20.  
  21.                 return (T)bf.Deserialize(gz);
  22.  
  23.             }
  24.  
  25.  
  26.  
  27.         }
  28.  

Two static methods that serve to input and output a object whose type implements ISerializable to and from a given stream. It also uses the GZipStream to compress and decompress that stream to try to save space.

Extending this directly to files is rather easy as well:

  1.  
  2. public static T ObjectFromFile<T>(String filename)  where T : ISerializable
  3. {
  4.    //open the file.
  5.    T acquireobject;
  6.    FileStream fstream = new FileStream(filename,FileMode.Open);
  7.    //input from the stream…
  8.    acquireobject = StreamToObject(filename);
  9.    fstream.Close();
  10. }
  11. public static ObjectToFile<T>(String filename,T Objectsave) where T:ISerializable
  12. {
  13.   //open the file
  14.   FileStream fstream = new FileStream(filename,FileMode.Create);
  15.   ObjectToStream(fstream,Objectsave);
  16.   fstream.Close();
  17. }
  18.  

And huzzah! suddenly you can read and write objects to and from a file with ease.

One might even venture to create extension methods on the Stream class that provide this sort of functionality for input and output of any object supporting ISerializable, like the following, which assumes the previous routines are within a ‘ObjectStreaming’ class definition.

  1.  
  2.  public static class StreamExtensions
  3.     {
  4.  
  5.         public static void WriteObject<T>(this Stream str,T writeobj) where T:ISerializable
  6.         {
  7.             ObjectStreaming.ObjectToStream(writeobj, str);
  8.            
  9.  
  10.  
  11.         }
  12.         public static T ReadObject<T>(this Stream str) where T : ISerializable
  13.         {
  14.  
  15.             return ObjectStreaming.StreamToObject <t> (str);
  16.  
  17.         }
  18.  
  19.     }
  20.  </t>  

And there you have it, suddenly you can write objects directly to any stream, using methods of that stream Object, and that saved data is automatically GZipped for you as well.

This Does, however, present it’s own issues. If there is an error anywhere in your serialization code, using a “filter” type of stream, such as the GZipStream, may cause difficult to trace errors that cause exceptions to fire from the GZipStream itself, most notably “unexpected end of stream” type errors. You can usually trace these in Visual Studio by checking “thrown” on System.Runtime.Serialization.SerializationException in the Debug->Exceptions dialog, and allowing the data to save to a normal stream (that is, directly to a filestream or memory stream rather than by way of the GZipStream). This will allow you to determine where you made the mistake elsewhere. Typically, in my experience, it’s usually something as innocent as a misspelling, or even inconsistent capitalization.

3,706 total views, 16 views today

Posted By: BC_Programming
Last Edit: 25 Oct 2011 @ 11:18 AM

EmailPermalinkComments (0)
Tags

 Last 50 Posts
 Back
Change Theme...
  • Users » 871
  • Posts/Pages » 193
  • Comments » 75
Change Theme...
  • VoidVoid « Default
  • LifeLife
  • EarthEarth
  • WindWind
  • WaterWater
  • FireFire
  • LightLight

PP



    No Child Pages.

Windows optimization tips



    No Child Pages.