Click here to Skip to main content
15,881,559 members
Articles / Desktop Programming / MFC
Article

WinDiff Visual Studio Add-in

Rate me:
Please Sign up or sign in to vote.
3.82/5 (7 votes)
1 Jan 20024 min read 101.7K   1.5K   34   5
A Visual Studio Add-in that allows the current file, or it's containing folder to be compared using WinDiff

Sample Image - windiffaddin.gif

Introduction

Like so many ideas I was inspired by someone else's work to develop this. The someone else in this case was Chris himself and the article in particular was his (and Daniel Lyons') WinDiff GUI front-end. I was very impressed and found it very useful compared to the native front-end on WinDiff. However, invariably I find myself needing to compare a file I'm working on in Visual Studio, or it's containing folder, so I thougth to myself "Wouldn't it be really useful if there was an add-in that allowed me to do this."

Well, the usual looking around at the CodeProject and wading through the MSDN didn't produce anything so I decided to do it myself. Now, I haven't had any exposure to COM development previously so I started off with the DevStudio Addin Wizard and then delved into the source for Chris' app for the WinDiff stuff and the source for Oz Solomonovich's excellent WndTabs add-in.

Four Steps to the Add-in

1. The DevStudio Add-in Wizard

The first thing to do was to generate the project using the Wizard. This generates loads of code, which to a COM newbie looked pretty weird, however, it builds and generates a dll. When you set DevStudio to be the executable for debugging, a new toolbar appears and when you click on the first button a message box appears. Hey presto! You've got yourself a working add-in.

2. Get the current document

The next step was to start turning this into my add-in, not some mickey mouse thing that shows message boxes. So I needed to determine the path for the current document. Thanks to the object model that DevStudio exposes this isn't a difficult task. So first off I removed the wizard generated code that displays a MessageBox and set about the MSDN to find out how to get the path for the current document. The code looks something like this:

STDMETHODIMP CCommands::(WindiffAddinCommand)()
{ 
    AFX_MANAGE_STATE    (AfxGetStaticModuleState ());
    VERIFY_OK (m_pApplication->EnableModeless    (VARIANT_FALSE)); 

    // Get hold of the current document 
    CComPtr<IDispatch> pActiveDocumentDisp;
        m_pApplication->get_ActiveDocument (&pActiveDocumentDisp);

    CComQIPtr<ITextDocument, &IID_ITextDocument> pActiveDocument(pActiveDocumentDisp);
    if (pActiveDocument) {
        BSTR bstr;
        HRESULT hr = pActiveDocument->get_FullName (&bstr);
        if (FAILED (hr)) {
            ::MessageBox (NULL, "Failed to get path for current document", 
                          "WinDiff Addin", MB_OK | MB_ICONWARNING);
        }
        else {
            CString strFullPath (bstr); // Path and filename
            // Do something with the path
        }
    }
    else {
        // This probably means that there isn't a file open
    }
}

3. Do something with WinDiff

Now that we've got the path for the current document, we can then start doing things with WinDiff. In case you didn't know, WinDiff has a command line interface (have a look in the help) and this is what we are going to use.

A quick glance at Chris' code showed the way forward on the approach for using WinDiff and it looks like this:

// ...
CString strCmdLine;
strCmdLine.Format (_T("\"%s\" %s \"%s\" \"%s\""), m_strWinDiffPath, 
                   strOptions, strFile1, strFile2);

// Inspired by Chris Maunder's Run WinDiff GUI
// If reuse is on, then look for a WinDiff and reuse it
if (TRUE == m_bReuse)
{
    HWND hWnd = ::FindWindow  (_T("WinDiffViewerClass"), _T("WinDiff")); 
    if (hWnd) { 
        ::SendMessage(hWnd, WM_CLOSE, NULL, NULL); 
    }
}

if (WinExec (strCmdLine, SW_SHOW) <= 31) 
{
    ::MessageBox (NULL, 
        _T("Unable to run WinDiff - check the location of the WinDiff executable"),
        "WinDiff Addin", MB_OK | MB_ICONWARNING);
}

Because I was going to provide a file compare and a directory compare that essentially do the same thing (call WinDiff with two file paths) I decided to put this functioanlity into a separate function. The only other thing to do before invoking WinDiff, is to find a file to compare against, which was done with CFileDialog.

4. Add some more commands

So now that something works it's time to tidy it up and add some more commands. The first thing I did was to rename the function, which wasn't quite so cut and dried. In a normal class all you have to do is rename the function declaration in the header file, rename the function definition in the source file and rename any calls to the function. A quick search in DevStudio revealed the function name appearing in many places, so I just renamed them all and rebuilt. To my great surprise it worked!

The next thing was to add some more commands. I wanted to be able to compare the folder of the current file as well and I also wanted to provide access to the useful command line options of WinDiff (outline only and recursive) so I needed a config window.

The commands are defined in the (helpfully named) CCommands class. So the starting point was to add more functions to this class using the same prototype as that generated for me by the Wizard. Next a definition needed to be added to the .odl file which is used to produce the type library. Finally, we need to inform DevStudio of the commands we've implemented in the OnConnection function in CDSAddIn. I used Oz's approach to this and used his AddInCmd structure. I defined an array of these structures and iterated through them, like this:

// Inspired by Oz Solomonovich's Window Tabs Addin
#define countof(arr)  (sizeof(arr)/sizeof(arr[0]))

struct AddInCmd
{ 
    LPCTSTR szCommand; 
    int     idCmdString; 
    bool    bToolBarByDefault;
};

// 1. the method name must be the command name
// 2. the commands must appear below in the order of their toolbar buttons
const AddInCmd AddInCommands[] =
{
    { _T("WindiffCurrentFile"), IDS_WINDIFF_FILE_CMD_STRING, true  },
    { _T("WindiffCurrentFolder"), IDS_WINDIFF_FOLDER_CMD_STRING, true  },
    { _T("WindiffSettings"), IDS_WINDIFF_SETTINGS_CMD_STRING, true  }
};

const int cAddInCommands = countof (AddInCommands);
...
// This is called when the user first loads the add-in, and on start-up
//  of each subsequent Developer Studio session
STDMETHODIMP CDSAddIn::OnConnection(IApplication* pApp, VARIANT_BOOL bFirstTime,
        long dwCookie, VARIANT_BOOL* OnConnection) {
// ...
    // Inform DevStudio of the commands we implement
  // Inspired by Oz Solomonovich's Window Tabs Addin
  VARIANT_BOOL    bRet;
  CString         strCmdString;
  CComBSTR        bszCmdString, bszMethod;

  for (int i = 0; i < cAddInCommands; i++) {
    strCmdString.LoadString (AddInCommands[i].idCmdString);
    const int pos = strCmdString.Find('\n');
    if (pos > 0) {
      strCmdString.Delete(0, pos);
    }
    strCmdString = AddInCommands[i].szCommand + strCmdString;
    bszCmdString = strCmdString;
    bszMethod    = AddInCommands[i].szCommand;
    VERIFY_OK (pApplication->
               AddCommand (bszCmdString, bszMethod, i, dwCookie, &bRet));

    if (bRet == VARIANT_FALSE) {
          // AddCommand failed because a command with this name already
          //  exists.  You may try adding your command under a different name.
          //  Or, you can fail to load as we will do here.
          *OnConnection = VARIANT_FALSE;
          return S_OK;
    }

      // Add toolbar buttons only if this is the first time the add-in
      //  is being loaded.  Toolbar buttons are automatically remembered
      //  by Developer Studio from session to session, so we should only
      //  add the toolbar buttons once.
      if (bFirstTime == VARIANT_TRUE)    {
          VERIFY_OK (pApplication->
        AddCommandBarButton (dsGlyph, CComBSTR(AddInCommands[i].szCommand), dwCookie));
      }
  }

After a bit of faffing about with graphics, splash screens, and toolbar buttons I was finally there, a working DevStudio Add-in that does something useful. I have to admit, I feel quite smug about it. I hope you find it useful, too.

Acknowledgements

The splash screen was implemented using Paul DiLascia's approach detailed in the October 1999 C++ Q&A in MSJ. This shows a specified bitmap resource for a specified number of milliseconds. The splash screen is launched in the OnConnection function.

The directory browsing dialog was taken from Chris' WinDiff GUI front-end. Not sure if it's his, Daniel Lyons' (co-author) or someone elses. Whowever wrote it, thanks very much :)

The config dialog makes use of Chris Maunder's (he gets everywhere doesn't he ;)) CHyperLink static control

As you can see I like re-use ;)

Enhancements

The only thing that I'd like to do that isn't included is to actually host the WinDiff window in DevStudio. Any ideas on how to do that?

History

2 Jan 2002 - updated source download

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionTool Window ? Pin
mcarbenay23-Feb-03 6:58
mcarbenay23-Feb-03 6:58 
AnswerRe: Tool Window ? Pin
Derek Lakin23-Feb-03 19:59
Derek Lakin23-Feb-03 19:59 
GeneralIdea: how to make window of windiff integrated into DevStudio Pin
Oleksandr Kucherenko3-Jan-02 22:53
Oleksandr Kucherenko3-Jan-02 22:53 
GeneralRe: Idea: how to make window of windiff integrated into DevStudio Pin
Jim Crafton4-Jan-02 6:41
Jim Crafton4-Jan-02 6:41 
GeneralRe: Idea: how to make window of windiff integrated into DevStudio Pin
Derek Lakin4-Jan-02 23:19
Derek Lakin4-Jan-02 23:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.