Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Microsft Visual Studio 2005 Doxygen Add-In

0.00/5 (No votes)
29 Jun 2010 1  
MSVS Add-in for Doxygen documentation system

Introduction

This MSVS Add-in allows to select multiple files contained in loaded solution and pass them to Doxygen. Also it gives availability to choose the name of the project and the destination folder. All other Doxygen options can be set using Doxywizard executed from the add-in. I tried to do it as simply as possible for future extension which will be done next time.

Prerequisites

Usage

  1. Microsoft Visual Studio 2005
  2. Doxygen. Download from http://www.stack.nl/~dimitri/doxygen/download.html#latestsrc

Build Sources

  1. Boost(I used boost.1.38.0). Download from http://www.boost.org/
  2. WTL 80. Download from http://www.microsoft.com/downloads/details.aspx?familyid=E5BA5BA4-6E6B-462A-B24C-61115E846F0C&displaylang=en

Description

Doxygen Add-in can be run from Tools menu of MSVS IDE.

This tab allows to select files for document generation:

dxaddin_files.JPG

This is used for Doxygen raw tuning:

dxaddin_opts.JPG

Doxygen executable” & “Doxywizard executable

They are searched when add-in starts. But I left an ability to choose them manually if you need it.

Name” is used for document generation and can be changed in Doxywizard.

Doxygen configuration file

When running Doxywizard, you can fine tune the options of generated documentation and save various configuration files for different purposes. Later, you can choose the configuration that you want and run Doxygen.

Output directory” is needed to tell Doxygen where to put the documentation. It can also be changed in Doxywizard mode.

Code

MSVS Add-in Architecture

First of all, I will try to explain (and to understand myself) some important moments of the MSVS Add-in architecture.

Let’s take a look at this diagram:

msvs_addin.JPG

Preload Add-in*

(*)This happens only if you use CommandPreload flag set to ‘1’ in your registration script(Addin.rgs). It indicates that your add-in must be preloaded to setup UI. After that, the add-in will be unloaded.

Important: Remember that all initialized class members and local objects will be destroyed after UI setup completes.

The first time the object <CConnect> is created in order to give availability to setup UI, what can be done in OnConnect( ConnectMode == ext_cm_UISetup ). At this point, we add our control to menu bar (this can also be done by “Visual Studio Add-in Wizard”). The command is created using AddNamedCommand2 method of EnvDTE80::Commands2 interface. It is senseless to store the created command because it is created only once at the first run and saved by the MSVS IDE enviroment. Later, I will show how to get it in order to remove when we uncheck availability of our add-in in “Add-in Manager”.

After that, we receive OnDisconnection( RemoveMode == ext_dm_UISetupComplete ) which tells us that “The add-in was unloaded after the user interface was set up”(MSDN).

And at last, our object will be destroyed. Call of FinalRelease() and subsequent call of object’s destructor tells us about it.

Load Add-in

After UI was set up, the add-in is loaded for the second (if CommandPreload is set) time(the new calls of constructror and FinalConstruct() indicates it).

Now we will receive OnConnect (ConnectMode == ext_cm_CommandLine ). At this point, our add-in was loaded by the MSVS eviroment.

The next point is OnAddInsUpdate() which “Occurs whenever an add-in is loaded or unloaded from the Visual Studio integrated development environment (IDE)”(MSDN). Its type(loaded/unloaded) we can detect by analyzing parameter ConnectMode::ext_ConnectMode in the last call of OnConnection() and RemoveMode::ext_DisconnectMode in OnDisconnection() methods.

And the final point is OnStartupComplete. It “Occurs whenever an add-in, which is set to load when Visual Studio starts, loads”.

Check/Uncheck Availability in “Add-in Manager” Notification

Unckeck Availability

When we uncheck the add-in in “Add-in Manager”, we receive OnDisconnect( RemoveMode == ext_dm_UserClosed ). At this point, we can remove our item from menu bar.

If we use CommandPreload flag set to ‘1’ (see “Preload Addin”), then we have no variable pointing to our command and we need to get it manually. This is done in such a way:

IfFailGoCheck(m_pDTE->get_Commands(&pCommands), pCommands);
CComQIPtr< EnvDTE::Command > dx_cmd;
IfFailGoCheck( pCommands->Item( CComVariant
	( "DoxygenAddin.Connect.DoxygenAddin" ), 0, &dx_cmd ), dx_cmd );
dx_cmd->Delete();  

Important: " DoxygenAddin.Connect.DoxygenAddin " is a full name of the command. It consists of 2 parts: the full name of the class “ DoxygenAddin.Connect ” and name of the command “ DoxygenAddin ”. The method Delete() of EnvDTE::Command removes it from the menu bar.

If our command was created not in OnConnection( ConnectMode == ext_cm_UISetup ), then we can remove it explicitly calling EnvDTE::Command::Delete() from created EnvDTE::Command object.

Check Availability

When we again check it, OnConnection( ConnectMode == ext_cm_AfterStartup ) is called. All we need to do is to re-add our control menu button using AddNamedCommand2 method of EnvDTE80::Commands2 interface as it was done earlier.

Unfortunately, it works correctly only when any solution loaded. When I remove button without loaded solution, I failed to add it again, :(. Why is it so – I don’t know. If anyone can explain this – I will be glad :).

Add-in Availability (enable/disable)

Another point is that I don’t want to execute my add-in when no solution is present. CSolutionEventsSink class was written to prevent it. It inherits IDispEventImpl with __uuidof(EnvDTE::_dispSolutionEvents) and receives “Opened” and “BeforeClosing” solution events.

BEGIN_SINK_MAP(SolutionEventsSink)
  SINK_ENTRY_EX(1, __uuidof(EnvDTE::_dispSolutionEvents), 1, Opened)
  SINK_ENTRY_EX(1, __uuidof(EnvDTE::_dispSolutionEvents), 2, BeforeClosing)
END_SINK_MAP()   

The code above easily solves this problem but I was interested to dig it deeper. I was interested in how to receive all dispids, function names and other information on them in the class which events I want to handle at run-time in order to perform dynamic advise.

Macroses and BEGIN_SINK_MAP and SINK_ENTRY_EX declare _GetSinkMap() static method and create static array of _ATL_EVENT_ENTRY entries which contain information needed to associate events by their dispids with user callbacks.

Our sink class derived from IDispEventImpl and its parent IDispEventSimpleImpl contain static object of type CComTypeInfoHolder. Getting ITypeInfo from it helps us to receive all type information of EnvDTE::_dispSolutionEvents. Now we can create an array of _ATL_EVENT_ENTRY entries at run-time.

After advising on interested events of EnvDTE::SolutionEvents object, all I have to do is to check the current state of solution in CConnect::QueryStatus and to make the menu item enabled or disabled.

CConnect::CheckHasSolution() helps to determine if the add-in was checked in “Add-in Manager” when the solution was already present. It is simply done by analyzing the EnvDTE::_Solution item count property (EnvDTE::_Solution object presents always but it may or may not contain child items).

Important

Take a look at the declaration of the class CSolutionEventsSink:

class CSolutionEventsSink : public IDispEventImpl< 1, CSolutionEventsSink
 , &__uuidof( EnvDTE::_dispSolutionEvents ), &EnvDTE::LIBID_EnvDTE, 8, 0 >

8 - is the major version of the type library. As it was built under MSVS 8.0, this number indicates its major version. In other versions of MSVS IDE, it will vary.

Acquisition of Solution Items

The acquisition of solution items is rather simple. We can obtain EnvDTE::_Solution object from EnvDTE80::DTE2. Then get the number of projects in it(EnvDTE::_Solution::get_Count()) and all EnvDTE::Project objects. Just the same, you can obtain EnvDTE::ProjectItems from EnvDTE::Project object.

The only interesting thing is to identify whether EnvDTE::ProjectItems points to a file or not. It is done by analyzing its Kind property. EnvDTE::vsProjectItemKindPhysicalFile indicates it.

Important: If you load a file to the empty IDE, it also creates a solution(EnvDTE::_Solution), project(EnvDTE::Project) and project items(EnvDTE::ProjectItems) (not shown in “Solution Explorer”) so you can easily get it.

Out of Scope

CCBTreeViewCtrl

To view and select files for document generation, I use class CCBTreeViewCtrl which extends WTL::CTreeViewCtrlEx.

It adds 3 new abilities to its parent:

  1. Selection/deselection of the whole brunch via its parent
  2. Grayscale parents when child items have different states
  3. Method ParseTree() which helps to process all children from the specified item

Boost::spirit parsing

It does not seem simple for understanding and usage but when you achieve what you wanted, it looks amazing, :).

pugi::pugixml

Lightweight and intuitive library for XML-parsing.

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