Tuesday, August 4, 2009

Constructors for VB.NET Modules


Did you know that you can have a constructor on a module? I first got the notion to try this after thinking about static constructors in C#. In C#, you can do something like this:

class Class1
{
private static int _magicNumber;

static Class1()
{
_magicNumber = 4;
}
}
So the next thing to try was to test this out in VB.NET:

Module Module1

Sub New()
Debug.WriteLine("New")
End Sub

Public Sub Test()
Debug.WriteLine("Test")
End Sub

Public Sub Test2()
Debug.WriteLine("Test2")
End Sub
End Module
Running this and calling the methods Test / Test2 (in that order) yields this:

New
Test
Test2

So as you might expect, the construct only gets called once, but is called before any members are accessed in the module. Handy!

Tuesday, June 30, 2009

Gettinginto MSBuild / TFS Build

As you will no doubt agree, it's handy as heck to be able to get a fresh installer for your app on demand. Right now our build is a relatively manual process that takes a number of hours to complete. It's inefficient and error prone. The answer - automation of course!

I've already set up our environment so that it will automatically build our code whenever somebody checks in - this is a good thing. However, we're thinking about moving to the next step and getting our installer to be automatically built. We're not sure exactly how we're going to do this yet, but the components are:
  • Our .NET solution (most of our code is here).
  • Some VB 6 code (just enough here to have to make the build interesting).
  • An InstallShield installer (and dealing with the InstallShield licensing nonsense).
Our main options are:
  • Create our own MSBuild setup with custom tasks. Getting this to happen on our build server would be ideal because then anyone on the team could make an installer through the TFS build interface.
  • Write a .NET app from scratch. Not quite as slick as using MSBuild (considering MSBuild was sort of designed to do just this sort of thing), but I'll take whatever works.
Just to keep track of the resources, here are some of the pages I'm looking at:
  • This deals with incorporating VB6 and MSBuild
  • This has some links for creating custom build steps
  • This is getting started with MSBuild
We need to check in some files, so here are some links for that:
More to come as we narrow down the solution.

Sunday, May 10, 2009

OPC at RVNUG: The code and the slides


First things first: Here is the code and here are the slides.

Thanks to everyone who attended (and participated in) my resent presentation on the Open Packaging Convention (OPC) at the Roanoke Valley .NET User Group (RVNUG). It was a rowdy group (my favorite kind) and it was a blast.

Besides coming up with various connotations for the words 'Package', 'Parts' and 'Stream', we discussed some handy ways to use OPC:
  • Exporting / backing up data
  • Storing a gallery of images with additional metadata (geocoding, timestamp information,etc)
  • Grouping together log files. This application seemed to generate the most interest.
OPC is a dirt simple way to store data in one place without needing a database.

Monday, April 20, 2009

DebuggerStepThroughAttribute

Let's say you have some code that often throws an exception, but you just swallow it and carry on. This is usually fine, unless you have your debugger set to break on. So take some code like this:

Public Shared ReadOnly Property IsInstalled() As Boolean
Get
Dim isInstalled As Boolean = False

Try
Dim temp as Object = CreateObject("Some.ActiveX")
isInstalled = True
Catch ex as Exception
End Try

Return isInstalled

End Get
End Property

If the COM object doesn't create properly, it will generate an exception. This sort of code is usually in the start up of an application, so you might have to hit this every time you debug your app. It's annoying.

However, if you add the handy attribute DebuggerStepThrough just before the Get clause:

Public Shared ReadOnly Property IsInstalled() As Boolean
<DebuggerStepThrough()> _
Get
Dim isInstalled As Boolean = False

Try
Dim temp as Object = CreateObject("Some.ActiveX")
isInstalled = True
Catch ex as Exception
End Try

Return isInstalled

End Get
End Property
Then Visual Studio just runs over the code like nothing ever happened.

Saturday, April 18, 2009

Roanoke Code Camp 2009 - The Code



Apologies for not getting this published sooner... Between getting ready to move houses and my normal level of laziness and procrastination, I haven't done it. It also took some Googling to figure out how to include code in a BlogSpot post. Enough whinging, here's the code for the OPC (Open Packaging Convention) class I did.

By the way, here is my DevX article on the same subject that got published recently.

Here is the code to create a package:

'Open the package
Using package As Package = package.Open("c:\Example.zip", IO.FileMode.OpenOrCreate)

'Create the uri for the text file
Dim uri As New Uri("/TextFiles/MyTextFile.Text", UriKind.RelativeOrAbsolute)

'Create the part for the text file
Dim part As PackagePart = package.CreatePart(uri, System.Net.Mime.MediaTypeNames.Text.Plain)

'Get the stream for the part
Using stream As Stream = part.GetStream()

'This is the text we'll put in the part
Dim myText As String = "This is an ugly picture of a flower - Roanoke!"

'We need to get the string into a byte array to make it easy to write to the part stream
Dim buffer As Byte() = System.Text.ASCIIEncoding.ASCII.GetBytes(myText)

'Write to the part stream
stream.Write(buffer, 0, buffer.Length)

End Using

'This is the uri for the flower
Dim uri2 As New Uri("/Images/Flower.jpg", UriKind.RelativeOrAbsolute)

'Create the part for the image
Dim part2 As PackagePart = package.CreatePart(uri2, _
System.Net.Mime.MediaTypeNames.Image.Jpeg)

'Get the stream for this part
Using stream As Stream = part2.GetStream()

'Read the data from the file
Dim buffer As Byte() = File.ReadAllBytes("c:\Flower.jpg")

'write the data to the part
stream.Write(buffer, 0, buffer.Length)

End Using

'Create a relationship between the parts
Dim relationship As PackageRelationship = _
part2.CreateRelationship(part.Uri, TargetMode.Internal, "flower_to_text")

End Using

And here is the code to read that package:

 'Create the package
Using package As Package = package.Open("c:\Example.zip", IO.FileMode.Open)

'We know the Uri for the flower, so let's start there
Dim flowerUri As New Uri("/Images/Flower.jpg", UriKind.RelativeOrAbsolute)

'Get the part using the uri we just set up
Dim flowerPackagePart As PackagePart = _
package.GetPart(flowerUri)

'Create a relationship from the
Dim relationships As PackageRelationshipCollection = _
flowerPackagePart.GetRelationshipsByType("flower_to_text")

'Get the first relationship (in real code, we would want some error checking here)
Dim relationship As PackageRelationship = _
relationships(0)

'Get the part from the relationship (once again, this would need some
' error protection because in a real world app this part might not exist anymore).
Dim textPart As PackagePart = package.GetPart(relationship.TargetUri)

'Get the stream
Using stream As Stream = textPart.GetStream()

'Create a buffer
Dim buffer(stream.Length) As Byte

'Read the stream
stream.Read(buffer, 0, stream.Length)

'Read the text
Dim myText As String = System.Text.ASCIIEncoding.ASCII.GetString(buffer)

'Show the text (the crowd should go wild here)
MessageBox.Show(myText)

End Using

End Using

End Sub

Wednesday, March 18, 2009

WPF Designer causes Visual Studio to Close

Okay, so we all know that the WPF designer in Visual Studio 2008 is suboptimal. And if you didn't, now you know. Besides not being particularly user friendly, it's buggy as hell.

Today, while trying to demonstrate the basics of XAML / WPF, I was trying to show the design view of a WPF Window. The problem was, every time I opened the designer, the IDE would just close. No error, no beep, just gone. That was awesome.

Here are the things that I did that didn't solve the problem:
  • Cursed.
  • Rebuilt all.
  • Cleaned solution, then rebuilt all.
  • Manually cleaned the solution by deleting the bin and object folders under each project in the solution.
  • Closed the solution and deleted the .suo file (Solution Options File).
  • Closed the solution and deleted the project user files.
  • Rebooted.
  • Cursed when it still didn't work.
  • Contemplated Harakiri.
Here's what did work:
  • Deleted all of the temp files in the temp directory.
WTF? So in XP, here is the temp directory that was causing the issue (it's a different path in Vista):

C:\Documents and Settings\\Local Settings\Temp

There were like almost 5,000 files in that directory. Apparently, the designer creates temp files. My best theory is that tt will keep bogging down trying to find unique temp file names until one day it runs out of combinations. Then there is no call for help, no suicide note, it just quietly and inexplicably goes away. It doesn't even hang itself (no process remains in Task Manager).

I had discovered this fix many moons ago, and had of course forgotten about it until months later when my temp directory filled up.

Thursday, March 5, 2009

Nomadic Programming

No, I don't mean driving all around town to program... Which isn't to say that wouldn't be fun. This refers to the tendency for a team to work on almost every module of a product at the same time.

So, how do you tell if this is happening? Draw a picture of your project. Break down your project into its major components. Pick a color scheme. This one is pretty straightforward:
  • Red - the module hasn't been touched
  • Yellow - the module is in progress
  • Green - the module is code complete.
And you'll end up with something like this:



Holy crap - there are very few modules that are complete, and very few that are untouched. That means that the team is wandering all over the featurescape almost randomly coding on different modules. This approach has all sorts of dangers:
  • This makes it very hard to ever get a project done because no individual item ever is completed.
  • This approach leads to the "throw it over the wall to testing at the end of a long development" method. Not a good thing. For example, a fundamental flaw in design / implementation could have happened months ago and propagated to the rest of the product. That could easily lead to orders of magnitude to fix all of those issues.
If all is going well, we would expect to see a very small part of the diagram showing up in yellow (in progress) with the rest of the modules being either red (untouched) or green (completed).

Tuesday, February 10, 2009

Special Characters in the Office 2007 Ribbon

Have you ever run into a teeny little problem that was incredibly annoying and almost impossible to debug? Well, I did. All I wanted to do was create a Ribbon Tab at runtime from a database for a Word 2007 Add-In.

Creating the tab and adding the controls was easy. Very easy actually. However, using"special characters" such as "'/" and "(" or ")" in the labels caused the entire tab to not be displayed. No error message, no log, just poof - ribbon gone. Sweet. I tried escaping the characters xml style, but that caused even more problems. I created a ribbon using the Visual Studio 2008 Ribbon Designer. The code I had written looked almost exactly like the code there. Well crap. I Googled the living snot out of the problem and couldn't find a damn thing. I asked team members. I stared at the ceiling. I mediated about it. I even programmed myself to dream about it. Actually, I gave up at the ceiling staring stage, but you get the idea.

Well, today I was looking through the designer code for the ribbon, and it had calls to ribbon.SuspendLayout(), ribbon.ResumeLayout(False) around the calls to add the ribbon controls. I didn't think this would do anything, but hey - why not give it go? Sure enough, that fixed my problems.

So, in summary, do this:

ribbon.SuspendLayout()

... 'Add a bunch of tabs, groups, controls, etc

ribbon.ResumeLayout(False)
and everything will be fine.

Tuesday, January 6, 2009

XML Literals with namespaces

I love Xml literals in VB. They're pretty slick. However, I ran into a little gotcha today... All I wanted to do was query some data from an xml that happened to be part of a Word 2007 template (a .dotm file). It looked like I had the right query syntax, but I wasn't getting any result.

Most of the examples I found on the web were searching through xml like this:
Dim document = <customers>
<customer firstname="John"></customer>
<customer firstname="Juliet"></customer>
</customers>

Dim firstNames = From customer In document.<customers>.<customer> _
Select customer.@firstName

This works like a champ. firstNames contains what you would expect:
{Length=2}
(0): "John"
(1): "Juliet"

But let's say our xml is part of a namespace (much more realistic)

Dim document = <customers xmlns="http://fowlcoder.blogspot.com/xmlLiteralExample">
<customer firstname="John"></customer>
<customer firstname="Juliet"></customer>
</customers>
If we use the same query code as above, we don't get any results. We actually have to add some code to make the query aware of the namespace. It's pretty straightforward.

First, add an Imports statement at the beginning of the file:
Imports <xmlns:fc="http://fowlcoder.blogspot.com/xmlLiteralExample">
Now, you have to alter the query a little bit:

Dim firstNames = From customer In document.<fc:customers>.<fc:customer> _
Select customer.@firstName

Voila! You get the results that you would expect (John and Juliet).