The Code Project View our advertisersThe intellisense upgrade for Visual C++ - make your IDE as smart as you.Advertise on the CodeProject
Home >> Macros and Add-ins >> DevStudio Add-ins

WWhizInterface: Enhancements to the Visual C++ Automation Interface
By Joshua Jensen

A C++ interface with a number of Visual C++ automation enhancements, allowing for more robust add-in programming. 
 VC 4-6, Win95-98, NT4, W2K, MFC
 Posted 9 Jan 2001
 Updated 29 Jul 2001
Articles by this author
Send to a friend
Printer friendly version
FAQ
What's New
Lounge
Contribute
Message Boards
22 users have rated this article. result:
4.91 out of 5.

Example of WWhizInterface's use in Workspace Whiz!

Introduction

Microsoft's Visual C++, like many Microsoft applications, exposes part of its functionality through a COM automation interface. Although Microsoft's intent was undoubtedly to put the power of Visual Studio in users' hands, those who have ever tried to do anything complex with the automation interface just become frustrated. Unlike the Microsoft Office products, which expose a rich automation interface, Visual C++'s looks like it was hacked in at the last moment.

Some time ago, Microsoft announced the Visual Studio Integration Program. Through the VSIP, developers would get full access to the headers and libraries to communicate with the various Visual Studio applications. The VSIP supposedly costs tens of thousands of dollars a year with a 5-year minimum commitment. For anyone who isn't a big company, this is a Bad Thing (TM).

Despite Microsoft's reluctance to provide average developers with the detailed Visual Studio specifications they need to create tightly integrated add-in applications, that has not stopped the ingenuity of several authors who have created some of the most phenomenal add-ins out there. Oz Solomonovich, for instance, writes an add-in called WndTabs that subclasses the main Visual C++ window to seamlessly insert a bar with window tabs into the user interface. Jerzy Kaczorowski's CvsIn integrates CVS with Visual C++, providing a powerful, free, alternative to source control systems such as Visual SourceSafe.

The purpose of this article is to describe the WWhizInterface SDK that complements Microsoft's Visual Studio automation interface. WWhizInterface was born of years of work developing the Visual C++ add-in Workspace Whiz! and its predecessor, the Workspace Utilities. WWhizInterface is a C++ interface providing access to certain Visual C++ capabilities the automation interface left out. The current iteration of WWhizInterface has been used in Jerzy Kaczorowski's CVS integration add-in, CvsIn, and Oz Solomonovich's Project Line Counter add-in. A previous form of WWhizInterface powers Mirec Miskufovic's Replace All Across Project Files add-in.

As mentioned previously, WWhizInterface and the Visual C++ automation interface work hand-in-hand. WWhizInterface exposes functionality the automation interface left out. WWhizInterface has an added benefit; most of its functionality can work without Visual C++ being active. WWhizInterface works seamlessly with Visual C++ 5.0, Visual C++ 6.0, and eMbedded Visual C++ 3.0.

WWhizInterface provides the following capabilities over the Visual C++ automation interface:

The latest WWhizInterface can be found online at http://workspacewhiz.com/WWhizInterface.html. The Workspace Whiz! source distribution, which contains the sample code described below, and the source documentation (viewable online and in an archive), is available from there, in addition to far more information about WWhizInterface. 

Note: If any sample crashes in a Debug build, it is likely that WWhizInterface2D.mod could not be found (the error checking in the samples is only so-so). Either add the HKLM\Software\WWhizInterface\DebugPath value to the registry or copy WWhizInterface2D.mod to the Working Directory. If any sample crashes in a Release build, be sure to have run WWhizInterfaceInstallerWithCtags212.exe first.

Working With Workspaces, Projects, and Files

Creating a WWhizInterface object

To use WWhizInterface, add WWhizInterface2Loader.cpp, WWhizInterface2Loader.h, and WWhizInterface2.h to your project.

First, we need an instance of the WWhizInterface object. Retrieve this instance by calling the function WWhizInterface2Create(), declared in WWhizInterface2Loader.h:

WWhizInterface* __cdecl WWhizInterface2Create(HINSTANCE hInstance, IApplication* pApplication); 

hInstance is AfxGetInstanceHandle() in an MFC application. A console application may just pass in NULL.

pApplication is the Visual Studio automation interface IApplication pointer. If the application is not a Visual Studio add-in, then NULL may be passed instead.

In a console application, initialization would be performed like:

WWhizInterface* g_wwhizInterface;
g_wwhizInterface = WWhizInterface2Create(NULL, NULL);

It is possible for WWhizInterface2Create() to fail. The function first checks the working directory for the appropriate WWhizInterface2.mod or WWhizInterface2D.mod. If it is not there, it relies on a path stored in the registry at HKLM\Software\WWhizInterface\Path (or HKLM\Software\WWhizInterface\DebugPath if using a Debug build). The WWhizInterface\Path key is created by the WWhizInterfaceInstaller. The WWhizInterface\DebugPath key must be created through REGEDIT.

Workspace Name

A significant function of WWhizInterface is the retrieval of the active workspace's filename. This takes advantage of a property of Visual C++ described by Nick Hodapp in his article Undocumented Visual C++ which appeared on the Code Project. A helper .pkg file installed in the Common\MSDev98\Bin\IDE directory by the installer used to distribute WWhizInterface. For a user of WWhizInterface, the retrieval of the name is merely a function call.

CString workspaceName = g_wwhizInterface->GetWorkspaceName();

The name returned is the full path to the workspace's .dsw file, not the actual name of the workspace.

Obtaining the current file

If the application is a Visual Studio add-in, WWhizInterface::GetCurrentFilename() may be used to obtain the active file's filename. Visual Studio's automation functionality can do the same thing but not without a lot of COM setup pain.

CString currentFilename;
if (g_wwhizInterface->GetCurrentFilename(currentFilename))
{
    // Retrieve the WWhizFile object for the currentFilename:
    WWhizFile* curFile = g_wwhizInterface->GetFileList().Find(currentFilename);
    // Do something with curFile...
}

Filename resolution

Many filenames come in relative path form. Some filenames include an environment variable embedded in them formatted as $(ENV)\Filename.ext. WWhizInterface::ResolveFilename() will resolve any given filename to its absolute path.

CString relativeFilename = "test.cpp";
CString environmentFilename = "$(HOMEDRIVE)\\test.cpp";
CString rootDirectory;   // Empty = current directory

g_wwhizInterface->ResolveFilename(rootDirectory, relativeFilename);
// fullPath now contains the absolute path to test.cpp.
g_wwhizInterface->ResolveFilename(rootDirectory, environmentFilename);
// environmentFilename now contains the absolute path to $(HOMEDRIVE)\test.cpp.

Project Retrieval

Another of WWhizInterface's capabilities is the retrieval of every file in every project of a workspace. Unlike Visual Studio, WWhizInterface can work with multiple workspaces at a time. This powerful function is the basis behind Workspace Whiz!'s Extra Files feature. Extra Files makes information about extra workspaces and projects available for all supported Workspace Whiz! functions.

Whether the application is a Visual Studio add-in or not, workspaces and projects may be added to WWhizInterface. This is done by passing a Visual Studio-compatible filename to WWhizInterface::AddProject().

g_wwhizInterface->AddProject("d:\mfc.dsp");
g_wwhizInterface->AddProject("$(HOMEDRIVE)\WorkspaceWhiz\Src\WorkspaceWhiz60.dsw");

If the WWhizInterface-enabled application is a Visual Studio add-in, then the active workspace and active projects are automatically added by refreshing the file list.

When the caller is ready to access the information from added projects and workspaces, call WWhizInterface::RefreshFileList().

g_wwhizInterface->RefreshFileList();

Retrieving the Project List

The project list may be retrieved via a call to WWhizInterface::GetProjectList().

WWhizProjectList& projectList = g_wwhizInterface->GetProjectList();

The project list is made up of all workspaces and projects registered with WWhizInterface. To print the names of all projects in the active workspace, each project must be queried through WWhizProject::IsWorkspaceProject().

for (int i = 0; i < projectList.GetProjectCount(); ++i)
{
    WWhizProject* project = projectList.GetProjectByIndex(i);
    if (project->IsWorkspaceProject())
    {
        AfxMessageBox(project->GetName());
    }
}

If WWhizInterface is used within an add-in, it is possible to retrieve the current project as a WWhizProject.

WWhizProject* project = g_wwhizInterface->GetCurrentProject();

Retrieving filenames

WWhizInterface maintains several file lists:

Upon obtaining a file list, iterating its members is performed via the WWhizFileList::GetCount() and WWhizFileList::Get() functions:

WWhizFileList& fileList = g_wwhizInterface->GetFileList();
for (int i = 0; i < fileList.GetCount(); ++i)
{
    WWhizFile* file = fileList.Get(i);
    const CString& fullName = file->GetFullName());
    printf("%s\n", fullName);
}

Clearing out the file list

To completely clear the file list, use the function WWhizInterface::RemoveAllFiles(). This invalidates all registered workspaces and projects.

g_wwhizInterface->RemoveAllFiles();

Querying the Global Include and Source directories

Some applications require the files from the global Include and Source directories. WWhizInterface makes it easy to obtain those files.

g_wwhizInterface->RefreshGlobalFileList();
WWhizFileList& globalFileList = g_wwhizInterface->GetGlobalFileList();

Example: Backup Projects Add-in

Using the information above, a small add-in will be built that makes a backup copy of every file in the workspace. This is done by simply making a copy of the file to a file ending in the extension .bak.

The code below is from CCommands::BackupProjectsAddinCommandMethod() in the Src/Samples/BackupProjectsAddin directory of the Workspace Whiz! source distribution.

// Create the WWhizInterface.
WWhizInterface* g_wwhizInterface = WWhizInterface2Create(AfxGetInstanceHandle(), m_pApplication);

// Refresh all the files.
g_wwhizInterface->RefreshFileList();

// Get the project list.
WWhizProjectList& projectList = g_wwhizInterface->GetProjectList();

// Iterate the projects in the list.
for (int i = 0; i < projectList.GetProjectCount(); ++i)
{
    // Get the project at the specified index.
    WWhizProject* project = projectList.GetProjectByIndex(i);

    // Is it a member of the workspace? If not, then skip it.
    if (!project->IsWorkspaceProject())
        continue<;

    // Ask if we should backup the project.
    if (AfxMessageBox("Backup the project " + project->GetName() + "?", MB_YESNO) == IDNO)
        continue<;

    // Get the project's file list.
    WWhizFileList& fileList = project->GetFileList();

    // Iterate all members of the file list.
    for (int j = 0; j < fileList.GetCount(); ++j)
    {
        // Get the file at the specified index.
        WWhizFile* file = fileList.Get(j);

        // Make a copy of it.
        CString existingFilename = file->GetCaseFullName();
        CString newFilename = existingFilename + ".bak";
        ::CopyFile(existingFilename, newFilename, FALSE);
    }
}

// Destroy the WWhizInterface.
WWhizInterface2Destroy();

Using Tags

Overview

WWhizInterface builds a variety of tag lists for every file in the workspace. Tag lists contain information about almost every type of identifier known within the C++ language.

WWhizInterface maintains several tag lists:

Tags

A single tag contains a myriad of information about the identifier.

Example: Show Functions Add-in

To show off a few of the capabilities of WWhizInterface's tag interface, we'll build an add-in to show all the functions in the current file (similar to Workspace Whiz!'s Find Tag Special command). Our add-in, though, will show the body of the function in another window as the user clicks on a function.

When the user presses the 'ShowFunctionsAddin' button in Visual C++, the method CCommands::ShowFunctionsAddinCommandMethod() is called. This function first checks to see if there is an active document. If there is, the CFunctionsDialog is displayed.

// Get the current file. CString currentFilename; g_wwhizInterface->GetCurrentFilename(currentFilename); // If there isn't a file currently open, then don't show the dialog. if (!currentFilename.IsEmpty()) { CFunctionsDialog dlg; dlg.DoModal(); }

CFunctionsDialog::OnInitDialog() does the work of filling in the functions in the list box. It first refreshes the file list and tag list.

// Refresh all the files.
g_wwhizInterface->RefreshFileList();

// Refresh the tags.
g_wwhizInterface->RefreshTagList();

The next step is to retrieve the ordered tag list from the currently open file.

// Get the current filename.
CString currentFilename;
g_wwhizInterface->GetCurrentFilename(currentFilename);

// Get the WWhizFile pointer based on the current filename.
WWhizFile* file = g_wwhizInterface->GetFileList().Find(currentFilename);

// Retrieve the ordered tag list.
m_orderedTagList = &file-;>GetOrderedTagList();

Lastly, we need to iterate the ordered tag list looking for functions. When a function is found, the fully qualified name should be inserted into the function list box.

// Iterate the ordered tag list looking for functions. UINT tagListCount = m_orderedTagList->GetCount(); for (UINT i = 0; i < tagListCount; ++i) { // Get a pointer to the tag. WWhizTag* tag = m_orderedTagList->Get(i); // Is it a function? if (tag->GetType() == WWhizTag::FUNCTION) { // Yes, build the qualified name. CString fullName = tag->GetParentIdent() + CString("::") + tag->GetIdent(); // Add it to the list box. int index = m_functionList.AddString(fullName); // Set the list box item's data to be the index of the tag within // the ordered tag list. m_functionList.SetItemData(index, i); } }

In the list box's OnSelChange(), we need to retrieve the block of text representing the function source code (an approximation, anyway) and display it in the rich edit control. To simplify the COM automation and potentially add portability to Visual C++ 5.0 and eMbedded Visual C++ 3.0, the helper class ObjModelHelper from the Workspace Whiz! source distribution is used.

// Get the current selection.
int curSel = m_functionList.GetCurSel();
if (curSel == LB_ERR)
    return;

// Set up the object model for the active document.
ObjModelHelper objModel;
objModel.GetActiveDocument();
	
// Retrieve the ordered tag list index from the current selection's item data.
int tagNumber = m_functionList.GetItemData(curSel);

// Get the selected tag.
WWhizTag* tag = m_orderedTagList->Get(tagNumber);

// Move to the line number the selected tag starts at.
objModel.MoveTo(tag->GetLineNumber(), 1, dsMove);

// Now extend the selection to the top of the next tag.
WWhizTag* nextTag = NULL;
if (tagNumber + 1 < m_orderedTagList->GetCount())
{
    // There was another tag to go off.  Use it.
    nextTag = m_orderedTagList->Get(tagNumber + 1);
    objModel.MoveTo(nextTag->GetLineNumber(), 1, dsExtend);
}
else
{
    / The selected tag was the last one in the file.
    objModel.EndOfDocument(dsExtend);
}

// Retrieve the text.
CString text = objModel.GetText();

// Set it into the rich edit control.
m_functionCode.SetWindowText(text);

Using the XML Interface

In version 2.12, Workspace Whiz! introduced Visual C++ 7.0 project support through WWhizInterface. Visual C++ 7.0 project files are written in XML form and have the file extension .vcproj. Some helper classes, XmlData and XmlNode (both of which reside in the Src/Shared/ directory of the source distribution) allow simple parsing and manipulation of XML files.

The XML project files are so convenient to iterate through that a large part of Visual C++ 5.0 and 6.0 .dsp files are internally converted to the XML form. Every WWhizInterface project provides access to the internal representation of the XML format.

The XML Project File Format

At this time, only the groups and files between the # Begin Target and # End Target of a .dsp file are converted into XML form. Configuration data may be converted at a later time, based on demand.

<VisualStudioProject ProjectType="Visual C++" Version="6.00" Name="Project Name">
  <Files>
    <Filter Name="Source Files">
      <File RelativePath=".\StdAfx.cpp">
      </File>
      <Filter Name="Subgroup">
      </Filter>
    </Filter>
  </Files>
</VisualStudioProject>

Full XML project information is available for .vcproj files. Please refer to the .vcproj file for more information on other XML sections available.

Adding XML Support to your Project

In order to access WWhizInterface's XML functionality, four files need to be added to your project: XmlData.cpp, XmlData.h, Node.cpp, and Node.h. These files exist in the Src/Shared/ directory of the Workspace Whiz! source distribution.

Be sure to #include "XmlData.h" in the appropriate source files.

Retrieving the XML Project Data

WWhizProject::GetXmlData() returns a reference to the internal XmlData object containing the project information.

WWhizProject& project = g_wwhizInterface->GetCurrentProject();
XmlData& xmlData = project.GetXmlData();
.

Iterating the XML Project Data

Since the only guaranteed section available is <Files>, we need to ask the XmlData object for the XmlNode pointing to <Files>.

XmlNode* filesNode = (XmlData*)xmlData.Find("Files");

If filesNode is NULL, then there was no section in the XML file called <Files>.

An XmlNode is derived from a base class called Node. Node provides basic tree traversal through the functions GetParent(), GetNextSiblingNode(), GetPrevSiblingNode(), GetFirstChildNode(), and GetLastChildNode(). Iteration of XmlNodes is done through the Node traversal functions.

Retrieving Attributes

Various attributes are stored within the WWhizInterface XML node representation. The retrieval of these attributes is performed by either calling a function that finds an attribute by name or by iterating the attribute list.

Finding an attribute by name occurs through the function XmlNode::FindAttribute(). It returns an XmlNode::Attribute pointer. If the pointer is NULL, the attribute was not found.

XmlNode::Attribute* attr = fileNode->FindAttribute("RelativePath");

Iterating through the attribute list is done via the XmlNode::GetAttributeHead() and XmlNode::GetAttributeNext() functions. They are very similar in design to CList::GetHeadPosition() and CList::GetNext().

Example: Show Groups Add-in

To iterate the supported WWhizInterface hierarchy, a recursive function must be used. The sample code, Src/Samples/ShowGroupsAddin, shows this process.

static void RecurseFileNodes(XmlNode* parentNode, CTreeCursor cursor)
{
    // If the parent is NULL, then abort.
    if> (!parentNode)
        return;

    // Start with the first child.
    XmlNode* node = (XmlNode*)parentNode->GetFirstChildNode();

    // Iterate the children until there are no more.
    while< (node)
    {
        // Is it a <File> node?
        if (node->GetName() == "File")
        {
            // Get the relative path of the filename.
            XmlNode::Attribute* attr = node->FindAttribute("RelativePath");
            if (attr)
            {
                // Add it to the appropriate place in the tree control.
                cursor.AddTail(attr->GetValue());
            }
        }

        // Else is it a <Filter> node?
        else if (node->GetName() == "Filter")
        {
            // Get the name of the filter (the group name).
            XmlNode::Attribute* attr = node->FindAttribute("Name");

            // Give it a default, in case something is wrong.
            CString name = "Unknown";
            if (attr)
            {
                name = attr->GetValue();
            }

            // Add it to the appropriate place in the tree control.
            CTreeCursor childCursor = cursor.AddTail(name);

            // Recurse deeper into the XML.
            RecurseFileNodes(node, childCursor);
        }

        // Go to the next XML node.
        node = (XmlNode*)node->GetNextSiblingNode();
    }
}

Redistribution

A custom installer made just for WWhizInterface, called the WWhizInterfaceInstaller, is available to ease end-user installations. Although not publicly available at this time, the WWhizInterfaceInstaller's MiniInstaller source code will be available soon. Please continue checking http://workspacewhiz.com/ for it. Special thanks to Jeremy Collake for his excellent PECompact software which compresses one version of the WWhizInterfaceInstaller executable to only 51k.

WWhizInterface is freely redistributable subject to the following restrictions:

License

The license from the Workspace Whiz! source distribution (which includes WWhizInterface) reads:

Workspace Whiz! - A Visual Studio Add-in Source Code
(http://workspacewhiz.com/) is Copyright 1999-2001 by Joshua C. Jensen
(jjensen@workspacewhiz.com).

The code presented in this source distribution may be freely used and
modified for all non-commercial and commercial purposes so long as due credit
is given and the source file header is left intact.

If the source module is from another author, that module may be used
subject to the restrictions of the author.

Workspace Whiz! and its accompanying files are provided "as is."
The author can not be held liable for any damages caused through the use of
this software, except for refund of the purchase price.

Conclusion

WWhizInterface has been invaluable for a number of add-in projects myself and others have created. It continues to be enhanced as Workspace Whiz! evolves and as users request new features. It is my hope that the functionality WWhizInterface provides can be of benefit to other add-ins and tools authors.

Thanks,
Joshua Jensen

About Joshua Jensen

Joshua Jensen is a gamer at heart and as such, creates games for a living. He is currently employed at Microsoft's games division in Salt Lake City, UT where he is enjoying making titles for the Xbox.

In his spare time, he maintains a Visual C++ add-in called Workspace Whiz! Find it at http://workspacewhiz.com/.

Click here to view Joshua Jensen's online profile.

[Top] Sign in to vote for this article:     PoorExcellent  
MSDN Magazine - Your guide to Microsoft tools, development environments, and technologies for Windows and the Web.
Premium Sponsor

View our advertisersDundas TCP/IP - The ultimate set of Internet/Intranet products for C++, MFC and ATL developersAdvertise on the CodeProject
Hint: For a faster board use IE 4+, choose 'Use DHTML' from the View dropdown and hit 'Set Options'.
 Search (exact phrase)
 View   Per page   Messages since
New threadMsgs 1 to 14 of 14 (Total: 14)[First] [Prev] [Next] [Last]
Subject 
Author 
Date 
  VSS and C++ automation
Unconfirmed/Anonymous posting Rastislav 8:40 22 May '01 
  One question
 Vitaly Belman 22:20 6 Apr '01 
  Re: One question
 Joshua Jensen 19:58 11 Apr '01 
  A weird error just when I add the files
 Vitaly Belman 0:22 5 Apr '01 
  Re: A weird error just when I add the files
 Joshua Jensen 0:28 5 Apr '01 
  Relevance of Virtual* file codes
Unconfirmed/Anonymous posting Anonymous 14:08 9 Jan '01 
  Re: Relevance of Virtual* file codes
 Joshua Jensen 15:39 9 Jan '01 
  Re: Relevance of Virtual* file codes
Unconfirmed/Anonymous posting Anonymous 19:05 9 Jan '01 
  Re: Relevance of Virtual* file codes
 Joshua Jensen 20:22 9 Jan '01 
  Re: Relevance of Virtual* file codes
Unconfirmed/Anonymous posting Anonymous 12:14 10 Jan '01 
  Reasons for doing the tag system the way it is
 Joshua Jensen 13:07 10 Jan '01 
  Re: Reasons for doing the tag system the way it is
Unconfirmed/Anonymous posting Anonymous 18:02 10 Jan '01 
  Re: Reasons for doing the tag system the way it is
 Joshua Jensen 18:36 10 Jan '01 
  Re: Reasons for doing the tag system the way it is
 Paul Selormey 15:18 8 Feb '01 
Last Visit: 12:00 Friday 1st January, 1999[First] [Prev] [Next] [Last]
Home >> Macros and Add-ins >> DevStudio Add-ins
Advertise on The Code Project
Article content copyright Joshua Jensen, 2001
everything else © CodeProject, 1999-2002.

DevelopersDexDevGuruProgrammers HeavenTek-Tips ForumsTopXMLVisualBuilder.comW3SchoolsXMLPitstopZVONSearch all Partners