 Wednesday, May 31, 2006
Visual Basic 6's App object exposes a useful property named PrevInstance, which allows you to determine whether there are other instances of the same application running. This property has never been implemented in VB.NET, even though a VB2005 application can use the StartupNextInstance event for this purpose.
' to display this code, open the Application page of the My Project designer and click the Application Events button
Namespace My Partial Friend Class MyApplication Private Sub MyApplication_StartupNextInstance(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs) Handles Me.StartupNextInstance ' another instance of this application has been launched End Sub End Class End Namespace
The problem of this approach is that the event is raised in the first instance of the application, not in the new instance, therefore when the VB.NET application starts you can't determine for sure whether it is the only instance in the system. In other words, you can learn only whether and when another application is launched, not if the current applicatin is the first and only running instance.
.NET developers have solved this problem in a variety of ways, for example using Mutexes or by iterating over the collection returned by the Process.GetProcesses method. In .NET we have a new and more elegant alternative, based on the System.Threading.Semaphore type. A semaphore is a counter which can be incremented and decremented. If its value becomes zero, a thread can't decrement it and must wait for another thread to increment it to a value >0. Interestingly, if the semaphore has a name it becomes a system-wid object which can be shared by all the applications that ask for a reference to a semaphore with that specific name. It is therefore sufficient that the app creates a semaphore with a unique name just after its launch (e.g. it can be a name that includes the executable's path) to have all the instances of a given app share the same semaphore and therefore the same counter. The only problem left to solve is ensure that the semaphore's counter be correctly resotred when the application terminates, but this is easy to achieve by means of a Finalize method.
In addition to the PrevInstance property - which returns False if the application was the only running instance when the application was launched - the following VB6App class exposes also the InstanceCount property, which returns the total number of instances in that moment (the current app is included in the count). Here's the VB2005 version of this class:
Class VB6App ' the default instance Private Shared DefValue As New VB6App
' the system-wide semaphore Private semaphore As System.Threading.Semaphore ' initial count for the semaphore (very high value) Private Const MAXCOUNT As Integer = 10000
Private Sub New() Dim ownership As Boolean = False ' create a unique name, but strip invalid characters Dim name As String = "VB6App_" & System.Reflection.Assembly.GetExecutingAssembly().Location.Replace(":", "").Replace("\", "") semaphore = New System.Threading.Semaphore(MAXCOUNT, MAXCOUNT, name, ownership) ' decrement its value semaphore.WaitOne() ' if we got ownership, this app has no previous instances m_PrevInstance = Not ownership End Sub
' the PrevInstance property returns True if there was a previous instance running ' when the default instance was created Private Shared m_PrevInstance As Boolean
Public Shared ReadOnly Property PrevInstance() As Boolean Get Return m_PrevInstance End Get End Property
' return the total number of instances of the same application that are currently running Public Shared ReadOnly Property InstanceCount() As Integer Get ' release the semaphore and grab the previous count Dim prevCount As Integer = DefValue.semaphore.Release() ' acquire the semaphore again DefValue.semaphore.WaitOne() ' eval the number of other instances that are currently running Return MAXCOUNT - prevCount End Get End Property
Protected Overrides Sub Finalize() ' increment the semaphore when the application terminates semaphore.Release() End Sub
End Class
Notice that this class has a Finalize method without implementing IDisposable. It is one of those special cases when it is OK to violate the Dispose-Finalize pattern.
To understand how the class works, just keep in mind that the Semaphore.Release method increments the internal counter, whereas the WaitOne method decrements it. You must test the VB6App.PrevInstance property as soon as the application starts, for example in the Sub Main method or in the Load event hander for the main form, as to let the VB6App class store the boolean value internally. The same form might then test the value of the InstanceCount property on exit, for example if you need to run some cleanup code when the last application instance is about to terminate:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Not VB6App.PrevInstance Then ' open the common log file ' ... End If End Sub
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing If VB6App.InstanceCount = 1 Then ' close the common log file. ' ... End If End Sub
Here's the C# version of the class. (I fixed the original version to fix two syntax errors mentioned in one of the comments below.)
public class VB6App { // the default instance private static VB6App DefValue = new VB6App(); // the system-wide semaphore private System.Threading.Semaphore semaphore; // initial count for the semaphore (very high value) private const int MAXCOUNT = 10000;
private VB6App() { // create a named (system-wide semaphore) bool ownership = false; // create the semaphore or get a reference to an existing semaphore
string name = "VB6App_" + System.Reflection.Assembly.GetExecutingAssembly().Location.Replace(":", "").Replace("\\", ""); semaphore = new System.Threading.Semaphore( MAXCOUNT, MAXCOUNT, name, out ownership); // decrement its value semaphore.WaitOne(); // if we got ownership, this app has no previous instances m_PrevInstance = !ownership; }
// the PrevInstance property returns True if there was a previous instance running // when the default instance was created
private static bool m_PrevInstance ;
public static bool PrevInstance { get { return m_PrevInstance ; } }
// return the total number of instances of the same application that are currently running
public static int InstanceCount { get { // release the semaphore and grab the previous count int prevCount = DefValue.semaphore.Release(); // acquire the semaphore again DefValue.semaphore.WaitOne(); // eval the number of other instances that are currently running return MAXCOUNT - prevCount; } }
~VB6App() { // increment the semaphore when the application terminates semaphore.Release(); } }
 Wednesday, May 03, 2006
Yesterday I got an email from reader Claudio Fontana, with the following, deceiptively simple request: how can you avoid flickering while updating many controls on a form? The problem is especially serious when you need to add thousands of items to a ListView or a TreeView.
In VB6 this problem can be solved quite simply by temporarily setting the Visible (or Enabled) property to False for all the controls about to be updated: the control isn't actually hidden, yet the result of the update operation appears istantaneously when the property is reset to True. Just as interesting: the update operation is carried out much faster if the control is invisible, often twice as faster. Alas, this trick doesn't work in .NET, because as soon as you set the Visible property to False the control is immediately hidden. It's necessary to find another solution.
A few Windows Forms controls - namely the ListBox, ComboBox, ListView, and TreeView controls - do expose the BeginUpdate and EndUpdate methods, which allow you to "freeze" the control while you add items to it. Not only do they solve the flickering problem, they also speed up the update operation, tipically by a factor of 2.5x. However, if your form contains many controls that do NOT expose these methods, you must devise something else, and this was exactly the problem that Claudio submitted, after he unsuccessfully googled around on the 'Net looking for a solution.
The problem is quite intriguing, thus I decided to spend some time on it, until I came to the following solution. The idea is simple, and can be split in the following steps: (1) take a snapshot of the current form's appearance, by making a pixel-by-pixel copy into a bitmap, (2) create a PictureBox control as large as the form, and load the bitmap into the PictureBox, (3) add the PictureBox to the form's Controls collection and bring the PictureBox in front of all other controls, (4) while the user looks at the "frozen" image of the form, update your controls, using the BeginUpdate/EndUpdate mthods if possible to speed up execution, (5) when the update operation is completed, remove the PictureBox from the Controls collection, so that the user can now see the real form.
You just need one dozen statements to implement this algorithim, but I prepared a class to make the code more reusable and to ensure that it releases all resources correctly:
Public Class FormFreezer Implements IDisposable
' The form being frozen Dim Form As Form ' the auxiliary PictureBox that will cover the form Dim PictureBox As PictureBox ' the number of times the Freeze method has been called Dim FreezeCount As Integer = 0
' create an instance associated with a given form ' and optionally freeze the form right away Public Sub New(ByVal form As Form, Optional ByVal freezeIt As Boolean = False) Me.Form = form If freezeIt Then Me.Freeze() End Sub
' freeze the form Public Sub Freeze() ' Remember we have frozen the form once more FreezeCount += 1 ' Do nothing if it was already frozen If FreezeCount > 1 Then Exit Sub
' Create a PictureBox that resizes with its contents PictureBox = New PictureBox() PictureBox.SizeMode = PictureBoxSizeMode.AutoSize ' create a bitmap as large as the form's client area and with same color depth Dim frmGraphics As Graphics = Form.CreateGraphics() Dim rect As Rectangle = Form.ClientRectangle PictureBox.Image = New Bitmap(rect.Width, rect.Height, frmGraphics) frmGraphics.Dispose()
' copy the screen contents, from the form's client area to the hidden bitmap Dim picGraphics As Graphics = Graphics.FromImage(PictureBox.Image) picGraphics.CopyFromScreen(Form.PointToScreen(New Point(rect.Left, rect.Top)), New Point(0, 0), New Size(rect.Width, rect.Height)) picGraphics.Dispose()
' Display the bitmap in the picture box, and show the picture box in front of all other controls Form.Controls.Add(PictureBox) PictureBox.BringToFront() End Sub
' unfreeze the form ' Note: calls to Freeze and Unfreeze must be balanced, unless force=true Public Sub Unfreeze(Optional ByVal force As Boolean = False) ' exit if nothing to unfreeze If FreezeCount = 0 Then Exit Sub ' remember we've unfrozen the form, but exit if it is still frozen FreezeCount -= 1 ' force the unfreeze if so required If force Then FreezeCount = 0 If FreezeCount > 0 Then Exit Sub
' remove the picture box control and clean up Form.Controls.Remove(PictureBox) PictureBox.Dispose() PictureBox = Nothing End Sub
' return true if the form is currently frozen Public ReadOnly Property IsFrozen() As Boolean Get Return FreezeCount > 0 End Get End Property
' ensure that resources are cleaned up correctly Public Overridable Sub Dispose() Implements IDisposable.Dispose Me.Unfreeze(True) End Sub End Class
This is the C# version, translated from VB by Claudio:
public class FormFreezer: IDisposable {
// The form being frozen
Form form;
// the auxiliary PictureBox that will cover the form
PictureBox pictureBox;
// the number of times the Freeze method has been called
int FreezeCount = 0;
// create an instance associated with a given form
// and freeze the form in base of flag freezeIt
public FormFreezer(Form form, bool freezeIt) {
this.form = form;
if (freezeIt) this.Freeze();
}
// freeze the form
public void Freeze() {
// Remember we have frozen the form once more
// Do nothing if it was already frozen
if (++FreezeCount > 1) return;
// Create a PictureBox that resizes with its contents
pictureBox = new PictureBox();
pictureBox.SizeMode = PictureBoxSizeMode.AutoSize;
// create a bitmap as large as the form's client area and with same color depth
Graphics frmGraphics = form.CreateGraphics();
Rectangle rect = form.ClientRectangle;
pictureBox.Image = new Bitmap(rect.Width, rect.Height, frmGraphics);
frmGraphics.Dispose();
// copy the screen contents, from the form's client area to the hidden bitmap
Graphics picGraphics = Graphics.FromImage(pictureBox.Image);
picGraphics.CopyFromScreen(form.PointToScreen(new Point(rect.Left, rect.Top)), new Point(0, 0), new Size(rect.Width, rect.Height));
picGraphics.Dispose();
// Display the bitmap in the picture box, and show the picture box in front of all other controls
form.Controls.Add(pictureBox);
pictureBox.BringToFront();
}
// unfreeze the form
// Note: calls to Freeze and Unfreeze must be balanced, unless force=true
public void Unfreeze(bool force) {
// exit if nothing to unfreeze
if ( FreezeCount == 0 ) return ;
// remember we've unfrozen the form, but exit if it is still frozen
FreezeCount -= 1;
// force the unfreeze if so required
if (force) FreezeCount = 0;
if (FreezeCount > 0) return;
// remove the picture box control and clean up
pictureBox.Controls.Remove(pictureBox);
pictureBox.Dispose();
pictureBox = null;
}
// return true if the form is currently frozen
public bool IsFrozen {
get { return (FreezeCount > 0); }
}
void IDisposable.Dispose()
{
this.Unfreeze(true);
}
}
Using the FormFreezer class is quite simple. Here's a code sample, which assumes that it is located inside a form class so that the Me keyword points to the current form:
Dim ff As New FormFreezer(Me, True) ' update controls here ' ... ff.Unfreeze()
The class implements IDisposable, thus you can bracket the update code in a Using block, either in C# or in VB2005, and avoid an explicit call to Unfreeze:
Using New FormFreezer(Me, True) ' Update controls here ' ... End Using
Notice that calls to Freeze and Unfreeze must be balanced. If you call Freeze twice you then need two calls to Unfreeze to actually restore the updated form. This behavior allows you to call Freeze and then invoke a method that calls Freeze again and still have the code work correctly (provided that all methods use the same instance of the FormFreeze class).
 Monday, May 01, 2006
Many, if not most, Windows Forms samples you can find on the 'net include one or more calls to unmanaged code in Windows DLLs, often in the form of calls to the SendMessage API methods to fix some of the (very few) missing features of .NET controls. The problem is, such a call to unmanaged code creates a problem when the program runs as a ClickOnce application, because it requires higher CAS privileges.
Even though this problem doesn't have a generic solution, when you just need to send a message to the control you are inheriting from, you can avoid an explicit call to SendMessage by invoking the protected DefWndProc method instead. For example, let's say that you are writing an enhanced ComboBox that exposes the TopIndex property, which can set or return the index of the first visible item in the list area. These two operations can be implemented by sending the control the CB_SETTOPINDEX or CB_GETTOPINDEX message, respectively. Here's how you can use the DefWndProc method instead of SendMessage:
Public Class ComboBoxEx Inherits System.Windows.Forms.ComboBox
Public Property TopIndex() As Integer Get Const CB_GETTOPINDEX As Int32 = &H15B Dim m As New Message() m.HWnd = Me.Handle m.Msg = CB_GETTOPINDEX Me.DefWndProc(m) Return m.Result.ToInt32() End Get Set(ByVal value As Integer) Const CB_SETTOPINDEX As Int32 = &H15C Dim m As New Message() m.HWnd = Me.Handle m.Msg = CB_SETTOPINDEX m.WParam = New IntPtr(value) Me.DefWndProc(m) End Set End Property
End Class
By the way, such an enhanced ComboBox can be useful when migrating a VB6 app to VB.NET. In fact, the VB6 ComboBox and ListBox controls expopse the TopIndex property, whereas under .NET only the ListBox control exposes this property. If you have any VB6 code that takes advantage of the ComboBox's TopIndex, the simplest approach is replacing the standard ComboBox with a ComboBoxEx instance.
 Monday, March 06, 2006
I am reorganizing my MP3 collection and found that I needed to rename a large quantity of files. Of course, there are many free utilities that allow this operation - and that can use MP3 tags in the process - but I thought that I might write one myself. Thanks to regular expressions, the task shouldn't be that hard. In fact, in a few minutes I came up with the following console application. As you see, most of the code is used to extract and validate arguments on the command line:
Imports System.Text.RegularExpressions Imports System.IO
Module Renx
Function Main(ByVal args() As String) As Integer Console.WriteLine("RENX (C) Francesco Balena / Code Architects Srl")
Dim recurse As Boolean = False Dim renameMode As Boolean = False Dim oldNamePattern As String = Nothing Dim newNamePattern As String = Nothing
' analyze each argument For Each arg As String In args Select Case arg.ToLower() Case "/s", "-s" recurse = True Case "/r", "-r" renameMode = True Case "/h", "-h" Return ShowHelp(0) Case Else If oldNamePattern Is Nothing Then oldNamePattern = "^" & arg & "$" ElseIf newNamePattern Is Nothing Then newNamePattern = arg Else Return ShowHelp(1) End If End Select Next
' check that we have both mandatory arguments If oldNamePattern Is Nothing OrElse newNamePattern Is Nothing Then Return ShowHelp(1) End If ' create the regex and check that pattern syntax is ok Dim reSearch As Regex Try reSearch = New Regex(oldNamePattern, RegexOptions.IgnoreCase) ' test the replace pattern as well Dim tmp As String = reSearch.Replace("a dummy string", newNamePattern) Catch ex As Exception Console.WriteLine("SYNTAX ERROR: {0}", ex.Message) Return 3 End Try Console.WriteLine()
' iterate over all files in current directory (and its subdirectories, if recurse mode) Dim searchOpt As SearchOption = SearchOption.TopDirectoryOnly If recurse Then searchOpt = SearchOption.AllDirectories
Dim parsedFilesCount As Integer = 0 Dim renamedFilesCount As Integer = 0 Dim errorsCount As Integer = 0 For Each oldFile As String In Directory.GetFiles(Directory.GetCurrentDirectory(), "*.*", searchOpt) parsedFilesCount += 1 ' the regex applies to name only Dim oldName As String = Path.GetFileName(oldFile) Dim ma As Match = reSearch.Match(oldName) If ma.Success Then ' this is the new name Dim newName As String = ma.Result(newNamePattern) Console.WriteLine(oldFile) Console.Write(" => {0}", newName) renamedFilesCount += 1 ' proceed with rename only if not in simulation mode If renameMode Then Try Dim dirName As String = Path.GetDirectoryName(oldFile) Dim newFile As String = Path.Combine(dirName, newName) File.Move(oldFile, newFile) Catch ex As Exception Console.Write(" -- ERROR: {0}", ex.Message) errorsCount += 1 End Try End If Console.WriteLine() End If Next
' Display a report If renameMode Then Console.WriteLine("Summary: {0} parsed files, {1} renamed files, {2} errors", parsedFilesCount, renamedFilesCount, errorsCount) Else Console.WriteLine("Summary: {0} parsed files, {1} files affected", parsedFilesCount, renamedFilesCount) Console.WriteLine() Console.WriteLine("NOTE: Running in simulation mode. Specify the /R option to actually rename files.") End If ' Return an error code If errorsCount = 0 Then Return 0 Else Return 2 End If End Function
Function ShowHelp(ByVal exitCode As Integer) As Integer Console.WriteLine() Console.WriteLine("Syntax: RENX <oldnamepattern> <newnamepattern> [/R] [/S] [/H]") Console.WriteLine(" oldnamepattern : regex that selects the files to be renamed") Console.WriteLine(" newnamepattern : regex that specifies how files must be renamed") Console.WriteLine(" /R : rename files") Console.WriteLine(" /S : iterate over subdirectories") Console.WriteLine(" /H : display this help") Console.WriteLine("NOTE: By default the program runs in simulation mode, and just displays how files would be renamed.") Console.WriteLine(" You must specify the /R option to actually rename the files.") Return exitCode End Function
End Module
At the very minimum, the RENX utility requires two arguments: a regex that specifies which files in the current directory (and its subdirectories, if you add the /S option) must be renamed, and a second regex that specifies how to rename the files that are matched by the first regex. The power of RENX is the fact that the first regex can (actually, must) specify one or more groups of characters, and these groups are then referenced in the second regex. For example, let's suppose that I have a folder with the following files:
01 Speak to Me.mp3 02 On the Run.mp3 03 Time.mp3 04 The Great Gig in the Sky.vb3 05 Money.mp3 06 Us and Them.mp3 07 Any Colour You Like.vbr 08 Brain Damage.mp3 09 Eclipse.vb3
and that I want to rename them as follows:
01 - Speak to Me - The Dark Side of the Moon.mp3 02 - On the Run - The Dark Side of the Moon.mp3 03 - Time - The Dark Side of the Moon.mp3 04 - The Great Gig in the Sky - The Dark Side of the Moon.vbr 05 - Money - The Dark Side of the Moon.mp3 06 - Us and Them - The Dark Side of the Moon.mp3 07 - Any Colour You Like - The Dark Side of the Moon.vb3 08 - Brain Damage - The Dark Side of the Moon.mp3 09 - Eclipse - The Dark Side of the Moon.vbr
Here's the RENX command that does it:
RENX "(\d\d) (.+?)(\..+)" "${1} - ${2} - The Dark Side of the Moon.${3}"
Notice that the first regex creates three groups by enclosing them in parenthesis: (\d\d) matches the song number, (.+?) matches the song title, and (\..+) matches the file extension, dot included. The second argument can then reorder these three groups, using the ${N}, where N is the position of the group as specified in the first regex. It is therefore to insert a dash after the song number, and the albumname after the song title.
Because the RENX utility is quite dangerous, by default it doe NOT rename the files, and it just lists how files would be renamed. To actually proceed with the rename operation, you must specify the /R option:
RENX "(\d\d) (.+?)(\..+)" "${1} - ${2} - The Dark Side of the Moon${3}" /R
That's all. You can play with the source code to extend the RENX utility as you prefer, and maybe turn it into a Windows Form application, or you can download the binary version from this link: Renx.zip (5.51 KB)
 Thursday, February 23, 2006
The longer I work with generics, the more I like them, and I continue to discover new ways to simplify my code by using them. More specifically, I like the ability to write type-safe code that is also more concise, efficient, and (above all) readable, because I don't have to use tons of CType and DirectCast operators. Today I gathered the generic methods I use more frequently in the following module. They are tiny and simple, yet they save me a lot of time and code.
Module GenericFunctions
' Swap two variables Public Sub Swap(Of T)(ByRef var1 As T, ByRef var2 As T) Dim tmp As T = var1 var1 = var2 var2 = tmp End Sub
' Type-safe version of the IIF function ' returns valueOnTrue if expression is True, else returns valueOnFalse Public Function IIf(Of T)(ByVal expression As Boolean, ByVal valueOnTrue As T, ByVal valueOnFalse As T) As T If expression Then Return valueOnTrue Else Return valueOnFalse End If End Function
' Type-safe version of the Choose function ' returns the N-th element of a list of values, or the default value for T if index ' is less than 0 or higher than the number of values Public Function Choose(Of T)(ByVal index As Integer, ByVal values() As T) As T If index >= 0 AndAlso index < values.Length Then Return values(index) Else Return Nothing End If End Function
' Return an array of the specified type Public Function NewArray(Of T)(ByVal ParamArray values() As T) As T() Return values End Function
' Return the min value of a list Public Function Min(Of T As IComparable)(ByVal firstValue As T, ByVal ParamArray values() As T) As T Dim result As T = firstValue For Each value As T In values If result.CompareTo(value) > 0 Then result = value Next Return result End Function
' Return the max value of a list Public Function Max(Of T As IComparable)(ByVal firstValue As T, ByVal ParamArray values() As T) As T Dim result As T = firstValue For Each value As T In values If result.CompareTo(value) < 0 Then result = value Next Return result End Function
' Return True if a value is in specific range Public Function InRange(Of T As IComparable)(ByVal testValue As T, ByVal minValue As T, ByVal maxValue As T) As Boolean Return testValue.CompareTo(minValue) >= 0 AndAlso testValue.CompareTo(maxValue) <= 0 End Function
' Retrieve a dictionary element of a given type, or the provided default value if the element isn't found ' (two overloads) Public Function GetDictionaryValue(Of TKey, TValue)(ByVal dict As Hashtable, ByVal key As TKey, ByVal defaultValue As TValue) As TValue If dict.ContainsKey(key) Then Return CType(dict(key), TValue) Else Return defaultValue End If End Function
Public Function GetDictionaryValue(Of TKey, TValue)(ByVal dict As Dictionary(Of TKey, TValue), ByVal key As TKey, ByVal defaultValue As TValue) As TValue ' If the key is in the dictionary, the following statement stores the corresponding value ' in defaultValue, else it leave defaultValue unchanged dict.TryGetValue(key, defaultValue) Return defaultValue End Function
End Module
Most methods are self-explanatory. One of the most useful ones is NewArray, which lets you create an array and pass it on-the-fly to a method. Let's say the the DoSomething method takes an array of Integers. These are the options you have in VB2005:
' 1. Create the array first, than pass it Dim values() As Integer = {1, 2, 3, 4, 5} DoSomething(values)
' 2. Create the array on the fly using the nearly-undocumented syntax DoSomething(New Integer() {1, 2, 3, 4, 5})
I often use the second syntax, but I noticed that relatively few developers know it. My code is much more readable with the NewArray method
' 3. Use the NewArray generic function to create the array on the fly DoSomething(NewArray(1, 2, 3, 4, 5))
The NewArray method proves to be quite useful also to build For loops whose index can take any sequence of values:
' Test whether "number" is a prime number in the range 1-1000 For Each n As Integer In NewArray(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31) If (number Mod n) = 0 Then Console.Write("{0} is not prime", number): Exit For Next
 Wednesday, February 15, 2006
In the VB6 world - and in the COM world, in general - you "destroy" an object by simply putting it to Nothing, unless of course there are other variables that are pointing to that specific instance. The VB6 runtime invokes the Class_Terminate method, if it exists, then it deallocates all the resources belonging to the object, memory included. This is a recursive process, and destroys any object that is owned by the object being set to Nothing.
We know well that setting a VB.NET object variable to Nothing doesn't fire any event, because the Terminate event isn't supported in .NET. If the object implements the Finalize method, this method is invoked by the garbage collection, but this invocation occurs some time later. The time interval between the Set to Nothing statement and the invocation of Finalize depends on many factors, but it could be as long as a few minutes or even hours, if the application doesn't allocate many objects and doesn't stress the garbate collector.
This behavioral difference can be a serious issue when migrating a VB6 application to VB.NET. For example, if the code in Class_Terminate closes a database connection, or a file, or a serial port, or deletes confidential information from disk, or unloads a form, then the delay between the "logical" destruction of the object (i.e. the Set to Nothing) and its "physical" destruction (when the Finalize method runs) can compromise the correct working of the application after its migration to the .NET world.
Unfortunately, it isn't possible to have VB.NET "automagically" behave like VB6 in this respect. However, it is possible to take a step that can greatly reduce the problem, without much impact on the code structure. Once again, this is possible thanks to generics. To see how, create a Module and add the following code to it, so that the SetNothing method is visible to the entire application:
Public Sub SetNothing(Of T)(ByRef obj As T) ' Dispose of the object if possible If obj IsNot Nothing AndAlso TypeOf obj Is IDisposable Then DirectCast(obj, IDisposable).Dispose() End If ' Decrease the reference counter, if it's a COM object If Marshal.IsComObject(obj) Then Marshal.ReleaseComObject(obj) End If obj = Nothing End Sub
Next, you can apply the search-and-replace feature in Visual Studio to the code produced by the migration wizard, to replace all occurrences of the var = Nothing pattern with the SetNothing.(var) pattern. Obviously, this technique doesn't really solve all the abovementioned problems, because it works only with the variables that are explicitly set to Nothing via code, and doesn't work with variables that are implicitly cleared when they exit the current scope (for example at the end of the method).
Notice that the search-and-replace operation include a variable part - that is, the name of the instance that is set to Nothing - therefore making this replacementement automatically seems impossible or unpractical. But, fortunately, Visual Studio supports regular expressions in search-and-replace operations (as I explain here), therefore you can search for the <{:i} = Nothing string and specify the SetNothing(\1) string in the Replace with field. Et voilà 
NOTE for those who are unfamiliar with regexes: the Find What string specifies that you want to search for a variable name (:i) that is located at the beginning of a word (<); curly braces in {:i} cause the variable name to be tagged, so that you can refer to the variable name in the Replace With regular expression, by means of the \1 placeholder. That's it.
 Friday, November 25, 2005
Every now and then I get an email from a reader or a customer, who asks for clarifications on object finalization and disposing. As far as I know, the best article on this topic is this essay by Joe Duffy. It's over 25-page long, covers both .NET 1.1 and 2.0, and includes comments from gurus such as Jeffrey Richter e Chris Brumme. This is easily the definitive article on this topic and I urge you to read it if you haven't already.
The Dispose-Finalize pattern is objectively a complex matter. However, in most cases it can be simplified significantly if you use the following approach: (1) the class with the Dispose/Finalize method should wrap only one single unmanaged resource, and (2) this finalizable class should be private and nested inside another disposable (but not finalizable) type. The outer class is the only class that can use the finalizable class.
This simple trick enables the GC to immediately release all the memory used by the wrapper (disposable) class even in the worst case - that is, if the client code omits to invoke the Dispose method - and simplifies the structure of the type that uses the unmanaged resource. A listing is worth one thousand words, thus here is the C# version of what I mean:
// the class that clients use to work with the unmanaged resource class WinResource : IDisposable { // private field that creates a wrapper for the unmanaged resource private UnmanagedResourceWrapper wrapper = null; // this is true if the object has been disposed of bool disposed = false;
public WinResource(string someData) { // allocate the unmanaged resource here wrapper = new UnmanagedResourceWrapper(someData); }
// a public method that clients call to work with the unmanaged resource public void DoSomething() { // throw if the object has been already disposed of if ( disposed ) throw new ObjectDisposedException("");
// this code can pass the wrapper.Handle value to API calls. // ... }
public void Dispose() { // avoid issues when multiple threads call Dispose at the same time. lock ( this ) { // do nothing if already disposed of if ( disposed ) return; // dispose of all the disposable objects used by this instance // including the one that wraps the unmanaged resource // ... wrapper.Dispose(); // remember this object has been disposed of disposed = true; } } // the nested private class that allocates and release the unmanaged resource private sealed class UnmanagedResourceWrapper : IDisposable { // an invalid handle value, that the wrapper class can use to check // whether the handle is valid public static readonly IntPtr InvalidHandle = new IntPtr(-1);
// a public field, but accessible only from inside the WinResource class public IntPtr Handle = InvalidHandle;
// the constructor takes some data and allocates the unmanaged resource (eg a file) public UnmanagedResourceWrapper(string someData) { // this is just a demo... this.Handle = new IntPtr(12345); }
// the Dispose method can be invoked only by WinResource class public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
// the finalizer ~UnmanagedResourceWrapper() { Dispose(false); }
// This is where the unmanaged resource is actually disposed of. // Notice that it takes an argument only for compliance with .NET coding standards // but the disposing argument is never used, because in all cases this class // can access and release only the single unmanaged resource it wraps. private void Dispose(bool disposing) { // exit now if this object didn't completed its constructor correctly if ( this.Handle == InvalidHandle ) return; // release the unmanaged resource // eg. CloseHandle(Handle); // finally, invalidate the handle this.Handle = InvalidHandle; } } }
Notice that, if the unmanaged resource must interact with other fields, this interaction should be taken care of inside the WinResource class, not in the nested class. The UnmanagedResourceWrapper works only as a wrapper for the handle and shouldn't contain other fields or methods, besides those shown in the above listing. The code in the WinResource class must coordinate all the resources being used, both managed and unmanaged ones, and must release all of them in its Dispose method. But if the client code omits to call the Dispose method, the destructor in the nested class will orderly release the unmanaged resource during the next garbage collection.
Let's see all the advantages of this simplified approach.
- The requirement that you shouldn't access reference fields from inside the Finalize method is automatically satisfied, because the only field of the UnmanagedResourceWrapper type is a handle (a value type).
- If the client code omits to invoke the WinResource.Dispose method before the WinResource object goes out of scope, the WinResource object is removed from the heap anyway at the first GC; only the few bytes used by the UnmanagedResourceWrapper object survive in the heap and will be promoted to generation 1 or 2. Therefore this technique is more efficient than writing a single finalizable object that allocates both managed and unmanaged resource.
- The UnmanagedResourceWrapper class is private and you can't inherit from it, therefore you can mark it as sealed. This means that you never have to worry about the Dispose/Finalize pattern in derived classes - a topic on which tons of digital ink has been spilled. It is possible to inherit from WinResource as you'd do with disposable class, therefore there are no limitations in this respect. (It's exactly like when you inherit from other disposable classes such as FileStream.)
- The UnmanagedResourceWrapper is private and nested in another type and it isn't possible to achieve a refernence to one of its instances; therefore, a client can't "resurrect" a UnmanagedResourceWrapper object during the finalization step, a technique that is rarely useful and often dangerous. (Even though I show in my Programming Visual Basic .NET book how you can use it to implement an object pool.)
- The UnmanagedResourceWrapper constructor performs a single "atomic" action; if this action fails, the value of the handle is still qual to InvalidHandle, therefore the code in the Finalize method can detect this special value and do nothing in that case. There are only two cases: either the unmanaged resource has been correctly allocated or an exception prevented it from being created, and you don't have to worry about an object that has been built only partially because of an exception in its constructor.
- Many other recommendations related to the Dispose/Finalize pattern become void, such as the one that dictates that you should neither write finalizers inside structures nor calling virtual methods from inside the finalizer. In fact, the UnmanagedResourceWrapper class is sealed and has no virtual methods. Nor do you have to worry about versioning issues.
- Another advantage: the UnmanagedResourceWrapper class is so simple and generic that you can ofter reuse it as-is (or with minor edits) inside other classes, by means of a plain copy-and-paste action. Being a nested class, you don't even need to change its name to avoid name collisions.
I am sure that in some cases this simplified pattern can't be used, though it always worked well in my applications. I believe that it's quite odd that this simplified approach is rarely mentioned in articles and books on this topic.
Technical matters aside, I think that another kind of consideration about the Dispose/Finalize pattern is in order.
In my opinion, it is essential to put a lot of emphasis on the fact that the Dispose/Finalize pattern should be used only when your type invokes unmanaged code that allocates unmanaged resources (including unmanaged memory) and returns an handle that you must use eventually to release the resource. If the unmanaged resource is already wrapped by a .NET object (e.g. a FileStream or a SqlConnection) or a COM object, the .NET class that uses the resource must implement IDisposable, not the finalizer. And you must implement the Finalize method only if your code assigns the handle to a class-level field. If the handle is assigned to a local variable and the unmanaged resource is released before exiting the method - possibly in the Finally section of a Try block - you don't even have to implement the IDisposable interface. One of the few exceptions to this rule is when your managed code allocates unmanaged memory directly, by means of the System.Runtime.InteropServices.Marshal type.
I talked to many developers who believe that, in doubt, they should always implement the Finalize method, just in case. This is a common mistake. Defining a finalizable class without a real reason to do so can hurt performance, because the CLR takes slightly longer to allocate finalizable objects, because it has to register them in the f-reachable queue. And in the worst case - that is if the caller omits to call to the Dispose method - a finalizable object can have even more impact on performance, because it will be promoted to a higher generation without any real reason for such an overhead.
To recap: when do you really need to implement the Finalize method? Thinking of all the commercial apps I worked on in these years, I'd say that I used this pattern no more than 4 or 5 times. For sure, I used it more frequently in books and articles than in the real world. 
 Monday, November 21, 2005
I am back from the Windows Professional Conference (Milan, Italy), for which I also server as the chairman for two of the four tracks. The conference touched virtually all the new features of Visual Studio and SQL Server 2005, plus tons of other topics. But that's another story.
When preparing the material for the conference I had to pack all my code samples in a zip file. Even after zipping them, I came up with a 7-8M file, which is a bit too much. So I was about to manually delete all the files that could be recreated by recompiling the projects. It's the same old story, that repeat itself with all conferences, books, articles for magazines and blog posts.
Instead of using the manual approach, this time I decided to write a tiny utility that does the work for me. It took (literally) two minutes to write a console utility that takes the path as an argument and recurses over that directory tree to remove all the folders named "bin" and "obj". If a delete operation fails, a message error is displayed. (This happens when an executable is running and is therefore locked by the operating system.) The code is especially concise thanks to an overload of the Directory.GetDirectories method (added in .NET 2.0) that returns all the directory in a tree.
Imports System.IO
Module Module1 Sub Main(ByVal args() As String) ' Use current directory if no argument has been specified Dim rootDir As String = Directory.GetCurrentDirectory() If args.Length > 0 Then rootDir = args(0) ' Read all the folder names in the specified directory tree Dim dirNames() As String = Directory.GetDirectories(rootDir, "*.*", SearchOption.AllDirectories) Dim errors As Integer = 0
' Delete all the BIN and OBJ subdirectories For Each dir As String In dirNames Dim dirName As String = Path.GetFileName(dir).ToLower() If dirName = "bin" OrElse dirName = "obj" Then Try Console.Write("Deleting {0} ...", dir) Directory.Delete(dir, True) Console.WriteLine("DONE") Catch ex As Exception Console.WriteLine() Console.WriteLine(" ERROR: {0}", ex.Message) errors += 1 End Try End If Next
Console.WriteLine() If errors = 0 Then Console.WriteLine("All directories were removed successfully") Else Console.WriteLine("{0} directories couldn't be removed") End If End Sub End Module
In addition to using this tool from the command line, you can add it to the Tools menu, so that you can quickly delete all the files produced by compiling the current solution, by using this command:
DELETEBINPATH $(SolutionDir)
where of course DeleteBinPath is the name you used when compiling the utility.
UPDATE: I have posted a new version of this utility in a more recent post.
 Friday, November 18, 2005
Let's consider the following code, which represents a typical situation: you are inside a nested loop and you want to exit both loops when a condition is true:
For i As Integer = 1 To 10 Dim exiting As Boolean = False For j As Integer = 1 To 20 ' If the Evaluate function returns zero you want to exit both loops If Evaluate(i, j) = 0 Then exiting = True Exit For End If ' Do something here Next If exiting Then Exit For Next
It isn't important to understand what the Evaluate function does, just consider that when this function returns zero you must exit both loops. The above code isn't optimized, because it repeatedly tests the exiting variable. You might optimize the loop by using a Goto statement that points to a label following the second Next keyword, but educated programmers don't use Gotos, right? So, the question is simple: how can you simplify this code and optimize it at the same time by dropping the exiting variable?
The solution is simple, and is based on the fact that Visual Basic supports as many as three different kinds of loops: For, Do, and While. Each kind of loop supports a corresponding Exit keyword (Exit For, Exit Do, and Exit While), thus you can rewrite the code as follows:
Dim i As Integer = 1 Do While i <= 10 For j As Integer = 1 To 20 If Evaluate(i, j) = 0 Then Exit Do ' Do something here Next i += 1 Loop
You can use the same technique when you have up to three nested loops.
Incidentally, you can't adopt this technique in C#, because its break statement doesn't have the same "semantics power" of the Exit keyword in VB.
 Sunday, November 13, 2005
Visual Studio 2005 comes with dozens of ready-to-use code snippets. You might argue on the usefulness of some of them, but for sure many of them are really well-conceived. For example, the prop expansion that creates public C# properties is a real time saver.
The Code Snippet Manager dialog box (in the Tools menu) enables you to inspect all the installed snippets, one by one, but doesn't offer the option to export a list of all the snippets, therefore you have to browse them one by one to take notice of their name, purpose, and keyboard shortcut. While I was working on chapter 4 of Programming Microsoft Visual Basic 5, I wrote this little throw-away program which decodes the snippet index and list them on a console window. Of course you can redirect the output to a file to have a document that you can use as a reference.
The program takes a parameter equal to the path of the SnippetIndex.xml (VB) or SnippetsIndex.xml (C#) file that contains the snippet index. (Oddly, this file has a slightly different name in the two languages.) If you run it without passing any argument, it uses the path of the VB snippet index in a default Visual Studio installation. A comment in the listing explains how you can use the default index for C# instead.
The output of this code is quite terse - just snippet names and shortcuts, grouped in categories - but you can easily modify the source code to extract and display more attributes.
Imports System.IO Imports System.Xml Imports System.Text.RegularExpressions
Module Module1 Dim snippetsPath As String Dim catNames As New Dictionary(Of String, String)
Sub Main(ByVal args() As String) ' If no argument has been provided, use default path for snippets. If args.Length = 0 Then args = New String() {"C:\Program Files\Microsoft Visual Studio 8\Vb\Snippets\1033\SnippetIndex.xml"} ' Uncomment next line to list C# snippets ' args = New String() {"C:\Program Files\Microsoft Visual Studio 8\VC#\Snippets\1033\SnippetsIndex.xml"} End If
Dim snippetsFile As String = args(0) snippetsPath = Path.GetDirectoryName(snippetsFile) ' Load the snippet index file. Dim xmlIndex As New XmlDocument() xmlIndex.Load(snippetsFile) ' We need two passes, because dirs and subdirs use a different XML element. ParseSnippetIndex(xmlIndex, "//SnippetDir") ParseSnippetIndex(xmlIndex, "//SnippetSubDir") ' Iterate over all the directories in the main snippet directory. For Each dir As String In Directory.GetDirectories(snippetsPath) ParseSnippetFolder(dir, "") Next End Sub
Sub ParseSnippetIndex(ByVal xmlIndex As XmlDocument, ByVal searchKey As String) ' Create the correspondence between relative paths and localized categories For Each xmlEl As XmlElement In xmlIndex.SelectNodes(searchKey) Dim elPath As XmlElement = DirectCast(xmlEl.SelectSingleNode("DirPath"), XmlElement) Dim elName As XmlElement = DirectCast(xmlEl.SelectSingleNode("LocalizedName"), XmlElement) catNames.Add(elPath.InnerText, elName.InnerText) Next End Sub
Sub ParseSnippetFolder(ByVal dir As String, ByVal parentCategory As String) ' Retrieve the relative name of this subdirectory. Dim relPath As String = dir.Substring(snippetsPath.Length) ' The default name for this category Dim categoryName As String = parentCategory & Path.GetFileNameWithoutExtension(dir) ' Search this relative path in the snippet index. Dim searchPath As String = "%InstallRoot%\Vb\Snippets\%LCID%" + relPath + "\" If catNames.ContainsKey(searchPath) Then ' If found, use the localized category as appears in the index file categoryName = parentCategory & catNames(searchPath) End If Console.WriteLine(categoryName.ToUpper()) ' Parse individual snippets in this directory. For Each file As String In Directory.GetFiles(dir, "*.snippet") ParseSnippetFile(file) Next ' Parse all sub-categories For Each subdir As String In Directory.GetDirectories(dir) ParseSnippetFolder(subdir, categoryName & " / ") Next End Sub
Dim reTitle As New Regex("<Title>(.+?)</Title>") Dim reShortcut As New Regex("<Shortcut>(.+?)</Shortcut>")
Sub ParseSnippetFile(ByVal snippetFile As String) Dim text As String = File.ReadAllText(snippetFile) ' We use regexes to extract information for individual snippet files. Dim maTitle As Match = reTitle.Match(text) Dim maShortcut As Match = reShortcut.Match(text) Dim title As String = maTitle.Groups(1).Value Dim shortcut As String = maShortcut.Groups(1).Value Console.WriteLine(" {0} [{1}]", title, shortcut) End Sub End Module
 Monday, November 07, 2005
Here's a non-orthodox but quite effective technique I sometimes use to detect and avoid recursive calls to a method. You typically detect recursive calls by defining a boolean class-level field and testing it on entry to a method. This technique is often used in event handlers, for example in TextChanged handlers that modify the Text property of a control and that would therefore trigger an endless recursion:
Dim insideTextChanged As Boolean
Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged ' Exit if this is a recursive call. If insideTextChanged Then Exit Sub ' Forbid recursive calls from now on. insideTextChanged = True ' ... TextBox1.Text = TextBox1.Text & " " ' Permit recursive calls. insideTextChanged = False End Sub
This approach works well, but it requires a lot of code and forces you to define a distinct boolean field for each event handler. If you have many handlers, it quickly becomes a nuisance. In addition, if there is any chance that the method throws an exception, you must wrap all the code in a try block,so that you can reset the insideTextChanged to false in the finally section. Wouldn't it great if you could use a method that allows you to test if you are inside a recursive call? I am thinking of something like this:
Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged ' Exit if this is a recursive call. If IsRecursive() Then Exit Sub ' ... TextBox1.Text = TextBox1.Text & " " End Sub
Here's how you can implement the IsRecursive method:
<System.Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)> _ Public Shared Function IsRecursive() As Boolean Dim st As New StackTrace ' Check whether any method in the call stack is the same as the immediate caller. For n As Integer = 2 To st.FrameCount - 1 If st.GetFrame(1).GetMethod() Is st.GetFrame(n).GetMethod() Then Return True Next Return False End Function
Here's the C# version:
[System.Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)] public static bool IsRecursive() { StackTrace st = new StackTrace(); // Check whether any method in the call stack is the same as the immediate caller. for ( int n= 2; n < st.FrameCount; n++ ) { if ( st.GetFrame(1).GetMethod() == st.GetFrame(n).GetMethod() ) return true; } return false; }
The IsRecursive method compares the immediate caller - that is, st.GetFrame(1).GetMethod() - with all the other methods on the call stack and returns True if it finds a match. It is essential that the IsRecursive method is decorated with the MethodImpl attribute, to ensure that the JIT compiler inlines it in its caller's body. In .NET 1.1 this should never happen, because the JIT compiler never inlines methods that contain loops, but I haven't checked under .NET 2.0 and obviously I can't make promises about future versions, therefore this attribute is your best defence.
 Tuesday, November 01, 2005
I am reviewing the chapter on execution flow in Visual Basic 2005, where I cover recursion - among the many things. In most programming books I've read, recusion is explained with the "classic" factorial example (which can be implemented more efficiently with a simple For loop) or as a means to visit tree structures. It looks like recursion isn't useful in the "average" business application, which of course isn't the case. As most programming techniques, it's mostly a matter of knowing when and where to exploit it.
Here's an example of recursion that you might find quite useful: a method that converts an integer into its textual representation, e.g. 1234 into "One Thousand Two Hundreds Thirty Four", taken from my forthcoming Microsoft Press book Programming Microsoft Visual Basic 2005.
Public Shared Function NumberToText(ByVal n As Integer) As String Select Case n Case Is < 0 Return "Minus " & NumberToText(-n) Case 0 Return "" Case 1 To 19 Dim arr() As String = {"One", "Two", "Three", "Four", "Five", "Six", _ "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", _ "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"} Return arr(n - 1) & " " Case 20 To 99 Dim arr() As String = {"Twenty", "Thirty", "Forty", "Fifty", "Sixty", _ "Seventy", "Eighty", "Ninety"} Return arr(n \ 10 - 2) & " " & NumberToText(n Mod 10) Case 100 To 199 Return "One Hundred " & NumberToText(n Mod 100) Case 200 To 999 Return NumberToText(n \ 100) & "Hundreds " & NumberToText(n Mod 100) Case 1000 To 1999 Return "One Thousand " & NumberToText(n Mod 1000) Case 2000 To 999999 Return NumberToText(n \ 1000) & "Thousands " & NumberToText(n Mod 1000) Case 1000000 To 1999999 Return "One Million " & NumberToText(n Mod 1000000) Case 1000000 To 999999999 Return NumberToText(n \ 1000000) & "Millions " & NumberToText(n Mod 1000000) Case 1000000000 To 1999999999 Return "One Billion " & NumberToText(n Mod 1000000000) Case Else Return NumberToText(n \ 1000000000) & "Billions " _ & NumberToText(n Mod 1000000000) End Select End Function
Here's the version for curly braces' lovers. C# switch keyword doesn't support ranges, thus I had to change the code to use a series of elseif blocks:
public static string NumberToText( int n) { if ( n < 0 ) return "Minus " + NumberToText(-n); else if ( n == 0 ) return ""; else if ( n <= 19 ) return new string[] {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}[n-1] + " "; else if ( n <= 99 ) return new string[] {"Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"}[n / 10 - 2] + " " + NumberToText(n % 10); else if ( n <= 199 ) return "One Hundred " + NumberToText(n % 100); else if ( n <= 999 ) return NumberToText(n / 100) + "Hundreds " + NumberToText(n % 100); else if ( n <= 1999 ) return "One Thousand " + NumberToText(n % 1000); else if ( n <= 999999 ) return NumberToText(n / 1000) + "Thousands " + NumberToText(n % 1000); else if ( n <= 1999999 ) return "One Million " + NumberToText(n % 1000000); else if ( n <= 999999999) return NumberToText(n / 1000000) + "Millions " + NumberToText(n % 1000000); else if ( n <= 1999999999 ) return "One Billion " + NumberToText(n % 1000000000); else return NumberToText(n / 1000000000) + "Billions " + NumberToText(n % 1000000000); }
These methods are much simpler than any similar code I've found on the Internet, thanks to recursion. I really love OOP, generics, attributes, regular expressions, and other advanced language features, but I also like to reming that you can often write elegant, compact, and efficient code just leveraging the features that mainstream languages have offered for decades.
|
|
Get RSS/Atom Feed
Search in the blog
Archive
| | Sun | Mon | Tue | Wed | Thu | Fri | Sat | | 28 | 29 | 30 | 1 | 2 | 3 | 4 | | 5 | 6 | 7 | 8 | 9 | 10 | 11 | | 12 | 13 | 14 | 15 | 16 | 17 | 18 | | 19 | 20 | 21 | 22 | 23 | 24 | 25 | | 26 | 27 | 28 | 29 | 30 | 31 | 1 | | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
Categories
Powered by: newtelligence dasBlog 1.8.5223.1
|