Author Archives: admin

The Antec Sonata Case

7
Filed under Hardware

image I’ve always been impressed by Antec cases, and the Sonata I just picked up for my NAS box is definitely no exception.

First, it’s packed well.

It came with a bag STUFFED to the gills with screws, knobs etc. way more than you’d ever actually need.

imageI needed some drive rails to mount a CDRom. Hmm. Didn’t have any right off hand, but lookee here, right behind the 5.25 drive bay cover…

Unbelievable. Rails clipped in behind it!

Plus, Antec uses silicone vibration isolation washers for their harddrive mounts, and special screws for that. Good stuff.

The Sonata is not quite as nice as the P182image

but it’s cheaper and smaller, so it fits the bill better for a NAS box with 2 drives in it and not a lot else.

NAS’s, The Gigabyte M68M-S2P Motherboard and gigabit Lan Ethernet speed

0
Filed under Hardware, Networking, Windows 7

One small element of replacing all my equipment damaged by a lightning strike was to figure out what to do about my NAS.

image I’d picked up a sweet little Iomega StorCenter 1TB NAS server about 4 years back and it had served me well (despite having Seagate drives in it, ack!). But, after the strike, it was toast. What to do?

My first inclination was to just replace it. The Iomega units tend to be a little on the pricy side and Fry’s had a sale on Western Digital NAS boxes, so what the heck. I picked up a My World Book II, 2TB (Dual 1TB drives) box and came on home.image

Brought it up, restored my backup and all was well. Till a few hours later. The NAS just dropped off the network. I could still ping it, but couldn’t browse to it, connect via mapped driver letter, or anything else.

I powered the box down, and brought it back up, and all was well, for a few more hours. Then, same problem.

After far too long on the phone with WD’s tech support, I wrapped it up and took it back. After a few more false starts, I eventually decided that, heck, for the cost of 2 2TB drives, an Antec Sonata case, some ram, a Motherboard and CPU, and an OS, I could just BUILD a NAS box for about the same money.

imageSo off I went. Settled for the Gigabyte M68M-S2P motherboard with an AMD Athlon 64 X3 tricore. Pretty smooth sailing. Got the box running, hooked it up to the network, and then started copying files over to it.

SSSSSSLLLLLOOOOOOWWWWWW……

What the heck!?

Checked the network adapter and it was auto negotiating at 10mbps! This is the onboard gigabit Ethernet port on the Gigabyte board. Googling came up with a number of people having similar problems but no solutions.

I tried downloading new drivers, installing WinXP 64, and Win7 64, you name it. I could never get the drivers to negotiate at 1gbps.

Finally, I happened to be in the Device Manager, and right clicked on the NVidia nForce Ethernet controller (again, I’d done this plenty of times!)

image

I right clicked it, selected UPDATE DRIVER SOFTWARE, then clicked BROWSE MY COMPUTER FOR SOFTWARE.

Then, clicked LET ME PICK FROM A LIST OF DEVICE DRIVERS ON MY COMPUTER.

Lo and behold, I get this:

image

The NVidia drivers were selected, since I’d installed them off the CD that came with the motherboard, BUT there was that “Microsoft” set of drivers. I selected it, let it install and rebooted.

Presto, the magic 1gbps connection speed!

image

VSTO 3 in Visual Studio 2008 under Office 2010

4
Filed under Office, Tweaks

Ok, bit of a weird combination. If you’re developing for Office 2010, you’re using VS2010, right? Uh huh…

Seriously, if you’re like a lot of folks, you might not be upgrading to VS2010 soon, so Microsoft can shake the bugs out in an SP1. But still, you have an Office VSTO addin in VS2008, that you’d like to be able to run in Office 2010, while debugging in the VS IDE.

If you’ve tried this without having Office 2007 installed ALSO along with the Office 2007 Primary Interops, you’ve most likely gotten a nasty message in the errors window telling you you “can’t compile this application because Office 2007 is not installed” or some such.

Well, fear not. With a little tweak to your addin’s project file, you should be good as gravy.

Here’s the untouched section from towards the bottom of a VSTO VBProj file. Notice the nasty line that starts with “VSTO_COMPATIBLEPRODUCTS”.

  <!-- This section defines VSTO properties that describe the host-changeable project properties. -->
  <ProjectExtensions>
    <VisualStudio>
      <FlavorProperties GUID="{BAA0C2D2-18E2-41B9-852F-F413020CAA33}">
        <ProjectProperties HostName="Word" HostPackage="{D2B20FF5-A6E5-47E1-90E8-463C6860CB05}" OfficeVersion="12.0" VstxVersion="3.0" ApplicationType="Word" Language="vb" TemplatesPath="" DebugInfoExeName="#Software\Microsoft\Office\12.0\Word\InstallRoot\Path#WINWORD.EXE" DebugInfoCommandLine="/w" AddItemTemplatesGuid="{2606E7C9-5071-4B63-9A83-C66A32B1669F}" />
        <Host Name="Word" IconIndex="0">
          <HostItem Name="MyAddin" Code="Connect.vb" CanonicalName="AddIn" CanActivate="false" IconIndex="1" Blueprint="Connect.Designer.xml" GeneratedCode="Connect.Designer.vb" />
        </Host>
        <ProjectClient>
          <VSTO_CompatibleProducts ErrorProduct="This project requires Microsoft Office Word 2007, but this application is not installed." ErrorPIA="This project references the primary interop assembly for Microsoft Office Word 2007, but this primary interop assembly is not installed.">
            <Product Code="{XX12XXXX-XXXX-XXXX-X000-X000000FF1CE}" Feature="WORDFiles" PIAFeature="WORD_PIA" />
          </VSTO_CompatibleProducts>
        </ProjectClient>
      </FlavorProperties>
    </VisualStudio>
  </ProjectExtensions>

The trick, it turns out, is to just comment that element out completely.

You’ll end up with this…

  <!-- This section defines VSTO properties that describe the host-changeable project properties. -->
  <ProjectExtensions>
    <VisualStudio>
      <FlavorProperties GUID="{BAA0C2D2-18E2-41B9-852F-F413020CAA33}">
        <ProjectProperties HostName="Word" HostPackage="{D2B20FF5-A6E5-47E1-90E8-463C6860CB05}" OfficeVersion="12.0" VstxVersion="3.0" ApplicationType="Word" Language="vb" TemplatesPath="" DebugInfoExeName="#Software\Microsoft\Office\12.0\Word\InstallRoot\Path#WINWORD.EXE" DebugInfoCommandLine="/w" AddItemTemplatesGuid="{2606E7C9-5071-4B63-9A83-C66A32B1669F}" />
        <Host Name="Word" IconIndex="0">
          <HostItem Name="MyAddin" Code="Connect.vb" CanonicalName="AddIn" CanActivate="false" IconIndex="1" Blueprint="Connect.Designer.xml" GeneratedCode="Connect.Designer.vb" />
        </Host>
        <ProjectClient>
          <!-- BEGIN COMMENTED OUT SECTION -->
          <!-- <VSTO_CompatibleProducts ErrorProduct="This project requires Microsoft Office Word 2007, but this application is not installed." ErrorPIA="This project references the primary interop assembly for Microsoft Office Word 2007, but this primary interop assembly is not installed.">
            <Product Code="{XX12XXXX-XXXX-XXXX-X000-X000000FF1CE}" Feature="WORDFiles" PIAFeature="WORD_PIA" />
          </VSTO_CompatibleProducts>
          END OF COMMENTING -->
        </ProjectClient>
      </FlavorProperties>
    </VisualStudio>
  </ProjectExtensions>

Happy VSTOing!

Lightning Strikes

0
Filed under Rants

And let me tell you, in this day and age, lightning can just plain suck.

Garage Door opener, AC units, all my computers, UPS’s, TIVO, home audio, phone systems, NAS drives, cable box. You name it, it got smoked.

I’ve spent the last three weeks scrambling to get new machines setup, get backups restored (fortunately, I had recent ones), get repair techs out for the big stuff, and take care of the smaller stuff myself.

And I had a whole house surge suppressor, and well as UPS units on all my machines and some of my audio gear. Smoked them all as well.

So, any lessons learned? Oh yeah.

  1. Make SURE you know what’s covered by your UPS “connected equipment guarantee”. Keep all that paperwork filed somewhere for quick retrieval.
  2. Keep good records of purchases of anything you plug in. I keep mine in Quicken. Then keep backups of your Quicken data and the program disks so you can install to another machine and retrieve the data.
  3. You’ll typically have about 10 days to file a claim. So keeping recent backups of that data will help immensely with quickly pulling together all your records of what you had, when you bought it, and how much you paid.
  4. Check with your homeowner’s insurance. You likely have a 1% or 2% deductable. If your house is 250,000$, that’s a sizable chunk of change you’ll be out. And keep in mind, the insurance companies will depreciate the value of your equipment by how long you’ve owned it, so it might be even tougher to get up to that 1 or 2%.
  5. UPS’s can protect equipment but they won’t always work. Have backups. Make sure they’re restorable.
  6. You can almost be guaranteed that it’ll cost you more to replace all the equipment that you lost than it did to buy it in the first place. Why? Because if it’s computer stuff, you’re not going to go out and buy an exact replacement, you’ll buy something more modern, more harddrive space, bigger monitor, faster CPU, more memory, etc. Prices have come down since you bought, after all.

With the backups I had, it took a while, but I’m back online, and with almost my complete previous setup. Still lots of little stuff to take care of, but it’s getting there.

The good news is, we weren’t home, and there was no damage to the house itself, short of a few blown circuit breakers, and a blown GFCI.

In other words, lucky!

Word and the IsObjectValid Function

0
Filed under Office

One thing that can be maddening about programming against the Word Object Model is that objects have a habit of “disappearing” on you.

Say you retrieve a range, retrieve the fields in that range, then delete the range.

You still have references to those fields, but they no longer actually exist. Attempting to access any of the properties or methods on a field in this condition will cause Word to throw an exception with the message “That object has been deleted”.

Most code I’ve seen tends to handle these kinds of situations in one of two ways…

  1. Attempt to write the code such that if you’ve caused an object to become “deleted” that you simply don’t access it after that point. Sometimes this is easier said than done.
  2. Just wrap access to the object in a Try Catch, or On Error Resume Next. Test for any error condition and react accordingly. However, this has that nasty “the normal flow of code is through an exception” code smell. Not pretty.

Fortunately, there is third way.

The Word APPLICATION object has a function tucked away on it, called, appropriately enough, “IsObjectValid”.

Pass it a Word object model object and it’ll pass back true if the object is valid, or false if it’s been deleted.

Dim Rng = WordApp.Content
Dim Fld = Rng.Fields(1)
Rng.Delete
If WordApp.IsObjectValid(Fld) Then
   Debug.Print Fld.Code.Range.Text
Else
   'the FLD object is no longer valid
End If

Yes, it’s been around for a good long while so this may not be news to everyone, but it’s a handy trick to have in your toolbox if you work with Word regularly.

When Debug.Print Doesn’t Work Anymore

2
Filed under .NET, Troubleshooting, VB Feng Shui

This is one of those “back to basics” posts that, none-the-less, tripped me up for a few minutes, just because it’d been so long since I’d even looking at the setting involved.

I’d been working on a project for a while, when, one day a few weeks back, debug.print just stopped working. Even weirder, when I’d debug, the debugging would completely skip over the Debug.print statement.

When it first happened, I was right in the middle of working through some other problem and didn’t want to get sidetracked. But it hit me again a few weeks later, and I decided to start looking around.

It didn’t take long before I realized this setting was amiss.

cap1

It’s checked in the image above, but in my project, somehow it’d become UNCHECKED, and in the Debug project configuration!?

Still not sure how that happened, other than probably got a little click happy one day with the mouse and didn’t realize it.

Amazing how the little things can still get ya’ sometimes…

(and nobody say anything about my dll base address <g>! )

Printing a Range of Pages from a PDF file, On the Cheap

0
Filed under PDF, Utilities

Lets say you need to print some pages from a PDF file. But you don’t need to print the whole file, just a specific range, say pages 3 to the end of the document.

And let’s say you DON’T have any of those handy (and expensive) PDF libraries available. But of course you do have Acrobat Reader.

And let’s say you’ve got a whole pile of these PDFs that you need to get printed.

What to do?

First with the Printing

A little digging turned up that the AcroRd32.exe application (that actual Adobe Reader app, at least for version 9.x) can be invoked with command line parameters to print a file and exit:

AcroRd32.exe /p /h “filenamehere.pdf”

It’ll print to the current default printer, but that’s good enough for me for this purpose.

Simple enough, but you can’t specify which pages to print.

The Secret Ingredient

I’d almost given up on this when I came across a very handy utility called the PDFToolkit, by Sid Steward. PDFTK is a very small, free, command line driven utility. It won’t print, but it can slice and dice a PDF (or multiple PDFs) just about any which way you can dream up.

Say you have a PDF called input.pdf

You want to print pages 3 to the end of the file.

So, invoke PDFTK like so:

PDFTK.exe A=input.pdf cat A3-end output scratchfile.pdf

and then invoke the AcroRd32.exe line from above on the resulting scratchfile.pdf, and presto, your pdf is printed minus the first two pages!

Granted, if you’re developing a commercial product (or really even an inhouse utility), you’ll be much better served shelling out (no pun intended) a few bucks for a decent PDF library.

But in a pinch, PDFTK and AcroRd32 can be a pretty powerful little combination.

Configuring Log4Net in a .net VSTO Word Addin

3
Filed under Code Garage, Installations, log4net, VB Feng Shui

VSTO (Visual Studio Tools for Office) is a great way to put together addins for the various MS Office applications (especially Word, Excel, and Outlook).

And Log4Net is a fantastic and unbelievably flexible logging framework for .net applications.

So naturally, I wanted to use them together.

And doing so is not bad at all, till it came to configuration…

A Disclaimer (with a note of Encouragement)

Log4Net is not the most straightforward package out there. It’s extremely flexible, and very easy to work with once you’ve gotten used to it, but getting into the Tao of the thing took a few days, for me, anyway.

Don’t let that discourage you. It really is a spectacular framework for handling virtually any aspect of logging in your applications. And it really doesn’t take much “setup code” at all to get it operational.

The Super Highway

The easiest way to configure log4net in a .net application (VSTO addins included) is to simply call Configure on the XMLConfigurator object:

log4net.Config.XmlConfigurator.Configure()

That’ll work, but unfortunately, since your VSTO addin is a DLL, log4net will, by default, look in the current app.config file, which, if you’re running in Word, for instance, will be WinWord.exe.config in the folder where WinWord.exe lives.

Since WinWord.exe.config is Word’s config file, it’s probably not the best idea in the world to go shoe-horning your own (or log4net’s) config stuff in there as well. Not to mention how do you get your config information easily into that file during installation (or properly remove it during an uninstall).

The Scenic Byway

What you really want is for your VSTO addin DLL to have it’s own config file. Something that lives in the same folder as your DLL itself, and can easily be installed and removed.

Sure enough, that Configure method has an overload that accepts a FileInfo structure for an arbitrary XML Config file. So you can just do this:

Dim MyConfigFile = Me.GetType.Assembly.ManifestModule.Name & ".config"
If My.Computer.FileSystem.FileExists(MyConfigFile) Then
    Dim fi = My.Computer.FileSystem.GetFileInfo(MyConfigFile)
    log4net.Config.XmlConfigurator.Configure(fi)
End If

What this does effectively is construct a filename based on the name of whatever assembly the current code is defined within, but with a “.config” extension.

It then checks for the existence of that file. Since there’s no path on the file, it only looks in the current directory, but that’s fine, since the config file will always be located in the same folder as it’s DLL.

And finally, if the file is found, it retrieves a FILEINFO object for it and configures Log4Net with that file.

Bumps along the Road

Unfortunately, that will get you farther, but not by much.

There are two problems.

  1. In debug mode in the IDE, the “current directory” is, indeed, the same folder as the one with your addin DLL in it. But, when running in release mode, in production, with Visual Studio completely out of the picture, the current directory is very likely the folder where WinWord.exe is located. Not good.
  2. Worse, in production, VSTO addins are generally copied to a “shadow cache” folder by the .net framework, so that the original files can be upgraded in place easily while the application is in use.

Unfortunately, your config will will not get copied to the shadow cache.

This means that for determining where to look for your config file, using something like:

  • Me.GetType.Assembly.CodeBase or
  • Me.GetType.Assembly.Location

won’t work, because often times, they’ll point you off into the wilds of the assembly cache folder and not  the \Program Files\MyCompany\MyProduct\ folder where you installed your addin and where you most likely would prefer your MyProduct.dll.config file to live.

Happy Trails

In the end, I found the best solution to be:

  1. Look in the “current directory” for your config file.
  2. If you find it, use it from there. This accommodated easy debugging while in the IDE because you can easily get to and edit your config file.
  3. If you don’t find it, construct the path to the app’s \Program Files\ folder and check there.
  4. If it’s not there either, just fall back to the default and call XMLConfigurator.Configure() with no parameters and let it default everything.

A routine that puts all that together looks like this:

Private Sub ConfigureLog4Net()
        Dim MyConfig = Me.GetType.Assembly.ManifestModule.Name & ".config"
        If Not My.Computer.FileSystem.FileExists(MyConfig) Then
            '---- not in current dir, so check in our Program Files folder
            Dim pth = Path.Combine(My.Computer.FileSystem.SpecialDirectories.ProgramFiles, My.Application.Info.CompanyName)
            pth = Path.Combinepth, My.Application.Info.ProductName)
            MyConfig = System.IO.Path.Combine(pth, MyConfig)
        End If
        If My.Computer.FileSystem.FileExists(MyConfig) Then
            Dim fi = My.Computer.FileSystem.GetFileInfo(MyConfig)
            log4net.Config.XmlConfigurator.Configure(fi)
        Else
            log4net.Config.XmlConfigurator.Configure()
        End If
End Sub

To use this, you’ll need to make sure that your installation package installs your addin DLL and it’s config file into the \Program Files\Company Name\Product Name folder.

This is the pretty typical case, though, so that shouldn’t be a worry.

Later on Down the Road

This obviously begs the next question. What about other application settings? Log4Net reads stuff out of an arbtrary config file  you specify, but the My.Settings object does not. It’ll still end up looking in the default place, which is the host application’s config file (again, WinWord.exe.config, or Excel.exe.config, etc).

I hope to cover a decent solution to that in a later post.

Implementing Document.SaveCopyAs in Word

1
Filed under Office, VB Feng Shui

If you’ve used the Excel object model, youmay have discovered how incredibly handy the SaveCopyAs function is. Essentially, it allows you to save a currently loaded Excel spreadsheet into some arbitrary file, without altering the state of the loaded copy. In other words, if the user has altered the spreadsheet loaded into Excel, but hasn’t saved it, after a “SaveCopyAs”, that copy of the sheet is still considered dirty, and the saved file on disk does not have the user’s changes saved within it.

This is a very wonderful thing!

Basically, it means you can save off the spreadsheet as it currently exists in memory, complete with all the users edits up to this point, perform any operation on that saved copy that you want, and then either keep that copy or throw it away, and the copy that the user is currently working on is not affected in anyway whatsoever!

Good stuff.

But Word has never had it!

I’d rectified this long ago, in VB6, but recently had a need for this capability again, in VB.net this time.

Turns out, it’s incredibly simple to implement with VB.net, and much more intuitive to.

With .net 3.5, you can even create the function as an Extension Method, and directly add it to the Word Document object interface! Very cool!

The thing is, I knew I’d done this before, but couldn’t remember exactly how. After quite some time googling this, I came across a post where it was theorized it might be possible to implement SaveCopyAs by using the Compare function in Word.

Hmm, I’d never even tried the Compare, but it sounded interesting. After a few false starts, I ended up with this:

Imports System.Runtime.CompilerServices

<System.Runtime.CompilerServices.Extension()> _ Public Sub SaveCopyAs(ByVal Document As Word.Document, ByVal FileName As String) Document.Compare(Name:=Document.FullName, CompareTarget:=Microsoft.Office.Interop.Word.WdCompareTarget.wdCompareTargetNew) With Document.Application.ActiveDocument If .Revisions.Count > 0 Then .Revisions.RejectAll() .SaveAs(FileName, AddToRecentFiles:=False) .Close() End If End With Document.Activate() End Sub

Notice that I’m using the CompilerServices.Extension attribute to flag this as an extension method. This is what adds this method directly to the Word Document object and makes it so much more intuitive to use.

Essentially, the idea here is to:

  1. Compare the version of the document loaded in memory to the last saved version out on disk.
  2. Write the comparison result to a new document in memory (i.e. what becomes the active document)
  3. Check the revision count.
  4. If there are revisions, you know there’s been changes made to the document since it was last saved, so Reject all the revisions, save the active document and close it.
  5. If there were no revisions, the user hasn’t made any changes to the document since the last time they saved, so there’s really nothing else to do.
  6. Reactivate the user’s originally active document

The real trick here was step 4. Originally, I was accepting the revisions and then saving, and it wasn’t working at all. Eventually I traced the problem back to the comparison order. It turns out, when you perform a compare to another file from a loaded Document object in Word, the results of the comparison are the changes required to convert from the version of the document you currently have open in Word to the other document.

So, if the user has added the word “Nostradamus” in a paragraph, then the comparison would yield a revision that says, essentially, “remove the word Nostradamus from this paragraph”. This is exactly backward from the behavior you want. Turns out that all I had to do was “Reject all changes” instead.

Believe it or not, this works a dream.

But…

I can’t imagine that performing a compare, especially on a large document, is going to be a particularly speedy process and I knew I’d done this before using a alternate interface that the Word Document object implemented, albeit undocumentedly so. I just couldn’t remember how…

Suffice it to say, eventually I stumbled across the long forgotten interface. That interface is the COM IPersistFile. It’s a basic interface COM typically uses when saving Structured Storage documents, which are what Word files usually are (though, fortunately, it works with Word 2007 for saving DOCX style files).

Turns out the Word Document object implements that interface. They just don’t make that fact common knowledge.

Using it is trivially easy, especially given that it’s a COM interface that the .net framework happens to define natively (though do note, you’ll need to Import the InteropServices.ComTypes Namespace as show below.

Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices.ComTypes

<System.Runtime.CompilerServices.Extension()> _ Public Sub SaveCopyAs(ByVal Document As Word.Document, ByVal FileName As String) Dim pf = DirectCast(Document, IPersistFile)  
pf.Save(FileName, False) 'pf.SaveCompleted(FileName) End Sub

About that last commented line, that calls the SaveCompleted function. From the docs, it sounds like that is necessary, but I’ve never found a situation where it made a difference calling it or not.

Maybe someone can illuminate that aspect of the interface better than me?

But at any rate, the IPersistFile method is likely to be many times faster than the Compare method, especially for large files or files that the user has made many changes to without saving. Still, I thought the Compare method was interesting enough to warrant a mention.

So there you have it. SaveCopyAs for Word, implemented as an extension method that directly extends the Word Document object.

Yet more VB.net sweetness!

VB.net Instance Variable Initializers vs the Constructor

0
Filed under VB Feng Shui

I ran into a very peculiar problem several days ago. Essentially, I had a class, with a few properties. One of those properties had some initialization code for a backing variable, like so:

   Public Property Count() As Integer
      Get
         Return _count
      End Get
      Set(ByVal Value As Integer)
         _count = Value
      End Set
   End Property
   Private _count As Integer = _collection.Count

Now, granted, this is a little contrived. But the idea is that this particular property is initialized with a value from a private object (the _collection variable), that is set up during the constructor.

The problem was that the app was throwing an object not initialized when try to initially set the _count variable’s value.

This completely threw me. How on earth was my constructor not being called? After some debugging, I quickly discovered that the constructor is called after all these private variable initialization lines are executed! Now, that doesn’t really make a whole lot of sense, but, it’s what’s happening.

Only later did I come across this post by Patrick Steele. He’s got a great explanation and description of what’s happening there, so I won’t repeat that.

But the key to the whole issue is a quote by him from the Visual Basic Language Specification:

When a constructor’s first statement is of the form MyBase.New(…), the constructor implicitly performs the initializations specified by the variable initializers of the instance variables declared in the type. This corresponds to a sequence of assignments that are executed immediately after invoking the direct base type constructor. Such ordering ensures that all base instance variables are initialized by their variable initializers before any statements that have access to the instance are executed.

(The emphasis is Patrick’s)

He goes on to quote the relevant part of the C# spec, which explicitly states exactly the opposite behavior for it’s initializers and constructors!

I haven’t been able to find any quote as to why there’s a difference in behavior, or more specifically, why on earth VB’s behavior in this case seems so wrong.

If anyone knows, please comment!