Products, the Universe and Everything

The Riverblade Developer's Blog

Beth demonstrating Visual Lint at the ACCU Conference 2008  Anna taking part in a discussion panel at the European Software Conference 2007 

Welcome to our developer's blog. We hope that this forum provides an insight into us, our products and how we develop them. Please feel free to write to us if you have anything to add to any of the posts here.

Current Entries | Archives |


ResOrg 1.6.1 is (almost) good to go
Wednesday, December 29, 2004

Yesterday we built the first release candidate for the next version of ResOrg - 1.6.1. Although it's been longer in the making than we hoped, the delay has proved to be worth it.

Not only does this version include one long requested feature (the ability to exclude symbols such as IDR_MAINFRAME from renumbering operations) it also introduces features such as symbol range checking, "Problem Symbol" Reports, and compatibility with Visual Studio .NET 2005 (Whidbey).

This is also the first version to be branded under the Riverblade name. It certainly won't be the last, and we hope the fact that there are now two of us available to support ResOrg will benefit it greatly. Ultimately we'd like to greatly improve the integration of the product with Visual Studio, but realistically that's some way off.

We hope you find the new version a worthwhile upgrade. Here's the full list of changes:

Symbols and Symbol Files
  • Added support for toolbar (IDT_xxx) and accelerator(IDK_xxx) symbols.

  • Added support for detection of out of range symbols, based on symbol type and the defined base values for the associated file.

  • Added support for the "_APS_NO_MFC" define sometimes found in non-MFC projects


Options
  • Added support for "Fixed" symbols. Such symbols (e.g. IDR_MAINFRAME) will be excluded from renumbering operations by default).

  • Added an "Autosave Symbol File Configuration" option to the"Symbols" page of the Options Dialog. When active, an XML file containing the symbol file configuration (Base Values etc.) is automatically written when a file is saved or the Base Values are changed.

  • Added a "Check for out of range symbols" control to the "Symbols" page of the Options Dialog. A corresponding control has also been added to the "General" page of the Symbol File Properties Dialog.


Displays
  • Added "Next Problem" and "Previous Problem" commands to Symbols Displays.

  • The "Properties" command on the main toolbar now displays the properties for the current file if the properties for a symbol cannot be displayed.

  • Added dynamic splitter windows to symbols displays

  • Double clicking status bar panes now does something vaguely sensible

  • Added a "File Properties" button to the "General" page of the Symbol File Properties Dialog to provide access to the general properties for the file.


Reports
  • Revised the layout of the Report Dialog. It is now resizeable, and the stylesheet pathname edit control is now a combo box showing all recently used report templates

  • Added Problem Symbol Report

  • Added table sorting capability to HTML reports (courtesy of Mike Hall - http://www.brainjar.com).

  • Various minor layout improvements


Miscellaneous
  • Added support for Visual Studio .NET 2005

  • Updated the "author" image in the About Box and rebranded as a Riverblade product

  • Toolbar buttons in the ResOrg.NET add-in are now stored in a satellite DLL (Visual Studio .NET 2005 requires this) and have transparent backgrounds

  • Added an icon to the toolwindow displayed by the ResOrg.NET add in

We hope to have the new version out in the next few days, once we've completed testing. Once it's available for download, all subscribers to the ResOrg mailing list will receive a message to let them know its available.

Posted by Anna at 21:07 | Get Link

 

Scrollbar voodoo, the MFC way
Monday, December 13, 2004

There are some development tasks that - no matter how you look at them - you just know are going to be messy.

I've just finished an small piece of functionality in the forthcoming version of ResOrg which falls into exactly that category, and it all started with such a simple idea - add a dynamic splitter to the MFC child frame class used for Symbols Displays so that the user could open a split view of the same file easily and quickly. Although adding such a splitter is really easy with MFC (just configure it in the OnCreateClient() override of the MDI child frame class, and MFC's document-view architecture does the rest), there turned out to be a nasty catch.

Before I explain what that is, it's worth a little digression into what is (I think) one of the messiest parts of the MFC framework - the duplication of common control classes between those dervied from CWnd (e.g. CListCtrl) and those derived from CView (e.g. CListView). Although on the face of it this provision of both CWnd and CView derived common control classes offers a great deal of flexibility in the way the classes can be used, unfortunately, it's not that straightforward. If you have a CWnd derived control class you also want to use as a view, this design leads many to assume that the only way to do so is to duplicate its functionality in a CView derived class. Messy.

Incidentally, Paul DiLascia explains the reasoning behind this aspect of the MFC design (and more importantly, its severe limitations) rather well in his article "C++ Q&A: Understanding CControlView, Changing Scroll Bar Color in MFC Apps". He makes one crucial point - although MFC uses CCtrlView to accomplish this trickery, you can't use it to use your own control classes as views if they have either virtual functions or member data.

Fortunately, there is a another approach, and one which I've been using for some time - simply create a CView derived class which hosts the CWnd derived control as a child window. ResOrg uses exactly this approach - the view class for the Symbols Display (CResOrgSymbolsListView) is a simple CView wrapper hosting a child control class (CResOrgSymbolsListCtrl). It's simple, effective and promotes reuseability...perfect, on the face of it.

Perfect that is, until you add a splitter window. Suddenly, I was confronted with the realisation that while the list control quite happily managed its scrollbars when necessary, the splitter also provides scrollbars...and they don't know about the list control, only the view that encloses it. The result is two sets of scrollbars, only one of which works:

Not only does the splitter provide scrollbars, but the list control does too.

When I first saw this, it went firmly into the "fix it later" category - mostly because I wasn't sure exactly how to correct the problem.

I finally dived in and had a real go at it this weekend. The first job was to configure the splitter's scrollbars to work the same way as those belonging to the control itself. This turned out to be straightforward - simply use the settings from the list control's scrollbars:

void CResOrgSymbolsListView::ConfigureScrollbars(void)
{
SCROLLINFO infoHorz;
m_ctrlSymbols.GetScrollInfo(SB_HORZ, &infoHorz);
SetScrollInfo(SB_HORZ, &infoHorz, true);

 
SCROLLINFO infoVert;
m_ctrlSymbols.GetScrollInfo(SB_VERT, &infoVert);
SetScrollInfo(SB_VERT, &infoVert, true);
}
This reconfiguration needs to be performed whenever the size or contents of the control changes, so the obvious places to do it are within OnSize() (which is already overridden to resize the list control when the view size changes) and OnItemChangedListCtrl() (the LVN_ITEMCHANGED handler).

Once the OnVScroll() and OnHScroll() handlers were implemented (I used MSDN as a reference to make sure all cases were handled) the outer scroll bars worked, and tracked those belonging to the list control itself.

So far, so good - although there still remained the problem of duplicated scrollbars of course. At this point it also became obvious that there was another problem - manipulating the list control via the keyboard didn't update the scrollbars.

Hiding the list control's scrollbars should be easy, but it's not at all obvious, as removing the appropriate window styles (WS_HSCROLL and WS_VSCROLL) at the outset stops the header control from being painted (there's an MSDN article on this somewhere). I finally found a solution which worked for me in the article Hide scrollbars from a CListCtrl by Lars Werner - remove the scrollbar styles in a WM_NCCALCSIZE handler:
void CResOrgSymbolsListCtrl::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) 
{
CWnd* pParent = GetParent();
 
if ( (NULL != pParent) && IsViewContainedInSplitterWindow(pParent) )
{
// If we're running inside a view within a splitter, remove our own scrollbars
// so the splitter scrollbars can do their thing instead
ModifyStyle(WS_HSCROLL | WS_VSCROLL, 0, 0);
}
CResOrgSymbolsListCtrl_BASE::OnNcCalcSize(bCalcValidRects, lpncsp);
}
IsViewContainedInSplitterWindow() is a simple static function to determine whether a given view is contained within a splitter window:
static bool IsViewContainedInSplitterWindow(CWnd* pView)
{
CWnd* pViewParent = pView->GetParent();
if (NULL != pViewParent)
{
if (pViewParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd) ) )
{
return true;
}
}
return false;
}
The final task was to trap the keypresses which would potentially change the scrollbar state. A little thought confirmed that doing so in the view wouldn't help, since a keypress handed by the control could still affect the state of the scrollbars.

There seemed no choice but to handle the keypresses in CResOrgSymbolsListCtrl itself. The solution was to handle the WM_KEYDOWN message, call the base class implementation to allow the control to perform it's default action, then grab the settings of its (invisible) scrollbars and pass them to the view. To retain some decoupling between the two classes, this was implemented as a registered message (WM_SETSCROLLINFO):
void CResOrgSymbolsListCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CResOrgSymbolsListCtrl_BASE::OnKeyDown(nChar, nRepCnt, nFlags);

switch (nChar)
{
case VK_PRIOR:
case VK_NEXT:
case VK_DOWN:
case VK_UP:
case VK_HOME:
case VK_LEFT:
case VK_RIGHT:
case VK_END:
{
// If we're running inside a view within a splitter, forward the settings from
// our own scrollbars to it so that its internal state is maintained
CWnd* pParent = GetParent();
if ( (NULL != pParent) && IsViewContainedInSplitterWindow(pParent) )
{
SCROLLINFO infoHorz;
GetScrollInfo(SB_HORZ, &infoHorz);
pParent->SendMessage(WM_SETSCROLLINFO, (WPARAM)SB_HORZ, (LPARAM)&infoHorz);
 
SCROLLINFO infoVert;
GetScrollInfo(SB_VERT, &infoVert);
pParent->SendMessage(WM_SETSCROLLINFO, (WPARAM)SB_VERT, (LPARAM)&infoVert);
}
}
break;

default:
break;
}
}



LRESULT CResOrgSymbolsListView::OnMsgSetScrollInfo(WPARAM wParam, LPARAM lParam)
{
int eBar = (int)wParam;
LPSCROLLINFO pScrollInfo = (LPSCROLLINFO)lParam;
 
switch (eBar)
{
case SB_VERT:
m_nLastVPos = pScrollInfo->nPos;
SetScrollInfo(eBar, pScrollInfo);
break;
 
case SB_HORZ:
m_nLastHPos = pScrollInfo->nPos;
SetScrollInfo(eBar, pScrollInfo);
break;
 
default:
ASSERT(false);
break;
}
return 0L;
}
With these changes in place, the final result is exactly what you'd expect - normal looking, working scrollbars. In the image below, you can see a Symbols Display split into two panes - the lower one displaying conflicts only:

A Symbols Display, showing the splitter in action and scrollbars configured correctly

Believe me, I'm rather glad to get this one out of the way!

Posted by Anna at 13:37 | Get Link

 

The challenge of add-in development
Saturday, December 11, 2004

The development of add-ins has got to be one of the most challenging types of product development I've encountered. Not only must you have a vision for where your project is going and how it should work, but you must also work within the constraints of an environment which is entirely defined by another product. Such extensibility interfaces are often not documented to the same standard as those we're used to in standalone product development, or tested to the same extent (although that's understandable given that the market is correspondingly smaller). As you can probably imagine, debugging is correspondingly more complex too.

There are rewards to balance the difficulties inherent of such development of course. One way of lookng at add-in development is as a symbiotic relationship between the add-in vendor (Riverblade in our case) and the supplier of the environment we're integrating into (Microsoft, in the case of Visual Studio).

Although there's an obvious inequality between the size and resources of the two companies, the sleeping giant we're partnered with is fortunately a benevolent one (at least as far as add-in development is concerned!). A successful add-in product (Visual Assist is a good example) can add real value to the environment it integrates into. That of course benefits users - and hence both vendors.

From the add-in vendor's perspective, there's also the potential opportunity to take advantage of the resources and marketing power of a large partner organisation. VSIP - the Visual Studio Industry Partner Programme (of which Riverblade is a member) is an example of this. I hope to talk more on the subject of VSIP soon.

Posted by Anna at 14:08 | Get Link

 

A tale of two add-ins
Friday, December 10, 2004

Over the last couple of months we've been concentrating our efforts on the development of Visual Lint. Unfortunately there are only a limited number of hours in the day and as a result we've had to delay the release of the forthcoming version (1.6.1) of ResOrg slightly.

That's a shame, as the new version has some features (for example the ability to exclude symbols such as IDR_MAINFRAME from automated renumbering) which have been on the wish list for quite some time. Although we really want to get it out soon, the delay is priving to be worthwhile as some of the lessons learnt during the development of Visual Lint are feeding back into ResOrg.

It's interesting to compare the code behind the two products. Although both products are written in unmanaged C++, ResOrg is fundamentally an MFC MDI application packaged as an add-in while Visual Lint is a more conventional style of add-in which has much simpler UI needs. That difference in emphasis shows itself in the frameworks used by the two products - while ResOrg relies heavily on MFC for its user interface needs, Visual Lint uses the rather more lightweight WTL instead.

Not surprisingly, the style of code in the two products reflects the four year difference in their inception (that's right, ResOrg is four years old now!). This is particularly noticeable in the style of the COM interface code of the two products. ResOrg uses raw COM interfaces to access the Visual Studio extensibility model:

Project* CVc7AutomationHelper::GetProject(const CString& sName) const

{
USES_CONVERSION;
 
_Solution* pSolution = NULL;
Projects* pProjects = NULL;
Project* pProject = NULL;
 
if ( SUCCEEDED( m_pDTE->get_Solution(&pSolution) ) )
{
if ( SUCCEEDED( pSolution->get_Projects(&pProjects) ) )
{
long lProjects = 0;
pProjects->get_Count(&lProjects);
 
// This bit me the first time. The index is 1 based. BLOODY VB PROGRAMMERS!
for (int n = 1; n <= (int)lProjects; n++)
{
_variant_t Index( (long)n);
pProjects->Item(Index, &pProject);
 
if (NULL != pProject)
{
if (0 == sName.CompareNoCase( GetName(pProject) ) )
{
break;
}
else
{
pProject->Release();
pProject = NULL;
}
}
}
pProjects->Release();
}
pSolution->Release();
}
return pProject;
}

By contrast, Visual Lint makes use of the easier and more concise code yielded by COM smart pointers:
    EnvDTE::ProjectsPtr ptrProjects = m_ptrSolution->GetProjects();

IfNullIssueError(ptrProjects);
 
for (int n = 1; n <= ptrProjects->GetCount(); n++)
{
EnvDTE::ProjectPtr ptrProject = ptrProjects->Item(n);
 
if (ptrProject != NULL)
{
CString sProjectFilePathName = ptrProject->GetFullName();
if (!sProjectFilePathName.IsEmpty() )
{
.
.
.
}
}
}

This difference in approach shows itself throughout both products and is a natural result of the learning process. ResOrg was the first add-in I'd worked on, and almost my first foray into COM. By contrast, Visual Lint was designed with the full capabilities of the Visual Studio IDE and it's extensibility interfaces in mind, and it shows!

Despite their different aims and approaches, the two add-ins have a surprising amount in common, and it should be no surprise that our recent experience with the development of Visual Lint is starting to feed back into ResOrg.

That can only be to the benefit of both products, of course.

Posted by Anna at 08:58 | Get Link

 

Welcome to the Riverblade Blog
Thursday, December 02, 2004

Welcome to the Riverblade Blog. We hope this blog will provide an insight into our products and how they're evolving.

Most importantly, we want this to provide a forum where you can tell us what you think about our products, and features you'd like to see us implement. Comments can be applied to any post to help you with this.

We'll also of course endeavour to entertain along the way!

Finally, if you have an RSS reader, we've implemented an RSS feed so you can be automatically notified of new posts.

Once again, welcome!

Posted by Anna at 13:07 | Get Link