Porting Word2MediaWikiPlus to VB.NET: Part 9

Previous articles in this series: Prologue, Part 1, Part 2, Part 3, Part 4, Part 6, Part 7, Part 8.]

Final Feature from ThisDocument.cls: Normal.dot ?

One of the annoyances about VSTO development in Word is that Word always seems inclined to write settings into the Normal.dot file (Word’s default template).  I’ve never really looked into what these settings typically are, and I’m not sure in the case of this project either, but during the first few rounds of trying to get the CommandBar code working, I was getting asked every time whether I wanted to update the Normal.dot file (and having to create a workaround — see Part 8 for details — to avoid this).

I haven’t seen this come up in the last few days of coding, so I’m not sure if (a) I’ve gotten over whatever hurdle causes this because I’ve finally fixed a bug, or (b) I inadvertently slipped and let the Normal.dot get updated.  I’ll check it out in the near future, and I’m sure I’ll have to change something because of it, but for now it’s just not worth worrying about.

Button Click() Event

I was flipping through a copy of Visual Studio Tools for Office (by the two Eric’s — Lippert and Carter) and I happened to catch the section in Chapter 7 (Working with Word Events) entitled “Visual Studio Generation of Event Handlers”.  This little gem finally drilled home what the WithEvents keyword means: it allows me to select one of the controls for which I’ve tagged WithEvents in its declaration, then select any of the available Events, and VS2005 will automatically generate the Event Handler stub, complete with all those nasty parameter declarations that always give me heartburn.

So now I understand why I declared the ButtonControl, UploadControl and ConfigControl using WithEvents.  Now I can easily generate the Click() Event Handlers for each of these CommandBarButtons.

The more I look at them however, the less I like the names “ButtonControl” etc.  They’re not a whole lot better than “ButtonButton”, “UploadButton” and “ConfigButton” (which are obviously redundant, especially with an IDE that automatically describes each object’s type as you’re using it).  Further, they really aren’t easy to find when you’re looking for one of the buttons while you’re typing code.

Somewhere along the line I picked up an elegant convention, which is to prefix UI controls with the string “ui”, as in “uiButton”, “uiUpload”, “uiConfig”.  That way, any code you’re writing for UI controls will be easy to use Intellisense to narrow down to just those controls with the “ui” prefix.

I’m going to change “ButtonControl” to “uiConvert” as well.  I don’t know what the name “ButtonControl” was supposed to mean (other than the most generic way of addressing this first control), and it’ll be easier to remember when the Button label and the object Name are similar.

Issue: Word Macro Settings blocking the Click() event

I dropped a MessageBox.Show() call into each Event handler and Debug’d the application.  What’s odd isn’t that Word 2003 complained about Macro settings (“The macro cannot be found or has been disabled because of your Macro security settings”), but that the Configure button first displayed the MessageBox, and after I clicked OK, then it complained like the others.  There’s nothing different in the way I constructed the Event Handlers for these three buttons, and they all appear to be doing the same thing, but obviously there’s something happening differently with the uiConfig_Click() handler.

Once I set Breakpoints on all three of these handlers, I realized that only the uiConvert_Click() is actually executing the Sub, which is weird — this event handler is catching the Click event for the Configure button; the other two buttons are erroring before they even try to execute their code.

…Hmm, looks like I found the problem.  I’d originally started out with three separate blocks of near-identical code to generate the three CommandBarButtons and all their Properties.  Then, when I got the crazy idea to make my code more elegant, I collapse them into a single For Each loop.  This really made the code easier to follow, but I forgot to deal with the variable used to configure each of the buttons.

Right now, the loop performs all operations in each iteration on the variable uiConvert, which is typed a CommandBarButton.  What I actually need to do is have the For Each loop act on three separate variables (uiConvert, uiUpload and uiConfig) during its three iterations, so that the three buttons get configured properly. 

To try to fix this, I’ve tried adding a member to the Structure of type CommandBarButton that references the CommandBarButton variable for each button.  Then I use that Structure member in the For Each loop to assign all button Properties, thinking that it’d implicitly be configuring the referenced CommandBarButton variable.  Unfortunately, it doesn’t seem to quite work that way.

This code fragment should explain where I’m currently at:

    Structure CommandBarButtonSettings
        Public ButtonVariable As Office.CommandBarButton
        Public Tag As String
        Public StyleProperty As Office.MsoButtonStyle
        ...
    End Structure
        ...



Dim buttonSettings As New CommandBarButtonSettings()
        Dim buttonsList As New ArrayList()

        buttonSettings.BeginGroupProperty = True
        buttonSettings.ButtonVariable = uiConvert
        buttonSettings.StyleProperty = Office.MsoButtonStyle.msoButtonIconAndCaption
        ...

        buttonsList.Add(buttonSettings)
        For Each _buttonSettings As CommandBarButtonSettings In buttonsList

            'Create the CommandBarButton if it doesn't exist
            If W2MWPPBar.FindControl(Tag:=_buttonSettings.Tag) Is Nothing Then
(*)                buttonSettings.ButtonVariable = CType(W2MWPPBar.Controls.Add(1), Office.CommandBarButton)
            Else
                ' If it already exists, do not create the CommandBarButton but just obtain a handle to it
(*)                buttonSettings.ButtonVariable = W2MWPPBar.FindControl(Tag:=_buttonSettings.Tag)
            End If

            Try
(*)                With buttonSettings.ButtonVariable
                    .BeginGroup = _buttonSettings.BeginGroupProperty
                    .Style = _buttonSettings.StyleProperty
                    ...
                End With

                ...

            End Try

        Next

The problem is with the code marked with an (*), and I’m stumped on now to resolve this problem.

Aside: Update for VSTO SE available

If you’re using VSTO SE add-in for Visual Studio, then make sure you’ve installed the VSTO SE Update that Microsoft released a couple of months ago.  I didn’t know about this, and though it probably won’t affect my code, it’s never a bad thing to be sure.

Yes, in fact I *do* get mistaken a lot for…

Couldn’t help myself, and I was still surprised by the result:

What American accent do you have?

Your Result: North Central
 

“North Central” is what professional linguists call the Minnesota accent. If you saw “Fargo” you probably didn’t think the characters sounded very out of the ordinary. Outsiders probably mistake you for a Canadian a lot.

The Midland
 
Boston
 
The West
 
Philadelphia
 
The Inland North
 
The South
 
The Northeast
 
What American accent do you have?
Quiz Created on GoToQuiz

Porting Word2MediaWikiPlus to VB.NET: Part 8

Previous articles in this series: Prologue, Part 1, Part 2, Part 3, Part 4, Part 6, Part 7.]

Troubleshooting ThisAddIn.Startup() continued…

OK, once more and gently (as my dad always used to say): my best theory now is that my code needs to create an object that represents the W2MWPP toolbar, and create this object whether the toolbar exists or not.  Once I have that object, then I can finally get the toolbar buttons instantiated and get on with the Wiki functionality [yeah, famous last words].

I figure that the code should test whether it can find an existing instance of the toolbar.  If it can’t find it, then it should create it; if it can find it, then just assign it to a variable and we’re done.

BTW, I found a great idea in VSTO for Mere Mortals (McGrath, Stubbs): rather than continuously referring to “Word2Wiki Toolbar” as a string, I could define a CONST and then reference the CONST instead.  [This has the added advantage that I could change the string value very easily if “Word2Wiki Toolbar” was no longer suitable.]  Why didn’t I think of this myself?

Here’s the code I’ve finally come up with to instantiate the Word2Wiki toolbar:

        Try
' Create a new CommandBar instance, if it doesn't already exist
If commandBarsCollection.FindControl(Tag:=TOOLBAR_NAME) Is Nothing Then
W2MWPPBar = commandBarsCollection.Add(TOOLBAR_NAME, Microsoft.Office.Core.MsoBarPosition.msoBarTop, False, True)
Else
W2MWPPBar = Application.CommandBars(TOOLBAR_NAME)
End If

Catch ex As System.ArgumentException
MessageBox.Show(TOOLBAR_NAME + "add-in's toolbar wasn't found - you won't be able to upload to the Wiki until you restart Word and/or reinstall the Add-in." + _
vbCrLf + vbCrLf + "Error: " + ex.Message, "Add-in Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End Try

Note: I’m not sure, but I suspect it’ll still thrown an exception on the first try.  However, it may just “take” on the second try, so that might be good enough for now.

Aside: CommandBars vs. Ribbon UI in Office 2007

While researching the CommandBar methods, I found this statement: “The use of CommandBars in some Microsoft Office applications has been superseded by the new Ribbon user interface.”  Oops, that’s right – some of this functionality may have to be re-written for Office 2007.  I’ll try to ensure the CommandBar-specific code can be decoupled from the application functionality, so we can get maximum reuse out of these efforts.

Next Issue: Word Template (changes to Normal.dot)

After instantiating the Toolbar successfully, Word 2003 at shutdown will ask me twice whether I want to save changes to Normal.dot.  The first prompt only allows you to overwrite Normal.dot or cancel out of the shutdown of Word.

If I hit Cancel, then the second time I try to shut down Word, it prompts me with:

“Changes have been made that affect the global template, normal.dot.  Do you want to save those changes?”

This time, I can say “No” to saving the changes, which leaves Normal.dot in its original form and finally lets me close Word.  The help text for this second prompt says,

“This message can appear if you made changes to items, such as macros, toolbars, or AutoText, that are stored in a global template that is attached to your document. The most commonly used global template is Normal.dot, which comes with Word.”

Next Issue: CommandBarButton creation

In the code, I’m using a well-documented sample to add the CommandBarButton to the CommandBar, and yet I’m getting the error

************** Exception Text **************
System.ArgumentException: Value does not fall within the expected range.
   at Microsoft.Office.Core.CommandBarsClass.get_Item(Object Index)
   at Word2MediaWiki__.ThisAddIn.ThisAddIn_Startup(Object sender, EventArgs e) in \Word2MediaWiki++\ThisAddIn.vb:line 50
   at Microsoft.Office.Tools.AddIn.OnStartup()
   at Word2MediaWiki__.ThisAddIn.FinishInitialization() in \Word2MediaWiki++\ThisAddIn.Designer.vb:line 65

Now that I know how to read this exception (see Part 7 for that whole twisty maze), I’ll spend a whole lot less time deciphering it.  This time it seems clear to me that it’s a problem with allocating a handle to the toolbar.  However, because I know that the toolbar is being created properly, a second glance at the offending line of code gives me the answer:

            ConvertControl = CType(Application.CommandBars("W2MWPPBar").Controls.Add(1), Office.CommandBarButton)

This one is easy: I’m mistakenly calling “W2MWPPBar” rather than “Word2Wiki Toolbar”.  Let’s fix that: highlight the string, right-click, choose Refactor (or Refactor!), and find…nothing.  D’oh — that’s right, the VB.NET refactoring tools (even the Refactor! add-on for Visual Studio) don’t have the Rename function that I’ve gotten used to in the C# world.  Guess I’m just going to have to try Edit, Find and Replace, Replace in Files instead.

With the Const now properly in place, the CommandBar and its buttons fall neatly into place.  Wasn’t that easy? 😉

Enhancement: Creating the CommandBarButton if it doesn’t exist

There’s two improvements I’ll make to the code that creates each CommandBarButton:

  1. I’ll mirror the way I construct the CommandBar – test if each CommandBar button exists, and if not, create it; if so, leave it alone.  [McGrath’s code in VSTO for Mere Mortals will Delete the existing button and then create it — I don’t know why this should be necessary, so I’ll simplify this code for now.]
  2. Replace calls to Application.CommandBars(TOOLBAR_NAME) with an object for the toolbar itself (W2MWPPBar).

Here’s the current code:

        For Each control As Microsoft.Office.Core.CommandBarControl In commandBarControlsCollection
If control.Tag = "W2MWPP Convert" Then
ConvertControl = control
buttonExists = True
Exit For
End If
Next

If buttonExists = False Then
'Create a new ControlButton
ConvertControl = CType(Application.CommandBars(TOOLBAR_NAME).Controls.Add(1), Office.CommandBarButton)
End If

And here’s my enhanced approach:

        If W2MWPPBar.FindControl(Tag:="W2MWPP Convert") Is Nothing Then
ConvertControl = CType(W2MWPPBar.Controls.Add(1), Office.CommandBarButton)
Else
ConvertControl = W2MWPPBar.FindControl(Tag:="W2MWPP Convert")
End If

Enhancement: implement For Each loop for the CommandBarButtons

This code is creating three CommandBarButtons using the same Methods and Properties, but doing it three separate times.  I know I’ve done this too, but I’d prefer to fix this.  Unfortunately, there’s one slight challenge for me: there’s too many variables to pass into a Sub, and I’m not very good with multi-dimensional arrays, so I don’t know what to do with all the variable strings that need to be fed in.

However, I recall another kind of construct somewhat like a multi-dimensional array, and a little digging on the ‘net and in my books leads to Structures.  Further, a nice little post to the MSDN Forums turns me on to another suitable idea: Arraylist.  Combine these two, and I should be able to pass in an arraylist of structures to a InstantiateButtons() method, and I’ll be able to loop through them all in one go.

The only trick is, the articles I’m finding right now don’t seem to give me useable advice for creating a structure in VB — or perhaps it’s just that Visual Studio isn’t cooperating, because if I type “Private Structure CommandBarButtonSettings” or “Private Type CommandBarButtonSettings”, Visual Studio doesn’t seem to generate the automatic “End Structure” or “End Type” statements that appear to be necessary.

The book “The Visual Basic .NET Programming Language” (Vick) showed a very simple way to write the code for a Structure, and once I tried that VS started instructing me on what I needed to add/rearrange for this to work.  One thing that hadn’t been clear is that the Structure has to appear outside of any Method, so I’ve moved it up to just after the Public Class statement.

BTW, I just stumbled across the concept of “composite formatting“, which I’m going to try to use in my MessageBox.Show() calls here.  The code sample in the MSDN Forum post mentioned above, from which I borrowed, happened to use composite formatting in their Console.Writeline() call, which tipped me off to this elegant way of generating strings with dynamic content scattered throughout.  I don’t know about you, but I’m a bit tired of all the ” + variable.ToString() + “ nonsense that I have to embed so often in my apps.

Enhancement: implement String.Empty

I’m not even sure where I read this, but it was related to some of my research into the rules that good code should follow: where an application needs to set a String variable with an empty value, we should use String.Empty instead of “”.  Thus I’m making changes such as from this:

        buttonSettings.DescriptionTextProperty = ""

to this:

        buttonSettings.DescriptionTextProperty = String.Empty

Milestone: Toolbar works!

Well I suspect you’re all just *dying* to see this app working — “wow, a toolbar with buttons that do *nothing*?  What a wonder!”  I’m going to mark the occasion on this day by creating a CodePlex project for this Add-In and uploading the current Source Code so everyone can have a laugh. 😉

Please have a look here, and leave any Comments, Issues or Suggestions that come to mind.  Any and all such assistance is appreciated.  Browse to here: http://www.codeplex.com/word2mediawikipp

Lightbulb joke for Dog Breeds

Copied from a post on Craigslist:

QUESTION: How many dogs does it take to change a light bulb?

Golden Retriever: The sun is shining, the day is young, we’ve got our whole lives ahead of us, and you’re inside worrying about a burned-out bulb?

Border Collie: Just one. And then I’ll replace any wiring that’s not up to code.

Dachshund: You know I can’t reach that ****ed stupid lamp!

Rottweiler: Make me.

Lab: Oh, me, me!!!! Pleeeeeeze let me change the light bulb! Can I? Can I? Huh? Huh? Huh? Can I?

Siberian Husky: Let the Border Collie do it. You can feed me while he’s busy.

Jack Russell Terrier: I’ll just pop it in while I’m bouncing off the walls and furniture.

Poodle: I’ll just blow in the Border Collie’s ear and he’ll do it. By the time he finishes rewiring the house, my nails will be dry.

Cocker Spaniel: Why change it? I can still pee on the carpet in the dark.

Doberman Pinscher: While it’s dark, I’m going to sleep on the couch.

Boxer: Who cares? I can still play with my squeaky toys in the dark……

Mastiff: Mastiffs are NOT afraid of the dark.

Chihuahua: Yo quiero Taco Bulb.

Irish Wolfhound: Can somebody else do it? I’ve got this hangover…..

Pointer: I see it, there it is, there it is, right there….

Greyhound: It isn’t moving. Who cares?

Australian Shepherd: First, I’ll put all the light bulbs in a little circle….

Old English Sheep Dog: Light bulb? I’m sorry, but I don’t see a light bulb?

German Shepherd: Alright, everyone stop where you are! Who busted the light? I SAID,”STOP WHERE YOU ARE!!!”

Hound Dog: ZZZZZZZZZzzzzzzzzz

Cat: Dogs do not change light bulbs. People change light bulbs. So the question is: How long will it be before I can expect light?

Porting Word2MediaWikiPlus to VB.NET: Part 7

Previous articles in this series: Prologue, Part 1, Part 2, Part 3, Part 4, [no Part 5 – apparently I lost the ability to count], Part 6.]

Troubleshooting ThisAddIn.Startup() continued…

Still struggling with getting the CommandBar and CommandBarButton instantiated in the ThisAddIn.Startup() Sub.  I’m finding that the initial exploration of the CommandBar to see if there is a pre-existing instance of the “W2MWPP Convert” button is not working.  The code starts off like this:

        Dim MyControl As Microsoft.Office.Core.CommandBarButton
        MyControl = Application.CommandBars("W2MWPPBar").FindControl(Tag:="W2MWPP Convert")

Then when I debug (F5) this addin, Word reports an unhandled exception with the error “Value does not fall within the expected range”.  I seem to recall having this same problem with my previous VSTO Word AddIn until I first had the button created — then, the next time I ran the addin, it had something to FindControl().  At present, since the button doesn’t exist, it appears that FindControl() is getting “jammed” and I’m never going to get anywhere (kind of a chicken-and-egg problem).

It will be easy to get around this problem on my computer, but I’m afraid that when I build and release this add-in for others to install, if I start the code with a FindControl() call when there’s no button to find, no one else will be able to use this addin either.

Alternative approach to creating the CommandBarButton?

I have to imagine that there’s another way to skin the cat: if we need to determine if the button exists before attempting to create it, but trying to find it by name isn’t working, then perhaps there’s some CommandBar control collection that we could iterate through, and compare the Tag value for each (if any) to find the one we want.  That should go something like this:

Dim commandBarControlsCollection As Office.CommandBarControls = W2MWPPBar.Controls
Dim buttonExists As Boolean

For Each control As Microsoft.Office.Core.CommandBarControl In commandBarControlsCollection
      If control.Tag = "W2MWPP Convert" Then
          MyControl = control
          buttonExists = True
      End If
Next

If buttonExists = False Then
      'Create a new ControlButton
      MyControl = Application.CommandBars("W2MWPPBar").Controls.Add(Type:=Microsoft.Office.Core.MsoControlType.msoControlButton)
End If
Is it a Variable Scope issue?

This still doesn’t resolve the error, so I’m continuing to search for good example code from folks who should know how to construct VSTO code.  This blog entry from the VSTO team has an interesting thing to say:

You should declare your variables for the command bar and buttons at the class level so that your buttons don’t suddenly stop working.

The referenced article (which I’ve linked from Archive.org — the Internet Wayback Machine”) says:

The solution is to always declare your toolbar/menu/form variables at the class level instead of inside the method where they’re called. This ensures that they will remain in scope as long as the application is running.

I wonder whether this advice was more relevant to document-based VSTO projects rather than the application-level add-ins that are possible today — but something tells me it can’t hurt either way, and it’s worth trying to see if it changes anything about the errors above.

Result: unfortunately, this isn’t a problem of variable scope.  In taking a closer look at the exception, here’s the first-level exception:

System.ArgumentException: Value does not fall within the expected range.
   at Microsoft.Office.Core.CommandBarsClass.get_Item(Object Index)

Am I looking at the wrong object?

What exactly is the problem?  Is this saying that get_Item() is failing to get the CommandBar, or the CommandBarButton?  I’ve assumed up to now that it’s a problem referencing the CommandBarButton, since the CommandBar is getting created in Word each time I Debug this add-in.  However, now that I’m looking at it, CommandBarsClass.get_Item() seems more likely to be acting on the CommandBar than the button (or else it’d refer to something like CommandBarButtonsClass.get_Item(), no?).

What’s odd, however, is that the VS Object Browser doesn’t even have an entry for CommandBarsClass — when I search for that term, no results come up, and when I search on “CommandBars”, the closest thing I can find is the “Class CommandBars” entry, which doesn’t have a get_Item() method.

Searching in MSDN, I found the entry for CommandBarsClass Members, which doesn’t reference the get_Item() method but does mention an Item Property.  That page says a very curious thing:

This property supports the .NET Framework infrastructure and is not intended to be used directly from your code.

I wonder what that’s all about then?  In fact, the documentation for the CommandBarsClass Class also says the same thing.  I can understand that there are some “internal functions” generated by the compiler that aren’t really meant for use in my code, but it’s really tough to debug a problem when these constructs barely get a stub page and there’s no information to explain what I should think when one of these things pops up in my day-to-day work.

I feel like I’m chasing my tail here — now I’m back on the Members page, hoping that one of the Properties or Methods that are documented will help me deduce whether this class references the CommandBar or the CommandBarButton when it calls get_Item() [and maybe even help me figure out why a just-created CommandBar object can’t be referenced in code].

The best clue I’m coming up with so far is that the page documenting the Parent Property shows that under J#, there’s what appears to be a get_Parent() method, not existing in the other languages mentioned, which leads me to believe that the get_Item() method is something generated by the compiler when it needs to get the value of the Item Property.  [At least I’m learning something for all my trouble…]

The only other tantalizing tidbit so far is that the CommandBarsClass page indicates that this class implements interfaces that all refer to CommandBar, not to any controls associated with the CommandBar: _CommandBars, CommandBars, _CommandBarsEvents_Event.  I can’t tell the difference between the first two (at least from the docs), but obviously the Event interface is its own beast.

Success: it’s the CommandBar!

I think I have confirmation, finally: the docs for _CommandBars.Item state that the Item Property “Returns a CommandBar object from the CommandBars collection.”  OK, so now I finally know: my code is barfing on trying to access the just-created CommandBar, not the CommandBarButton as I thought all along.  Whew!

Aside: Re-Using Variables

I’m not much of a code snob yet — there’s very few things I know how to do “right”.  However, I’ve found something that just doesn’t seem right in the original VBA code that I’m going to change.

The original code sets up three separate buttons in the CommandBar, and each time the code looks for the button, then creates it, then configures it, it’s using the exact same variable each time: MyControl.  I know this obviously worked (at least in the VBA code), so it’s hardly illegal, but it seems much safer to create three variables and instantiate them separately.  Maybe it’s just so that I can follow the code easier, I don’t know.  In any case, I’m having a hard time with it so I’m going to call them each something else.

However, I’m not so much of a snob that I’ll create three boolean variables to track whether I’ve found an existing instance of the button, so I’m going to re-use the buttonExists variable.

 

Keep tuning in… someday I’ll actually start getting into Wiki-related code (I swear!)

Porting Word2MediaWikiPlus to VB.NET: Part 6

[This series has six previous articles: the prologue, Part 1, Part 2, Part 3, Part 4 and Part 5.] 

My First round of debugging

Figuring that it would be wise to debug the code so far before moving on to the next module, I fired up F5 and let ‘er rip:

Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))

************** Exception Text **************
System.Runtime.InteropServices.COMException (0x80020005): Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))
   at Microsoft.Office.Core.CommandBarsClass.get_Item(Object Index)
   at Word2MediaWiki__.ThisAddIn.ThisAddIn_Startup(Object sender, EventArgs e) in C:\Documents and Settings\msmithlo\My Documents\personal\VS2005 Projects\Word2MediaWiki++\Word2MediaWiki++\ThisAddIn.vb:line 11
   at Microsoft.Office.Tools.AddIn.OnStartup()
   at Word2MediaWiki__.ThisAddIn.FinishInitialization() in C:\Documents and Settings\msmithlo\My Documents\personal\VS2005 Projects\Word2MediaWiki++\Word2MediaWiki++\ThisAddIn.Designer.vb:line 65
   at Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManagerInternal.ExecutePhase(String methodName)
   at Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManagerInternal.ExecuteCustomizationStartupCode()
   at Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManagerInternal.ExecuteEntryPointsHelper()
   at Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManagerInternal.Microsoft.VisualStudio.Tools.Applications.Runtime.IExecuteCustomization2.ExecuteEntryPoints()

************** Loaded Assemblies **************

That sure didn’t take long.  .NET is barfing on the following statement:

Dim MyControl As Microsoft.Office.Core.CommandBarControl = Application.CommandBars(W2MWPPBar).FindControl(Microsoft.Office.Core.MsoControlType.msoControlButton, Tag:="W2MWPP Convert")

I’m not entirely sure where the “type mismatch” is coming from, but given that this is a command directly ported from VBA, it’s probably safe to assume there’s a different/better approach in VB.NET.  [If I had to guess, however, I’d think that the MyControl can’t be equated to Application.CommandBars() when it’s just been declared As Microsoft.Office.Core.CommandBarControl – there’s something about casting Application.CommandBars to Office.CommandBars that tells me “Application” and “Office” just can’t be equated like that.  I really wish I knew exactly why, but someday I’m sure I’ll look back and wonder why I didn’t “get” it.]

Based on my experience with a previous Word VSTO add-in, I tried  Office.CommandBarButton rather than CommandBarControl, and I think I’ll rework some of the logic to parallel the way that I’d made this same Bar + Button construction work in the past.

I’ve let this task sit for a few days while I debate with myself the merits of (a) making as little syntactic as well as functional changes to the code as humanly possible, (b) replacing whole swaths of code with stuff I know that “works” — even if I’m not convinced that the replacement code is any more “elegant” or performant than the stuff I’m replacing, or (c) bashing my head against a wall with a few attempts at [a] until I confirm that I won’t be able to figure it out easily, at which point I revert to [b].

I was originally inclined towards [a], so that I didn’t offend the original author and I didn’t act like some impatient arrogant twit who thinks that “they know better”.  However, the longer I stall on any piece of code for which I don’t have a clear idea which specific calls, variables or syntax is causing the incompatibility — though I feel I understand its overall function — the more I’m getting to the point that I’d rather put out something that works than spend another six months trying to be as surgically precise as possible in my upgrade.

I guess the most important thing is to document (a) what I’m replacing, (b) how I expect the replacement code should work/should accomplish the same thing, and (c) use as many of the same variable names as possible so that anyone familiar with the old code would have the best chance of understanding the new code as well.

So my thinking is, I’ll try to preserve the variable names, and as much of the overall logic as possible, but that I’ll replace any non-functioning code with whatever I know (or can find online) that works so that this project doesn’t get permanently stalled (which is a serious risk with me).

 Response to Redistribution Request for Wikifunctions.dll

The whole loosely organized nature of open source means that you might never get a straight answer to any question, but I did receive a response to my inquiry about whether it would be acceptable to redistribute the compiled binary of Wikifunctions.dll.  The response indicated,

“No problem if you include the binary you downloaded yourself. Don’t forget, it’s copyleft. Of course, you’ll have to honor GPL by mentioning that “this software includes parts from AWB developed by blah blah blah…”. Be advised though that WikiFunctions is designed primarily for AWB and thus has some limitations such as requirement for approval to be able to edit pages. Probably you will find something else more useful. For example, WikiAccess (with docs in Russian, hehe).” 

 

Join us again… well, you know the drill by now…

Porting Word2MediaWikiPlus to VB.NET: Part 4

[This series has four previous articles: the prologue, Part 1, Part 2 and Part 3.] 

Proposed Class Hierarchy

I’m trying to think through how this will be basically grouped and structured.  This is the basic class hierarchy I’m thinking of implementing:

  • W2MW++: functions to perform the basic setup & teardown, general functions
  • W2MW++.Format: functions for converting the formatting & layout
  • W2MW++.Image: functions for manipulating images embedded in the Word document
  • W2MW++.Text:
  • W2MW++.Table
  • W2MW++.Wiki
  • W2MW++.Wiki.Publish: functions for making the remote calls necessary to publish the translated document to the Wiki

Aside: Searching SourceForge.net (D’oh!?)

It just occurred to me that I should probably dig through SF.net before diving into a headlong rush of code… it would really suck if I got halfway through, 40 hours into it and then found out that someone else had already developed a really robust Office add-in or set of libraries for MediaWiki upon which I could’ve built.  Although that would sure be in the spirit of the open source community… 😉

I went over to SF and searched on “wiki edit” and came up with very little, so I tried just “wiki” and came back with 60+ pages of results.  Filtering on Operating System = “All 32-bit Windows” reduces that list to 8 pages.  [It’s unfortunate that there’s no way to filter Programming Language = “{.NET languages}”, but so it goes in the land of SF – best I can do is re-run this three times, for C#, Visual Basic and Visual Basic.NET.]

Here’s some of the more interesting, potentially complementary codebases I’ve found so far:

  • WikiAccess Library: (C#, GPL)  Seems to have implemented a comprehensive set of methods and properties for accessing a MediaWiki-based site.  [This might just end up supplying the W2MW++.Wiki class I was thinking about.]
  • DotNetWikiBot Framework: (C#, MIT (X11) license) Seems to have a similarly-large set of methods & properties for accessing a MediaWiki-based site.
    • What’s even more coincidental is that the author (according to the embedded copyright) shares an almost-identical name (Vassiliev) with the author of WikiAccess Library (Vasiliev). 
    • I’m assuming it’s the same guy with two accounts, though I don’t know why he’d reimplement the same stuff over again.
    • However, I have to admit the CHM file documentation that’s included, as well as much greater amount of activity, makes me feel very good about importing the library as a foundation for this app.  [If I understand these licensing arrangements, I don’t have to worry about license conflict if I don’t re-use any of the source code, but only place a dependency on the compiled DLL.]
  • Wiki Word Importer: (VB.NET, GPL) An empty project.  Next!
  • Wiki Editing Suite: (VB.NET, GPL) A populated project, with completely barren source files.  Next!
  • wikiTech: (VB.NET, APL) Yet another empty project.  Next!
  • excel2Wiki: (Visual Basic, GPL) Empty project.  What a surprise – Next!
  • Wiki2HTML: (Visual Basic, LGPL) A VBScript that converts Wiki-formatted content to common HTML.
  • AutoWikiBrowser: (C#, GPL) A very well-staffed project with an amazing amount of code activity.  Has a page on Wikipedia as well, which describes the WikiFunctions.dll API library that they encourage others to use in their standalone projects.  Perhaps they’d be okay with including it here…?

Fascinating, just fascinating.  This is a real decision point for this project then:

  • Do we take a dependency on one or more external libraries?  If so, which one(s)?
  • If so, then how do we negotiate/maintain redistribution rights?  [Presumably it’d be a pain in the arse to force our users to go download and decompress the DLL(s) necessary from other packages.]
  • If not, do we import source code from one of these projects?  If so, do we only focus on GPL code, so that there won’t be any conflicts with the Word2MediaWikiPlus basis?

AutoWikiBrowser’s WikiFunctions Assembly

I’m pretty much decided on taking the dependency on AutoWikiBrowser’s WikiFunctions.dll (as it appears at this time to have the most robust support), so now it’s just a question of whether to add any others:

  • The AutoWikiBrowser talk archives from October 2006 indicate that DotNetWikiBot “…is significantly more advanced/complicated than WikiFunctions.Editor”.  I’ll see how much editing functionality is needed and only take the dependency on DotNetWikiBot if there are major functions that it has that we need.

I’ve left a note on the AutoWikiBrowser Talk page to inquire about redistribution rights, and on the assumption that I’ll be able to include this (or at least, that I’ll be able to find some way to minimize the pain of separately downloading the DLL), I’ll start working on this add-in now.

 

Join us again next time — same Bat time, same Bat channel!

Porting Word2MediaWikiPlus to VB.NET: Part 3

[This series has three previous articles: the prologue, Part 1 and Part 2.]

Digging into modWord2MediaWikiPlus

This is the motherlode, right?  Here’s where all the action happens — all the reformatting, text extraction and Wiki-izing.  Yes, this monster VBA module probably has the majority of the code I’ll be porting over to VSTO.

So here’s a canonical list of the Functions and Sub’s contained therein — the best way for me to get to know this beast is to strip it down, piece by piece:

  • MW_CloseProgramm()
  • Word2MediaWikiPlus_Config()
  • Sub Word2MediaWikiPlus_Upload()
  • MediaWikiConvert_CleanUp()
  • MediaWikiConvert_Comments()
  • MediaWikiConvert_EscapeChars()
  • MediaWikiConvert_Fields()
  • MediaWikiConvert_FontColors()
  • MediaWikiConvert_FootNotes()
  • MediaWikiConvert_FormFields()
  • MediaWikiConvert_Headings()
  • MediaWikiConvert_HTMLChars()
  • MediaWikiConvert_Hyperlinks()
  • MW_ImageInfoReset()*
  • MediaWikiExtract_Images()*
  • MediaWikiExtract_ImagesHtml()*
  • MediaWikiExtract_ImagesPhotoEditor()*
  • MediaWikiConvert_Indention()
  • MediaWikiConvert_IndentionTab()
  • MediaWikiConvert_Lists()
  • MediaWikiConvert_Paragraphs()
  • MediaWikiConvert_Prepare()
  • MediaWikiConvert_Tables()
  • MediaWikiConvert_TabTables()
  • MediaWikiConvert_TextFormat()
  • MediaWikiImageUpload()*
  • MediaWikiOpen()
  • MediaWikiReplaceQuotes()
  • MW_CheckFileName()
  • MW_CheckFileNameTitle()
  • MW_ClearFormatting()
  • MW_Convert_Table()
  • MW_ReplaceSpecialCharactersFirst()
  • MW_ReplaceCharacter()
  • MW_FindNormalWidth()
  • MW_FontFormat()
  • MW_FormatCategoryString()
  • MW_GetEditorPath()
  • MW_GetImageNameFromFile()*
  • MW_GetImagePath()*
  • MW_GetScaleIS()*
  • MW_GetUserLanguage()
  • MW_ImageExportPowerpointPNG()*
  • MW_ImageExtract()*
  • MW_ImageExtract2()*
  • MW_ImagePathName()*
  • MW_ImageUpload_File()*
  • MW_Initialize()
  • MW_InsertPageHeaders()
  • MW_LanguageTexts()
  • MW_PhotoEditor_Convert()*
  • MW_PowerpointQuit()
  • MW_ReplaceString()
  • MW_ScaleMax()*
  • MW_ScaleMaxOK()*
  • MW_SearchAddress()
  • MW_SetWikiAddressRoot()
  • MW_ChangeView()
  • MW_Statusbar()
  • MW_SurroundHeader()
  • MW_TableInfo()
  • MW_WordVersion()
  • GetRegValidate()
  • RemoveDir()
  • MediaWikiExtract_ImagesHtml2002()*
  • MakeDir()
  • TestSendMessage()
  • TestImageInfo()*
  • TestUnicode()
  • TestReadUnicode()
  • TestCopyDoc()
  • MW_SetOptions_2003()

My initial reaction

There’s a few things that stand out for me so far from this code module:

  1. Some functions are prefixed “MW_“, others “MediaWiki“, but only those with the “MediaWiki” prefix have the “copyright by Gunter Schmidt” notice.  I imagine the “MW_” functions are inherited from another codebase.  Just something to watch out for.
  2. There are references to Word 97 and Word 2000 in this code, but it occurs to me that VSTO probably doesn’t support anything less than Word XP or Word 2003.  I should check on that, and then I’ll know what code has to be cut out.
  3. There’s a ton of code that’s focused on migrating images from the source document to the target wiki page…including a bunch of SendKeys operations that I at first suspected couldn’t be easily implemented in .NET.  However, it looks like .NET implements this as the System.Windows.Forms.SendKeys class.
  4. I’m puzzled by the appearance of PowerPoint in the code — I wonder what it’s really being used for?
  5. There’s also a bunch of talk of using Microsoft Photo Editor (which seems to have died off at Office XP) — I wonder if .NET 2.0 has any image-manipulation classes that will relieve us of this dependency?  Photo Editor’s replacement (Picture Manager) doesn’t provide much in the way of editing functionality, and Microsoft’s other suggestion (Digital Image Pro) has recently been “de-hired” as well.
  6. There is a framework in this code for supporting a wide variety of languages, but in the worst case, it appears that only English and German are actually enabled.
  7. There are frequent instances of debug code in there, which I presume is just used as the equivalent to the built-in Breakpoint & exception handling in Visual Studio 2005.  I’ll likely drop it entirely, except where DEBUG or TRACE functionality would be useful.
  8. I’m also noticing a trend towards using the Registry to store a bunch of temporary settings that are only relevant to this Macro, not to Word or Windows in general.  I’ll likely convert this over to the XML Settings approach of .NET — there’s something that just seems “wrong” about using the Registry for such ephemeral data (except of course when there are no other options).
  9. I’m fascinated by the naming of all the objects in this code, and looking forward to making things a lot easier to understand.  All these cryptic variables, the prefixes that won’t be needed once the OO hierarchy is actually available to get the context for functions.  I’ll do my best to implement all the Microsoft guidelines for writing good .NET code – the more of others’ code I read, the more I appreciate those few who follow these guidelines.

Plan of attack

  1. I think I’m going to have to carve this down into releases, and not try to implement this all at once.
  2. I’m thinking that the image-manipulation code, while very sexy and useful, isn’t critical for the core use cases for a Word-based DOC-to-MediaWiki convertor.  [All those functions are labelled with a *.]  These are probably for “release 3”.
  3. The table-manipulation code seems more important, but still it’s probably not essential.  That seems like a “release 2” kind of thing.

Right now I’m feeling like I have a pretty good handle on what’s in store for me, and the “itch” to stop planning and start coding is getting to be too much to resist.  I think I’ll make a list of the top ten things I’ll want to start on, and then just get down to it.

Join us again — same Bat time, same Bat channel!

Porting Word2MediaWikiPlus to VB.NET: Part 2

[This series has two previous articles: the prologue and Part 1.]

Initial source code examination

So I figured I’d get right into it.  I opened up Word 2003, went into the Tools > Macro > Macros… menu, and thinking there’d be some obvious Import function, hit the Organizer button.  No such luck, so I tried again via Tools > Macro > Visual Basic Editor, muddled around for a few minutes, and eventually just imported all the files via the File > Import File… menu option.

I don’t know much about how VBA multi-file applications are organized, but I figured that the one listed under the Class Modules folder would likely be the right one to start — Classes are the basic building block for all OO programs, right?

I had a quick look through the Sub’s defined in Class Modules > ThisDocument1 (which must correspond to the ThisDocument.cls file), and thought, “heh, this’ll be easy — there’s only a couple hundred lines of code and a handful of Sub’s”:

  • cmdCopyModuls_Click()
  • CreateSymbol()
  • CreateSymbol2()
  • CopyModulesToNormal()
  • cmdSymbols_Click()
  • cmdUninstall_Click()

Ah, but wait: there’s _Click() routines in them thar hills…which means there’s Form UI to deal with, and now that I look more closely, those .BAS files ended up as a list of entries under the Modules folder.

modW2MWP_FileDialog

Yipes!  There’s a few more complicated questions to deal with than I’d originally thought.  For example, modW2MWP_FileDialog contains at least some code with a copyright heading, which could make life a bit tougher:

'***************** Code Start **************
'This code was originally written by Ken Getz.
'It is not to be altered or distributed, except as part of an application.
'You are free to use it in any application, provided the copyright notice is left unchanged.
'
' Code courtesy of:
' Microsoft Access 95 How-To
' Ken Getz and Paul Litwin
' Waite Group Press, 1996

This may not be so bad though, as the Functions in this module referring to file operations (e.g. GetOpenFile(), ahtCommonFileOpenSave())may be superfluous with the System.IO namespace available in VSTO.  We might not need these file I/O functions, though TestIt(), ahtAddFilterItem(), TriumNull() and TrimTrailingNull() may be needed.This brings up a really good point that I’ve seen mentioned in a couple of places, including John R. Durant’s blog from two years ago:

The real issue I see in migrating from VBA to VSTO is not the language or switching to a new runtime. […]  The more important factor is how the migration will affect the architecture of the application.  It is important to ask questions like: Does the new runtime make it possible for me to code this differently at a more fundamental level?  Can I cut lots of code? […]

This’ll be a fine line for me to walk: I’d like to make the conversion as quickly as possible, but it may not always be easy to figure out what the original code was meant to do.  It doesn’t help matters that many of the Comments in the source code are in German — as much as I’d like to think I can still understand German, it’s been sixteen years since I was in Germany and I’m more than a little rusty.  Hopefully the online German-to-English dictionaries will be able to sort out the gist of it.

modW2MWP_Registry

The module modW2MWP_Registry is almost entirely focused on Registry interactions (which are well represented by the Microsoft.Win32.Registry class) except for the Uninstall_Word2MediaWikiPlus() Sub.  However, any necessary code has a more appropriate home in the VSTO add-in’s ThisAddIn_Shutdown() Sub.

modWord2MediaWikiPlus

This “module” is a monster — it’s a wonder it hasn’t been broken down into a whole namespace of classes — or I suppose in the VBA world, the closest they have is “a set of Modules” [no hierarchy.]  And it’s the central code:

‘ Function: Converts a word document to the wiki syntax

Anyway, it has a great deal of functionality — I’ll dig into it later.  This’ll be the majority of the work here.

modWord2MediaWikiPlusGlobal

This module is much smaller than modWord2MediaWikiPlus, but has some interesting functionality as well.  After studying them for a bit, I think I’ve figured out what general category of purpose each of them has:

Filesystem functions

Process functions

Text functions

GetShortPath()
GetSpecialFolder()
DirExists()
DirExistsCreate()
FileExists()
GetFileName()
GetFilePath()
FormatPath()
KillToBin()
IExplorer()
AppActivatePlus()
fGetCaption()
GetWindowTitleHandle()
ReplaceStr()
RGB2HTML()
OleConvertColor()
AnonymizeText()
AnonymizeWords()

Oh, and a DisplayError() for good measure.

 

I think I’m ready to start digging through the code in modWord2MediaWikiPlus – wish me luck!

Join us again — same Bat time, same Bat channel!

Porting Word2MediaWikiPlus to VB.NET: Part 1

[This series of articles starts off with a prologue here.]

Initial Setup

First up, I’d downloaded the source code from here.  When I downloaded it the browser would only let me choose between htm, mht and txt format (I chose txt).  Then, figuring that my work would be easiest if opened the file in Visual Studio 2005 Team edition (with the VSTO SE addition), I needed to figure out what file extension I should assign to ensure Visual Studio recognized it for the kind of code that it is (and get as much of the Visual Studio Intellisense auto-completion and auto-colouring as possible).

I tried .BAS and .VBA, but neither of those extensions had any associated icons, and when I opened the .VBA, every line was displayed entirely in black.

Then, in exploring the filesystem to re-open the file, I noticed I’d created a C#–based project [since my VS2005 install is oriented to C# — having forced myself to write an app in it to prove to myself I wasn’t forever tainted by my earlier VB.NET experiences] instead of VB.NET, so I deleted that one and tried again (File > New > Project > Other languages > Visual Basic > Office > 2003 Add-ins > Word Add-in).  I chose Word 2003 because (a) that’s what I’m currently using, (b) that’s the platform I have a little experience with, and (c) it’s what most folks around the world would be using — at least, much moreso than Word 2007.  I chose to name the solution Word2MediaWiki++, to honour the fine work upon which I’m building.

[I’ve thought about whether this will become just one Project in a larger Solution, and I assume at some point there’ll be a need to rename either the Solution or this first Project to distinguish between this single piece of functionality and the overall Word-to-MediaWiki functionality.  It’s quite likely in fact, but I don’t want to overthink this or over-engineering the proceedings too badly – I have to remind myself that just getting the same functionality in VSTO add-in form is my primary goal.]

Quick Aside

I’ve done some reading about VBA to VSTO conversions in the past (while I was still at Microsoft), and I’d actually developed another Word 2003 add-in — from scratch — as a way to provide a platform for re-implementing a very comprehensive VBA macro-based application we were using.  So I already had some idea that this was possible, and that there are many resources (from Microsoft* and elsewhere) to help in making both the high-level transition and in converting many of the low-level VBA constructs.

A simple search on Google for “vba to vsto” comes up with 828,000 results, and many of them on the first page or two are good, first-person lessons.  However, the articles/white papers/blog posts I’d previously read that had given me the confidence I’m building on now include these:

Licensing

Oh, and before I actually go do any work on this code, I should double-check that the original author hasn’t put any restrictions on it (copyright, restrictive license, “all rights reserved”)…

… So according to the SourceForge home for W2MWP, this is licensed under the GPL.  It looks like I’m in the clear, so long as I publish this back out under GPL too – wouldn’t want to run afoul of the GPL police now would we?

 

Join us again – same Bat time, same Bat channel!

[*Footnote: My only gripe about Microsoft’s VSTO SE add-in work is that they’re so freakin’ focused on Office 2007 development that I have to do some serious detective work – either trolling through old VBA or pre-add-in documentation and hoping the classes are similar, or winding through the “everything is wonderful” Office 2007 documentation and hoping the stuff I need hasn’t been reinvented for the wondrous new world of Office 12.]