Category Archives: VB Feng Shui

UnBatching in MSBuild With Team Foundation Server

0
Filed under MSBuild, VB Feng Shui

I’ve been working on getting MSBuild setup and configured to handle some Continuous Integration builds for our company. One task that came up was needing to get a large batch of files from our TFS server and pull them down to the appropriate directories on a local machine.

But here’s the catch. I didn’t want to just pull everything  from those folders down. Some folders contain very large files that didn’t really need to come down at all, because the intent was that the build process would be building them.

So, it would have been relatively easy to use the MSBuild Extensions pack to get all the files recursively in my project.

    <Target Name="GetInstallFileSet">
        <MSBuild.ExtensionPack.VisualStudio.TfsSource TaskAction="Get" ItemPath="..\InstallFiles" WorkingDirectory="$(MSBuildProjectDirectory)" Recursive ="true" Force ="true" />
    </Target>

The WorkingDirectory parameter indicates where TFS will base all relative file specs from. The ItemPath indicates the folder location (relative to where the MSBuild proj file is located that this Target is in) that TFS should retrieve. The TaskAction of GET just retrieves all files in that folder, and the Recursive parameter tells TFS to get all files in that ItemPath and all it’s subfolders.

Pretty simple, but if there were large files anywhere in the path, TFS will obligingly retrieve them as well, which could suck up a lot of time and bandwidth.

So, first, how to specify only those files that I really want to retrieve? Easy, just use an ItemGroup.

    <Target Name="GetInstallFileSet">
        <ItemGroup>
            <InstallFileSet Include="..\..\Install;
                                     ..\..\Install\System;
                                     ..\..\Install\OtherFiles;
                                     ..\..\Install\DiskSet">
            </InstallFileSet>
        </ItemGroup>
    </Target>

The InstallFileSet ItemGroup will end up with these specific folder names (all relative to the path to the proj file the target is defined in). Unfortunately, I don’t see any straightforward way to “leave out” specific files, because using any wildcard specs when defining this ItemGroup would be based on files that already exist on the local workstation, and hence we run the risk of NOT getting newly added files that exist in TFS but not locally.

But, if those files happen to live in specific subfolders, we CAN leave out those subfolders from the list in the INCLUDE attribute of the Item definition above.

Ah, but what about recursion? In the above case, I specifically DO NOT want to recurse down from the first path in the list (..\..\Install), but I DO want to recurse on all other paths.

That’s where the ‘metadata’ aspect of MSBuild comes into place. Modify the ItemGroup slightly.

    <Target Name="GetInstallFileSet">
        <ItemGroup>
            <InstallFileSet Include="..\..\Install>
                <Recurse>false</Recurse>
            </InstallFileSet>
            <InstallFileSet Include="..\..\Install\System;
                                     ..\..\Install\OtherFiles;
                                     ..\..\Install\DiskSet">
                <Recurse>true</Recurse>
            </InstallFileSet>
        </ItemGroup>
    </Target>

Notice that I’ve split the InstallFileSet item into two pieces, and added the Recurse attribute to each piece.

Now, all the items are still in the single InstallFileSet ItemGroup, but one has a Recurse property of false, the others have it set to true.

Using the ItemGroup

All I have to do now is indicate how to use the ItemGroup that I’ve defined

...
        <MSBuild.ExtensionPack.VisualStudio.TfsSource TaskAction="Get" ItemPath="@(InstallFileSet)" WorkingDirectory="$(MSBuildProjectDirectory)" Recursive ="%(InstallFileSet.Recurse)" Force ="false" All="true" />
    </Target>

Adding this one TaskAction=”Get” line will no perform the Get from TFS on all those Items.

So I ran the build, and… Fail.

TFS gave me a “parsing” error on the ItemName argument. Apparently, it doesn’t like being passed a long list of semicolon delimited path names.

Gah. This is where the UnBatching comes in.

Batching Explained

As a build engine, MSBuild will generally try to “batch” multiple items together into one processing “call”, so that as few invocations of that call as possible are made, under the assumption that fewer invocations will be faster.

Unfortunately, in some cases, you really don’t want the processing batched. In this case, batching would cause multiple paths to be supplied to one invocation of TF.exe (the TFS client app), which, as I mentioned, doesn’t know how to deal with that.

The key to understanding batching is MSBuild will batch by default, but will separate out batches based on two things

  1. Any metadata attributes associated with the batch items
  2. How you reference the items in the batch.

1) is easy. In the above example, I’ve got two values of the metadata tag “Recurse”, so MSBuild by default will execute the GET task twice, once, with the Item(s) with Recurse=true, and once for those with Recurse=false.

But 2) is harder to grok. As it is now, I’m using ItemPath=”@(InstallFileSet)” to reference the itemgroup. That allows MSBuild to batch the items, but then it splits the batch by the Recurse attribute.

However, if I reference the items by the built in identity metadata tag, MSBuild will consider each item in the group has having unique metadata associated with it, so it will execute the GET task once for each item, do that by using %(InstallFileSet.identity) instead, as in:

...
        <MSBuild.ExtensionPack.VisualStudio.TfsSource TaskAction="Get" ItemPath="%(InstallFileSet.identity)" WorkingDirectory="$(MSBuildProjectDirectory)" Recursive ="%(InstallFileSet.Recurse)" Force ="false" All="true" />
    </Target>

If you build now, you’ll notice that the TF.EXE process is launched once for each path in the InstallFileSet ItemGroup, and that the Recurse attribute is applied appropriately depending on which item is being processed.

Conclusion

MSBuild is definitely powerful. In some ways, it’s simpler to deal with than the MAKE/NMAKE systems of old, and the source proj files are definitely more flexible in how they can be used.

But, many of the more advanced functions (that you’ll end up needing quite quickly) can leave you scratching your head. And the available documentation and examples often don’t help much.

The key is patience and experimentation.

.NET Interoperability with COM Office Apps, a Hidden Gotcha

0
Filed under .NET, ActiveX, Office, Troubleshooting, VB Feng Shui

image I’ve been working with .NET and Office interoperability for some time now, and, other than a few minor hiccups here and there, things have generally been very smooth.

But a colleague recently ran into a problem that required a good deal of hunting to resolve. Long story short, even though .NET interoperability with Office objects is virtually foolproof, releasing your references has some hidden dangers lurking amongst the weeds.

As a general rule, it’s safe to let the .NET garbage collector (GC) do it’s thing while your application is running.

BUT, when you’re app shuts down, you need to take a few extra steps to make sure everything’s cleaned up before quitting.

The details are laid on in this article on MSDN. It’s from 2005, but, from what I can tell, it’s just as applicable now as it was then.

Essentially, the trick boils down to this:

When you’re preparing to shut your application down, you need to make SURE you’ve released all your references to any Office objects. This means setting all your various references to Nothing, or, more typically, letting the GC do its thing.

Unfortunately, the GC doesn’t necessarily do its thing during a shutdown, so you have to force the issue. And that requires code essentially like this:

WordApp.Quit() 
WordApp = Nothing 
GC.Collect() 
GC.WaitForPendingFinalizers() 
GC.Collect() 
GC.WaitForPendingFinalizers() 

The article noted above doesn’t really go into why it’s necessary to call Collect and WaitForPendingFinalizers twice, but it does appear to be required. I’m guessing it’s due to the way the Finalizer sweeps object references; it could be possible for it to release objects in a certain order, causing it to skip some objects. But that’s just a guess.

And as a final note, if your .NET code uses any COM interoperability, it might be a good idea to do some final cleanup along these lines as well.

Multi-Line String Literals

0
Filed under .NET, VB Feng Shui, XML

VB has long been my favorite development language, but, as is typical, even it can definitely be improved in innumerable ways.

One aspect of VB that has always bothered me is string literals. Since VB treats EOL as significant, you can’t have strings that wrap across multiple lines in the environment. This is especially problematic when you deal with SQL queries, report formatting strings, and the like. For instance, this is generally what you see when you have to deal with long SQL queries in VB code.

Dim q as String = "Select *" & vbcrlf
q &= "From MyTable" & vbcrlf
q &= "Where MyColumn = 1"

Now, you can do a few formatting tricks and clean it up a little, but even then, it’s going to be cumbersome to work with.

One workaround has always been to embed these long, multi-line literals in a Resource file. The VS 2008 IDE even goes so far as to allow you to enter multi-line strings in the resource editor, although you have to use Ctrl-Enter to get a new line:

image

If you look at the RESX file for the above resource, you’ll see something like this:

image

All this is certainly workable, and, in many cases, isolating strings to a resource file (of some sort) can be a really good thing, but in other cases, it’s just a pain.

In VB10, Microsoft is finally doing what VB should have done all those years ago. They’re adding “intelligent line continuation” which is to say, you can continue a line in certain syntactic situations simply by continuing the code on a new line. No need for the line continuation underscore char, etc.  It won’t necessarily work everywhere (esp in places where it the code would end up ambiguous), but hey, that only makes sense.

In the meantime, you can actually have multi-line string literals in VB.Net without too much pain.

The trick is to use XML Literals. Generally, I’m no fan of XML literals. Once they get peppered with variable references, embedded For loops and other code, they become just so much spaghetti code, albeit with a modern twist.

But, they can be handy for multi-line string literals.

Dim q as String = <String>Select *
From MyTable
Where MyColumn = 1
</String>.value

So what’s going on here? First, I dim the result variable Q as a string (technically, this isn’t necessary as VB can infer the type, but you get the picture).

Then, I declare the XML Literal with the <String></String> tags. Everything between those tags (even across lines), gets treated as part of the string, no quotes or line continuation needed.

Finally, since the result of the <String></String> tags is going to be an XElement object, and what I want is a string, I need to invoke the value property of the XElement object to retrieve its string value.

And there you go.

But, as with anything good, there are a few caveats.

  1. line breaks AND whitespace are preserved in the string, which means if you indent those lines, you’ll get space characters where you might not have wanted them.
  2. if you want to include certain characters that are illegal in XML (for instance, the “<” or “>” characters), you’ll need to escape them.

Handling Escape Characters

One option you have for handling escape characters is to use the XML escape sequences, as in:

Dim q as String = <String>Select * as My&gt;Test&lt;var
From MyTable
Where MyColumn = 1
</String>.value

The &gt; and &lt; will be replaced with > and < respectively. You can escape any arbitrary character with &#nnn; where nnn is the ascii code of the character.

But this is code, dammit, not XML, and, at least for me, I’d much rather see normal C-style escape sequences (\r\n anyone <g> ) here than have to muck with XML “&” sequences. If we’re talking about web pages and HTML, that might be a different story, though.

At any rate, you can have your cake and eat it too, as it turns out.

Just feed that string through System.Text.RegularExpressions.Regex.Unescape to convert those sequences, hence:

Dim q as String = System.Text.RegularExpressions.Regex.Unescape(<String>Select * as MyTestvar \r\n
From MyTable
Where MyColumn = 1
</String>.value)

Notice how the XElement.value is simply passed through to the Unescape method to process that \r\n into a normal carriage return/linefeed pair.

Using that, you can now easily embed C-style escaped multi-line strings in your VB code.

But wait, that Unescape call is fairly nasty to look at and use. Wouldn’t it be nice if that was more or less automatic? Well, with a nice Extension Method, it can be.

<System.Runtime.CompilerServices.Extension()> _
Public Function UnEscaped(ByVal v As XElement) As String
    Return System.Text.RegularExpressions.Regex.Unescape(v.Value)
End Function

.....

Dim q as String = <String>Select * as MyTestvar \r\n
From MyTable
Where MyColumn = 1
</String>.Unescaped

First, define an Extension Method that would apply to the XElement object. This is a very simple method that just takes the associated XElement object, retrieves it’s value, and runs that string through the Regex Unescape method.

Since the extension method extends the XElement itself, you can now simply call the Unescaped method directly on the long string definition (the very last line in the code above).

You can’t get much cleaner than that. Or can you? Let me know!

Agile Development

0
Filed under Rants, VB Feng Shui

Billy Hollis has out an interesting article in the latest Visual Studio Magazine about the possible destiny of Agile Development.

(sidenote: what the hell has happened to VBPJ VSM? Am I the only one that remembers the days when that mag was just shy of a phonebook and literally every article was worth the read? This latest issue is barely enough paper to wipe your a$s! Seriously. It’s sad).

Anyway, Hollis calls out Agile Dev fans, sort of. He takes a bit of a middle of the road stance, which, actually, I find quite refreshing. To me, Agile has always seemed more like a tactic to avoid really learning to improve your craft and rather, just plowing forward, balls-to-wall to get something working, dammit! It kind of reminds me of a quote I once read supposedly from Steven King. When asked what his process was for writing books, he said something like “I just keep flailing away at the damn thing till I have something I like.”

Now, don’t get me wrong. Getting something working is important. But the more I look at the tenets of Agile, the more I wonder whether it’s really the consultants recommending it and the experts teaching it for 1000$ a day that stand to gain anything from it. It just sounds like yet another in a long line of silver bullets.

The best line from Hollis’s article is:

Most developers love to write code, and agile does a pretty good job of rationalizing why developers don’t need to do anything else.

Ouch!

Shared Class Members or Modules?

0
Filed under .NET, Rants, VB Feng Shui

I ran into a chunk of code today that threw me.

The code was contained within a particular class definition.

Every member (properties and methods) were marked Shared.

Public Shared Sub MyMethod()
blah....
End Sub

The net effect was a class that, even if you could instantiate it, wouldn’t have any purpose as in instance. Its sole use was to provide essentially a dumping ground for a bunch of general purpose library routines.

At first, I just wrote this off as odd and continued on.

But, on further reflection, this just doesn’t seem right. As Scott Hanselman might say, it’s got a rather nasty Code Smell.

So, I did what any reasonable person would do; I googled it.

Apparently, there are more than a few people out there contemplating the same thing. What’s scary though, is that usually, the arguments end up along these lines:

  1. Modules in VB.Net are a throwback, a hack, and a nod to compatibility and should be avoided.
  2. Modules can’t be accessed from other .NET languages.
  3. Other .Net Languages don’t have modules, so you’re better off using shared members on a class definition.
  4. Modules aren’t OOP. Classes are. Use classes.

It’s already been proven that, internally, VB.Net modules compile to exactly the same code as a Class with shared methods. Further, the modules have been documented as a first class feature of VB.Net, the language, not some soon-to-be-deprecated nod to VB6 compatibility. And Modules CAN be accessed from other languages. So I’m not going to bother with any of those bits again.

Personally, I think in comes down to two simple concepts, syntactics and semantics.

The Syntactics

Syntactically, defining members in a Module allows them to be accessed without  qualifying the member with the module name. So, given this Module definition:

Public Module MyModule
   Public Shared Sub MyMethod()
      blah....
   End Sub
End Module

I can access the method MyMethod by simply invoking the name MyMethod(); I don’t have to qualify it, as in MyModule.MyMethod().

This is a minor issue, except that it effectively allows you to extend the VB language. You can add your own RIGHT() function or LEFT() function, etc. This can be a fantastic aid to code readability when used appropriately.

The Semantics

However, I believe the even bigger argument is semantics. A class, is, by its very definition, the definition of an object that can be instantiated. Even singleton classes can be instantiated at least once. But a class of shared methods is a class that has no reason to be instantiated. And that really isn’t a class, it’s just a set of loosely related, if even related at all, functions. And that is the definition of a Module!

Do you regularly create an integer type variable when what you really need is a DATE? Then why collect a bunch of shared functions in a class when a module is specifically intended for such purposes?

Discoverability

The only rational I can come up with is discoverability. Forcing developers to prefix shared methods with the class name provides a means (through Intellisense) of discovering the capabilities of that class. But, you can optionally prefix module members in exactly the same way, so again, shouldn’t we be using the features of the language as they’re intended?

Why Shared Members at all?

Given this, what benefit is there of using shared members at all?  I’ve seen a few examples for defining singleton objects where you retrieve the single instance via a shared member of the class. That certainly makes logical sense, but from a coding perspective, it seems ugly to me. Why ask the class for the single available instance of itself? Shouldn’t you ask something else for that instance? It also makes for some odd-reading code:

Dim TheOneInstance = MySingletonClass.GetTheOneInstance

And other than singletons, what other purpose is there for shared members?

(UPDATE 10/29/08: After further experiments and reading, it turns out that, as far as I can tell, the ONLY way to truly get a singleton object is to use a shared method within the object itself. Nasty, but that appears to be the only way to do it).

I know the .NET framework uses them extensively, but are they used effectively?

For example, take the String class. It exposes quite a few shared members, such as Compare and Copy. But in practice, using those shared members doesn’t quite make sense. If I’m comparing two strings, the object-oriented thinker in me naturally gravitates to:

If OneString.Compare(OtherString) Then

Not

If String.Compare(OneString, OtherString) then

The bottom line is, if I’m comparing two strings or copying a string, I’ll always be working with an instance of a string, so why shouldn’t those methods be instance methods and not shared methods?

I suppose String.Format() is a passable example, but even that would seem to make more sense as:

dim x = "Insert a parameter here {0}".Format(Arg0)

Do you have a good use for shared methods that you’d like to, uh, share?

Let me know!

Tooltips for Disabled Controls

3
Filed under .NET, Code Garage, VB Feng Shui

We recently got into a discussion where I work about the best way to explain to a user why a control is disabled.

One person argued that it made sense to leave the “disabled” controls enabled, and, when a user tried to click/use the control, pop up a messagebox explaining why it won’t work in this instance.

To me, that seemed to go against everything I’d ever learned about UI design and controls, namely, if a control is disabled, it ought to look disabled on screen and it shouldn’t do anything if you poke at it.

Still, I’d had plenty of experience myself with apps where controls were disabled and I had no idea why they were disabled, much less how to go about getting that functionality enabled. That can certainly make for a frustrating time.

Then someone suggested a tooltip or a status bar message. If you tried to click the disabled control, or hovered your mouse over it, you’d get a little, innocuous message somewhere telling you why that control was disabled.

Awesome idea!

Only one problem.

Tooltips don’t work for disabled controls. Actually, they do for menus, toolbars and likely a few others, but that’s another story.

And you don’t get MouseOver events on disabled controls.

Sigh.

Well, I couldn’t just walk away from this.

A quick google turned up a few bits:

  • There was this from Roy Auchterlounie, but it’s MFC.
  • But then there was this nugget from a post by Linda Lui, apparently with MS Support. It’s in C#, but it’s relatively easy to translate to VB.

The only thing was, Lui’s solution was not exactly what I’d call an encapsulated solution.  As Scott Hanselman would say, dropping snippets of code like this all over my forms just doesn’t have a wonderfully fragrant code smell.

I’d messed around with Control Extenders under ASP.NET some time ago, and this seemed like the perfect excuse to try it out on a good ol’ WinForms app.

A little refactoring later, and I’ve ended up with the “Disabled Tool Tip” Extender Control. It directly inherits from the out-of-the-box tooltip in VS2008. As a result, there’s not a lot of code here. Also, it should work with VS2005, but I’m not guaranteeing as much.

Add this class to your project, recompile, then drop one onto a form.

Zip! Boom! Pow! Every control on your form should now have a “ToolTip on DisabledToolTip” property. You simply set this new property to the tooltip you want to show when the control is disabled and you’re done.

image

(my test rig, she is much fine, no?<g>)

A few notes about this class.

One significant issue I ran into immediately, was how do you retrieve a reference to the containing form if you’re a component sited on that form. All the obvious stuff didn’t work. There’s gotta be a more straightforward way to do it, but I failed to find it, at least with respect to a Component type control (one of those that isn’t actually sited ON the form, but rather in that little area at the bottom of the designer).

I ended up caching an instance of some control during the SetToolTip method, since this method is called during the form initialization by any control on the form that had a tooltip set for it via the designer.

   Public Shadows Sub SetToolTip(ByVal control As Control, ByVal caption As String)
      MyBase.SetToolTip(control, caption)

      '---- if we don't have the parent form yet...
      If rParentForm Is Nothing Then
         '---- attempt to get it from the control
         rParentForm = control.FindForm
         '---- if that doesn't work
         If rParentForm Is Nothing Then
            '---- cache the control for use a little later
            rControl = control
         End If
      End If
   End Sub

But, you can’t use the FindForm method here, necessarily, because if the form is still being initialized, you’ll get back nothing.

So, I ended up implementing the ISupportInitialize interface, and, during the EndInit method, if I have a cached control reference, I use it at this point to retrieve the parent form via FindForm.

   Public Sub EndInit() Implements ISupportInitialize.EndInit
      '---- if we weren't able to retrieve the form from the control
      '     before, we should be able to now
      If rControl IsNot Nothing Then
         rParentForm = rControl.FindForm
      End If
   End Sub

Roundabout, yes, but it seems to work in a very stable way, and it means I don’t have to resort to MFC style subclassing and the like. I can just monitor events on the parent form via a simple WithEvents variable reference.

Anyway, the full code for the class is here. It’s short enough that I’m not going to bother with a ZIP file at this point.

And finally, as with any code you pick up off the net, I’m making no guarantees of any sort. If it works for you, great. If not. Well, I’ll certainly do my best to help if you let me know. It works for me, but it is necessarily full on, battle tested, bulletproof stuff? Uh. No.

Enjoy! If you see any improvements to be made, please share!

And please, if you post it elsewhere, give me (and Linda Lui) proper credit!

Imports System.ComponentModel

''' <summary>
''' Custom ToolTip Component that is based on a normal tooltip component but tracks tips 
''' for disabled controls
''' Note that the because this is a separate extender, all the controls on a form
''' can have an "Enabled" tip (as normal) AND a "disabled" tip.
''' 
''' By Darin Higgins 2008
''' Based on a code example by Linda Lui (MSFT)
'''
''' </summary>
''' <remarks></remarks>
''' <editHistory></editHistory>
Public Class DisabledToolTip
   Inherits ToolTip
   Implements ISupportInitialize

   '---- hold onto a reference to the host form
   '     to monitor the mousemove
   Private WithEvents rParentForm As System.Windows.Forms.Form

   Private _rbActive As Boolean = True
   ''' <summary>
   ''' Active for the Disabled ToolTip has a slightly different meaning
   ''' than "Active" for a regular tooltip
   ''' </summary>
   ''' <value></value>
   ''' <remarks></remarks>
   <DefaultValue(True)> _
   Public Shadows Property Active() As Boolean
      Get
         Return _rbActive
      End Get
      Set(ByVal value As Boolean)
         If _rbActive <> value Then
            _rbActive = value
         End If
      End Set
   End Property



   '---- hold on to a control temporarily while we wait for things to 
   '     settle
   Private rControl As Control

   ''' <summary>
   ''' Shadow the settooltip function so we can intercept and save a control
   ''' reference. NOTE: the form MIGHT not be setup yet, so the control
   ''' might not know what it's parent is yet, so we cache the the first control
   ''' we get, and use it later, if necessary
   ''' </summary>
   ''' <param name="control"></param>
   ''' <param name="caption"></param>
   ''' <remarks></remarks>
   Public Shadows Sub SetToolTip(ByVal control As Control, ByVal caption As String)
      MyBase.SetToolTip(control, caption)

      '---- if we don't have the parent form yet...
      If rParentForm Is Nothing Then
         '---- attempt to get it from the control
         rParentForm = control.FindForm
         '---- if that doesn't work
         If rParentForm Is Nothing Then
            '---- cache the control for use a little later
            rControl = control
         End If
      End If
   End Sub


   Public Sub BeginInit() Implements ISupportInitialize.BeginInit
      '---- Our base tooltip is disabled by default
      '     because we don't want to show disabled tooltips when
      '     a control is NOT disabled!
      MyBase.Active = False
   End Sub


   ''' <summary>
   ''' Supports end of initialization phase tasks for this control
   ''' </summary>
   ''' <remarks></remarks>
   Public Sub EndInit() Implements ISupportInitialize.EndInit
      '---- if we weren't able to retrieve the form from the control
      '     before, we should be able to now
      If rControl IsNot Nothing Then
         rParentForm = rControl.FindForm
      End If
   End Sub


   Public Sub New(ByVal IContainer As IContainer)
      MyBase.New(IContainer)
   End Sub


   ''' <summary>
   ''' Monitor the MouseMove event on the host form
   ''' If we see it move over a disabled control
   ''' Check for a tooltip and show it
   ''' If the cursor moved off the control we're displaying
   ''' a tip for, hide the tip.
   ''' </summary>
   ''' <param name="sender"></param>
   ''' <param name="e"></param>
   ''' <remarks></remarks>
   Private Sub rParentForm_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles rParentForm.MouseMove
      Static ctrlWithToolTip As Control = Nothing

      Dim ctrl = rParentForm.GetChildAtPoint(e.Location)

      If ctrl IsNot Nothing Then
         If Not ctrl.Enabled Then
            If ctrlWithToolTip IsNot Nothing Then
               If ctrl IsNot ctrlWithToolTip Then
                  '---- if we're not over the control we last showed
                  '     a tip for, close down the tip
                  Me.Hide(ctrlWithToolTip)
                  ctrlWithToolTip = Nothing
                  MyBase.Active = False
               End If
            End If
            If ctrlWithToolTip Is Nothing Then
               Dim tipstring = Me.GetToolTip(ctrl)
               If Len(tipstring) And Me.Active Then
                  '---- only enable the base tooltip if we're going to show one
                  MyBase.Active = True
                  Me.Show(tipstring, ctrl, ctrl.Width / 2, ctrl.Height / 2)
                  ctrlWithToolTip = ctrl
               End If
            End If

         ElseIf ctrlWithToolTip IsNot Nothing Then
            '---- if we're over an enabled control
            '     the tip doesn't apply anymore
            Me.Hide(ctrlWithToolTip)
            ctrlWithToolTip = Nothing
            MyBase.Active = False
         End If
      ElseIf ctrlWithToolTip IsNot Nothing Then
         '---- if we're not over a control at all, but we've got a
         '     tip showing, hide it, it's no longer applicable
         Me.Hide(ctrlWithToolTip)
         ctrlWithToolTip = Nothing
         MyBase.Active = False
      End If
   End Sub
End Class

No More Simulated Multithreading

0
Filed under .NET, VB Feng Shui

Way back in the wild west days of VB6, if you wanted a long running utility to ALSO pop up a progress bar, be cancelable, and be nicely encapsulated in a reusable class, you didn’t have a lot of options.

The first was to actually use VB6’s multithreading support via a thread per object ActiveX exe. It worked, but it wasn’t a trivial exercise.

One trick I used on a number of occasions was the modal form callback.

Essentially, it works like this.

  • You call into a class to “start” the long running process.
  • The class opens an associated progress form, and passes itself to the form.
  • The Progress form stores a WithEvents reference to the class, so it can be notified of progress as the class does its work.
  • Then, the form shows itself modally, and in the Activate event, calls back into the class to actually start the processing.
  • The class then starts it’s long running processing, raising progress events as appropriate, and yielding via DOEVENTS at appropriate times.

It’s a tad complex, to be sure, but certainly less complex than some of the alternatives and it still yields a nicely responsive UI that the user can easily cancel out of.

Fast forward to today. I had a similar requirement for a little VB.NET utility, so I figured, what the hey, might as well use that old trick.

After a bit of coding, I run the app and am rewarded with this:

image

Huh? I wasn’t “Attempting to call into managed code without transitioning out first.” Heck, there wasn’t even any API calls in this mix, Much less “Low Level extensibility points”!

Of course, the VS help for this message proved utterly useless, so I start googling.

Equally useless. Surely, I’m not the only guy out there to get this treat, right?

Anyway, eventually, I decided that maybe, just maybe, something about the reentrancy into the class from a modal dialog was causing the problem, though I still don’t have concrete proof of that.

I reworked the class to cooperate with the BackgroundWorker component (a nifty little bit of new .NET goodness), and everything’s back on track.

The only bit of nastiness left is that my background worker class raises several events, which my progress form monitors and presents, but doing anything within those events directly resulted in an exception about accessing UI objects from a thread other than the one they were created on.

The VS Help was helpful for this though, and adding code like this to the event handlers…

   Delegate Sub ScanningFileCallback(ByVal FileName As String, ByVal Count As Integer)
   Private WithEvents MyClass As MyClass

   Private Sub MyClass_ScanningFile(ByVal FileName As String, ByVal Count As Integer) Handles MyClass.ScanningFile
      If Me.prgProgress.InvokeRequired Then
         Dim d As New ScanningFileCallback(AddressOf MyClass_ScanningFile)
         Me.Invoke(d, New Object() {FileName, Count})
      Else
         me.prgProgress.Maximum = MyClass.FileCount
         me.prgProgress.Value = Count
      End If
   End Sub

worked around that problem nicely, although it’d be nice if the background worker class could somehow handle this transition itself. Hmmm….

DIM x AS NEW SpeechSynthesizer

0
Filed under .NET, VB Feng Shui

Here’s an interesting one for you.

I was playing with the SpeechSynthesis namespace recently when I ran off into a coding ditch. You know, one of those times when things are moving along nicely, and you swerve to avoid some minor nit, only to find yourself stuck upside down with the wheels half sunk in muck, trying to claw yourself out with an ICE and log files…

Anyway, I had this:

Public Class Connect
   Private WithEvents rSynth As New SpeechSynthesizer
   ...

And a little later in the class, I was using it like so…

      If rSynth.State <> SynthesizerState.Speaking Then
         rSynth.SpeakAsync(Speak)
      End If

Simple enough.

But it didn’t work. State was always coming back Ready, not Speaking, so I was getting a rather laughable stuttering intro to tracks as they were playing… “Now…Now…Now…Playing….Playing….Swerve…Playing….Swerve…”

(Sorry, the app in question is a little MP3 player plug in to announce tracks and allow you to navigate through the player using only a remote and no screen.)

I tried several different approaches but none worked. I could never get the proper state of the synthesizer to be reflected.

Then, on a lark, I remove the New from the SpeechSynthesizer declaration line:

Public Class Connect
   Private WithEvents rSynth As SpeechSynthesizer
   ...

And just assigned a new instance to the rSynth var from Sub New.

It worked!? Huh?

What was going on here?

I’m still not 100% sure, and the documentation on the NEW modifier for a DIM statement certainly doesn’t help to clarify things. From what I can tell, it looks like each reference to the regional var rSynth ends up retrieving a new SpeechSynthesizer object. Definitely not the behavior I was expecting. And not behavior I’ve seen before given this use of the New clause.

In reality, I’ve had far too many lessons about Dim x as New from VB6 to normally use it like this anyway. I guess I thought VB was past all that now in .NET, but I’m slowing learning that VB.NET is every bit as quirky as VB6, just differently so.

Ah, progress.

Very Cool VS2008 Add In

0
Filed under .NET, Utilities, VB Feng Shui

I’ve always been a big fan of the MZTools addin for VB6, so it was a little disappointing, if not understandable, that for his .NET foray, he’s wanting to get paid a little something for it.

Not that that’s bad, I just haven’t been able to commit to MZTools for .NET just yet.

In the meantime, I stumbled across a very slick little VS addin called DPack.

It’s actually a small set of different addins. Doesn’t look like you get the source, which is unfortunate, but the features are pretty slick, plus they work in VB, C++ and even C# and Ruby.

The Code Browser alone is worth the download. With one keystroke, you get a window like this:

image

It’s nicely sorted, browsable, and with one other click, takes you directly to the given code element. Even better, you can assign direct keystrokes to filter only methods, classes, properties, etc. By default, ALT-M shows just the methods in the current file, for instance. Reminds me of the old F2 (Function list) key that used to be in older VB’s. I missed that key<sob>.

Also particularly nice is the “Surround Selection With…” feature. You can’t customize the surrounding text but they give you the most common items (surround with TRY CATCH, FOR NEXT, DO WHILE, REGION, etc, etc.), so that’s not too bad.

It seems very quick and stable. And the best part is, it’s completely free!

Line Numbers and VB.NET

3
Filed under .NET, Troubleshooting, VB Feng Shui

Line numbers have been around ever since the dawn of time in BASIC.

Remember this classic:

10 Print “Hello”

20 Goto 10

But what many people may not know is that line numbers still exist in VB.Net!

Yep, that’s right, you can write the above routine, as I show it, in VB.NET.

Well, with one exception. Line numbers in .NET are treated as full on labels, and labels must be followed by a colon. So you’d have this in .NET:

10: Print “Hello”

20: Goto 10

Now, before I start getting hate mail about GOTO’s and such, let me say that I’m only a fan of a GOTO in a few very controlled circumstances.

But GOTO’s are the point here. Way back in the Visual Basic (Pre .NET days), line numbers combined with the ERL function made for a surprisingly effective field debugging tool. If you line numbered your code before compiling, and used ERL appropriately during your error handling, you’d be able to precisely pinpoint the offending line of code very quickly and painlessly.

I’ve used that approach for years, and even wrote an article in VB Programmers journal at one point about a very extensive generic error handling facility.

Recently, I decided to look into updating that facility for VB.NET, and unfortunately have good and bad things to report.

First the good.

  • As I said before, VB.NET still supports line numbers and the ERL statement, as well as all the old favorites ON ERROR RESUME NEXT, ON ERROR GOTO, RESUME, and RESUME NEXT.
  • You can easily compile VB.NET projects from the command line, making it relatively trivial to create a simple batch file to line number your code, compile it, then clean up the line numbered version.
  • VB.NET supports a much more sophisticated StackTrace/StackFrame functionallity that would seem, at first glance to obviate the need for ERL entirely.

but now the bad…

  • The StackTrace/StackFrame functionality works fantastically, as long as you’re willing to ship the program debugging database (PDB files) that are generated when you compile your project.
  • Worse still, you can’t use the old style ON ERROR GOTO, in the same method as the new style TRY CATCH FINALLY exception handling.

image

  • And finally, the ultimate bit of nastiness. While trapping an error with the old style ON ERROR GOTO will generate a standard exception (See the Err.GetException method), trapping an exception with structured exception handling (also known as SEH, or TRY CATCH), does not affect the ERR object at all, specifically, throwing an error in a TRY block will not set the ERL value, even if the code is line numbered.

image

So what’s the end result of all this?

Basically, do not even bother with ON ERROR GOTO style error handling in VB.Net. Even though it would appear to be supported, and even though retrieving the line number of the failing code isn’t possible without shipping your PDB, the fact that line numbers aren’t reported in code using SEH makes them pretty much useless. SEH is quite useful in many situations, so you wouldn’t want to not use it solely because of this.

But that doesn’t address two very real issues.

  1. Line numbers in stack traces are an incredibly useful debugging tool
  2. You don’t want to distribute your PDBs just to get line numbered stack traces if at all possible.

More to ponder…