Figure 7   Sample Macros


 Sub CloseExceptActive ()
 'DESCRIPTION: Closes all editor windows except the current one.

    'Windows.Item(1) is always the currently active window. So to close all
    ' the windows except the active one, keep looping until there is no
    ' longer a Windows.Item(2).
    do while Windows.Count > 1
       Windows.Item(2).Close(dsSaveChangesPrompt)
    Loop
 End Sub

 Sub CommentOut ()
 'DESCRIPTION: Comments out a selected block of text.
    Dim win
    set win = ActiveWindow
    if win.type <> "Text" Then
      MsgBox "This macro can only be run when a text editor window is active."
    else
       TypeOfFile = FileType(ActiveDocument)
       If TypeOfFile > 0 And TypeOfFile < 5 Then    'C & Java use the same
                                                    'style of comments.
          ActiveDocument.Selection = "/*" + ActiveDocument.Selection + "*/"
          ElseIf TypeOfFile = 5 Then
             ActiveDocument.Selection = "<!— " + ActiveDocument.Selection + " —>"
          ElseIf TypeOfFile = 6 Or TypeOfFile = 7 Then
          'There is no group comment like there is in the other file types,
          'so we need to iterate through each line, and prepend a ' to the line.
          'Also, because VBS/DEF does not have a 'end the comment at this
          'particular column' delimiter, entire lines of code must be
          'commented out, not sections.
             If TypeOfFile = 6 Then
                CommentType = " ' "
             Else
                CommentType = " ; "
             End If

             StartLine = ActiveDocument.Selection.TopLine
             EndLine = ActiveDocument.Selection.BottomLine
             If EndLine < StartLine Then
                Temp = StartLine
                StartLine = EndLine
                EndLine = Temp
             End If

             If EndLine = StartLine Then
                ActiveDocument.Selection = CommentType + ActiveDocument.Selection

             Else
                For i = StartLine To EndLine
                   ActiveDocument.Selection.GoToLine i
                   ActiveDocument.Selection.SelectLine
                   ActiveDocument.Selection = CommentType + _
                      ActiveDocument.Selection
                Next
             End If
          Else
             MsgBox("Unable to comment out the highlighted text" + vbLf + _
                    "because the file type was unrecognized." + vbLf + _
                    "If the file has not yet been saved, " + vbLf + _
                    "please save it and try again.")
       End If
    End If
 End Sub

 'Allows the user to make sure the current header file is included only once.
 ' There are two ways to do this, using the #pragma once directive or
 ' surrounding the entire file in a #ifndef/#endif structure. The first way
 ' is much cleaner, but it is Visual C++ specific, and therefore not portable. If
 ' you plan on compiling your code with other compilers, use the
 ' #ifndef/#endif method, otherwise, the #pragma once option is preferable.
 Sub OneTimeInclude ()
 'DESCRIPTION: Adds code to the current header file so it is included only once
 ' per c/cpp file.

    ext = ActiveDocument.Name
    If ext = "" Then
       If MsgBox("The file you are working with does not have a file extension." +_
          vbLF + "Are you sure this is a C/C++ header file?", 4) = vbCancel Then
             Exit Sub
       End If
       ext = "nofilenamegiven.h"
    End If
    DocName = UCase(ext)
    pos = Instr(ext, ".")
    Do While pos <> 1
       ext = Mid(ext, pos, (Len(ext) - pos + 1))
       pos = Instr(ext, ".")
    Loop
    ext = LCase(ext)
    pos = Instr(DocName, ".")
    If ext = ".h" Or ext = ".hpp" Then
       'Warn user that this will not work with a compiler other than Visual C++.
       If MsgBox("This macro uses the Visual C++ dependant #pragma once" + _
                 vbLf + "Is the source to be portable across compilers?", 4) _
                 = 6 Then
          ActiveDocument.Selection.StartOfDocument (False)
          Examp = "__" + Left(DocName, pos - 1) + "_" + _
                  UCase(Right(ext, len(ext) - 1)) + "__"
          ControlVarName = InputBox("What should the control variable be?" _
                           + vbLf + vbLf + "Example: #ifdef " + _
                           Examp, "One time header include protection", Examp)
          If ValidId (ControlVarName) = True Then
             ActiveDocument.Selection = "#ifndef " + ControlVarName + _
                                        vbLf + "#define " + ControlVarName + vbLf
             ActiveDocument.Selection.EndOfDocument(False)
             ActiveDocument.Selection = vbLf + "#endif //" + _
                                        ControlVarName
          Else
             MsgBox(ControlVarName + " is not a valid c identifier." + _
                    vbLf + "please re-run the macro with a valid C identifier")
          End If
       Else
          ActiveDocument.Selection.StartOfDocument(False)
          ActiveDocument.Selection = "#pragma once" + vbLf + vbLf
       End If
    Else
       MsgBox("This macro can only be run on .h or .hpp files")
    End If
 End Sub


Figure 10   Wizard-generated Add-in Classes

Class Name
Description
CCommands
Implements one or more commands that are made available to Developer Studio by exposing an IDispatch-derived interface named ICommands.
CDSAddIn
Implements the IDSAddIn custom interface declared in header files provided with Developer Studio. This interface exposes two methods that Developer Studio uses to communicate with the add-in: OnConnection and OnDisconnection.
CProjectNameApp
This CWinApp-derived class provides initialization and registration code for the add-in.


Figure 11   CDSAddIn


 // AddInMod.cpp : implementation file
 //

 #include "stdafx.h"
 #include "Replace.h"
 #include "DSAddIn.h"
 #include "Commands.h"

 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif

 // 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)
 {
       AFX_MANAGE_STATE(AfxGetStaticModuleState());

       // Store info passed to us
       IApplication* pApplication = NULL;
       if (FAILED(pApp->QueryInterface(IID_IApplication, (void**) &pApplication))
             || pApplication == NULL)
       {
             *OnConnection = VARIANT_FALSE;
             return S_OK;
       }

       m_dwCookie = dwCookie;

       // Create command dispatch, send info back to DevStudio
       CCommandsObj::CreateInstance(&m_pCommands);
       m_pCommands->AddRef();

       // The QueryInterface above AddRef'd the Application object.  It will
       //  be Release'd in CCommand's destructor.
       m_pCommands->SetApplicationObject(pApplication);

       // (see stdafx.h for the definition of VERIFY_OK)

       VERIFY_OK(pApplication->SetAddInInfo((long) AfxGetInstanceHandle(),
                                     (LPDISPATCH) m_pCommands, IDR_TOOLBAR_MEDIUM,
                                     IDR_TOOLBAR_LARGE, m_dwCookie));

       // Inform DevStudio of the commands we implement

       // TODO: Replace the AddCommand call below with a series of calls,
       //  one for each command your add-in will add.

       // The command name should not be localized to other languages.  The
       //  tooltip, command description, and other strings related to this
       //  command are stored in the string table (IDS_CMD_STRING) and should
       //  be localized.
       LPCTSTR szCommand = _T("ReplaceCommand");
       VARIANT_BOOL bRet;
       CString strCmdString;
       strCmdString.LoadString(IDS_CMD_STRING);
       strCmdString = szCommand + strCmdString;
       CComBSTR bszCmdString(strCmdString);
       CComBSTR bszMethod(_T("ReplaceCommandMethod"));
       CComBSTR bszCmdName(szCommand);
       VERIFY_OK(pApplication->AddCommand(bszCmdString, bszMethod, 0, m_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, bszCmdName,
                                                         m_dwCookie));
       }

       *OnConnection = VARIANT_TRUE;
       return S_OK;
 }

 // This is called on shut-down, and also when the user unloads the add-in
 STDMETHODIMP CDSAddIn::OnDisconnection(VARIANT_BOOL bLastTime)
 {
       AFX_MANAGE_STATE(AfxGetStaticModuleState());

       m_pCommands->Release();
       m_pCommands = NULL;

       // TODO: Perform any cleanup work here

       return S_OK;
 }


Figure 12   Ccommands


 // Commands.cpp : implementation file

 #include "stdafx.h"
 #include "Replace.h"
 #include "Commands.h"
 #include "ReplaceDlg.h"

 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif

 /////////////////////////////////////////////////////////////////////////////
 // CCommands

 CCommands::CCommands()
 {
       m_pApplication == NULL;
 }

 CCommands::~CCommands()
 {
       ASSERT (m_pApplication != NULL);
       m_pApplication->Release();
 }

 void CCommands::SetApplicationObject(IApplication* pApplication)
 {
       // This function assumes pApplication has already been AddRef'd
       //  for us, which CDSAddIn did in its QueryInterface call
       //  just before it called us.
       m_pApplication = pApplication;
 }

 /////////////////////////////////////////////////////////////////////////////
 // CCommands methods

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

       CReplaceDlg dlg(m_pApplication);
       dlg.DoModal();

       VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
       return S_OK;
 }


Figure 15   BreakpointHit Event Handler


 HRESULT CCommands::XDebuggerEvents::BreakpointHit(IDispatch* pBP)
 {
       AFX_MANAGE_STATE(AfxGetStaticModuleState());
       CComQIPtr<IBreakpoint, &IID_IBreakpoint> pBreakpoint = pBP;

       // We only care about line# breakpoints.  We can tell whether this
       //  is a line# breakpoint by seeing whether its Location property
       //  begins with a period (e.g., ".253")

       CComBSTR bstrLocation;
       pBreakpoint->get_Location(&bstrLocation);
       if (bstrLocation.Length() == 0 || *(BSTR)bstrLocation != '.')
             return S_OK;

       // Is it enabled as a tracepoint?

       CComBSTR bstrFile;
       pBreakpoint->get_File(&bstrFile);
       CString strFullInfo = (BSTR) bstrFile;
       strFullInfo += (BSTR) bstrLocation;
       BOOL* pEnable = NULL;
       m_pCommands->m_mapEnable.Lookup(strFullInfo, (void*&)pEnable);
       if (pEnable == NULL || *pEnable == FALSE)
             return S_OK;

       // Yes, it's a tracepoint.  Let's output the expressions.

       CStringArray* pExprArray = NULL;
       m_pCommands->m_mapExpr.Lookup(strFullInfo, (void*&)pExprArray);
       ASSERT(pExprArray);

       IDebugger* pDebugger = m_pCommands->GetDebuggerObject();
       IApplication* pApplication = m_pCommands->GetApplicationObject();
       for (int nLoop = 0; nLoop < pExprArray->GetSize(); nLoop++)
       {
             CComBSTR bstrValue(_T("<Expression could not be evaluated>"));
             pDebugger->Evaluate(CComBSTR(pExprArray->GetAt(nLoop)),
                                 &bstrValue);

             CComBSTR bstrOut(pExprArray->GetAt(nLoop));
             bstrOut += CComBSTR(_T(" = "));
             bstrOut += bstrValue;

             pApplication->PrintToOutputWindow(bstrOut);
       }

       pDebugger->Go();
       return S_OK;
 }


Figure 17   A Developer Studio Project File


 // Partial sample DSP file

 !IF  "$(CFG)" == "Signature - Win32 Release"

 # PROP BASE Use_MFC 2
 # PROP BASE Use_Debug_Libraries 0
 # PROP BASE Output_Dir "Release"
 # PROP BASE Intermediate_Dir "Release"
 # PROP BASE Target_Dir ""
 # PROP Use_MFC 2
 # PROP Use_Debug_Libraries 0
 # PROP Output_Dir "Release"
 # PROP Intermediate_Dir "Release"
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_AFXDLL" /Yu"stdafx.h" /FD /c
 # ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_AFXDLL" /D "_USRDLL" /Yu"stdafx.h" /FD /c
 # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
 # ADD BASE RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL"
 # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL"
 BSC32=bscmake.exe
 # ADD BASE BSC32 /nologo
 # ADD BSC32 /nologo
 LINK32=link.exe
 # ADD BASE LINK32 /nologo /subsystem:windows /dll /machine:I386
 # ADD LINK32 /nologo /subsystem:windows /dll /machine:I386 /out:"Release\Signature.ocx"
 # Begin Custom Build - Registering ActiveX Control...
 OutDir=.\Release
 TargetPath=.\Release\Signature.ocx
 InputPath=.\Release\Signature.ocx
 SOURCE=$(InputPath)

 "$(OutDir)\regsvr32.trg" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
       regsvr32 /s /c "$(TargetPath)"
       echo regsvr32 exec. time > "$(OutDir)\regsvr32.trg"

 # End Custom Build


Figure 18   CustomizeProject


 void CWebDCAppWiz::CustomizeProject(IBuildProject* pProject)
 {
    IConfigurations* pConfigs = NULL;
    pProject->get_Configurations(&pConfigs);
    ASSERT(pConfigs);

    CComPtr<IUnknown> pUnk;
    CComQIPtr<IEnumVARIANT, &IID_IEnumVARIANT> pNewEnum;
    if (SUCCEEDED(pConfigs->get__NewEnum(&pUnk)) && pUnk != NULL)
    {
       pNewEnum = pUnk;
       VARIANT varConfig;
       CComQIPtr<IConfiguration, &IID_IConfiguration> pConfig;
          while (pNewEnum->Next(1, &varConfig, NULL) == S_OK)
       {
          ASSERT (varConfig.vt == VT_DISPATCH);
          pConfig = varConfig.pdispVal;
          VariantClear(&varConfig);

             // Add Control registration custom build step

             VARIANT reserved;
             CComBSTR bstrCommand(
                "regsvr32 /s /c \"$(TargetPath)\"\n"
                "echo regsvr32 exec. time > \"$(OutDir)\\regsvr32.trg\"");
             CComBSTR bstrOutput("$(OutDir)\\regsvr32.trg");
             CComBSTR bstrDescription("Registering ActiveX Control...");
             pConfig->AddCustomBuildStep(bstrCommand, bstrOutput,
                                         bstrDescription, reserved);

             // Remove old output name (DLL)

             CComBSTR bstrTool = "link.exe";
             CComBSTR bstrOption = "/out:";
             pConfig->RemoveToolSettings(bstrTool, bstrOption, reserved);

             // Change output name to OCX

             USES_CONVERSION;
          CComBSTR bstrName;
          pConfig->get_Name(&bstrName);
             BOOL bDebug = (BOOL) _tcsstr(OLE2T(bstrName), _T("Debug"));
             bstrOption = bDebug ? "/out:Debug\\" : "/out:Release\\";
             bstrOption += m_Dictionary[_T("safe_root")].AllocSysString();
             bstrOption += ".ocx";
             pConfig->AddToolSettings(bstrTool, bstrOption, reserved);
       }
    }
 }