Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

Writing Developer Studio Add-Ins

A Developer Studio add-in is a COM object that enhances the Developer Studio environment. An add-in has access to the same Developer Studio object model used by the VBScript macros discussed in the first part of this chapter. As you can with a macro, you can use an add-in to implement commands that are accessible via the toolbar, a menu item, or the command line. The main advantage of add-ins over macros is that an add-in is compiled and therefore offers improved performance over a macro.

All Developer Studio add-ins are in-process COM objects implemented in DLLs. The DLL must be placed in the Common\MSDev98\AddIns subdirectory, where it is discovered by Developer Studio when the compiler is launched. Add-ins can be written in Visual Basic or Visual C++, but because this is a book about Visual C++, we'll use C++ for our example.

Using the Developer Studio Add-In Wizard

The simplest way to write an add-in using Visual C++ is to use the Developer Studio Add-In Wizard. This wizard creates a project that includes the necessary source files and resources used to create the skeleton of a Developer Studio add-in.

To use the Developer Studio Add-In Wizard, open the New dialog box by pressing Ctrl+N or choosing New from the File menu. Figure 17.2 shows the New dialog box.

17fig02.gif

Figure 17.2 Selecting the Add-In Wizard in the New dialog box.

As shown in Figure 17.2, you must select DevStudio Add-In Wizard as the project type. After supplying a project name and clicking OK, you see the one-page Developer Studio Add-In Wizard (see Figure 17.3).

17fig03.gif

Figure 17.3 The Developer Studio Add-In Wizard.

You can specify the following information in the wizard:

As with most projects created using Developer Studio wizards, you can immediately compile the add-in and use it in Developer Studio. To make your add-in useful, you generally need to perform the following actions:

These steps are discussed in the following sections.

The Developer Studio Add-in Architecture

An add-in created with the Developer Studio Add-In Wizard supports two interfaces:

If the add-in responds to events from Developer Studio, two additional interfaces are supported:

The add-in project consists of both ATL and MFC code. The COM interfaces are supported using ATL, while MFC is used for the DLL framework and user interface. The Add-In Wizard creates three main classes for the project:

If your add-in responds to Developer Studio events, two nested classes are placed in the CCommands class:

The main COM interface exposed by Developer Studio is IApplication. If your add-in is created by the wizard, a pointer to this interface is available as the CCommands::m_pApplication member variable.

Adding Command-Handling Code

Each command in an add-in has a corresponding function in the add-in's ICommands interface. If the name of your add-in is Bobs File Counter, for example, the ICommands interface includes a function named BobsFileCounterCommandMethod.

Developer Studio imposes a few user-interface restrictions on your add-in:

The Developer Studio object model is available to your add-in, just as with a VBScript macro. Listing 17.3 shows a typical command handler. This command handler displays a modal message box that contains the number of currently open files in Developer Studio.

Example 17.3. A Simple Command Handler for a Developer Studio Add-In

STDMETHODIMP CCommands::BobsLineCounterCommandMethod()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    // Retrieve the Documents collection Automation interface
    CComPtr<IDispatch> pDocsDisp;
    VERIFY_OK(m_pApplication->get_Documents(&pDocsDisp));
    // Query for the vtable interface
    CComQIPtr<IDocuments, &IID_IDocuments> pDocs(pDocsDisp);
    if(!pDocs) return E_FAIL;

    // Get the total number of open documents.
    long docCount;
    VERIFY_OK(pDocs->get_Count(&docCount));

    // Format a message to be displayed to the user
    TCHAR szMsg[48];
    if(docCount != 1)
        wsprintf(szMsg, _T("There are %d open documents"), docCount);
    else
        wsprintf(szMsg, _T("There is one open document"));

    // Disable modeless dialog boxes, and display the message
    // to the user.
    VERIFY_OK(m_pApplication->EnableModeless(VARIANT_FALSE));
    MessageBox(NULL, szMsg, "XSourceInfo", MB_OK | MB_ICONINFORMATION);
    VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
    return S_OK;
}

Listing 17.3 begins by calling the AFX_MANAGE_STATE macro. You must call this macro in DLL entry points that are exported by DLLs using MFC. For more information about AFX_MANAGE_STATE, see Technical Note 58, "MFC Module State Implementation," in the MSDN Library.

After the AFX_MANAGE_STATE macro is called, the number of open documents is retrieved. This process consists of several actions:

  • Them_pApplication interface pointer is used to get the pointer to the Automation interface of the documents collection. This pointer is stored in an instance of CComPtr, an ATL wrapper class for COM interface pointers.
  • The ATL CComQIPtr class is used to query for the IDocument interface. CComQIPtr is another ATL smart pointer class that automatically queries for a specific interface. Both CComPtr and CComQIPtr are discussed in more detail in Chapter 29, "ATL Architecture."
  • Finally, the number of active documents is retrieved by calling the IDocuments::get_Count function.

Note that before displaying the modal message box dialog, modeless dialog boxes are disabled by calling the EnableModeless function. After the message box is dismissed by the user, EnableModeless is called again to re-enable modeless dialog boxes in Developer Studio.

In Listing 17.3, all functions that return an HRESULT are tested using the VERIFY_OK macro. In debug builds, this macro verifies that the function returns S_OK. If the function returns a different HRESULT value, a debug trace is created. In release builds, this macro has no effect.

A more complete example of an add-in is presented later in this chapter in the section "The SourceInfo Add-In."

Handling Developer Studio Events

In order to enable add-ins to be fully integrated into Developer Studio, the add-in architecture provides for events to be sent from the IDE to an add-in when specific events occur. These events include opening documents, starting project builds, and hitting a debugger breakpoint. There are two types of events: Application events are reported through the IApplicationEvents interface, and Debugger events are reported through the IDebuggerEvents interface.

The following Developer Studio events are provided to add-ins that support the IApplicationEvents interface:

Event Sent …
BeforeBuildStart As a build starts.
BuildFinish When a build ends. The number of warnings and errors is passed as parameters with the event.
BeforeApplicationShutDown As Developer Studio is shut down.
DocumentOpen When a document is opened in Developer Studio. A pointer to the document's IDispatch interface is passed as a parameter with the event.
BeforeDocumentClose Just before a document is closed. A pointer to the document's IDispatch interface is passed as a parameter with the event.
DocumentSave After a document is saved. A pointer to the document's IDispatch interface is passed as a parameter with the event.
NewDocument After a new document is created. A pointer to the document's IDispatch interface is passed as a parameter with the event.
WindowActivate After a window has become active. A pointer to the window's IDispatch interface is passed as a parameter with the event.
WindowDeactivate After a window has been deactivated. A pointer to the window's IDispatch interface is passed as a parameter with the event.
WorkspaceOpen When a workspace is opened in Developer Studio.
WorkspaceClose When a workspace is closed in Developer Studio.
NewWorkspace When a new workspace is created in Developer Studio.

In addition to the preceding events, add-ins that implement the IDebuggerEvents can receive the following event generated by the Developer Studio debugger:

IDebuggerEvents Sent after a breakpoint is hit. A pointer to the breakpoint's IDispatch interface is passed as a parameter with the event.

In order for your add-in to be notified of Developer Studio events, you must select the Responds to Developer Studio events option offered by the Add-In Wizard when your project is created. The wizard inserts the necessary code into your project and provides skeleton implementations for each of the Developer Studio events.

These events are used by add-ins that need to update their internal state when specific events occur. An add-in that tracks the status of project builds, for example, would provide a meaningful implementation for the BuildFinish event.

The default implementation provided by the Add-In Wizard simply returns S_OK for each event. As Listing 17.4 shows, events are handled in two nested classes enclosed in the CCommands class. The XApplicationEvents class handles application events, and the XDebuggerEvents class handles the debugger event.

Example 17.4. An Example of the Skeleton Event-Handling Code Provided By the Add-In Wizard

HRESULT CCommands::XApplicationEvents::BeforeApplicationShutDown()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    return S_OK;
}

Event-handling functions have the same restrictions as the command-handling functions discussed in the previous section. You must use the AFX_MANAGE_STATE macro, you cannot use modeless dialog boxes or views, and you must disable Developer Studio's modeless dialog boxes before displaying any of your own modal dialog boxes.

Share ThisShare This

Informit Network