Do you Wiki? Word2MediaWiki.NET first release is live!

Snippy0001

I just wanted to let my faithful readers know that I have finally completed a beta version of the Word2MediaWiki.NET add-in for Microsoft Word 2003.

This’ll be useful to anyone that regularly contributes to Wikipedia, or those that have a bunch of Word documents that they’d like to share on Wikipedia.

It’s just as useful for any of the other hundreds of wikis that use the same server software as Wikipedia (known as MediaWiki), and it’ll be of interest to those folks who’ve used Word2MediaWikiPlus (from whose VBA source code I converted much of the basis of Word2MediaWiki.NET).

Grab a copy here:

http://www.codeplex.com/Word2MediaWikiDotNET

And please, let me know about any issues or experiences you have with it by posting a Discussion item here:

http://www.codeplex.com/Word2MediaWikiDotNET/Thread/List.aspx

Advertisements

Note to self: review these VSTO articles

[aside: I have to remember to review these articles for any tricks that’ll help me troubleshoot/improve the VBA-to-VSTO conversion I’m doing for Word2MediaWiki++…]

Migrating a VBA Solution to a Visual Studio Tools for Office Add-In

Migrating Word VBA Solutions to Visual Studio Tools for Office

Convert VBA Code to Visual Basic When Migrating to Visual Studio 2005 Tools for Office

John R. Durant’s Consolidated List of Word 2003 Developer Resources

…and as a catch-all:

VSTO Forum: Non-VSTO Question/Issue Resources

Porting Word2MediaWikiPlus to VB.NET: Part 14 (Mysteries Abound)

[Previous articles in this series: Prologue, Part 1, Part 2, Part 3, Part 4, Part 5, Part 6, Part 7, Part 8, Part 9, Part 10, Part 11 (The Return), Part 12 (Initialization continued), Part 13 (VBA Oddities).]

Mysterious Character: Vertical Tab (VT) — Do These Still Show Up in Word Documents?

In working through the code in MediaWikiConvert_Lists(), I ran across a block of code that purports to “replace manual page breaks”, and is using the Chr(11) construct to do so.  I must’ve been feeling extra-curious ’cause I went digging into what this means, and the harder I looked, the more puzzled I became.

According to ASCIITables.com, the character represented by decimal “11” is the so-called “vertical tab”.  I’ve never heard of this before (but then, there’s a whole host of ASCII & Unicode characters I’ve never paid attention to before), so I had to check with a half-dozen other references on the ‘net before I was sufficiently convinced that this wasn’t some “off-by-one” problem where the VBA coders were intending to look for Chr(10) (aka “line feed”) or Chr(12) (aka “form feed”).

On the assumption that we’re really and truly looking for “vertical tab”, I had to do some deep digging to figure out what this might actually represent in a Word document.  There’s the obligatory Wikipedia entry, which only said that “The vertical tab is  but is not allowed in SGML (including HTML) or XML 1.0.”.  Then I found this amusing reference to one of the Perl RFCs, which quotes Russ Allbery to say “The last time I used a vertical tab intentionally and for some productive purpose was about 1984.”.  [Sometimes these quotes get better with age…]

OK, so if the vertical tab is so undesirable and irrelevant, what could our VBA predecessors be thinking?  What is the intended purpose of looking for an ASCII character that is so unappreciated?

Mysterious Code Fragment: “If 1 = 2” – WTF?

I started to notice these odd little appendages growing out of some of the newer code in the VBA macro.  At first I figured there must be some special property of VBA that makes “If 1=2” a valid statement under some circumstances, and I just had to ferret out what that was.

Instead, the more I looked at it, the more puzzled I became.  What the hell could this possibly mean?  Under what circumstances would *any* logical programming language ever treat “If 1 = 2” as anything but a comparison of two absolute numbers, that will ALWAYS evaluate to False?

Eventually I had to find out what greater minds that mine thought about this, and so off to Google I go.  As you might expect, there’s not much direct evidence of any programming practices that include adding this “If 1 = 2” statement.  In fact, though it appears in the odd piece of code here and there, it’s surprisingly infrequent.  However, I finally ran across what I take to be the best lesson on what this really means (even if I had to unearth it through the infamous “Google cache”):

>>>Anyone know how to comment out a whole section in VBA rather than just
>>>line by line with a ” ‘ “?
>>
>>If the code is acceptable (won’t break because some control doesn’t
>>exist, etc), I sometimes to
>>
If 1 = 2 then
>> ….existing code
>> End If
>>
>>The code will never fire until the day 1 = 2.
>>
> Thanks, think Id prefer the first option. The second option might
> confuse any programmers that try and read my code.

Now that’s the understatement of the year.

So as far as I’m concerned, I’m going to go back and comment out any and all instances where I find this statement, as it tells me the original programmer didn’t want this code to fire, and was thinking of coming back to it someday after their last check-in.

Mysterious Approach: Localization via Macro?  No way.

There are a few routines that attempt to implement localization at runtime.  While this makes sense for VBA, this makes little if any sense for the use of VB.NET.  Any English-only strings can be substituted in the corresponding Resources file that will accompany this code.

Thus, the MW_LanguageTexts() routine will be skipped, since it had little if any effect anyway.

Mysterious Exception: “add-in could not be found or could not be loaded”

I’ve been struggling for a few days to try to actually run this add-in, and after finding out why, I can say with confidence that there was no good troubleshooting guide for this.

Here’s the setup:

  • I could Build the add-in just fine — no build-time errors, only two compiler warnings (about unused variables).
  • However, when I tried to either (a) Debug the project from within Visual Studio, or (b) add the add-in manually to Word, I was completely stymied.
  • When I started the Debug sequence (F5) from Visual Studio, it would launch Word 2003, which created all its default menus and toolbars, and then threw this error dialog:
    Office document customization is not available - An add-in could not be found or could not be loaded.
  • The details of this exception read:
  • Could not create an instance of startup object Word2MediaWiki__.ThisAddIn in assembly Word2MediaWikiPlusPlus, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1a75eafd9e81be84.

    ************** Exception Text **************
    Microsoft.VisualStudio.Tools.Applications.Runtime.CannotCreateStartupObjectException: Could not create an instance of startup object Word2MediaWiki__.ThisAddIn in assembly Word2MediaWikiPlusPlus, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1a75eafd9e81be84. —> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.NullReferenceException: Object reference not set to an instance of an object.
       at Word2MediaWiki__.Word2MediaWikiPlusPlus.Convert..ctor() in C:\VS2005 Projects\Word2MediaWiki++\Word2MediaWiki++\Convert.vb:line 44
       at Word2MediaWiki__.ThisAddIn..ctor(IRuntimeServiceProvider RuntimeCallback) in C:\VS2005 Projects\Word2MediaWiki++\Word2MediaWiki++\ThisAddIn.vb:line 29
       — End of inner exception stack trace —

  • If I tried to load the add-in from within Word (using the Tools > COM Add-ins… menu — which you can add with these instructions), Word would only tell me:
  • Load Behavior: Not loaded. A runtime error occurred during the loading of the COM Add-in.

    I won’t even bore you with the details of all the stuff I tried to do to debug this issue.   It turned out that I was instantiating my Application object too early in the code (at least, the way I’d constructed it).

    Broken Code

    ThisAddin.vb (relevant chunk)

    Imports Office = Microsoft.Office.Core
    Imports Word2MediaWiki__.Word2MediaWikiPlusPlus.Convert
    
    Public Class ThisAddIn
    
    #Region " Variables "
    
        Private W2MWPPBar As Office.CommandBar
        WithEvents uiConvert As Office.CommandBarButton
        WithEvents uiUpload As Office.CommandBarButton
        WithEvents uiConfig As Office.CommandBarButton
    
        Dim DocumentConversion As Word2MediaWikiPlusPlus.Convert = New Word2MediaWikiPlusPlus.Convert ' Line 29
    
    #End Region

    Convert.vb (relevant chunk)

    Imports Word = Microsoft.Office.Interop.Word
    
    Namespace Word2MediaWikiPlusPlus
    
    Public Class Convert
    
    #Region "Variables"
            Dim App As Word.Application = Globals.ThisAddIn.Application 'PROBLEM - Line 44
            Dim Doc As Word.Document = App.ActiveDocument 'PROBLEM
    #End Region
    #Region "Public Subs"
            Public Sub InitializeActiveDocument()
    
                If Doc Is Nothing Then
                    Exit Sub
                End If
            End Sub

    #End Region

    #Region “Public Subs”

    Fixed Code

    Convert.vb (relevant chunk)

    Imports Word = Microsoft.Office.Interop.Word
    
    Namespace Word2MediaWikiPlusPlus
    
    Public Class Convert
    
    #Region "Variables"
    
            Dim App As Word.Application 'FIXED 
            Dim Doc As Word.Document 'FIXED 
    
    #End Region
    
    #Region "Public Subs"
            Public Sub InitializeActiveDocument()
    
                App = Globals.ThisAddIn.Application 'NEW
                Doc = App.ActiveDocument 'NEW
    
                If Doc Is Nothing Then
                    Exit Sub
                End If
    
            End Sub
    
    #End Region

    What I Think Went Wrong

    As much as I understand of this, it seems like when the ThisAddIn class tries to create a new instance of the Convert class as a DocumentConversion object, the ThisAddIn object hasn’t been instantiated yet, so the reference in the Convert class to Globals.ThisAddIn.Application can’t be resolved (how can you get the ThisAddin.Application object if its parent object — ThisAddIn — doesn’t exist yet?) causes the NullReferenceException that is the heart of the problem.

    By pulling out that instantiation code from the App variable declaration, and delaying it instead to one of the Convert class’s Subs, there was no need for the managed code to “chase its tail” — trying to resolve an object reference back through the calling code, which hadn’t been instantiated yet.

    Y’know, I’m sure I read somewhere over the last year that combining the declaration with the instantiation of a variable is bound to lead to subtle debugging issues, but man.  Losing three days to this?  What a disaster.

    Lesson for the day: It never pays to take shortcuts.

    Porting Word2MediaWikiPlus to VB.NET: Part 13 (VBA Oddities)

    [Previous articles in this series: Prologue, Part 1, Part 2, Part 3, Part 4, Part 5, Part 6, Part 7, Part 8, Part 9, Part 10, Part 11 (The Return), Part 12 (Initialization continued).]

    How to convert the VBA String() Function?

    There’s a more-complicated-than-it-probably-needs-to-be subroutine in the Word2MediaWikiPlus codebase — called MW_SurroundHeader() — that seems to only be there to cleanup and reformat text in a Word document that has one of the Headings styles.  It uses a function from VBA called simply String(), which is one of the first cases of a VBA function for which I cannot find an equivalent in VB.NET.

    It turns out I found out what I needed from an oreilly.com article, and after running into a few brick walls in looking for a reference to this in MSDN, I started a more intelligent search.  I kept coming back to references to the String Data Type, so I next looked at the “Strings in Visual Basic” topic that was referenced by “For more information on string manipulation…”.  From there the next most logical leap was to “Building Strings in Visual Basic“, which led to “How to: Create Strings Using a StringBuilder in Visual Basic“.

    Once there, I figured that since this was so helpful to me, I’d like to save someone the trouble next time so I added a little of that “Community Content” sauce that I myself appreciate so much.

    Converting the Selection Object from VBA?

    The MW_FontFormat() subroutine also uses a no-longer-supported VBA-ism, the Selection object.  This isn’t all that well documented online either — or at least, I wasn’t able to find anything useful online to help figure out how to translate this into VB.NET.  The best I could find was a mention that the Range object in VB shares some common methods & properties with the Selection object in VBA.

    However, I happened to have a copy of an old book called the Microsoft Office XP Developer’s Guide, which was surprisingly results-oriented for an MSPress book.  Pages 176-177 actually discuss “The Selection Object vs. the Range Object”, in which I am told that the Range object is actually superior to the Selection object, and should always be favoured wherever possible.

    I’m not feeling up to the subtleties of Selection vs. Range right now, so I’ll leave this for another time.

    Converting the Font Colour to HTML-compatible values?

    This is another interesting puzzler… It seems that MediaWikiConvert_FontColors() calls RGB2HTML(), which calls OleConvertColor(), which calls OleTranslateColor(), which is a p/invoke to OLEAUT32.DLL.  [Man, this is starting to read like a book of the Old Testament…]

    I have a really strong gut instinct that there’s a managed code equivalent to this that will make the intended conversion in one step, and I intend to find it.  There’s no good reason at this point to (a) have this many calls going on the stack, just to get access to a “simple” math function, or (b) to preserve an unmanaged call just because it’s been used all the way up to now.

    I can think of at least three ways to try to find the managed class I’m after: search on OleTranslateColor, search on “RGB & HTML”, or start browsing books on managed web development.

    According to this “Format Color for HTML” article, the call to OleTranslateColor is only necessary in cases where you’re using “system color constants” or “palette indices”.  Since we’re getting very predictable input here that doesn’t appear to be using either of these two alternatives, right away we should be able to eliminate the unmanaged code.

    That is, if I’m reading this right, then I should just be able to remove OleConvertColor() from the initial call in RGB2HTML() and leave the first line of code as

    nRGBHex = Right("000000" & Hex(rgbColor), 6)

    However, upon double-checking, it seems that other code blocks on the VBA macro are passing in some of the Word.WdColor enumeration constants — which I assume are equivalent to “system color constants”.

    Rather than have the RGB2HTML() routine always thunk down to unmanaged code, it’d be smarter if we checked whether the color value of interest is a member of the Word.WdColor enumeration.  But do the routines that generate the input parameter to RGB2HTML() generate either Long or WdColor values?  Or alternatively, would the code implicitly convert from WdColor to Long as the RGB2HTML() routine initialized?  I didn’t notice any overloaded instances of RGB2HTML() that took the input parameter as a WdColor value, so I have to assume that no matter what goes on outside this routine, all operations inside RGB2HTML() will only operate on colors of type Long.

    If that assumption is correct, then we should be able to safely ignore the possibility that the input parameter may start out as a WdColor datatype, and that means we can safely eliminate the OleConvertColor() and OleTranslateColor() routines.  [For the moment, having already had to dig them back up once, I’ll just comment them out and leave myself a note to delete them once I’ve had time to test these colour conversions and confirm this assumption is true.]

    Colours in VBA vs. Colours in .NET

    A more interesting question, however, is whether we’re losing colour fidelity in the conversions being performed here.  According to VSTO For Mere Mortals, Chapter 4, “In VBA, colors are of type Long, and there are eight constants that can be used… In Visual Studio 2005, colors are of type Color, and there are more than 100 choices”.

    Is it possible that the calls being used to derive the colours from the Active document are limited to the VBA colour constants, and that I should be looking to switch to other calls that return the .NET Color constants?  I’ll just add this as another Task to the CodePlex project list, and deal with it later — it seems to me like this is hardly the biggest problem facing this Addin at the moment.

    Porting Word2MediaWikiPlus to VB.NET: Part 12 (initialization continued…)

    [Previous articles in this series: Prologue, Part 1, Part 2, Part 3, Part 4, Part 5, Part 6, Part 7, Part 8, Part 9, Part 10, Part 11.]

    MW_Initialize()

    Much of this function seems to repeat the actions taken in Word2MediaWikiPlus(), so it’s a bit weird to see it done here as well (since this function is called explicitly by the other).  While some of it can be immediately discarded, other bits have to be examined more closely – mostly because they’re poorly documented (at least at the point from which they’re being called):

    • Again we have an ImagePath enumeration and/or creation
      • There’s an interesting new function I haven’t seen before: IIf(x,y,z)
        • VBA For Dummies tells me it does “test for ‘x’; if true, do ‘y’; if false, do ‘z'”.  Fairly tidy little function there.
      • Looking deeper into what’s going on here, the macro is assigning the ImagePath setting to a folder named “wiki” under the user’s My Pictures folder
      • This doesn’t make a lot of sense for a folder of temporary files that are deleted at the end of the session (or before the beginning of the next)
      • Therefore I’m going to make two changes:
        • this folder will be created as a subfolder of the user’s %TEMP% location
        • this folder will not only be emptied at the beginning of a session, but (as a good citizen of the computer) it will also empty its contents once it has completed a conversion
    • Again we have an EditorPath enumeration
      • it appears that the only path being set is the Microsoft Photo Editor (which we’ve previously confirmed is no longer available)
      • Is there any way to actually perform the image manipulation to which so much code has been devoted?

    The more I look at this image extraction code, the more complicated it gets.  At this point I’ve pretty much determined that, for all the effort it’ll cost to implement these image features, it’s just not worth the trouble in v1.  I’ll continue to add TODO: comments to the VSTO add-in to show where the image code will eventually go, but I’m not going to do any further work to understand the image code until the rest of the Add-in is working.

    Finally, there are the control characters that are being assigned (^l, ^m, ^p, ^s).  They’re not documented in the code, and I’m having a hard time finding any documentation that discusses the use of these control characters.  It doesn’t help that Google and MSDN Search don’t seem to allow you to search on “^p” — it seems they treat this as either “p” or “<p".

    I believe I could treat these as global constants in the Convert class, but what isn’t clear is whether these control characters are:

    1. special substitutions in Word, and will get converted to the native Word paragraph/new line/blank/page break code (in which case I should just use the native VSTO/VBA enumerations), or
    2. treated by Word as ASCII text and sent to the Wiki server, which converts them to HTML when displaying the resulting article (in which case I should probably make sure there isn’t a better way to represent these in MediaWiki format).

    Aha!  After trying over & over, I finally came up with a search in Microsoft’s Knowledge Base that gave me an article talking about the “^p” (which it calls a “paragraph mark”):

    WD2000: Text Converted to One-Row Table (Paragraph Marks Ignored)

    These appear to be ancient character sequences (as early as Word 1.0), so I’m going to first try using the native Word enumerations for these character strings wherever possible.  If I have to go back to using these character sequences, then I’ll drop them back in to the Convert class as Constants.

    Aside: today I stumbled on an invaluable reference: the Microsoft Word Visual Basic Reference online.  This implies it’s an authoritative reference for all VBA available in Microsoft Word.  Should prove useful.

    MediaWikiConvert_Prepare

    From what I can tell by a single read through this routine’s code, this all appears to affect the ActiveDocument.  That means all this code can go into the InitializeActiveDocument() subroutine (which I’ve conveniently already defined).

    • MW_SetOptions_2003() is just caching the Application.Options.SmartParaSelection value and then returning it once conversion is complete.  This can be handled as with the other cached settings.
    • I don’t understand this code fragment at all:
          'Now, if we might have some problems, if we are in a table
          pg.Range.Select
          If Selection.Information(wdWithInTable) Then Selection.SplitTable

    • If a variable like convertPageHeaders was always False (as I can’t find anything that sets it True), then why would such a huge block of code be hidden inside this code block:
      If GetReg("convertPageHeaders")... EndIf
      It's just hard to guess what the programmer's intentions were with a never (rarely?) called piece of code.
    • Then there’s a lot of boring code conversion, where I’m just giving methods and variables more meaningful names, adding appropriate prefixes to all the Word enums being used, and just commenting the crap out of things where I don’t have a clue how to fix some weird or cryptic code routines.

    Reference to a non-shared member requires an object reference

    The most interesting thing I’ve had to research so far was the problem I created for myself by implementing the code into two classes (so far).  I finally got around to calling the Convert class’ public methods in the ThisAddin class’ uiConvert_Click() handler.  As the naive little programmer that I am, I of course first tried to just set the Imports statement at the top of the ThisAddin class, and then call the public methods “naked” like so:

            InitializeActiveDocument()
    
            InitializeConversion()
    
            PerformConversion()

    Of course that didn’t work, but I didn’t know why at the time.  Instead, I scratched my head for quite a while over how to handle the compiler warning “Error 232: Reference to a non-shared member requires an object reference“.

    I’ve run up against this before, and I’m pretty sure I was lured at the time down the path to hell: I started adding Shared declarations all over the place.  It’s really tempting — when the IDE implies you should try an easy fix like this, it’s hard to know why this should be bad.  “Didn’t the IDE’s developers know what they were doing?”  “Why would they lead morons like me astray?

    Unfortunately, this is akin to tugging at that first loose strand of a nice wool sweater: pretty soon I’d added so many additional Shared declarations that I’m sure the code was wide open to all sorts of future, stealthy issues I have no idea about.

    This time around, once I saw that one Shared begat yet another implied request to add another Shared declaration, I stopped and did some further digging around.  While I wasn’t able to find any articles or MSDN docs that really spelled it out for me, I think I figured out a worthy approach on my own.  [This forum thread was as good as any.]

    I’ve published the following as Community Content to the “Error 232” page on MSDN.

    Avoid adding the Shared keyword

    While this error message tempts the inexperienced programmer with the “easy” solution of just adding the Shared keyword to the requested Method, I advise strongly against it.  Unfortunately there’s little documentation or advice out there aimed at the programmers like me who don’t really understand the problems they’ve created, nor the trade-offs in the possible solutions being (cryptically) recommended.  Hopefully this’ll help out other folks like myself avoid the really nasty mistake I’ve already made a few times.

    The trouble with adding the Shared keyword to a second Class’ Method is that it rarely stops there.  Once you’ve shared a method, whether Public, Private or otherwise, many of that method’s members will also need adjustments.  At least in my experience, the first Shared keyword will work as well as cutting off the Hydra’s head: it usually leads to one or more instances of the error “Error 227: Cannot refer to an instance member of a class from within a shared method or shared member initializer without an explicit instance of the class.”  The first time I tried to kill this Hydra, I had tried to rewrite a bunch of code, and ended up with a rat’s nest of Shared keywords scattered everywhere.

    A Better Approach than Adding the Shared Keyword

    As the advice on this page (cryptically) recommends, try creating an instance of the class.  The big fear that initially scared me off was that I’d end up either (a) unknowingly creating and destroying tons of unnecessary instances of that Class as objects, or (b) not understanding when the object I’d created fell out of scope (and would creep up on me with unpredictable garbage collection-derived errors).

    What I did to alleviate this issue was to declare a “class-level” variable in the calling class of the type of the class being called, and then use that variable as the root of all subsequent uses of the called class’ methods.

    This example should illustrate:

    Public Class BusinessLogic   ' This is the "called" class
        Public Sub PerformAction()
            Action()
        End Sub
        Private Sub Action()
                ...
        End Sub
    End Class
    
    Public Class UserInterface   ' This is the "calling" class
      Imports BusinessLogic  ' Doesn't help with Error 232, and may not be necessary at all
        Dim documentLogic As New BusinessLogic ' class-level variable 
        Private Sub uiButton_Click(ByVal Ctrl As Microsoft.Office.Core.CommandBarButton, ByRef CancelDefault As Boolean) Handles uiButton.Click
            PerformAction()  ' Causes Error 232
            documentLogic.PerformAction() ' This call is OK
        End Sub
        ...
    End Class
    

    Y’know, sometimes I’m just documenting this stuff for myself, since I know that in a few weeks’ time I’ll have completely forgotten the solution and the logic behind it.  The rest of you happen to be benefiting from my lack of memory, and I wish I could say I was being completely selfless, but I’m getting too old to be lying to folks I never even met. 🙂

    Porting Word2MediaWikiPlus to VB.NET: Part 11 (The Return)

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

    After a few weeks’ hiatus to work on some other projects (including a couple of releases of CacheMyWork — now with more filtering!), I decided to come back to the W2MWPP effort.  And my overwhelming feeling right now is: thank god I was blogging as I coded!  As it is, it took me probably a full hour to figure out where to focus my attention next:

    • while the Configuration dialog still isn’t finished, I’m going to set that aside for now — it should be pretty easy to figure out what I need to add, especially once I know better which functionality is or isn’t required.
    • I’m going to start into the fundamentals of the code that’ll be called when the “Convert to Wiki” button is clicked.  This code will be called by ThisAddIn.uiConvert_Click().
    • The code in question will be derived from the VBA project’s modWord2MediaWikiPlus.bas file, from the Public Sub Word2MediaWikiPlus() routine.

    Sub Word2MediaWikiPlus(): Overview

    There are many types of constructs in this library, many of which I’m sure will be called as part of the base Word2MediaWikiPlus() routine.  However, the routine itself is only about 150 lines of code, so in itself the routine shouldn’t be too difficult to implement.

    However, it’s probably a good idea to familiarize myself with some of the constructs in this library:

    • There are three unmanaged code Functions — two for MessageBox construction, and one called SendMessage.  I’m sure I’ll understand what they’re supposed to be for once I see them in context, and likely I can use some very simple managed code Methods in their place.
    • There are Constants both private and public.  [Thankfully, most of these seem to have been reasonably well documented.]
      • Some, like WMPVersion, will be taken care of by Visual Studio.
      • Others, like WikiOpenPage, should be implemented as configuration settings not code constants.
      • The majority, such as NewParagraphWithBR, will likely be preserved as stylistic choices whose usage choices I may not personally agree with, but likely are things that have been requested over the years and which if I dropped them, would probably piss off a whole bunch of folks who’ve grown to know and love the VBA macro.
    • There are plenty of Variables as well of course, and while the usage of the majority will likely become self-evident, there are a few (e.g. Word97) that I can probably safely drop.
    • There are some constructs labelled as Types as well here — I’m not sure, but they resemble the use of Structures in managed VB, and I’ll likely see them in heavy use.

    At this point, I’m going to implement these constructs as needed.  I’d rather not add all these up front, ’cause (1) it’ll be pretty confusing for me, and (2) there’s likely some code that is no longer needed (or even has been forgotten).

    Sub Word2MediaWikiPlus(): Initialize ActiveDocument

    Even before generating the first set of routines, I figured I should do what I could to setup the Class Library with a little forethought:

    • Added a new Class to the Word2MediaWiki++ project called “Convert.vb”
    • Wanted to add this class to a global W2MWPP namespace (see Part 4 for details)
    • I once tried to add a namespace designation to a project after I’d already started coding it, and it was a disaster — references broke everywhere, and I’m not sure I ever got it cleaned up
    • Not knowing enough about namespace and class naming, I flipped through a couple of the books I had on hand and determined that this should be trivial to do up front
    • I wrapped the Public Class…End Class statements with Namespace Word2MediaWikiPlusPlus…End Namespace statements, and proceeded onwards

    The first sixty lines or so of code in Word2MediaWikiPlus() all have to do with acquiring a handle to an active document.  In the world of Document add-ins, I presume this can be very tricky, since the code itself starts from a single document’s context and has to navigate outwards from there.  However, when we’re working with VSTO Application add-ins, it seems fairly easy to me to get access to an active document.

    In fact, for the purposes of this tool, I’m going to assume that the user wants to convert whichever Word document is the active document.  The only error condition I should need to check is that there is at least one document object open.  This leads to the following code:

                Dim App as Word.Application = Globals.ThisAddIn.Application
                Dim Doc as Word.Document = App.ActiveDocument
    
                If Doc Is Nothing
                    'When there's no active document open just return back to Word
                    Exit Sub
                End If
    

    The only code left in the document initialization block is this:

        DocInfo.DocName = ActiveDocument.Name
        DocInfo.DocNameNoExt = DocInfo.DocName
        p = InStrRev(DocInfo.DocName, ".")
        If p > 0 Then DocInfo.DocNameNoExt = Left$(DocInfo.DocName, p - 1)
    

    I was baffled by this, so a quick trip to MSDN Library cleared it up.  The macro appears to be trying to parse out the document Name without the file extension.  DocInfo itself is an instance of the DocInfoType Type, which is just a structure to store various properties of the document.  There’s no particular reason I can see so far to use a structure for the DocName & DocNameNoExt properties, and the other properties to me don’t seem particularly related to the document itself.  At this point, I’ll assume DocInfo isn’t needed.  [Certainly my searches of the modWord2MediaWikiPlus.bas source only found two references to the DocInfo.DocNameNoExt property: once to assist the MW_GetImagePath() function, and once to set DocInfo.Articlename.  Both should be doable without this Structure, since .NET provides a Path.GetFileNameWithoutExtension function.]

    However, this leads to the MW_Initialize() function, which I would guess also should be part of the Convert class’ initialization code.  I’ll check that out soon.

    The last of the code in this section is the call to MW_LanguageTexts().  This is a localization macro, that will set a series of Registry values and Msg_* variables depending on a language setting retrieved from the Registry.  All this can be managed quite well using Resource files, so there’s no need to mess with setting all this via code at the moment.

    I’m interested in knowing whether all the Registry settings being used by this VBA project are for localization, so let’s enumerate them all…

    Registry value Purpose
    AllowWiki  
    CategoryArticle  
    CategoryImagePreFix  
    CategoryImages  
    CategoryImagesUse  
    ClickChartText localization
    convertFontSize  
    convertPageFooters  
    convertPageHeaders  
    deleteHiddenChars  
    EditorKeyLoadPic localization
    EditorKeyPastePicAsNew localization
    EditorKeySavePic localization
    EditorPaletteKey localization
    ImageConvertCheckFileExists  
    ImageConverter  
    ImageDescription  
    ImageExtraction  
    ImageExtractionPE  
    ImageMaxPixelSize  
    ImageMaxWidth  
    ImageNamePreFix  
    ImagePastePixel  
    ImagePath  
    ImagePixelSize  
    ImageReload  
    ImageUploadAuto  
    ImageUploadTabToFileName  
    InsertTitlePageIfNeeded  
    isCustomized  
    Language  
    ListNumbersManual  
    OptionSmartParaSelection  
    PauseUploadAfterXImages  
    txt_Footnote localization
    txt_PageFooter localization
    txt_PageHeader localization
    txt_TitlePage localization
    UnableToConvertMarker localization
    UsePowerPoint  
    WikiAddressRoot  
    WikiAddressRootTest  
    WikiCategoryKeyWord localization
    WikiSearchTitle localization
    WikiSystem  
    WikiUploadTitle localization
    Z_finished  

    Wow, what a…symphony of Registry settings here, only a handful of which are directly used for localization.  There’s quite a number of them used for various Image manipulation operations, and others for various application settings etc.  Generally, from what I can tell, there’s no reason these too can’t be stored in the project’s app.config file.

    There’s the Msg_* variables as well – are all these devoted to localization as well?

    Variable name Purpose
    Msg_CloseAll localization
    Msg_Finished localization
    Msg_LoadDocument localization
    Msg_NoDocumentLoaded localization
    Msg_Upload_Info localization

    Yes, that’s all of them.

    Further Config and Init

    The final bit of code I’ll deal with today is the “user dialog on config settings”.  It appears that this calls some additional initialization code:

    1. performs customization if never performed
      • opens the frmW2MWP_Config form
      • sets ImageExtractionPE Registry setting
      • sets the variables EditorPath, OptionHtml, OptionPhotoEditor, OptionPhotoEditor.Enabled
      • sets language settings
      • sets any configured customization settings from the Config form (aka the VSTO UI Config dialog)
    2. sets Article name
    3. sets convertImagesOnly = False
      • this variable is used in Word2MediaWikiPlus() to determine whether to convert the whole document or only the embedded images.
      • Since this is apparently never set to True, I’ll remove the references.
    4. checks Word version and sets some control characters
      • It doesn’t appear that we’ll need this, as VSTO only seems to support Word 2003 and above
    5. sets Image Path
    6. sets the image editor path
    7. caches the value of a couple of Word’s Tools, Options settings
      • I’ve commented in these commands in case they’re necessary later
      • However, I assume there are better ways of preserving Word’s initial configuration than caching before changes and then flushing after changes
      • That is, it should be possible to make per-session changes to such settings, without having to make sure that Word’s settings are explictly reconfigured after the session has completed

     

    That’s enough for one sitting, eh?

    Porting Word2MediaWikiPlus to VB.NET: Part 5

    [Note: I finally found this article in the Google search cache, so while it’s posted out of order, at least it’s finally available again.]
    [This series has five previous articles: the prologue, Part 1, Part 2, Part 3 and Part 4.]

    Code Kick-off

    When you create an Office Add-in Project with VS2005 + VSTO, it automatically generates two Sub’s in the ThisAddIn class: ThisAddIn_Startup() and ThisAddIn_Shutdown(). Now I recall using these before for some basic background behaviour, but I have a feeling there are a few other Events in which it’s recommended to put the kind of code that’s usually used for setup, tear-down, opening persistent connections & closing them, etc.

    However, the only unused Events that I can see when browsing the Method Name drop-down are New, Finalize, IsCached, StartCaching and StopCaching. Nothing really compelling so far, and none of those ring any bells. Onwards.

    My plan is to work from the Word UI inwards, so that (a) if the AutoWikiBrowser folks aren’t able to grant redistribution rights for this project I’m less likely to be committed to links to their code, and (b) I’ll have more time to absorb the innards of the Word2MediaWikiPlus Macro.

    Thus, I figure I’ll start with two easy tasks:

    1. Port any necessary code from ThisDocument.cls to ThisAddin.vb (since ThisDocument.cls is my best guess for the code closest to the Word UI)
    2. Drop in the code for instantiating the CommandBar, CommandBarButton(s) and any related bits o’ code.

    ThisDocument.cls

    • cmdCopyModuls_Click(): as it says, it copies code to Normal.dot. IIRC, VSTO Application Add-ins don’t need to link themselves into Normal.dot, so I’m going to skip this code for now.
    • CreateSymbol(): It’s not clear what they meant by “symbol” here. Do they mean “toolbar button”? Debug symbols? I suspect it refers to toolbar (aka CommandBar), as that’s what the code seems to suggest. However, before dismissing this entirely, I needed to understand what Application.OrganizerCopy does. It’s listed among the Word Interop methods, and in the Word 2003 VBA Language Reference it’s described as “Copies the specified AutoText entry, toolbar, style, or macro project item from the source document or template to the destination document or template.” It appears the Macro is copying the object from Normal.dot to the current document, so this Sub can safely be ignored.
    • CreateSymbol2(): This also instantiates the toolbar, but it’s closer to what I need in the Startup() method. I like the organization of buttons and text for the buttons so far, and there’s only three to create: Convert To Wiki, Upload Images and Configure Word2MediaWikiPlus. I’ll tuck that away for when I load in my CommandBar code.
    • CopyModulesToNormal(): once again there are references to NormalTemplate and Macros. It implements an “update” routine, to copy code to Normal.dot; in the VSTO application add-in world, however, this is unnecessary.
    • cmdSymbols_Click(): calls one method, CreateSymbol(). Since that routine is unneeded, this method (and the UI Button that calls it) can safely be skipped.
    • cmdUninstall_Click(): calls one method, Uninstall_Word2MediaWikiPlus(). Wherever that method is, it’s hardly necessary when this VSTO add-in will have its own installer and uninstall routine tied to Add/Remove Programs.

    Wow, that was a little too easy. The rest of the “port” will get harder.

    Creating the CommandBar

    I already know that this code should be factored out of the Startup() method, but it’s easier to refactor than create the code and link it back. I suspected there’d be a Code Snippet for instantiating a CommandBar, so I started browsing through the Code Snippet hierarchy (Edit menu > Intellisense > Insert Snippet…) starting with Office Development > Office > Environment – Menus, Action Panes and chose “Create a Command Bar“.

    It took me a while to figure out what the “CommandBarHost” replacement should be in

    Dim commandBarsCollection1 As Office.CommandBars = DirectCast(commandBarHost.CommandBars, Office.CommandBars)

    It kept telling me to “Replace with a reference to the Excel Application, Word Application or a Word Document.” I kept thinking I’d need to find some Object buried four levels deep in the namespaces, but it turns out all I needed was

    Dim commandBarsCollection As Office.CommandBars = DirectCast(Application.CommandBars, Office.CommandBars)

    [Someday I really hope I understand all this Cast nonsense – I’m sure there’s a very good reason why all this has to be done, but boy does it feel like a primitive hack on what is otherwise a pretty elegant language.]

    Then the other lesson for the evening kicked in: I learned that my favoured variable naming convention – using “Word2MediaWiki++” as a common prefix wouldn’t work – Visual Studio at times tried to insert a “(” character before the plus signs, and otherwise told me that “Method arguments must be enclosed in parentheses”. Grrr, W2MWPP it is.

    In trying to understand what the Set keyword was supposed to accomplish in VBA, I ran across a really strong article in MSDN that I’ve been looking for all weekend: Convert VBA Code to Visual Basic When Migrating to Visual Studio 2005 Tools for Office. Among the many, many tips on resolving specific code conversion issues is an explanation that indicates the Set keyword can simply be removed, leaving the remaining VBA intact.

    A few Dim keywords here & there, a few more WithEnd With statements, and some string substitution to update to the name of this add-in, and we’re almost done. One last thing: what is this DoEvents method that’s called just after the toolbar is made visible? I’ve searched through all the Macro code and see it repeated dozens of times throughout, but no definition for it. Is it a built-in VBA method? Back to the “Convert VBA Code…” article it seems.

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

    Porting Word2MediaWikiPlus to VB.NET: Part 10

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

    CustomizationContext vs. Normal.dot

    I just experienced another warning that Normal.dot was updated, and did I want to save it?  That tells me that the CustomizationContext is needed.  I’ve implemented a custom Word Template called “W2MWPPTemplate.dot” and the minimal code that I believe should cover this requirement.  [This code was copied from a previous VSTO project of mine.]

    Next code library: modW2MWP_FileDialog.bas

    • Function TestIt() calls the custom function ahtCommonFileOpenSave(), and according to the code comments TestIt() is there just as an example
    • Function ahtCommonFileOpenSave() calls either aht_apiGetOpenFileName() or aht_apiGetSaveFileName(), which both appear to just be performing string functions — nothing interesting there
    • Function GetOpenFile() calls ahtCommonFileOpenSave() as well
    • The functions ahtAddFilterItem(), TrimNull() and TrimTrailingNull() are all just minor string manipulations

    The most interesting thing about this file’s code is that it defines a few FileOpen filters, for Access, dBASE, Text and All (*.*).  I don’t know why this application would open or save from these specific file types, which leads me to wonder if this code is even in use.  It’s possible it was left behind from some experiments.  In any case, I’ll keep an eye out for something calling this code, but I’ll likely just use native System.IO methods once I find the caller.

    Next up: frmW2MWP_Config.frm

    If I remember correctly, the typical way to add a dialog box to a VSTO add-in is using a Windows Form.  I added a Windows Form to the project and named it W2MWPP UI Config.vb.  However, trying to infer what controls were on the VBA version of this form isn’t as easy as I thought it’d be from the code.  However, I’ll see what I can do.

    First up I added the New() function to the form’s code-behind using the drop-downs along the top of the editing panel in VS2005.  In the New() method I’m adding functionality currently contained in UserForm_Initialize().  From what I’m seeing in this function, and what I’ve seen in the “old” documentation page, there are:

    1. a Registry setting for the Editor path/ImageExtractionPE (which appears to be unused now since the MS Photo Editor is no longer supported)
    2. a Registry setting and combo box (cboLanguage) for a Language setting (stored in a languageArr() variable)
    3. a Registry setting and textbox (txtURLTest) for the “Wiki Address Root” used for testing
    4. a Registry setting and textbox (txtURLProd) for the “Wiki Address Root” used for ‘production’
    5. a Registry setting and textbox (txtImagePath) for the filesystem folder path used to store extracted images
    6. a Registry setting and textbox (txtTabtoFileName) — though a NumericUpDown control would be better — to count the number of Tab characters needed to edit the Filename when automatically uploading image files
      • There also is a “Simulate upload” function documented here
    7. some sort of option to enable/disable “allow articles without category” is documented, but doesn’t seem to have an initialization state
    8. some sort of control to link to the Project’s online help pages

    I’ll add the necessary controls to the Config form for (2) through (7).  However, since the Word2WikiPlus VBA project doesn’t support the MS Photo Editor any longer, I’ll skip (1).  As well, I’ll try using tooltips & status bar messages to provide help on what to do with each configuration control.

    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

    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…