Click here to Skip to main content
15,894,405 members
Articles / Desktop Programming / ATL

Increment File and Product Version Number - Multiple IDE

Rate me:
Please Sign up or sign in to vote.
4.58/5 (32 votes)
21 Oct 2008CPOL12 min read 293.6K   1.2K   134  
An add-in to automatically increment the FileVersion and ProductVersion fields in your application's resource file. Works in VC6 and VS2005, and probably all versions in between.
/**
 * \file DSAddIn.cpp
 *
 * \brief Implementation file for class CDSAddIn
 *
 * $Id: DSAddIn.cpp, v1.1.1.1 2006/09/24 23:12:30 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 "DSAddIn.h"            // For class CDSAddIn

#include <time.h>

#include <sstream>

////////////////////////////////////////////////////////////////////////
// Class CDSCommands

void CDSCommands::SetParam(CAddIn *pParent)
{
	ATLASSERT(NULL != pParent);
	m_pParent = pParent;
}

void CDSCommands::IncVersion()
{
	USES_CONVERSION;

	ATLASSERT(NULL != m_pParent);

	// User has pressed the IncVersion button.
	IGenericProject* pProject = NULL;
	if(SUCCEEDED(m_pParent->GetApplicationObject()->get_ActiveProject((LPDISPATCH*)&pProject)))
	{
		USES_CONVERSION;
		CComBSTR bstrProjectName;
		if(SUCCEEDED(pProject->get_FullName(&bstrProjectName.m_str)))
		{
			std::string strProjectName = OLE2T(bstrProjectName.m_str);
			if(m_pParent->isCPlusPlusProject(strProjectName))
			{
				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;
			}
		}
		pProject->Release();
	}
}

void CDSCommands::Configure()
{
	ATLASSERT(NULL != m_pParent);
	m_pParent->Configure();
}

////////////////////////////////////////////////////////////////////////
// Class CDSAppEvents

void CDSAppEvents::SetParam(CAddIn *pParent)
{
	ATLASSERT(NULL != pParent);
	m_pParent = pParent;
}

void CDSAppEvents::Connect(IUnknown* pUnk)
{
	HRESULT hr = AtlAdvise(pUnk, this, IID_IApplicationEvents,
		&m_dwAdvise);
	if (FAILED(hr)) throw _com_error(hr);
}

void CDSAppEvents::Disconnect(IUnknown* pUnk)
{
	AtlUnadvise(pUnk, IID_IApplicationEvents, m_dwAdvise);
}

////////////////////////////////////////////////////////////////////////
// Interface IApplicationEvents

STDMETHODIMP CDSAppEvents::BeforeBuildStart()
{
	USES_CONVERSION;

	ATLTRACE2(atlTraceIncVersion, 2, "BeforeBuildStart\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.
	IApplication *pApplication = m_pParent->GetApplicationObject();
	IGenericProject* pProject = NULL;
	if(SUCCEEDED(pApplication->get_ActiveProject((LPDISPATCH*)&pProject)))
	{
		CComBSTR bstrProjectName;
		if(SUCCEEDED(pProject->get_FullName(&bstrProjectName.m_str)))
		{
			std::string strProjectName = OLE2T(bstrProjectName.m_str);
			if(m_pParent->isCPlusPlusProject(strProjectName))
			{
				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_IDE_BuildStart,
					strListResourceName,
					strListResourceContent,
					strListOrigResourceContent,
					listIncWarningMessages);

				if(listIncWarningMessages.size())
				{
					time_t ltime;
					time(&ltime);
					struct tm today;
					_localtime64_s(&today, &ltime);
					char timeBuf[128];
					strftime(timeBuf, 128, "%#c", &today);
					pApplication->PrintToOutputWindow(CComBSTR(""));
					pApplication->PrintToOutputWindow(CComBSTR("IncVersion - ") + (timeBuf));
					pApplication->PrintToOutputWindow(CComBSTR("----------------------------------------------------------------"));

					std::list<std::string>::iterator it = listIncWarningMessages.begin();
					std::list<std::string>::iterator itEnd = listIncWarningMessages.end();
					for(; it != itEnd; ++it)
					{
						CComBSTR strOutStr(it->c_str());
						pApplication->PrintToOutputWindow(strOutStr);
					}
				}

				g_mapListResourceName[strProjectName] = strListResourceName;
				g_mapListResourceContent[strProjectName] = strListResourceContent;
				g_mapListOrigResourceContent[strProjectName] = strListOrigResourceContent;
			}
		}
		pProject->Release();
	}
	return S_OK;
}

STDMETHODIMP CDSAppEvents::BuildFinish(long nErrors, long /*nWarnings*/)
{
	USES_CONVERSION;

	ATLTRACE2(atlTraceIncVersion, 2, "BuildFinish\n");
	if(nErrors != 0)
	{
		// If the build failed, reset the resource version numbers to the original content
		IGenericProject* pProject = NULL;
		if(SUCCEEDED(m_pParent->GetApplicationObject()->get_ActiveProject((LPDISPATCH*)&pProject)))
		{
			CComBSTR bstrProjectName;
			if(SUCCEEDED(pProject->get_FullName(&bstrProjectName.m_str)))
			{
				std::string strProjectName = OLE2T(bstrProjectName.m_str);
				m_pParent->updateResource(g_mapListResourceName[strProjectName],
					g_mapListOrigResourceContent[strProjectName]);
			}
			pProject->Release();
		}
	}
	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;
		bool bDisplayWarnings = DEF_DISPLAY_WARNINGS;
		getOptionsFromReg(dwDigit, bResetLowerVersions, bRevertToBuildNumber, bDisplayWarnings);
		if(bRevertToBuildNumber)
		{
			dwDigit = BUILD_NUMBER_DIGIT;
			setOptionsToReg(dwDigit, bResetLowerVersions, bRevertToBuildNumber, bDisplayWarnings);
		}
	}
	return S_OK;
}

STDMETHODIMP CDSAppEvents::BeforeApplicationShutDown()
{
	ATLTRACE2(atlTraceIncVersion, 2, "BeforeApplicationShutDown\n");
	return S_OK;
}

STDMETHODIMP CDSAppEvents::DocumentOpen(IDispatch * /*pDocument*/)
{
	ATLTRACE2(atlTraceIncVersion, 2, "DocumentOpen\n");
	return S_OK;
}

STDMETHODIMP CDSAppEvents::BeforeDocumentClose(IDispatch * /*pDocument*/)
{
	ATLTRACE2(atlTraceIncVersion, 2, "BeforeDocumentClose\n");
	return S_OK;
}

STDMETHODIMP CDSAppEvents::DocumentSave(IDispatch * /*pDocument*/)
{
	ATLTRACE2(atlTraceIncVersion, 2, "DocumentSave\n");
	return S_OK;
}

STDMETHODIMP CDSAppEvents::NewDocument(IDispatch * /*pDocument*/)
{
	ATLTRACE2(atlTraceIncVersion, 2, "NewDocument\n");
	return S_OK;
}

STDMETHODIMP CDSAppEvents::WindowActivate(IDispatch * /*pWindow*/)
{
	ATLTRACE2(atlTraceIncVersion, 2, "WindowActivate\n");
	return S_OK;
}

STDMETHODIMP CDSAppEvents::WindowDeactivate(IDispatch * /*pWindow*/)
{
	ATLTRACE2(atlTraceIncVersion, 2, "WindowDeactivate\n");
	return S_OK;
}

STDMETHODIMP CDSAppEvents::WorkspaceOpen()
{
	ATLTRACE2(atlTraceIncVersion, 2, "WorkspaceOpen\n");
	return S_OK;
}

STDMETHODIMP CDSAppEvents::WorkspaceClose()
{
	ATLTRACE2(atlTraceIncVersion, 2, "WorkspaceClose\n");
	return S_OK;
}

STDMETHODIMP CDSAppEvents::NewWorkspace()
{
	ATLTRACE2(atlTraceIncVersion, 2, "NewWorkspace\n");
	return S_OK;
}


////////////////////////////////////////////////////////////////////////
// Class CDSAddIn

const TCHAR * const CDSAddIn::VAL_CREATED_DSTB =
_T("CreatedDSTb");

const TCHAR * const CDSAddIn::WND_BASE_NAME =
_T("###INCVERSION-MessageWnd-f61141fa-8c74-11da-bf67-0007e946f29c-");

const TCHAR * const CDSAddIn::WND_CLASS_NAME =
_T("###INCVERSION-WNDCLASS-f61141f9-8c74-11da-bf67-0007e946f29c###");


CDSAddIn::CDSAddIn() : m_pParent(NULL)
{ }

/**
 * \brief ATL-defined initialization routine
 *
 * \sa OnConnection
 *
 *
 * \return S_OK on success, a failed HRESULT to abort object creation
 *
 *
 * I don't care for the trivial constructor/initialization routine
 * approach.  ATL does, however, and when in Rome...
 *
 * This method will create a hidden window for posting message to
 * ourselves.  See the documentation for OnConnection as to why this
 * would be useful.  The message window is destroyed in FinalRelease.
 *
 *
 * \pre m_pParent has been initialized
 *
 *
 */

HRESULT CDSAddIn::FinalConstruct()
{
	ATLTRACE2(atlTraceCOM, 2, "CDSAddIn::FinalConstruct\n");

	ATLASSERT(NULL != m_pParent);

	// Create our message window:
	WNDCLASS wndclass;
	::memset(&wndclass, 0, sizeof(WNDCLASS));
	wndclass.lpfnWndProc   = MsgWindowProc;
	wndclass.hInstance     = _AtlBaseModule.m_hInst;
	wndclass.lpszClassName = WND_CLASS_NAME;

	if (!::RegisterClass(&wndclass))
	{
		DWORD dwStatus = ::GetLastError();
		if (ERROR_CLASS_ALREADY_EXISTS != dwStatus)
		{
			return HRESULT_FROM_WIN32(::GetLastError());
		}
	}

	// We should concoct a unique Window name, in case multiple DevStudio
	// instances are running simultaneously.  We use this DLL's HINSTANCE.
	ATLASSERT(_tcslen(WND_BASE_NAME) + 8 + 3 + 1 <= WND_NAME_STR_LEN);

	TCHAR lpszWndName[WND_NAME_STR_LEN];
	_sntprintf_s(lpszWndName, WND_NAME_STR_LEN, _T("%s%08x###"),
		WND_BASE_NAME, HandleToUlong(_AtlBaseModule.m_hInst));

	// Create our hidden, message-only Window:
	m_hWnd = ::CreateWindow(WND_CLASS_NAME,         // Window class
		lpszWndName,            // Window name
		0U,                     // Window style
		0,                      // x position
		0,                      // y position
		0,                      // width
		0,                      // height
		HWND_MESSAGE,           // Parent Window
		NULL,                   // Menu
		_AtlBaseModule.m_hInst, // Owning instance
		this);                  // Create params
	if (NULL == m_hWnd)
	{
		return HRESULT_FROM_WIN32(::GetLastError());
	}

	ATLTRACE2(atlTraceWindowing, 3, "Created a message window with HWND"
		" 0x%08x.\n", m_hWnd);

	return S_OK;
}

void CDSAddIn::FinalRelease()
{
	ATLTRACE2(atlTraceCOM, 2, "CDSAddIn::FinalRelease\n");

	// Destroy *our* message window...
	::DestroyWindow(m_hWnd);

	// & if that was the last one...
	if (!::FindWindow(WND_CLASS_NAME, NULL))
	{
		// de-register the WNDCLASS.
		::UnregisterClass(WND_CLASS_NAME, _AtlBaseModule.m_hInst);
	}

} // End CDSAddIn::FinalRelease.

void CDSAddIn::SetParam(CAddIn *pParent)
{
	ATLASSERT(NULL != pParent);

	m_pParent = pParent;
}


////////////////////////////////////////////////////////////////////////
// Interface ISupportsErrorInfo

STDMETHODIMP CDSAddIn::InterfaceSupportsErrorInfo(REFIID riid)
{
	return InlineIsEqualGUID(IID_IDSAddIn, riid) ? S_OK : S_FALSE;
}


////////////////////////////////////////////////////////////////////////
// Interface IDSAddIn

/**
 * \brief Connect to this AddIn
 *
 *
 * \param pApp An IApplication reference on the DevStudio 6.0
 * Application object
 *
 * \param vfFirstTime If this is the first time the AddIn has been
 * loaded, DevStudio will set this to VARIANT_TRUE
 *
 * \param lCookie A cookie this AddIn will use to identify itself to
 * DevStudio in assorted method calls
 *
 * \param pvfOnConnection On successful return, this parameter should be
 * set to VARIANT_TRUE.
 *
 * \return S_OK on success, a stock HRESULT otherwise
 *
 *
 * I've implemented this method in a different way from the code the
 * AppWizard generates.  First, we don't depend on the vfFirstTime
 * parameter, leaving us the possibility of registering the AddIn at
 * installation, so the user doesn't have to browse from Tools |
 * Customize | Addins & Macro Files.
 *
 * This complicates toolbar creation, since a) we need to keep track
 * ourselves whether or not it's been created, and b) calling
 * AddCommandBarButton when vfFirstTime is \em not true will fail.
 *
 * I've solved problem a) by just writing down a boolean in the Registry.
 * I've solved problem b) by posting a message to our hidden message
 * window (it turns out that AddCommandBarButton will succeed if we call
 * it outside the context of OnConnection).
 *
 * I learned how to do this from Nick Hodapp's article "Undocumented
 * Visual C++" (http://www.codeproject.com/macro/openvc.asp).
 *
 *
 */

STDMETHODIMP CDSAddIn::OnConnection(IApplication *pApp,
									VARIANT_BOOL  vfFirstTime,
									long          lCookie,
									VARIANT_BOOL *pvfOnConnection)
{
	HRESULT hr = S_OK;            // Eventual return value...

	try
	{
		// Validate our parameters...
		if (NULL == pApp)            throw _com_error(E_INVALIDARG);
		if (NULL == pvfOnConnection) throw _com_error(E_POINTER);

		// & get to work -- assume failure until we know we're done:
		*pvfOnConnection = VARIANT_FALSE;

		ATLTRACE2(atlTraceHosting, 3, "DevStudio is connecting to VisEmacs "
			"%s(pApp is 0x%08x & our cookie is 0x%08x).\n",
			VARIANT_TRUE == vfFirstTime ? "for the first time " :
			" ", pApp, lCookie);

		// Store this for future reference. N.B. The assignment to a smart
		// ptr implicitly calls AddRef.
		m_pApp = pApp;

		m_lCookie = lCookie;

		// DevStudio wants a dispatch interface describing all the commands
		// we implement.  For now, I've implemented that interface on a
		// separate COM component.  However, given how peculiar that object
		// is, I've chosen *not* to make it externally creatable:
		HRESULT hr;
		IUnknown *pOuterUnk = GetControllingUnknown();
		// N.B. We don't keep a reference to the Commands object around once
		// we've handed it off to DevStudio.
		IDispatch *pCommands = NULL;
		hr = CreateInstanceWithParamItf(pOuterUnk, m_pParent, &pCommands,
			(CComObject<CDSCommands>**)NULL);
		// 'pCommands' now has one outstanding reference...
		if (FAILED(hr)) throw _com_error(hr);

		m_pParent->SetApplicationObject(m_pApp);

		// Ok -- we've initialized our data structures & we've got a command
		// object.  It's time to tell DevStudio about *us*.  N.B. DevStudio
		// will take out another reference:
		LONG lInstance = HandleToLong(_AtlBaseModule.m_hInst);
		hr = m_pApp->SetAddInInfo(lInstance,   // DLL Instance
			pCommands,   // Dispinterface
			IDB_DSTBMED, // Medium bmps
			IDB_DSTBLRG, // Large bmps
			m_lCookie);  // Our cookie
		pCommands->Release();       // No longer need it, since either
									// DevStudio took out a reference, or we
									// failed!
		if (FAILED(hr)) throw _com_error(hr);

		// Next, we sink the Application's Events:
		hr = CreateInstanceWithParam(pOuterUnk, m_pParent, &m_pAppEvents);
		if (FAILED(hr)) throw _com_error(hr);

		m_pAppEvents->Connect(m_pApp);

		// The only remaining step is to apprise DevStudio of our commands
		// and their toolbar buttons.  We add commands every time we're
		// loaded, but we only want to create the toolbar once.  The intent
		// was that `vfFirstTime' would be used to distinguish this.
		// However, since we "self-register", we may never be called with
		// this flag set to VARIANT_TRUE.  Hence, we note creation in the
		// Registry.
		bool fCreateTb = false;
		if (VARIANT_TRUE == vfFirstTime)
		{
			// Either we never self-registered, and this really is the first
			// time we've been loaded into DevStudio.
			fCreateTb = true;
		}
		else
		{
			// We should have scribbled down the fact that we created the
			// toolbar, if in fact we've done so.  Look that up:
			CRegKey hKey;
			LONG lStatus = hKey.Create(HKEY_CURRENT_USER, SOFTWARE);
			if (ERROR_SUCCESS != lStatus) throw _com_error(HRESULT_FROM_WIN32(lStatus));

			lStatus = hKey.Create(hKey, KITTYLION);
			if (ERROR_SUCCESS != lStatus) throw _com_error(HRESULT_FROM_WIN32(lStatus));

			lStatus = hKey.Create(hKey, INCVERSION);
			if (ERROR_SUCCESS != lStatus) throw _com_error(HRESULT_FROM_WIN32(lStatus));

			DWORD dwCreated;
			lStatus = hKey.QueryDWORDValue(VAL_CREATED_DSTB, dwCreated);
			if (ERROR_FILE_NOT_FOUND == lStatus)
			{
				dwCreated = VAL_CREATED_DSTB_NO;
			}
			else if (ERROR_SUCCESS != lStatus)
			{
				throw _com_error(HRESULT_FROM_WIN32(lStatus));
			}

			fCreateTb = (dwCreated != VAL_CREATED_DSTB_YES);
		}

		// With that knowledge, we can finally add our commands:
		AddCommands(fCreateTb);

		// Indicate success to DevStudio:
		*pvfOnConnection = VARIANT_TRUE;
	}
	catch (const _com_error &ex)
	{
		LPCTSTR lpszMsg = NULL;

		_bstr_t bstrDsc = ex.Description();
		if (0 != bstrDsc.length()) lpszMsg = bstrDsc;
		else                       lpszMsg = ex.ErrorMessage();

		hr = AtlReportError(CLSID_CoDSAddIn, lpszMsg, IID_IDSAddIn, ex.Error());
	}
	catch (const std::exception &ex)
	{
		hr = AtlReportError(CLSID_CoDSAddIn, ex.what(), IID_IDSAddIn, E_FAIL);
	}

	return hr;

}

/**
 * \brief Disconnect from this AddIn
 *
 *
 * \param vfLastTime  This will be set to VARIANT_TRUE if the user is
 * uninstalling the AddIn; if DevStudio is merely shutting down, it will
 * be set to VARIANT_FALSE
 *
 * \return S_OK on success, a stock HRESULT else
 *
 *
 */
 
STDMETHODIMP CDSAddIn::OnDisconnection(VARIANT_BOOL /*vfLastTime*/)
{
	HRESULT hr = S_OK;            // Eventual return value...

	try
	{
		m_pAppEvents->Disconnect(m_pApp);
		m_pApp = NULL;
	}
	catch (const _com_error &ex)
	{
		LPCTSTR lpszMsg = NULL;

		_bstr_t bstrDsc = ex.Description();
		if (0 != bstrDsc.length()) lpszMsg = bstrDsc;
		else                       lpszMsg = ex.ErrorMessage();

		hr = AtlReportError(CLSID_CoDSAddIn, lpszMsg, IID_IDSAddIn, ex.Error());
	}
	catch (const std::exception &ex)
	{
		hr = AtlReportError(CLSID_CoDSAddIn, ex.what(), IID_IDSAddIn, E_FAIL);
	}

	return hr;
}


////////////////////////////////////////////////////////////////////////
// Toolbar & Command Management

/**
 * \brief Add our commands to DevStudio
 *
 *
 * \param fCreateTb The caller should set this to true in order to
 * create a toolbar in addition to adding our commands
 *
 *
 * Refer to the documentation for OnConnection for why we're posting a
 * message to create the toolbar.
 *
 *
 */

void CDSAddIn::AddCommands(bool fCreateTb)
{
	HRESULT hr = S_OK;
	VARIANT_BOOL vfStatus;

	hr = m_pApp->AddCommand(
		CComBSTR(_T("IncVersion.IncVersion\n")		// Cmd name
		_T("Increment\n")							// Button text
		_T("Increment the Version\n")				// Status bar text
		_T("Increment the Version")),				// Tooltip
		CComBSTR(_T("IncVersion")),					// Disp method name
		0L,											// Bitmap offset
		m_lCookie,									// DS-provided cookie
		&vfStatus);									// [out] status

	if (FAILED(hr)) throw _com_error(hr);

	if (VARIANT_FALSE == vfStatus)
	{
		ATLTRACE2(atlTraceHosting, 1, "Warning: DevStudio returned VARIANT"
			"_FALSE when adding the IncVersion command.\n");
	}

	hr = m_pApp->AddCommand(
		CComBSTR(_T("IncVersion.Configure\n")		// Cmd name
		_T("Configure the IncVersion AddIn\n")		// Button text
		_T("Configure\n")							// Status bar text
		_T("Configure")),							// Tooltip
		CComBSTR(_T("Configure")),					// Disp method name
		1L,											// Bitmap offset
		m_lCookie,									// DS-provided cookie
		&vfStatus);									// [out] status

	if (FAILED(hr)) throw _com_error(hr);

	if (VARIANT_FALSE == vfStatus)
	{
		ATLTRACE2(atlTraceHosting, 1, "Warning: DevStudio returned VARIANT"
			"_FALSE when adding the Configure command.\n");
	}

	// If we try to create our toolbar when DevStudio set `vfFirstTime' to
	// VARIANT_FALSE in OnConnect, our attempt will fail.  However, if we
	// return from OnConnect, and *then* make the attempt, the call will
	// succeed.

	// We arrange for this to happen by posting a message to our hidden
	// window & calling ourselves back later.
	if (fCreateTb)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		::PostMessage(m_hWnd, WM_CREATETOOLBAR, 0U, 0L);
	}
}

/**
 * \brief Create the VisEmacs toolbar in DevStudio
 *
 *
 * One of the annoying things about writing DevStudio AddIns is that we
 * have no chance to name it -- the new toolbar will be called "Toolbar
 * n".  From Nick Hodapp "Undocumented Visual C++", I learned a cool
 * little trick -- hook the creation of the toolbar window & change the
 * window name.
 *
 *
 */

void CDSAddIn::CreateToolbar()
{
	HHOOK hHook = ::SetWindowsHookEx(WH_CBT,
		DsToolbarHook,
		_AtlBaseModule.m_hInst,
		::GetCurrentThreadId());

	m_pApp->AddCommandBarButton(dsGlyph,
		CComBSTR(_T("IncVersion.IncVersion")),
		m_lCookie);

	m_pApp->AddCommandBarButton(dsGlyph,
		CComBSTR(_T("IncVersion.Configure")),
		m_lCookie);

	if (NULL != hHook) ::UnhookWindowsHookEx(hHook);

	CRegKey hKey;
	LONG lStatus = hKey.Create(HKEY_CURRENT_USER, SOFTWARE);
	if (ERROR_SUCCESS != lStatus) throw _com_error(HRESULT_FROM_WIN32(lStatus));

	lStatus = hKey.Create(hKey, KITTYLION);
	if (ERROR_SUCCESS != lStatus) throw _com_error(HRESULT_FROM_WIN32(lStatus));

	lStatus = hKey.Create(hKey, INCVERSION);
	if (ERROR_SUCCESS != lStatus) throw _com_error(HRESULT_FROM_WIN32(lStatus));

	lStatus = hKey.SetDWORDValue(VAL_CREATED_DSTB, VAL_CREATED_DSTB_YES);
	if (ERROR_SUCCESS != lStatus)
	{
		throw _com_error(HRESULT_FROM_WIN32(lStatus));
	}
}

LRESULT CALLBACK CDSAddIn::DsToolbarHook(int nCode,
										 WPARAM wParam,
										 LPARAM lParam)
{
	if (HCBT_CREATEWND == nCode)
	{
		CBT_CREATEWNDA *pcw = (CBT_CREATEWNDA*)lParam;

		if (pcw->lpcs->lpszName)
		{
			if (0 == strncmp(pcw->lpcs->lpszName, "Toolbar", 7))
			{
				strcpy((char*)pcw->lpcs->lpszName, "IncVersion");
			}
		}
	}

	return CallNextHookEx(NULL, nCode, wParam, lParam);
}

LRESULT CALLBACK CDSAddIn::MsgWindowProc(HWND hWnd,
										 UINT nMsg,
										 WPARAM wParam,
										 LPARAM lParam)
{
	LRESULT lResult;

	CDSAddIn *pAddIn = NULL;
	LONG lUserData = ::GetWindowLong(hWnd, GWL_USERDATA);

	if (0L != lUserData)
	{
		pAddIn = reinterpret_cast<CDSAddIn*>(LongToPtr(lUserData));
	}

	try
	{
		switch (nMsg)
		{
		case WM_CREATE:
			{
				LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
				::SetWindowLong(hWnd, GWL_USERDATA, PtrToLong(pcs->lpCreateParams));
				lResult = ::DefWindowProc(hWnd, nMsg, wParam, lParam);
			}
			break;
		case WM_CREATETOOLBAR:
			{
				pAddIn->CreateToolbar();
				lResult = 0L;
			}
			break;
		default:
			lResult = ::DefWindowProc(hWnd, nMsg, wParam, lParam);
		}
	}
	catch (const _com_error &ex)
	{
		ATLTRACE2(atlTraceWindowing, 0, "WARNING: Unhandled COM exception "
			"in CoDSAddIn's message window procedure: 0x%08x!\n",
			ex.Error());
		ex; // Shutup the compiler in Release builds...
		lResult = -1L;
	}
	catch (const std::exception &ex)
	{
		ATLTRACE2(atlTraceWindowing, 0, "WARNING: Unhandled exception in C"
			"oDSAddIn's message window procedure: %s!\n", ex.what());
		ex; // Shutup the compiler in Release builds...
		lResult = -1L;
	}

	return lResult;
} // End MsgWindowProc.

// Local Variables:
// fill-column: 72
// indent-tabs-mode: nil
// show-trailing-whitespace: t
// End:

// DSAddIn.cpp ends here.

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
Ok, it's about time I updated this profile. I still live near 'Beastly' Eastleigh in Hampshire, England. However I have recently been granted a permamant migration visa to Australia - so if you're a potential employer from down under and like the look of me, please get in touch.
Still married - just, still with just a son and daughter. But they are now 8 and 7 resp and when together they have the energy of a nuclear bomb.
I worked at Teleca UK for over 8.5 years (but have now moved to TikitTFB) and have done loads of different things. Heavily involved with MFC, SQL, C#, The latest is ASP.NET with C# and Javascript. Moving away from Trolltech Qt3 and 4.
Jordan.

Comments and Discussions