Eldritch Coding Horrors, Part 1

I found this terror today in a legacy application.

Program A uses SetWindowLong to hook the message loop for a Windows Form:


Friend Function StartListening() As Boolean
SetWindowLong(m_MappedWndHandle, GWL_WNDPROC, AddressOf WindowProcedure)
End Function

Program A then shells program B. Program B sends a WM_COPYDATA message to program A. Program A intercept the message, reads the data, and then passes the message to the form:


Public Function WindowProcedure(ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If msg = WM_COPYDATA Then
objMapMemoryData.ReadMessage lParam
End If
WindowProcedure = CallWindowProc(objMapMemoryData.GetWindowProcedureAddress, hwnd, msg, wParam, lParam)
End Function

objMapMemoryData.ReadMessage raises an event that is handled by the Form:


Friend Function ReadMessage(ByVal lParam As Long) As Boolean
‘SNIP: Copy memory around
RaiseEvent MapMemoryDataEvent
End Function

Private Sub objMapMemoryData_MapMemoryDataEvent()
‘SNIP: Process event here
End Sub

To Bind or Not To Bind That is the Question

One of the things that VB6 is very good at is late binding. Back in the days before .NET late binding or OLE Automation as it was also known was one of the only ways to do certain tasks such as create reports in Excel, run Word mail merges from databases, or print nicely formatted documents. However there were downsides too. Applications that late bound to DLLs and EXEs had no means to ensure that the target supported the call being made or even that the target was installed on the PC. This allowed difficult to debug problems to creep into your program.

 So the best solution to late binding problems was to early bind to any DLLs that you wanted to use. This way you could ensure that the interface was correct and that the DLL was installed on the system. However this led to another problem: DLL Hell. When an updated DLL was installed your system had to keep track of each version of the DLL and make sure that the right program called the right version. If a DLL was relied upon by a lot of programs and was regularly updated then a new installation could mess up an old program.

 So .NET included ‘xcopy’ installation – simply include the right version of each referenced DLL in the same directory as the application and late binding became something that only ‘bad’ programmers did. Early bound DLLs also need to be loaded by the system when your program initiates.

 The thing is that late binding solves a certain class of problem, one that often occurs in legacy applications. When adding new features supported by a DLL or refactoring old features to use new DLLs early binding to the DLL can cause problems with your installation program/routine, especially where DLL Hell issues have been previously overcome or there are a large number of support DLLs required for the application.

 So a simple approach to enhancing or refactoring an old application is to develop with the references early bound and then switch to late binding and copy the required DLLs into either the application directory or another suitable location.

String Enums and Constants

The ever occuring problem of how to implement a String Enum or a Constant in an elegant way is an annoyance for all programmers. No programmer likes hardcoding constants or enums but they are often necessary. Below is another way to accomplish this.


Imports System.ComponentModel
Imports MyPatterns.TypeCodes
Imports MyPatterns.CategoryCodes

Public Enum TypeCodes
    <Description("Type A")> TypeA = 0
    <Description("Type B")> TypeB
    <Description("Type C")> TypeC
End Enum

Public Enum CategoryCodes
    <Description("General")> General = 0
    <Description("Specific")> Specific
    <Description("Other")> Other
End Enum

Public NotInheritable Class Constants

#Region "Resources"

    Private Shared myTypes As Dictionary(Of TypeCodes, ConstantItem) = Nothing

    Public Shared ReadOnly Property Types() As Dictionary(Of TypeCodes, ConstantItem)
        Get
            If myTypes Is Nothing Then
                myTypes = New Dictionary(Of TypeCodes, ConstantItem)
                BuildTypes(myTypes)
            End If
            Return myTypes
        End Get
    End Property

    Private Shared Sub BuildTypes(ByRef dict As Dictionary(Of TypeCodes, ConstantItem))
        With dict
            .Add(TypeCodes.TypeA, New ConstantItem(TypeCodes.TypeA.Description, "Type A are..."))
            .Add(TypeCodes.TypeB, New ConstantItem(TypeCodes.TypeB.Description, "Type B are..."))
            .Add(TypeCodes.TypeC, New ConstantItem(TypeCodes.TypeC.Description, "Type C are..."))
        End With
    End Sub

#End Region

#Region "Categories"

    Private Shared myCategories As Dictionary(Of CategoryCodes, ConstantItem) = Nothing

    Public Shared ReadOnly Property Categories() As Dictionary(Of CategoryCodes, ConstantItem)
        Get
            If myCategories Is Nothing Then
                myCategories = New Dictionary(Of CategoryCodes, ConstantItem)
                BuildCategories(myCategories)
            End If
            Return myCategories
        End Get
    End Property

    Private Shared Sub BuildCategories(ByRef dict As Dictionary(Of CategoryCodes, ConstantItem))
        With dict
            .Add(CategoryCodes.General, New ConstantItem(CategoryCodes.General.Description, "General category"))
            .Add(CategoryCodes.Specific, New ConstantItem(CategoryCodes.Specific.Description, "Specific category"))
            .Add(CategoryCodes.Other, New ConstantItem(CategoryCodes.Other.Description, "Other category"))
        End With
    End Sub

#End Region

End Class

Public NotInheritable Class ConstantItem

#Region "Constructors"
    ''' <summary>
    ''' Default constructor.
    ''' </summary>
    Public Sub New()
        'Do nothing
    End Sub
    ''' <summary>
    ''' Simple constructor.
    ''' </summary>
    Sub New(value As String)
        Me.Name = value
        Me.Description = value
    End Sub
    ''' <summary>
    ''' Proper constructor.
    ''' </summary>
    Sub New(name As String, description As String)
        Me.Name = name
        Me.Description = description
    End Sub

#End Region

    Property Name As String
    Property Description As String

    ''' <summary>
    ''' See http://stackoverflow.com/questions/293215/default-properties-in-vb-net
    ''' </summary>
    Public Shared Widening Operator CType(value As String) As ConstantItem
        Return New ConstantItem(value)
    End Operator
    ''' <summary>
    ''' See http://stackoverflow.com/questions/293215/default-properties-in-vb-net
    ''' </summary>
    Public Shared Widening Operator CType(value As ConstantItem) As String
        Return value.Name
    End Operator

End Class

Public Class ConstantsExample

    Public Sub UseConstant()

        Dim value As String = Constants.Types(TypeA)
        Dim category As String = Constants.Categories(General)

    End Sub

End Class

Which endpoint is actually being used?

Private Function GetEndpointAddress(name As String) As String
Debug.Print("--- GetEndpointAddress ---")
Dim address As String = "Unknown"
Dim appConfig As Configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
Debug.Print("app.config: " & appConfig.FilePath)
Dim serviceModel As ServiceModelSectionGroup = ServiceModelSectionGroup.GetSectionGroup(appConfig)
Dim bindings As BindingsSection = serviceModel.Bindings
Dim endpoints As ChannelEndpointElementCollection = serviceModel.Client.Endpoints
For i As Integer = 0 To endpoints.Count - 1
Dim endpoint As ChannelEndpointElement = endpoints(i)
Debug.Print("Endpoint: " & endpoint.Name & " - " & endpoint.Address.ToString)
If endpoint.Name = name Then
address = endpoint.Address.ToString
End If
Next
Debug.Print("--- GetEndpointAddress ---")
Return address
End Function

Retrieving files from application directory using Reflection

When retrieving a list of files in the directory of the executing application/DLL you can use the following code:

 Dim uri As String = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) Dim local As String = New Uri(uri).LocalPath
 Dim filenames() As String = System.IO.Directory.GetFiles(local, “*.*”)
 

CodeBase returns a URI which means that if you pass the result of GetDirectoryName directly to GetFiles a ‘URI formats are not supported’ error is thrown. Thus we need to convert the URI to a local path.

An interesting way to get around the limitations of a Using block when calling WCF Services

As you may have already experienced there are potential problems with a Using block when interacting with a WCF Service. For example this code can easily produce an undecipherable WCF CommunicationObjectFaultedException  error for no apparent reason:

Using service As New WCF_ServiceReference.MessageClient
'This is the 'payload' code block:
For Each item As Message In MessageList
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
End Using

After some research you will discover that the Microsoft recommendation is to wrap the ‘payload’ code in a Try…Catch block instead of a Using block. See ‘Expected Exceptions’ (http://msdn.microsoft.com/en-us/library/aa354510.aspx) for details. This results in the rather longwinded code below:

Dim service As New WCF_ServiceReference.MessageClient
'Exceptions that are thrown from communication methods on a Windows Communication Foundation (WCF) client are either expected or unexpected.
Try
'Here is the 'payload' code block again
For Each item As Message In MessageList
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
service.Close()
Catch timex As TimeoutException
'Expected exceptions from communication methods on a WCF client include TimeoutException, CommunicationException, and any derived class of CommunicationException. These indicate a problem during communication that can be safely handled by aborting the WCF client and reporting a communication failure. Because external factors can cause these errors in any application, correct applications must catch these exceptions and recover when they occur.
service.Abort()
Catch commex As CommunicationException
'Code that calls a client communication method must catch the TimeoutException and CommunicationException. One way to handle such errors is to abort the client and report the communication failure.
service.Abort()
Catch ex As Exception
'Typically there is no useful way to handle unexpected errors, so typically you should not catch them when calling a WCF client communication method.
'Throw
Finally
service.Dispose()
End Try

However there is another way to implement this in a manner much more similar to a Using block:

ServiceHelper.Using(Of WCF_ServiceReference.MessageClient)(
Sub(service)
'Here is the 'payload' code block again
For Each item As Message In MessageList 'Note that MessageList is in scope because it is part of the containing class
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
End Sub)

This method relies on a ServiceHelper class with a Using function:

Public Shared Sub [Using](Of ServiceT As {ICommunicationObject, IDisposable, New})(action As Action(Of ServiceT))
Dim service = New ServiceT()
Dim serviceclosed As Boolean = False
Try
action(service)
If service.State <> CommunicationState.Faulted Then
service.Close()
serviceclosed = True
End If
Finally
If serviceclosed = False Then
service.Abort()
End If
End Try
End Sub

The using function takes a generic parameter that matches the interfaces supported by a WCF service and an anonymous subroutine (aka lambda). What this means in practice is that we call ServiceHelper.Using passing a WCF service type and a subroutine that consists of the code inside the original Using block. The Using function executes the procedure and, crucially, handles any faulted errors, calling close or abort as required. This has the advantages of being considerably fewer lines of code then in the original procedure, handles errors gracefully, and can be debugged easily.

Note that the code inside the anonymous procedure must not call service.Close or service.Abort as these are handled by the Using procedure.

The only problem with the ServiceHelper class is that we can only pass a subroutine and not a function. So going further we can overload the Using procedure by adding another procedure to our ServiceHelper class to allow for a function that returns a result:

Public Shared Function [Using](Of ServiceT As {ICommunicationObject, IDisposable, New}, TResult)(action As Func(Of ServiceT, TResult)) As TResult
Dim service = New ServiceT()
Dim serviceclosed As Boolean = False
Dim result As TResult
Try
result = action(service)
If service.State <> CommunicationState.Faulted Then
service.Close()
serviceclosed = True
End If
Finally
If serviceclosed = False Then
service.Abort()
End If
End Try
Return result
End Function

Which can be used like this:

Dim result As Boolean = ServiceHelper.Using(Of WCF_ServiceReference.MessageClient, Boolean)(
Function(service) As Boolean
'Here is the 'payload' code block again
For Each item As Message In MessageList 'Note that MessageList is in scope because it is part of the containing class
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
Return True
End Function)

Naturally you can carry on adding other Using procedures with different signatures to account for all the variations of the  Action and Func delegates  that are available but the simplest solution is to add a single input parameter, which can take an object of any type, and use that to pass in any required data. This gives us two more procedures to round out our ServiceHelper class:

Public Shared Sub [Using](Of ServiceT As {ICommunicationObject, IDisposable, New}, TParameter)(input As TParameter, action As Action(Of ServiceT, TParameter))
Dim service = New ServiceT()
Dim serviceclosed As Boolean = False
Try
action(service, input)
If service.State <> CommunicationState.Faulted Then
service.Close()
serviceclosed = True
End If
Finally
If serviceclosed = False Then
service.Abort()
End If
End Try
End Sub

Public Shared Function [Using](Of ServiceT As {ICommunicationObject, IDisposable, New}, TParameter, TResult)(input As TParameter, action As Func(Of ServiceT, TParameter, TResult)) As TResult
Dim service = New ServiceT()
Dim serviceclosed As Boolean = False
Dim result As TResult
Try
result = action(service, input)
If service.State <> CommunicationState.Faulted Then
service.Close()
serviceclosed = True
End If
Finally
If serviceclosed = False Then
service.Abort()
End If
End Try
Return result
End Function

Which are used like this:

Dim messages As New List(Of Message)
ServiceHelper.Using(Of WCF_ServiceReference.MessageClient, List(Of Message))(messages,
Sub(service, msglist)
'Here is the 'payload' code block again
For Each item As Message In msglist 'Note that we pass in the message list as a parameter and thus do not rely on the containing class's property
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
End Sub)

Dim messages As New List(Of Message)
Dim result As Boolean = ServiceHelper.Using(Of WCF_ServiceReference.MessageClient, List(Of Message), Boolean)(messages,
Function(service, msglist) As Boolean
'Here is the 'payload' code block again
For Each item As Message In msglist 'Note that we pass in the message list as a parameter and thus do not rely on the containing class's property
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
Return True
End Function)

Which config is which?

You can use

AppDomain.CurrentDomain.SetupInformation.ConfigurationFile

to determine which app.config file is being used by ConfigurationManager.

Note that if you are writing a service then the service uses its own app.config file and not the one of the program calling it. This can lead to confusion when developing services in Visual Studio using a test driven development methodology as the app.config in the test project is not the one used by the service being tested.

A Little Code Monkey!

Imports System.Text

''' <summary>
''' The Typing monkey generates random strings.
''' </summary>
''' <remarks>
''' See http://stackoverflow.com/questions/1546472/generate-random-strings-in-vb-net
''' </remarks>
Class TypingMonkey

    Private Const AlphanumericCharacters As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
    Private Const NumericCharacters As String = "1234567890"
    Private Const AlphabeticCharacters As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    Private random As Random

#Region "Singleton"

    Public Shared ReadOnly Instance As TypingMonkey = New TypingMonkey
    ''' <summary>
    ''' Prevent anyone from instantiating this class by making New private
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub New()
        random = New Random
    End Sub

#End Region
    ''' <summary>
    ''' Internal key bashing function.
    ''' </summary>
    ''' <param name="size"></param>
    ''' <param name="characters"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function TypeAway(size As Integer, characters As String) As String
        Dim builder As New StringBuilder()
        Dim ch As Char
        For i As Integer = 0 To size - 1
            ch = characters(random.[Next](0, characters.Length))
            builder.Append(ch)
        Next
        Return builder.ToString()
    End Function
    ''' <summary>
    ''' The Typing Monkey Generates a random alphabetic string with the given length.
    ''' </summary>
    ''' <param name="size">Size of the string</param>
    ''' <returns>Random string</returns>
    Public Function TypeAlphabetic(size As Integer) As String
        Return TypeAway(size, AlphabeticCharacters)
    End Function
    ''' <summary>
    ''' The Typing Monkey Generates a random alphanumeric string with the given length.
    ''' </summary>
    ''' <param name="size">Size of the string</param>
    ''' <returns>Random string</returns>
    Public Function TypeAlphanumeric(size As Integer) As String
        Return TypeAway(size, AlphaNumericCharacters)
    End Function
    ''' <summary>
    ''' The Typing Monkey Generates a random string of numbers with the given length.
    ''' </summary>
    ''' <param name="size">Size of the string</param>
    ''' <returns>Random string</returns>
    Public Function TypeNumeric(size As Integer) As String
        Return TypeAway(size, AlphaNumericCharacters)
    End Function

End Class

Don’t Use My.Settings

There is no benefit to using My.Settings in VB.NET development.

Firstly My.Settings is only available in the VB.NET environment so it’s confusing for anyone else not familiar with the environment and a bad habit to get into.

Secondly the XML to support it in the app.config file is verbose, unwieldy, and typo prone making it difficult to cut and paste to another app.config file.

Thirdly, if you are creating a class library the settings are compiled into the DLL meaning that they cannot be altered and may be used unexpectedly.

Using ConfigurationManager.AppSettings is much easier and the XML to support it is much smaller, simpler and easier to transfer between app.config files without error.

Posting Source Code in WordPress

This is so simple that it’s easily overlooked. Wrap your source code in [sourcecode] tags. Add a language element for specific styles.

Reference: http://en.support.wordpress.com/code/posting-source-code/