May 2005 - Posts

OT: New Project

Spam Deploy! v0.1.4

This is a screenshot of:

  • my newest application
  • an example of a programmer not setting access keys
  • Microsoft's latest soon-to-be acquired Outlook add-in
  • a crap joke reflecting frustration with having to delete spam every day (no harm intended to DNJ, I know they're working on fixing the problem!)

Mwahahaha...

Thomas Williams Barcode

Wow, Tejas points me to a barcode generator. So here's me in CODE128 (available from http://www.idautomation.com/java/linearservlet.html):

Thomas Williams Barcode

I'm sure some interesting things could be done with these...

New Work PC

Woohoo, I got a new PC at work (my 4-year-old HP was getting a bit on). So for a couple of months at least I've got the newest computer in our little department, if you don't count my boss's shiny new widescreen Dell laptop ;-)

Here's the specs:

PC: Dell Optiplex GX280
Processor: Pentium 4 3.0GHz (1MB cache, 800MHz FSB) with Hyper-Threading
Memory: 1GB DDR2 SDRAM (4 x 256MB)
Storage: 2 x 200GB SATA in RAID 1 Array
CD/DVD: LG "Super Multi" write-anything/read-anything dual layer DVD burner (model GSA-4163B), plus a left-over TEAC DVD+RW (model DV-W58E)

That's the new bits - I'm still using my old 21" HP monitor and 15" Dell LCD, and I stuck with my new mouse. The Dell case is quite nice - it slides open on runners for maintenance, is a mini tower and is very quiet, but *I think* the graphics are integrated on the motherboard, so no room for upgradeability there.

For comparison purposes (highly subjective) - my old PC took about a minute and a half to be ready to go after logging in, the new one is down to a number of seconds.

The next big task is getting over all the bits that make up my development environment. I decided I'd do a bit of a directory reshuffle so it's a case of loading each solution and fixing the missing references. And for the first time in a long time, I am not going to put VB6 on the machine (I've decided to do any VB6 development on the old PC). I wonder if this decision is going to come back and bite me?

Mentor-ish advice on How to be a Programmer

Jeff Atwood links to an online essay entitled “How to be a Programmer: A Short, Comprehensive, and Personal Summary” by Robert Read.

Robert's article tries to deal with all the stuff a programmer/developer might face in his/her job, from technical (“how to debug“) to personal (“how to deal with difficult people“) to career choices (“how to grow professionally“), and he does it well. He makes it clear that some of the topics he covers are going to be subjective and represent his opinion only, but knowing that from the outset helps and I reckon that's OK to look at his essay as “mentor-ish advice“.

I'm about half way through and am enjoying the bits on personal & inter-personal skills. Robert points out an important distinction between your company and your “tribe”, which is also interesting.

Networking (The Other Kind)

One of the things I do really poorly is “networking“, say, among my peers. Too often I either (a) leave these kind of “networking“ moments, or (b) just hang around by myself. Option (c) would be to stick closely with fellow geeks I already know, which is becoming more viable as I gradually meet them. Of course the presence of Option C proves that I must meet some people ;-)

As a result of attending Code Camp Oz and naturally bumping into other humans, I've added some subscriptions (in no particular order):

Rory Primrose, who coincidentally has the exact same anniversary date as me (linked from Geoff Appleby): http://www.highertendencies.com/Default.aspx?LTKey=11

Darren Niemke: http://markitup.com/Blog/

Mitch Denny: http://notgartner.com/

Bill McCarthy: http://msmvps.com/bill/

If you were at Code Camp Oz, I'd love to check out your blog, so please leave something in the comments.

The "Thinking Machine"

Sashidhar Kokku points to Thinking Machine, a AI program that does a graphical visualisation of how a chess program is plotting it's strategy, while you play it in a game of chess. Cool.

Make sure you check the About page on the site for an explanation of the visualisations.

Plugin-Based Architecture in VB.NET Windows Forms

This post is a mental placeholder for me and contain only links to articles discussing plugins in .NET. My task is to take these articles at some stage in the future and create a plugin-based approach to a Windows Forms application, using a central navigation “shell“ (like MMC) which provides services like logging, error reporting, user preference management, and most importantly, the ability to load the GUI from elsewhere. The GUI components - sets of related forms or “pages” - will be compiled into exterior DLLs (that's the plan, anyway). This will be used to manage related but separate areas of functionality (“sub-projects“) and allow the developers (which may not just be me!) to build and enhance their pieces of code in a sub-project. Here's what I've dug up that I don't want to lose track of:

http://www.codeproject.com/csharp/extensibleui.asp
http://www.codeproject.com/csharp/DynamicPluginManager.asp
http://www.devcity.net/newsletter/archive/devcity/devcity20031015.htm#ni090
http://www.devcity.net/newsletter/archive/devcity/devcity20031030.htm#ni090
http://www.divil.co.uk/net/articles/plugins/plugins.asp

Now off to delete some comment spam. I nearly died the other day when I started deleting this blog's comment spam and was hit with the confirmation message “Are you sure you wish to delete comment 62129?”, and I thought to myself, surely there has not been 62000 comments on my blog...they wouldn't all be spam would they! Thankfully the comment identifiers seem to be allocated across everyone on DotNetJunkies.

Commiserations to all those who have been left several invitations to play online poker. What worries me is that it's a programmer/developer/whatever that wrote code to deploy the online poker invitations, one that crossed over to the dark side...

Code Camp Oz Diaries

The last installment of my Code Camp Oz diaries are now up - Sunday covers my interview with Geoff Appleby, a visit to the peer discussion room and the end of a great weekend.

Subversion Up and Running, Thanks To Miguel Jimenez

Our test server crashed last week, and during the rebuild process I finally had the chance to install the Subversion source control system and get it up and running. The advice on Miguel Jimenez's blog was indispensable (Miguel, I can't leave you a comment as your CAPTCHA images aren't working - but, thanks mate!)

I am trying to do the right thing here and have source control in place even for my single-user development, but I wonder if I trust our little test server...what arguments can I put forth to the powers that be to get them to put a source control system on a real server?

VB.NET 2003 "Previous Instance" that doesn't rely on "FindWindow" API

Recently I had the chance to improve some old "previous instance" detection code to add the ability to activate the previous instance. The code I used to detect a previous instance looked like this:

 Imports System.Diagnostics
 Private Function PrevInstance() As Boolean
     If UBound(Diagnostics.Process.GetProcessesByName(System.Reflection.Assembly.GetExecutingAssembly.GetName.Name)) > 0 Then
         Return True
     Else
         Return False
     End If
 End Function

Looking at this code now I can see many ways to optimise it. It returns "True" if there's another instance of the program running. I usually make a call to "PrevInstance" after I show a splash screen, and if there's another instance I simply pop up a message box advising the user to switch with ALT+TAB, then close the current instance.

My enhancement was that I wanted to bring the previous instance to the foreground. To do that I need the window handle, and most of the code to get the window handle relies on the FindWindow API. The FindWindow API takes a window title - there's some code at Paul Laudeman's blog "Windows Forms Tip: Ensure only one instance of your application is running at a time" which uses a mutex to do this (this code also works perfectly, if you know the window title, which leads me to my next point).

If you change the window title and you don't know the exact window title, it is still possible to get a window handle using FindWindow based on the class name alone. However I would have had to loop through windows of the type "WindowsForms10.Window.8.app*" (Paul's original post has a "3" in place of the "*", I found I only got results when I used a "9" in place of the "*") to find my window. To further complicate matters, I have my splash screen showing, so it also has the window title that I'm looking for. There may have been a partial solution in VB6 at "Win32 Window Title and Class Name Demo" which has a "FindWindowLike" function which might have helped (I did not have to go that far, thankfully).

I eventually went back to my existing code to see what could be done and played with the properties of the "Process" class, and made the following changes:

 Imports System.Diagnostics
 Private Function PrevInstance(ByRef handle as IntPtr) As Boolean
     ' array of running processes with this assembly name
     Dim p() As Process = Nothing
     
     Try
         ' put the processes into an array
         p = Diagnostics.Process.GetProcessesByName(System.Reflection.Assembly.GetExecutingAssembly.GetName.Name())
         ' if there's more than one process with this name
         If p.Length > 1 Then
             ' get the handle of the main window of the first running instance
             handle = p(0).MainWindowHandle
             ' program is running...return True
             PrevInstance = True
         Else
             PrevInstance = False
         End If
         
     Catch ex As System.Exception
         ' set return to False
         handle = IntPtr.Zero
         PrevInstance = False
         
     Finally
         ' clean up
         p = Nothing
     End Try
 End Function

Note: as you can see, my above code uses reflection, which incurs a performance hit. All valid comments/criticisms accepted.

The main change I made was to return a ByRef handle to the main window of the previous instance using the "MainWindowHandle" property. Easy.

I also have a function called "ShowPreviousInstance" which takes the window handle:

 ' parameter for "nCmdShow" for ShowWindow
 Private SW_RESTORE As Integer = 9
 
 ''' <summary>
 ''' Is the passed window handle minimised to the taskbar?
 ''' http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/isiconic.asp
 ''' </summary>
 Private Declare Auto Function IsIconic Lib "user32" (ByVal hWnd As IntPtr) As IntPtr
 
 ''' <summary>
 ''' Sets a passed window to be in the foreground.
 ''' http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/setforegroundwindow.asp
 ''' </summary>
 Private Declare Auto Function SetForegroundWindow Lib "user32" (ByVal hwnd As IntPtr) As Long
 
 ''' <summary>
 ''' Show a window with the passed command (state)
 ''' http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/showwindow.asp
 ''' </summary>
 Private Declare Auto Function ShowWindow Lib "user32" (ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As IntPtr

 Private Function ShowPreviousInstance(ByVal handle As IntPtr) As Boolean
     
     ' if we got a window of the currently-running instance
     If handle.ToInt32 <> IntPtr.Zero.ToInt32 Then
         
         Try
             ' if the window IsIconic (is in the taskbar), restore it
             If IsIconic(handle).ToInt32 <> 0 Then
                 ShowWindow(handle, SW_RESTORE)
             End If
             
             ' make foreground window
             SetForegroundWindow(handle)
             
             ' return True to say we switched to the previous instance
             ShowPreviousInstance = True
             
         Catch ex As System.Exception
             ' return false
             ShowPreviousInstance = False
         End Try
     Else
         ' no valid handle passed, return false
         ShowPreviousInstance = False
     End If
     
 End Function

If "ShowPreviousInstance" returns True, I close down the new instance of the application.

I like this code because it's a little shorter than some of the other samples I've seen, and it doesn't use a lot of API calls. The ultimate ease-of-use setting is coming in Visual Basic 2005, but this code is going to do until then.

UPDATE: for this code to work, you need to call Application.Run with an Application COntext, and not just a form. Here's some democode that supplements my original code above:

    '(in module)
    ' private instance of Main form
    Private m_frmMain As frmMain ' **** change to your main form here ****
    ' private Application context
    Private m_appContext As ApplicationContext
    
    <STAThread()> _
    Public Sub Main()
        ' initialise Application context
        m_appContext = New ApplicationContext
        
        ' process handle for currently-running application
        Dim handle As IntPtr = IntPtr.Zero
        ' is the program currently running, and have we activated it?
        Dim blnQuit As Boolean = False
        
        ' check for previous instance
        If PrevInstance(handle) Then
            MessageBox.Show("App is already running!")
            
            ' did we get a handle from "PrevInstance"?
            If handle.ToInt32 <> IntPtr.Zero.ToInt32 Then
                
                ' check if we can show the window, and if so, set quit flag to true
                blnQuit = ShowPreviousInstance(handle) 
                
            End If
        End If
        
        If Not blnQuit Then
            ' set up main form
            m_frmMain = New frmMain ' **** change to your main form here ****
            ' show it
            m_frmMain.Show()
            
            ' **** this line sets the main window, allowing process "MainWindowHandle" call to work! ****
            m_appContext.MainForm = m_frmMain
            ' run the application context
            Application.Run(m_appContext)
            ' Application will exit when closing main Form
            
        End If
        
        ' clean-up code can go here
        
    End Sub