/**
* \file DTEAddIn.cpp
*
* \brief Implementation file for class CDTEAddIn
*
* $Id: DTEAddIn.cpp, v1.2 2006/09/26 15:52:40 mgh Exp $
*
*
* Copyright (C) 2006 Michael G. Herstine <sp1ff@pobox.com>
*
* Permission to use, copy, or modify this source code is hereby granted
* free of charge, provided that this copyright notice appear on all
* copies and on all source code derived from this code. No
* representation is made regarding the suitability of this software for
* any purpose. It is provided "as is" without express or implied
* warranty.
*
*
*/
////////////////////////////////////////////////////////////////////////////
// Modified by Jordan Walters, 01.03.2008 for multi-IDE version increment //
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h" // Pre-compiled header
#include "AddIn.h" // For CreateInstanceWithParamItf
#include "DTEAddIn.h" // For class CDTEAddIn
#include "IncVersionUI/resource.h" // Resource definitions for our
#include <time.h>
#include <sstream>
#include <algorithm>
_ATL_FUNC_INFO OnClickButtonInfo = {
CC_STDCALL, VT_EMPTY, 2, { VT_DISPATCH, VT_BYREF | VT_BOOL }
};
////////////////////////////////////////////////////////////////////////
// Class CWindowEventsSink
CWindowEventsSink::CWindowEventsSink(CDTEAddIn *pParent) :
m_pParent(pParent)
{ }
void __stdcall
CWindowEventsSink::WindowClosing(EnvDTE::Window * /*pWindow*/)
{
ATLTRACE2(atlTraceHosting, 4, "CWindowEventSink::WindowClosing\n");
}
void __stdcall
CWindowEventsSink::WindowMoved(EnvDTE::Window * /*pWindow*/,
long /*nTop*/,
long /*nLeft*/,
long /*nWidth*/,
long /*nHeight*/)
{
ATLTRACE2(atlTraceHosting, 4, "CWindowEventSink::WindowMoved\n");
}
void __stdcall
CWindowEventsSink::WindowActivated(EnvDTE::Window * /*pGotFocus*/,
EnvDTE::Window * /*pLostFocus*/)
{
ATLTRACE2(atlTraceHosting, 4, "CWindowEventSink::WindowActivated\n");
}
void __stdcall
CWindowEventsSink::WindowCreated(EnvDTE::Window * /*pWindow*/)
{
ATLTRACE2(atlTraceHosting, 4, "CWindowEventsSink::WindowCreated\n");
}
////////////////////////////////////////////////////////////////////////
// Class CWindowEventsSink
CBuildEventsSink::CBuildEventsSink(CDTEAddIn *pParent) :
m_pParent(pParent)
{ }
void __stdcall CBuildEventsSink::BeforeBuildStart(EnvDTE::vsBuildScope /*Scope*/, EnvDTE::vsBuildAction /*Action*/)
{
g_mapListResourceName.clear();
g_mapListResourceContent.clear();
g_mapListOrigResourceContent.clear();
m_listIncWarningMessages.clear();
}
void __stdcall CBuildEventsSink::AfterBuildFinish(EnvDTE::vsBuildScope /*Scope*/, EnvDTE::vsBuildAction /*Action*/)
{
// Print/Display any error messages here.
if(m_listIncWarningMessages.size() > 0)
{
CComPtr<EnvDTE::Windows> pWindows = NULL;
if(SUCCEEDED(m_pParent->GetApplication()->get_Windows(&pWindows)))
{
CComPtr<EnvDTE::Window> pWindow = NULL;
if(SUCCEEDED(pWindows->Item(CComVariant(CComBSTR(EnvDTE::vsWindowKindOutput)), &pWindow)))
{
CComPtr<IDispatch> pDisp = NULL;
if(SUCCEEDED(pWindow->get_Object(&pDisp)))
{
CComQIPtr<EnvDTE::OutputWindow> pOutputWindow = pDisp;
CComPtr<EnvDTE::OutputWindowPanes> pOutputWindowPanes = NULL;
if(SUCCEEDED(pOutputWindow->get_OutputWindowPanes(&pOutputWindowPanes)))
{
CComPtr<EnvDTE::OutputWindowPane> pIncVersionWindowPane;
if(FAILED(pOutputWindowPanes->Item(CComVariant("IncVersion"), &pIncVersionWindowPane)))
{
pOutputWindowPanes->Add(CComBSTR("IncVersion"), &pIncVersionWindowPane);
}
pIncVersionWindowPane->Activate();
// Finally print the stuff out
pIncVersionWindowPane->Clear();
time_t ltime;
time(<ime);
struct tm today;
_localtime64_s(&today, <ime);
char timeBuf[128];
strftime(timeBuf, 128, "%#c", &today);
pIncVersionWindowPane->OutputString(CComBSTR("\n"));
pIncVersionWindowPane->OutputString(CComBSTR("IncVersion - ") + (timeBuf));
pIncVersionWindowPane->OutputString(CComBSTR("\n----------------------------------------------------------------\n"));
std::string strWarning;
std::list<std::string>::iterator it = m_listIncWarningMessages.begin();
std::list<std::string>::iterator itEnd = m_listIncWarningMessages.end();
for(; it != itEnd; ++it)
{
strWarning = *it + "\n";
CComBSTR strOutStr(strWarning.c_str());
pIncVersionWindowPane->OutputString(strOutStr);
}
}
}
}
}
}
}
void __stdcall CBuildEventsSink::BeforeProjConfBuildStart(BSTR sProject,
BSTR /*sProjectConfig*/,
BSTR /*sPlatform*/,
BSTR /*sSolutionConfig*/)
{
USES_CONVERSION;
ATLTRACE2(atlTraceHosting, 4, "CBuildEventsSink::BeforeProjConfBuildStart\n");
// We must call the version increment stuff BEFORE the build actually happens
// so that the rc/rc2 files get saved with the new versions.
// An (unfortunate) side effect is that the version gets updated every time you
// do a build, even if there have been no code changes.
// This function is called for each project built in the active configuration.
std::string strProjectName = m_pParent->GetSolutionPath() + "\\" + OLE2T(sProject);
// We can sometimes receive two of these notifications per project.
// So if we have already serviced an event for a project, ignore any subsequent
// events for that project.
if(g_mapListResourceName.find(strProjectName) == g_mapListResourceName.end())
{
std::list<std::string> strListResourceName;
std::list<std::string> strListResourceContent;
std::list<std::string> strListOrigResourceContent;
m_pParent->GetParent()->IncVersion(strProjectName,
es_IDE_BuildStart,
strListResourceName,
strListResourceContent,
strListOrigResourceContent,
m_listIncWarningMessages);
g_mapListResourceName[strProjectName] = strListResourceName;
g_mapListResourceContent[strProjectName] = strListResourceContent;
g_mapListOrigResourceContent[strProjectName] = strListOrigResourceContent;
}
}
void __stdcall CBuildEventsSink::BuildProjConfFinish(BSTR sProject,
BSTR /*sProjectConfig*/,
BSTR /*sPlatform*/,
BSTR /*sSolutionConfig*/,
bool bSuccess)
{
USES_CONVERSION;
ATLTRACE2(atlTraceHosting, 4, "CBuildEventsSink::BuildProjConfFinish\n");
std::string strProjectName = m_pParent->GetSolutionPath() + "\\" + OLE2T(sProject);
std::map<std::string, std::list<std::string>>::iterator rProject = g_mapListResourceName.find(strProjectName);
// We can sometimes receive two of these notifications per project.
// So if we have already serviced an event for a project, ignore any subsequent
// events for that project.
if(rProject != g_mapListResourceName.end())
{
if(!bSuccess)
{
// If the build failed, reset the resource version numbers to the original content
m_pParent->GetParent()->updateResource(g_mapListResourceName[strProjectName],
g_mapListOrigResourceContent[strProjectName]);
}
else if(g_mapListResourceContent.size() > 0)
{
// We incremented a digit. If it was not the build number, but the
// major/minor version/revision, and if the user has configured to revert
// to incrementing the build number, do this here.
DWORD dwDigit = DEF_DIGIT;
bool bResetLowerVersions = DEF_RESET;
bool bRevertToBuildNumber = DEF_REVERT;
getOptionsFromReg(dwDigit, bResetLowerVersions, bRevertToBuildNumber);
if(bRevertToBuildNumber)
{
dwDigit = BUILD_NUMBER_DIGIT;
setOptionsToReg(dwDigit, bResetLowerVersions, bRevertToBuildNumber);
}
}
// And remove the map entries.
g_mapListResourceName.erase(rProject);
}
}
////////////////////////////////////////////////////////////////////////
// Class CDTEAddIn
const wchar_t * CDTEAddIn::CMD_BAR_NAME = L"IncVersion";
const CComBSTR CDTEAddIn::CMD_INCREMENT(_T("IncVersion"));
const CComBSTR CDTEAddIn::CMD_DSC_INCREMENT(_T("Increment Version"));
const wchar_t * CDTEAddIn::CMD_FULL_INCREMENT = L"IncVersion.CoAddIn.IncVersion";
const CComBSTR CDTEAddIn::CMD_CONFIGURE(_T("Configure"));
const CComBSTR CDTEAddIn::CMD_DSC_CONFIGURE(_T("Configure the IncVersion AddIn"));
const wchar_t * CDTEAddIn::CMD_FULL_CONFIGURE = L"IncVersion.CoAddIn.Configure";
CDTEAddIn::CDTEAddIn() :
m_nHost(Host_Unknown),
m_objWindowEvents(this),
m_objBuildEvents(this)
{
}
HRESULT CDTEAddIn::FinalConstruct()
{
ATLTRACE2(atlTraceCOM, 2, "CDTEAddIn::FinalConstruct\n");
return S_OK;
}
void CDTEAddIn::FinalRelease()
{
ATLTRACE2(atlTraceCOM, 2, "CDTEAddIn::FinalRelease\n");
}
void CDTEAddIn::SetParam(CAddIn *pParent)
{
ATLASSERT(NULL != pParent);
m_pParent = pParent;
}
////////////////////////////////////////////////////////////////////////
// Interface ISupportsErrorInfo
STDMETHODIMP CDTEAddIn::InterfaceSupportsErrorInfo(/*[in]*/ REFIID riid)
{
static const IID* arr[] = {
&EnvDTE::IID_IDTCommandTarget,
&AddInDesignerObjects::IID__IDTExtensibility2,
};
for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); ++i)
{
if (InlineIsEqualGUID(*arr[i],riid)) return S_OK;
}
return S_FALSE;
}
////////////////////////////////////////////////////////////////////////
// IDTCommandTarget
/**
* \brief Returns the current status of the specified named command
*
*
* \param bszCmdName The name of the command to check
*
* \param nNeededText A vsCommandStatusTextWanted constant specifying if
* information is returned from the check, and if so, what type of
* information is returned (Name, None, or Status)
*
* \param pnStatus A vsCommandStatus specifying the current status of
* the command (Enabled, Invisible, Latched, Supported, or Unsupported)
*
* \param pvtCommandText The text to return if
* vsCommandStatusTextWantedStatus is specified
*
*
*/
STDMETHODIMP CDTEAddIn::QueryStatus(BSTR bszCmdName,
EnvDTE::vsCommandStatusTextWanted nNeededText,
EnvDTE::vsCommandStatus *pnStatusOption,
VARIANT * /*pvtCommandText*/)
{
const EnvDTE::vsCommandStatus STATUS_ON =
(EnvDTE::vsCommandStatus)(EnvDTE::vsCommandStatusEnabled +
EnvDTE::vsCommandStatusSupported);
if (EnvDTE::vsCommandStatusTextWantedNone == nNeededText)
{
if (!_wcsicmp(bszCmdName, CMD_FULL_INCREMENT) ||
!_wcsicmp(bszCmdName, CMD_FULL_CONFIGURE))
{
*pnStatusOption = STATUS_ON;
}
}
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Get active project. In VS.NET, return the first startup project.
// INPUT: None
// OUTPUT: Active project object.
// NOTE: DTE::Solution::solutionBuild::StartupProjects[0]
// StartupProjects contains a list of projects that "start" when the Run command is issued.
///////////////////////////////////////////////////////////////////////////////////////////
CComPtr<EnvDTE::Project> CDTEAddIn::GetActiveProject()
{
CComPtr<EnvDTE::Project> prj = GetFirstStartupProject();
if (prj) return prj;
// IMPORTANT NOTE: Startup project may be empty.
// ATLASSERT(prj!=NULL);
// Temp solution: use ActiveSolutionProjects[0] instead.
VARIANT actPrjs;
HRESULT re = m_pApp->get_ActiveSolutionProjects(&actPrjs);
ATLASSERT(SUCCEEDED(re));
#ifndef _DEBUG
UNREFERENCED_PARAMETER(re);
#endif
SAFEARRAY *a=actPrjs.parray;
VARIANT tempVariant;
LONG i[1]={a->rgsabound[0].lLbound};
for(LONG p = i[0];p<static_cast<long>(a->rgsabound[0].cElements);p++)
{
HRESULT re = SafeArrayGetElement(a,&p,&tempVariant);
if(FAILED(re))break;
IDispatch* idp = tempVariant.pdispVal;
CComPtr<EnvDTE::Project> tempPrj;
idp->QueryInterface(__uuidof(EnvDTE::Project), (LPVOID*)&tempPrj);
if(tempPrj)
{
return tempPrj;
}
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Get active project. In VS.NET, return the first startup project.
// INPUT: None
// OUTPUT: First startup project object.
// NOTE: DTE::Solution::solutionBuild::StartupProjects[0]
// StartupProjects contains a list of projects that "start" when the Run command is issued.
// Return value may be empty, for example App.vcproj, the project name is App1.
///////////////////////////////////////////////////////////////////////////////////////////
CComPtr<EnvDTE::Project> CDTEAddIn::GetFirstStartupProject()
{
CComPtr<EnvDTE::_Solution> so;
HRESULT re = m_pApp->get_Solution(&so);
ATLASSERT(SUCCEEDED(re));
CComPtr<EnvDTE::SolutionBuild> sb;
re = so->get_SolutionBuild(&sb);
ATLASSERT(SUCCEEDED(re));
BSTR s;
so->get_FileName(&s);
CComVariant variant;
sb->get_StartupProjects(&variant);
if(variant.vt == VT_EMPTY)
return NULL;
SAFEARRAY* array = variant.parray;
long ix = 0;
VARIANT var;
if(SafeArrayGetElement(array, &ix, &var) != S_OK)
return NULL;
//_bstr_t startUpRrjName(var.bstrVal,false);
// The return startup project is subdir\file.vcproj for example: App\App.vcproj. Remove the file name.
/*
CString sFile(var.bstrVal);
int nBegin = sFile.ReverseFind('\\');
nBegin ++;
int nEnd = sFile.ReverseFind('.');
if (nEnd==-1) nBegin = 999;
CString sPrj = sFile.Mid(nBegin,nEnd-nBegin);
*/
_bstr_t startUpRrjName(var.bstrVal);
CComPtr<EnvDTE::Project> prj = GetProjectByFileName(startUpRrjName);
return prj;
// IMPORTANT NOTE: Startup project may be empty.
}
///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Get active project name
// INPUT: None
// OUTPUT: The active project name.
///////////////////////////////////////////////////////////////////////////////////////////
_bstr_t CDTEAddIn::GetActiveProjectName()
{
BSTR bstr = NULL;
// if don't get the active project then return NULL
if(!GetActiveProject())
{
return _bstr_t(NULL, false);
}
else
{
GetActiveProject()->get_FullName(&bstr); /* here don't alloc memory to bstr */
}
_bstr_t bstrRet = _bstr_t(bstr, false);
return bstrRet;
}
/*******************************************************************************************
DESCRIPTION: Get project object in workspace/solution.
INPUT: The project name
OUTPUT: The project object.
*******************************************************************************************/
CComPtr<EnvDTE::Project> CDTEAddIn::GetProjectByFileName(const _bstr_t &sPrjFileName)
{
return GetProject(sPrjFileName, TRUE);
}
/*******************************************************************************************
DESCRIPTION: Get project object in workspace/solution.
INPUT: Project name if bIsFileName is FALSE by default
or the relative file name if bIsFileName is TRUE.
OUTPUT: The project object.
*******************************************************************************************/
CComPtr<EnvDTE::Project> CDTEAddIn::GetProject(const _bstr_t &sProject, BOOL bIsFileName)
{
USES_CONVERSION;
CComPtr<EnvDTE::_Solution> m_pSolution;
CComPtr<EnvDTE::Projects> m_pProjects;
ATLASSERT(m_pApp != NULL);
HRESULT re = m_pApp->get_Solution(&m_pSolution);
ATLASSERT(SUCCEEDED(re));
re = m_pSolution->get_Projects(&m_pProjects);
ATLASSERT(SUCCEEDED(re));
CComPtr<EnvDTE::Project> spFoundProj = NULL;
ATLASSERT(m_pProjects != NULL);
long count = GetProjectCount();
VARIANT va;
VariantInit(&va);
V_VT(&va)= VT_UINT;
for(long i = 1; i <= count && !spFoundProj; i++)
{
V_UINT(&va) = i;
CComPtr<EnvDTE::Project> spTempProj;
m_pSolution->Item(va, &spTempProj);
BSTR pname = NULL;
if (bIsFileName)
{
spTempProj->get_FullName(&pname);
std::string sFullFileName(_bstr_t(pname, false)), sFile(sProject);
std::transform(sFullFileName.begin(), sFullFileName.end(), sFullFileName.begin(), tolower);
std::transform(sFile.begin(), sFile.end(), sFile.begin(), tolower);
std::string::size_type nLen = sProject.length();
if (sFullFileName.length() > nLen && sFullFileName.substr(sFullFileName.length() - nLen) == sFile)
{
spFoundProj = spTempProj;
}
}
else
{
spTempProj->get_FullName(&pname);
_bstr_t s(pname, false);
if(0 == _wcsicmp(s, sProject)) // not case sensitive
{
spFoundProj = spTempProj;
}
}
}
VariantClear(&va);
return spFoundProj;
}
///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Get project count in workspace/solution.
// INPUT: None
// OUTPUT: The number of projects.
///////////////////////////////////////////////////////////////////////////////////////////
long CDTEAddIn::GetProjectCount()
{
ATLASSERT(m_pApp != NULL);
CComPtr<EnvDTE::_Solution> m_pSolution;
CComPtr<EnvDTE::Projects> m_pProjects;
HRESULT re = m_pApp->get_Solution(&m_pSolution);
ATLASSERT(SUCCEEDED(re));
re = m_pSolution->get_Projects(&m_pProjects);
ATLASSERT(SUCCEEDED(re));
long lCount = 0;
re = m_pProjects->get_Count(&lCount);
ATLASSERT(SUCCEEDED(re));
return lCount;
}
///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Get the full path to the solution - minus the actual solution name itself
// INPUT: None
// OUTPUT: The full solution path.
///////////////////////////////////////////////////////////////////////////////////////////
std::string CDTEAddIn::GetSolutionPath()
{
BSTR bstr = NULL;
CComPtr<EnvDTE::_Solution> m_pSolution;
HRESULT re = m_pApp->get_Solution(&m_pSolution);
ATLASSERT(SUCCEEDED(re));
re = m_pSolution->get_FullName(&bstr);
ATLASSERT(SUCCEEDED(re));
std::string strFullPathAndFilename = _bstr_t(bstr, false);
// Strip off the actual project name to leave just the full path.
std::string::size_type nBackslash = strFullPathAndFilename.find_last_of('\\');
std::string strFullPath = strFullPathAndFilename.substr(0, nBackslash);
return strFullPath;
}
/**
* \brief Execute a given named command
*
*
* \param bszCmdName The name of the command to execute
*
* \param nExecuteOption A vsCommandExecOption constant specifying the
* execution options
*
* \param pvtVariantIn A value passed to the command
*
* \param pvtVariantOut A value passed back to the invoker Exec method
* after the command executes
*
* \param pvfHandled true indicates that the command was
* implemented. false indicates that it was not
*
*
*/
STDMETHODIMP CDTEAddIn::Exec(BSTR bszCmdName,
EnvDTE::vsCommandExecOption nExecuteOption,
VARIANT * /*pvtVariantIn*/,
VARIANT * /*pvtVariantOut*/,
VARIANT_BOOL *pvfHandled)
{
USES_CONVERSION;
*pvfHandled = VARIANT_FALSE;
if (EnvDTE::vsCommandExecOptionDoDefault == nExecuteOption)
{
if (!_wcsicmp(bszCmdName, CMD_FULL_INCREMENT))
{
// User has pressed the IncVersion button.
std::string strProjectName = _bstr_t(GetActiveProjectName().GetBSTR(), false);
std::list<std::string> strListResourceName;
std::list<std::string> strListResourceContent;
std::list<std::string> strListOrigResourceContent;
std::list<std::string> listIncWarningMessages;
m_pParent->IncVersion(strProjectName,
es_User_ButtonPress,
strListResourceName,
strListResourceContent,
strListOrigResourceContent,
listIncWarningMessages);
if(listIncWarningMessages.size())
{
std::stringstream ssWarningText;
std::list<std::string>::iterator it = listIncWarningMessages.begin();
std::list<std::string>::iterator itEnd = listIncWarningMessages.end();
for(; it != itEnd; ++it)
{
ssWarningText << *it << "\n";
}
MessageBox(NULL, ssWarningText.str().c_str(), INCVERSION, MB_OK | MB_ICONWARNING);
}
g_mapListResourceName[strProjectName] = strListResourceName;
g_mapListResourceContent[strProjectName] = strListResourceContent;
g_mapListOrigResourceContent[strProjectName] = strListOrigResourceContent;
*pvfHandled = VARIANT_TRUE;
}
else if (!_wcsicmp(bszCmdName, CMD_FULL_CONFIGURE))
{
ConfigureInternal();
*pvfHandled = VARIANT_TRUE;
}
}
return S_OK;
}
////////////////////////////////////////////////////////////////////////
// IDTExtensibility2
/**
* \brief Called by our host to setup a connection to this AddIn
*
*
* \param pApplication An IDispatch interface on the object representing
* our host; we'll need to QI to find out exactly what interfaces it
* supports
*
* \param nMode An enumerated value indicating the circumstances of the
* connection:
*
* <ol>
*
* <li>ext_cm_AfterStartup (0) Add-in was loaded after the application
* started, or by setting the Connect property of the corresponding
* AddIn object to True.
*
* <li>ext_cm_Startup (1) Add-in was loaded at startup.
*
* <li>ext_cm_External (2) Add-in was loaded externally by another
* program or component.
*
* <li>ext_cm_CommandLine (3) Add-in was loaded through the Host's
* command line.
*
* <li>ext_cm_Solution (4) The Add-in was loaded when a user loaded a
* solution that required the Add-in.
*
* <li>ext_cm_UISetup (5) The Add-in was started for the first time
* since being installed.
*
* </ol>
*
* \param pAddInInst An IDispatch reference on an AddIn instance
* representing this AddIn
*
* \return S_OK on success, or a stock HRESULT else
*
*
* This method will check to be sure that our commands are there (adding
* them if they're not). Depending on the value of nMode &
* INCVERSION_COMMAND_BAR_STYLE, we may also setup our command bars.
* Finally, here is also where we sink any events we're interested in.
*
*
*/
STDMETHODIMP CDTEAddIn::OnConnection(IDispatch *pApplication,
AddInDO::ext_ConnectMode nMode,
IDispatch *pAddInInst,
SAFEARRAY ** /*ppCustom*/)
{
HRESULT hr = S_OK; // Eventual return value
try
{
// Validate our parameters...
if (NULL == pApplication) throw _com_error(E_INVALIDARG);
if (NULL == pAddInInst) throw _com_error(E_INVALIDARG);
// take a reference on the AddIn object representing us,
m_pAddIn = com_cast<EnvDTE::AddIn>(pAddInInst);
// & try to figure out what DTE-compatible host we're currently
// loaded into:
m_nHost = GuessHostType(pApplication);
m_pParent->SetApplicationObject((IApplication *)pApplication);
ATLTRACE2(atlTraceHosting, 2, "CoDTEAddIn has been loaded with a connect mode of %d (our host is %d).\n", nMode, m_nHost);
// Ok -- that done, we can actually carry on this method's actual work:
AddCommands(nMode); // adding our commands (and maybe toolbars),
SinkEvents(nMode); // & sink any events we care about.
// That's it -- we're done...
}
HANDLE_COM_ERROR(CLSID_CoDTEAddIn, AddInDO::IID__IDTExtensibility2, hr)
HANDLE_EXCEPTION(CLSID_CoDTEAddIn, AddInDO::IID__IDTExtensibility2, hr)
return hr;
} // End CDTEAddIn::OnConnection.
/**
* \brief Called by our host when this AddIn is unloaded
*
*
* \param nMode An ext_DisconnectMode enumeration value that informs an
* add-in why it was unloaded
*
* \param ppCustom An empty array that you can use to pass host-specific
* data for use after the add-in unloads
*
*
* This method un-advise any event sinks we've setup. Depending on the
* value of nMode & INCVERSION_COMMAND_BAR_STYLE, we may also remove our
* command bars.
*
*
*/
STDMETHODIMP CDTEAddIn::OnDisconnection(AddInDO::ext_DisconnectMode nMode,
SAFEARRAY ** /*ppCustom*/)
{
HRESULT hr = S_OK; // Eventual return value
try
{
ATLTRACE2(atlTraceHosting, 2, "CoDTEAddIn is being unloaded with"
" a removal code of %d.\n", nMode);
// Cleanup in the reverse order:
UnSinkEvents(nMode); // Un-advise any event sinks we've setup,
RemoveCommands(nMode); // & cleanup our command bars.
// That done, we just Release any outstanding references we may have
// on our host (or host-provided objects):
m_pAddIn = NULL;
m_pApp = NULL;
}
HANDLE_COM_ERROR(CLSID_CoDTEAddIn, AddInDO::IID__IDTExtensibility2, hr)
HANDLE_EXCEPTION(CLSID_CoDTEAddIn, AddInDO::IID__IDTExtensibility2, hr)
return hr;
} // End CDTEAddIn::OnDisconnection.
STDMETHODIMP CDTEAddIn::OnAddInsUpdate(SAFEARRAY ** /*custom*/)
{
return S_OK;
}
STDMETHODIMP CDTEAddIn::OnStartupComplete(SAFEARRAY ** /*custom*/)
{
return S_OK;
}
STDMETHODIMP CDTEAddIn::OnBeginShutdown(SAFEARRAY ** /*custom*/)
{
return S_OK;
}
void /*__stdcall*/ CDTEAddIn::OnClickInc(
IDispatch * /*Office::_CommandBarButton**/ /*pCtrl*/,
VARIANT_BOOL * /*pCancelDefault*/)
{
// User has pressed the IncVersion button.
std::string strProjectName = _bstr_t(GetActiveProjectName().GetBSTR(), false);
std::list<std::string> strListResourceName;
std::list<std::string> strListResourceContent;
std::list<std::string> strListOrigResourceContent;
std::list<std::string> listIncWarningMessages;
m_pParent->IncVersion(strProjectName,
es_User_ButtonPress,
strListResourceName,
strListResourceContent,
strListOrigResourceContent,
listIncWarningMessages);
if(listIncWarningMessages.size())
{
std::stringstream ssWarningText;
std::list<std::string>::iterator it = listIncWarningMessages.begin();
std::list<std::string>::iterator itEnd = listIncWarningMessages.end();
for(; it != itEnd; ++it)
{
ssWarningText << *it << "\n";
}
MessageBox(NULL, ssWarningText.str().c_str(), INCVERSION, MB_OK | MB_ICONWARNING);
}
g_mapListResourceName[strProjectName] = strListResourceName;
g_mapListResourceContent[strProjectName] = strListResourceContent;
g_mapListOrigResourceContent[strProjectName] = strListOrigResourceContent;
}
void /*__stdcall*/ CDTEAddIn::OnClickCfg(
IDispatch * /*Office::_CommandBarButton**/ /*pCtrl*/,
VARIANT_BOOL * /*pCancelDefault*/)
{
ConfigureInternal();
}
////////////////////////////////////////////////////////////////////////
/**
* \brief Make sure our commands are present & setup any needed toolbars
*
* \sa OnConnection
*
*
* \param nMode Enumerated value indicating the nature of this
* connection
*
*
* This method is only called from OnConnection; I just factored it out
* to make that method more readable.
*
*
*/
void CDTEAddIn::AddCommands(AddInDO::ext_ConnectMode nMode)
{
switch (m_nHost)
{
case Host_VS2003:
AddCommandsVS2003(nMode);
break;
case Host_VS2005:
AddCommandsVS2005(nMode);
break;
default:
ATLTRACE2(atlTraceHosting, 0, "CoDTEAddIn does not (yet) support"
" host %d!\n", m_nHost);
break;
} // End switch on host application.
} // End CDTEAddIn::AddCommands.
/**
* \brief Add our commands to Visual Studio .Net 2003
*
* \sa OnConnection, AddCommands, cmdbar_defns
*
*
* \param nMode Reason the host is connecting to us
*
* \throw _com_error On non-recoverable COM method failure
*
* \pre m_nHost is Host_VS2003
*
*
* Here is where we add commands & command bars to Visual Studio 2003.
* Each time this method is invoked, it will check the host app for our
* commands & add them, if they're not there.
*
* It may also add a command bar -- see \ref cmdbar_defns "here" for a
* discussion of how command bars are handled.
*
*
*/
void CDTEAddIn::AddCommandsVS2003(AddInDO::ext_ConnectMode nMode)
{
HRESULT hr = S_OK;
CComPtr<EnvDTE::Commands> pCmds;
CComPtr<IDispatch> pDisp, pDisp1, pDisp2;
CComPtr<EnvDTE::Command> pCmdInc, pCmdCfg;
ATLTRACE2(atlTraceHosting, 4, "CDTEAddIn::AddCommandsVS2003: %d\n",
nMode);
ATLASSERT(Host_VS2003 == m_nHost);
// Ok -- the first thing we do is check to see if our commands are
// defined. If they're not, we define 'em, no matter what 'nMode'
// is. We can find that out through the EnvDTE::Commands interface.
// We can get that interface from our host:
if (FAILED(hr = m_pApp->get_Commands(&pCmds))) throw _com_error(hr);
// We can check to see if a given command exists by calling the 'Item'
// method on the Commands interface with the fully-qualified command
// name (e.g. "IncVersion.CoAddIn.IncVersion").
hr = pCmds->Item(CComVariant(CMD_FULL_INCREMENT), -1L, &pCmdInc);
// If there is no such command, 'Item' will return E_INVALIDARG:
if (E_INVALIDARG == hr)
{
// It's not; this could be because it's the first time we've been
// loaded, or because the command was removed for some reason.
// Either way, add it now:
hr = pCmds->AddNamedCommand(
m_pAddIn, // Us -- this AddIn
CMD_INCREMENT, // Name
CMD_INCREMENT, // Button text
CMD_DSC_INCREMENT, // Tooltip
VARIANT_FALSE, // *Not* an MSO button
IDB_INC16, // res ID
NULL, // Context GUIDs
EnvDTE::vsCommandStatusSupported + // Disabled flags
EnvDTE::vsCommandStatusEnabled,
&pCmdInc); // [out] param
} // End if on no IncVersion command.
if (FAILED(hr)) throw _com_error(hr);
// Ok, now do the same for the "Configure" command:
hr = pCmds->Item(CComVariant(CMD_FULL_CONFIGURE), -1L, &pCmdCfg);
if (E_INVALIDARG == hr)
{
// It's not; this could be because it's the first time we've been
// loaded, or because the command was removed for some reason.
// Either way, add it now:
hr = pCmds->AddNamedCommand(
m_pAddIn, // Us-- this AddIn
CMD_CONFIGURE, // Name
CMD_CONFIGURE, // Button text
CMD_DSC_CONFIGURE, // Tooltip
VARIANT_FALSE, // *Not* an MSO button
IDB_CFG16, // Satellite DLL res ID
NULL, // Context GUIDs
EnvDTE::vsCommandStatusSupported + // Disabled flags
EnvDTE::vsCommandStatusEnabled,
&pCmdCfg); // [out] param
} // End if on no Configure command.
if (FAILED(hr)) throw _com_error(hr);
// At this point, we know that our commands are there. We next deal
// with our command bars...
#if(INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_TEMPORARY)
// We're using temporary command bars. In this case, we ignore the
// one time only "UISetup" invocation; we only create our command bar
// when the AddIn is loaded normally:
if (AddInDO::ext_cm_AfterStartup == nMode || // Loaded after app startup
AddInDO::ext_cm_Startup == nMode) // Loaded on app startup
{
ATLTRACE(atlTraceHosting, 2, "Creating a temporary command bar.\n");
// We'll create a temporary command bar. Visual Studio 2003 uses
// the Office Command Bars model. Specifically, we'll need
// Office::_CommandBars interface. We can get one by asking our
// host...
hr = m_pApp->get_CommandBars(&pDisp);
if (FAILED(hr))
{
ATLTRACE2(atlTraceHosting, 0, "WARNING: _DTE::get_CommandBars fa"
"iled with HRESULT 0x%08x! Skipping command bar creati"
"on.\n", hr);
return;
}
// & QI'ing for the interface we want:
CComQIPtr<Office::_CommandBars> pBars = pDisp;
if (NULL == pDisp) throw _com_error(hr); // Something's *really* wrong
CComVariant vtCmdBarName(CMD_BAR_NAME);
// At this point, we can add the Command Bar. First, however, let's
// be paranoid & see if it already exists:
CComQIPtr<Office::CommandBar> pBar; // Will hold a reference on our
// command bar, one way or
// the other...
// Just like our commands, we call the 'Item' method,
hr = pBars->get_Item(vtCmdBarName, &pBar);
// which will return E_INALIDARG if no such item exists:
if (E_INVALIDARG == hr)
{
hr = pBars->Add(
vtCmdBarName, // Name
CComVariant((LONG)Office::msoBarTop), // Position
CComVariant(), // Parent
CComVariant(VARIANT_TRUE), // Temporary
&m_pCommandBar); // [out] param
if (FAILED(hr)) throw _com_error(hr);
// If we're here, we have a brand new Command Bar. Now we need to
// add some controls to it; specifically, buttons invoking our
// commands.
hr = pCmdInc->AddControl(m_pCommandBar, 1L, &pDisp1);
if (FAILED(hr)) throw _com_error(hr);
hr = pCmdCfg->AddControl(m_pCommandBar, 2L, &pDisp2);
if (FAILED(hr)) throw _com_error(hr);
m_pCommandBar->put_Visible(VARIANT_TRUE);
} // End if on non-existent command bar.
if (FAILED(hr)) throw _com_error(hr);
} // End if on connect mode
#else // INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_PERMANENT ||
// INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_SEMIPERMANENT
// Ok -- we're using permanent command bars. In this scheme, we'll
// create them once, when we get the one-time-only startup code
// "UISetup":
if (5/*AddInDO::ext_cm_UISetup*/ == nMode)
{
pDisp = NULL;
CComBSTR bstrCmdBarName(CMD_BAR_NAME);
// Note that this time, we're calling EnvDTE::Commands::AddCommandBar,
// rather than Office::_CommandBars->Add.
hr = pCmds->AddCommandBar(
bstrCmdBarName, // Name
EnvDTE::vsCommandBarTypeToolbar, // Type
NULL, // Parent
0L, // Position
&pDisp); // [out] parameter
if (FAILED(hr)) throw _com_error(hr);
// This is irritating: the new Command Bar reference comes back as
// an IDispatch, but to do anything usefull, we have to QI to
// Office::CommandBar.
CComQIPtr<Office::CommandBar> pBar = pDisp;
if (NULL == pBar) throw _com_error(hr);
// Ok -- now we've got an Office::CommandBar reference on a new
// Command Bar, just like in the temporary case. Just the same, we
// need to add buttons:
hr = pCmdInc->AddControl(pBar, 1L, &pDisp1);
if (FAILED(hr)) throw _com_error(hr);
hr = pCmdCfg->AddControl(pBar, 2L, &pDisp2);
if (FAILED(hr)) throw _com_error(hr);
pBar->put_Visible(VARIANT_TRUE);
}
#endif // INCVERSION_COMMAND_BAR_STYLE
} // End AddCommandsVS2003.
/**
* \brief Add our commands to Visual Studio 2005
*
* \sa OnConnection, AddCommands, cmdbar_defns
*
*
* \param nMode Reason the host is connecting to us
*
* \throw _com_error On non-recoverable COM method failure
*
* \pre m_nHost is Host_VS2005
*
*
* Here is where we add commands & command bars to Visual Studio 2005.
* Each time this method is invoked, it will check the host app for our
* commands & add them, if they're not there.
*
* It may also add a command bar -- see \ref cmdbar_defns "here" for a
* discussion of how command bars are handled.
*
*
*/
void CDTEAddIn::AddCommandsVS2005(AddInDO::ext_ConnectMode nMode)
{
HRESULT hr = S_OK;
CComPtr<IDispatch> pDisp, pDisp1, pDisp2;
CComPtr<EnvDTE::Command> pCmdInc, pCmdCfg;
ATLTRACE2(atlTraceHosting, 4, "CDTEAddIn::AddCommandsVS2005: %d\n",
nMode);
ATLASSERT(Host_VS2005 == m_nHost);
// Ok -- the first thing we do is check to see if our commands are
// defined. If they're not, we define 'em, no matter what 'nMode' is.
// Now, EnvDTE::get_Commands still returns an EnvDTE::Commands
// interface, but I see that VS 2005 introduced a *new* interface,
// EnvDTE80::Commands. I'm not sure which one to use, but better to
// use the latest & greatest, I guess:
CComPtr<EnvDTE::Commands> pCmds;
if (FAILED(hr = m_pApp->get_Commands(&pCmds))) throw _com_error(hr);
CComQIPtr<EnvDTE80::Commands2> pCmds80 = pCmds;
if (NULL == pCmds80) throw _com_error(E_NOINTERFACE);
// We can check to see if a given command exists by calling the 'Item'
// method on the Commands interface with the fully-qualified command
// name (e.g. "IncVersion.CoAddIn.IncVersion").
hr = pCmds80->Item(CComVariant(CMD_FULL_INCREMENT),-1L, &pCmdInc);
// If there is no such command, 'Item' will return E_INVALIDARG:
if (E_INVALIDARG == hr)
{
// It's not; this could be because it's the first time we've been
// loaded, or because the command was removed for some reason.
// Either way, add it now:
hr = pCmds80->AddNamedCommand2(
m_pAddIn, // Us -- the AddIn
CMD_INCREMENT, // Name
CMD_INCREMENT, // Button text
CMD_DSC_INCREMENT, // Tooltip
VARIANT_FALSE, // *Not* an MSO button
CComVariant(IDB_INC16), // Satellite DLL res ID
NULL, // Context GUIDs
EnvDTE::vsCommandStatusSupported + // Disabled flags
EnvDTE::vsCommandStatusEnabled,
EnvDTE80::vsCommandStylePict, // Style flags
EnvDTE80::vsCommandControlTypeButton,// Control Type
&pCmdInc); // [out] param
} // End if on no Increment command.
if (FAILED(hr)) throw _com_error(hr);
// Ok, now do the same for the "Configure" command:
hr = pCmds80->Item(CComVariant(CMD_FULL_CONFIGURE),-1L, &pCmdCfg);
if (E_INVALIDARG == hr)
{
// It's not; this could be because it's the first time we've been
// loaded, or because the command was removed for some reason.
// Either way, add it now:
hr = pCmds80->AddNamedCommand2(
m_pAddIn, // Us-- the AddIn
CMD_CONFIGURE, // Name
CMD_CONFIGURE, // Button text
CMD_DSC_CONFIGURE, // Tooltip
VARIANT_FALSE, // *Not* an MSO button
CComVariant(IDB_CFG16), // Satellite DLL res ID
NULL, // Context GUIDs
EnvDTE::vsCommandStatusSupported + // Disabled flags
EnvDTE::vsCommandStatusEnabled,
EnvDTE80::vsCommandStylePict, // Style flags
EnvDTE80::vsCommandControlTypeButton,// Control Type
&pCmdCfg); // [out] param
} // End if on no Configure command.
if (FAILED(hr)) throw _com_error(hr);
// At this point, we know that our commands are there. We next deal
// with our command bars...
#if(INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_TEMPORARY)
// We're using temporary command bars. In this case, we ignore the
// one time only "UISetup" invocation; we only create our command bar
// when the AddIn is loaded normally:
if (AddInDO::ext_cm_AfterStartup == nMode || // Loaded after app startup
AddInDO::ext_cm_Startup == nMode) // Loaded on app startup
{
ATLTRACE(atlTraceHosting, 2, "Creating a temporary command bar.\n");
// We'll create a temporary command bar. Just like Visual Studio
// 2003, we begin by asking the app for it's Command Bars
// collection:
hr = m_pApp->get_CommandBars(&pDisp);
if (FAILED(hr))
{
ATLTRACE2(atlTraceHosting, 0, "WARNING: _DTE::get_CommandBars fa"
"iled with HRESULT 0x%08x! Skipping command bar creati"
"on.\n", hr);
return;
}
// Visual Studio 2005 introduced its own set of new Command Bar
// interfaces. Again, I'm not sure if we can just use the old ones.
CComQIPtr<MSVSCBs::_CommandBars> pBars = pDisp;
if (NULL == pDisp) throw _com_error(E_NOINTERFACE);
CComVariant vtCmdBarName(CMD_BAR_NAME);
// At this point, we can add the Command Bar. First, however, let's
// be paranoid & see if it already exists:
CComQIPtr<MSVSCBs::CommandBar> pBar;
// Just like our commands, we call the 'Item' method,
hr = pBars->get_Item(vtCmdBarName, &pBar);
// which will return E_INALIDARG if no such item exists:
if (E_INVALIDARG == hr)
{
CComVariant vtPosition((LONG)MsoBarPosition::msoBarFloating);
CComVariant vtEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR);
hr = pBars->Add(vtCmdBarName, // Name
vtPosition, // Position
CComVariant(VARIANT_FALSE), // Menu bar
CComVariant(VARIANT_TRUE), // Temporary
&m_pCommandBar80); // [out] param
if (FAILED(hr)) throw _com_error(hr);
// If we're here, we have a brand new Command Bar. Now we need to
// add some controls to it; specifically, buttons invoking our
// commands.
hr = pCmdInc->AddControl(m_pCommandBar, 1L, &pDisp1);
if (FAILED(hr)) throw _com_error(hr);
hr = pCmdCfg->AddControl(m_pCommandBar, 2L, &pDisp2);
if (FAILED(hr)) throw _com_error(hr);
m_pCommandBar80->put_Visible(VARIANT_TRUE);
} // End if on non-existent command bar.
if (FAILED(hr)) throw _com_error(hr);
} // End if on connect mode
#else // INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_PERMANENT ||
// INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_SEMIPERMANENT
// If this is the first time the AddIn's being loaded, create a
// Toolbar, as well.
if (5/*AddInDO::ext_cm_UISetup*/ == nMode)
{
CComBSTR bstrCmdBarName(CMD_BAR_NAME);
pDisp = NULL;
hr = pCmds80->AddCommandBar(bstrCmdBarName,
EnvDTE::vsCommandBarTypeToolbar,
NULL,
1L/*MSVSCBs::MsoBarPosition::msoBarTop*/,
&pDisp);
if (FAILED(hr)) throw _com_error(hr);
CComQIPtr<MSVSCBs::CommandBar> pBar = pDisp;
if (NULL == pBar) throw _com_error(hr);
hr = pCmdInc->AddControl(pBar, 1L, &pDisp1);
if (FAILED(hr)) throw _com_error(hr);
hr = pCmdCfg->AddControl(pBar, 2L, &pDisp2);
if (FAILED(hr)) throw _com_error(hr);
pBar->put_Visible(VARIANT_TRUE);
}
#endif // INCVERSION_COMMAND_BAR_STYLE
} // End AddCommandsVS2005.
void CDTEAddIn::ConfigureInternal()
{
ATLASSERT(Host_Unknown != m_nHost);
switch (m_nHost)
{
case Host_VS2003:
case Host_VS2005:
{
HRESULT hr = S_OK;
hr = m_pApp->ExecuteCommand(CComBSTR("Tools.Options"), CComBSTR(""));
hr;
break;
}
default: // Should never be here...
ATLASSERT(false);
throw std::logic_error("You didn't keep CDTEAddIn::ConfigureInternal up-to-date with the Host enumeration!");
}
} // End CDTEAddIn::ConfigureInternal.
Host CDTEAddIn::GuessHostType(IDispatch *pApp)
{
HRESULT hr = S_OK;
// Are we being hosted by Visual Studio 2005? I suspect this will be
// the most common case. Check by asking for an ENVDTE80::DTE2
// interface...
EnvDTE80::DTE2 *pDTE2Raw;
hr = pApp->QueryInterface(EnvDTE80::IID_DTE2, (void**)&pDTE2Raw);
if (SUCCEEDED(hr))
{
m_pApp = com_cast<EnvDTE::_DTE>(pApp);
pDTE2Raw->Release();
return Host_VS2005;
}
// Ok -- maybe it's Visual Studio 2003...
EnvDTE::_DTE *pDTERaw;
hr = pApp->QueryInterface(EnvDTE::IID__DTE, (void**)&pDTERaw);
if (SUCCEEDED(hr))
{
m_pApp = pDTERaw;
return Host_VS2003;
}
return Host_Unknown;
} // End GuessHost.
void CDTEAddIn::RemoveCommands(AddInDO::ext_DisconnectMode nMode)
{
switch (m_nHost)
{
case Host_VS2003:
RemoveCommandsVS2003(nMode);
break;
case Host_VS2005:
RemoveCommandsVS2005(nMode);
break;
default:
ATLTRACE2(atlTraceHosting, 0, "I don't support this host!\n");
} // End switch on host application.
}
/**
* \brief Remove our commands from Visual Studio .Net 2003
*
*
* \param nMode Reason indicating why we're being unloaded
*
*
*/
void CDTEAddIn::RemoveCommandsVS2003(AddInDO::ext_DisconnectMode nMode)
{
#if(INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_TEMPORARY)
// Temporary command bars are always deleted on unload...
nMode; // Shutup the compiler
if (NULL != m_pCommandBar)
{
HRESULT hr = m_pCommandBar->Delete();
if (FAILED(hr))
{
ATLTRACE2(atlTraceHosting, 0, "WARNING: Failed to delete the IncVersion (temporary) command bar!\n");
}
}
#elif(INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_PERMANENT)
// Do nothing -- the commands & command bar will be removed
// independently at un-install time.
nMode; // Shutup the compiler
#elif (INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_SEMIPERMANENT)
// If the user manually unloaded us, remove the command bar as well as
// our commands:
if (AddInDO::ext_dm_UserClosed == nMode)
{
HRESULT hr = S_OK;
CComPtr<EnvDTE::Commands> pCmds;
if (FAILED(hr = m_pApp->get_Commands(&pCmds))) throw _com_error(hr);
// First, remove our toolbar (if it's there):
CComPtr<IDispatch> pDisp;
if (FAILED(hr = m_pApp->get_CommandBars(&pDisp))) throw _com_error(hr);
CComQIPtr<Office::_CommandBars> pBars = pDisp;
CComPtr<Office::CommandBar> pBar;
hr = pBars->get_Item(CComVariant(CMD_BAR_NAME), &pBar);
if (SUCCEEDED(hr))
{
hr = pCmds->RemoveCommandBar(pBar);
if (FAILED(hr))
{
ATLTRACE2(atlTraceHosting, 0, "WARNING: Failed to delete the Increment Command Bar.\n");
}
}
// Now, remove our named commands:
CComPtr<EnvDTE::Command> pCmd;
hr = pCmds->Item(CComVariant(CMD_FULL_INCREMENT), -1L, &pCmd);
if (SUCCEEDED(hr))
{
hr = pCmd->Delete();
if (FAILED(hr))
{
ATLTRACE2(atlTraceHosting, 1, "WARNING: Failed to delete the IncVersion command.\n");
}
}
hr = pCmds->Item(CComVariant(CMD_FULL_CONFIGURE), -1L, &pCmd);
pCmd = NULL;
if (SUCCEEDED(hr))
{
hr = pCmd->Delete();
if (FAILED(hr))
{
ATLTRACE2(atlTraceHosting, 1, "WARNING: Failed to delete the Configure command.\n");
}
}
}
#endif // INCVERSION_COMMAND_BAR_STYLE
} // End RemoveCommandsVS2003.
/**
* \brief Remove our commands from Visual Studio .Net 2005
*
*
* \param nMode Reason indicating why we're being unloaded
*
*
*/
void CDTEAddIn::RemoveCommandsVS2005(AddInDO::ext_DisconnectMode nMode)
{
#if(INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_TEMPORARY)
// Temporary command bars are always deleted on unload...
nMode; // Shutup the compiler
if (NULL != m_pCommandBar80)
{
HRESULT hr = m_pCommandBar80->Delete();
if (FAILED(hr))
{
ATLTRACE2(atlTraceHosting, 0, "WARNING: Failed to delete the IncVersion (temporary) command bar!\n");
}
}
#elif(INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_PERMANENT)
// Do nothing -- the commands & command bar will be removed
// independently at un-install time.
nMode; // Shutup the compiler
#elif (INCVERSION_COMMAND_BAR_STYLE == INCVERSION_COMMAND_BAR_SEMIPERMANENT)
if (AddInDO::ext_dm_UserClosed == nMode)
{
HRESULT hr = S_OK;
CComPtr<EnvDTE::Commands> pCmds;
if (FAILED(hr = m_pApp->get_Commands(&pCmds))) throw _com_error(hr);
CComQIPtr<EnvDTE80::Commands2> pCmds2 = pCmds;
// First, remove our toolbar (if it's there):
CComPtr<IDispatch> pDisp;
if (FAILED(hr = m_pApp->get_CommandBars(&pDisp))) throw _com_error(hr);
CComQIPtr<MSVSCBs::_CommandBars> pBars = pDisp;
if (NULL == pBars) throw _com_error(hr);
CComPtr<MSVSCBs::CommandBar> pBar;
hr = pBars->get_Item(CComVariant(CMD_BAR_NAME), &pBar);
if (SUCCEEDED(hr))
{
hr = pCmds2->RemoveCommandBar(pBar);
if (FAILED(hr))
{
ATLTRACE2(atlTraceHosting, 0, "WARNING: Failed to delete the Increment Command Bar.\n");
}
}
// Now, remove our named commands:
CComPtr<EnvDTE::Command> pCmd;
hr = pCmds2->Item(CComVariant(CMD_FULL_INCREMENT), -1L, &pCmd);
if (SUCCEEDED(hr))
{
hr = pCmd->Delete();
if (FAILED(hr))
{
ATLTRACE2(atlTraceHosting, 1, "WARNING: Failed to delete the IncVersion command.\n");
}
}
pCmd = NULL;
hr = pCmds2->Item(CComVariant(CMD_FULL_CONFIGURE), -1L, &pCmd);
if (SUCCEEDED(hr))
{
hr = pCmd->Delete();
if (FAILED(hr))
{
ATLTRACE2(atlTraceHosting, 1, "WARNING: Failed to delete the Configure command.\n");
}
}
}
#endif // INCVERSION_COMMAND_BAR_STYLE
} // End RemoveCommandsVS2005.
void CDTEAddIn::SinkEvents(AddInDO::ext_ConnectMode nMode)
{
if (AddInDO::ext_cm_AfterStartup == nMode || // Loaded after app startup
AddInDO::ext_cm_Startup == nMode) // Loaded on app startup
{
switch (m_nHost)
{
case Host_VS2003:
case Host_VS2005:
{
CComPtr<EnvDTE::Events> pEvents;
HRESULT hr = m_pApp->get_Events(&pEvents);
if (FAILED(hr)) throw _com_error(hr);
hr = pEvents->get_WindowEvents(NULL, &m_pWinEvents);
if (SUCCEEDED(hr))
{
m_objWindowEvents.DispEventAdvise(m_pWinEvents);
}
hr = pEvents->get_BuildEvents(&m_pBuildEvents);
if (SUCCEEDED(hr))
{
m_objBuildEvents.DispEventAdvise(m_pBuildEvents);
}
break;
}
default:
ATLTRACE2(atlTraceHosting, 0, "CoDTEAddIn does not sink events "
"for host %d!\n", m_nHost);
}
}
} // End CDTEAddIn::SinkEvents.
void CDTEAddIn::UnSinkEvents(AddInDO::ext_DisconnectMode /*nMode*/)
{
switch (m_nHost)
{
case Host_VS2003:
case Host_VS2005:
if (m_pWinEvents)
{
m_objWindowEvents.DispEventUnadvise(m_pWinEvents);
m_pWinEvents = NULL;
}
if (m_pBuildEvents)
{
m_objBuildEvents.DispEventUnadvise(m_pBuildEvents);
m_pBuildEvents = NULL;
}
break;
}
} // End CDTEAddIn::UnSinkEvents.
// Local Variables:
// fill-column: 72
// indent-tabs-mode: nil
// show-trailing-whitespace: t
// End:
// DTEAddIn.cpp ends here.