/************************************************************************
*
* Resource ID Organiser Add-In for Visual C++ .NET
*
* (c) Copyright 2000-2005 by Riverblade Limited (UK). All rights reserved.
*
************************************************************************
*
* Description : CConnect class - primary interface to Visual Studio .NET
*
* Compiler : Microsoft Visual C++ .NET 2003
*
* Target
* Environment : Windows 2000/XP
*
* NOTE:
*
* This software is provided "as is". All title and copyrights in and
* to the software, including but not limited to any images, text etc.
* etc. incorporated into it, are the property of Anna-Jayne Metcalfe
* and Riverblade Limited, except where acknowledged otherwise.
*
* Your may freely use this code in your own products, PROVIDED
* this notice is not removed or modified.
*
* Please visit http://www.riverblade.co.uk/products/resorg or email
* support@riverblade.co.uk for latest updates and product support
*
************************************************************************
*
* MODIFICATION HISTORY:
*
* This is a controlled document. See project configuration
* control tool for latest version and full version history.
*
* $Archive: /Projects/AddIns/ResOrg/ResOrgNETAddIn/Connect.cpp $
* $Revision: 20 $
* $Date: 31/12/04 16:28 $
* $Author: Anna $
*
* $History: Connect.cpp $
*
* ***************** Version 20 *****************
* User: Anna Date: 31/12/04 Time: 16:28
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* 1. Menu commands now have the icons displayed correctly
* 2. Added a separator before the "About ResOrg..." command
*
* ***************** Version 19 *****************
* User: Anna Date: 28/12/04 Time: 9:40
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* 1. Added satellite DLL
* 2. Added icon to toolwindow
* 3. Minor mods for stability
* 4. Updated banner
*
*
* ***************** Version 18 *****************
* User: Anna Date: 20/12/04 Time: 10:13
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* 1. Changed the initialisation sequence to attempt to make it more
* reliable
* 2. Added event unadvise during OnDisconnection()
* 3. Added icon to the toolwindow
*
* ***************** Version 17 *****************
* User: Anna Date: 19/03/03 Time: 18:29
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* CConnect::OnConnection() now shows a warning message if it fails to
* configure commands
*
* ***************** Version 16 *****************
* User: Anna Date: 25/11/02 Time: 15:27
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* Changed website address in banner
*
* ***************** Version 15 *****************
* User: Anna Date: 22/10/02 Time: 14:15
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* Changed name/email address (at last!)
*
* ***************** Version 14 *****************
* User: Andy Date: 13/08/02 Time: 12:48
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* CConnect::OnConnection() no longer recreates the commands etc. in
* release builds if the connection mode is
* AddInDesignerObjects::ext_cm_AfterStartup.
*
* This prevents a crash if the add-in is loaded as a result of invoking a
* command installed previously. Note that to facilitate testing, the
* debug build still does this...
*
* ***************** Version 13 *****************
* User: Andy Date: 10/08/02 Time: 22:46
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* Fixed a compiler warning
*
* ***************** Version 12 *****************
* User: Andy Date: 10/08/02 Time: 22:21
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* 1. Fixed OnConnection() code...commands are now only setup if the
* add-in has just been installed or manually reloaded
* 2. Tidied up the code
*
* ***************** Version 11 *****************
* User: Andy Date: 10/08/02 Time: 0:16
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* 1. Added groups to the toolbar (to make it wider so the caption would
* fit!)
* 2. Fixed a window parenting problem when the main window of ResOrg was
* opened on an expired version
*
* ***************** Version 10 *****************
* User: Andy Date: 9/08/02 Time: 17:02
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* Added "Options" and "AboutBox" commands
*
* ***************** Version 9 *****************
* User: Andy Date: 8/08/02 Time: 20:36
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* Added (debug only) code to retrieve the pathname of the satellite
* DLL...so I can be sure I'm not really going mad here
*
* ***************** Version 8 *****************
* User: Andy Date: 7/08/02 Time: 22:15
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* Improved the way menu commands are added
*
* ***************** Version 7 *****************
* User: Andy Date: 6/08/02 Time: 10:36
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* Removed unused code
*
* ***************** Version 6 *****************
* User: Andy Date: 5/08/02 Time: 13:49
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* 1. Moved menu entries to a submenu under the Tools menu
* 2. Tidied up the code a little
*
* ***************** Version 5 *****************
* User: Andy Date: 1/08/02 Time: 16:44
* Updated in $/Projects/AddIns/ResOrg/ResOrgNETAddIn
* Further development of ResOrg.NET - particularly the tool window
*
* ***************** Version 4 *****************
* User: Andy Date: 22/06/02 Time: 0:36
* Updated in $/Projects/AddIns/ResOrg/ResOrgAddInVc7
* Minor tidying up
*
* ***************** Version 3 *****************
* User: Andy Date: 13/06/02 Time: 17:02
* Updated in $/Projects/AddIns/ResOrg/ResOrgAddInVc7
* Removed compiler warnings and changed ProgID string from
* "ResOrgAddInVc7.Connect" to "ResOrgAddInVc7"
*
* ***************** Version 2 *****************
* User: Andy Date: 7/06/02 Time: 22:37
* Updated in $/Projects/AddIns/ResOrg/ResOrgAddInVc7
* Initial integration
*
* ***************** Version 1 *****************
* User: Andy Date: 3/06/02 Time: 16:53
* Created in $/Projects/AddIns/ResOrg/ResOrgAddInVc7
*
* $Nokeywords: $
*
************************************************************************/
#include "StdAfx.h"
#include "AddIn.h"
#include "ResOrgAddInApp.h"
#include "MainFrame.h"
#include "Connect.h"
/////////////////////////////////////////////////////////////////////////////
// Support functions
HPALETTE CreateDIBPalette (LPBITMAPINFO lpbmi, LPINT lpiNumColors)
{
LPBITMAPINFOHEADER lpbi;
LPLOGPALETTE lpPal;
HANDLE hLogPal;
HPALETTE hPal = NULL;
int i;
lpbi = (LPBITMAPINFOHEADER)lpbmi;
if (lpbi->biBitCount <= 8)
*lpiNumColors = (1 << lpbi->biBitCount);
else
*lpiNumColors = 0; // No palette needed for 24 BPP DIB
if (lpbi->biClrUsed > 0)
*lpiNumColors = lpbi->biClrUsed; // Use biClrUsed
if (*lpiNumColors)
{
hLogPal = GlobalAlloc(GHND, sizeof (LOGPALETTE) + sizeof (PALETTEENTRY) * (*lpiNumColors) );
lpPal = (LPLOGPALETTE) GlobalLock (hLogPal);
lpPal->palVersion = 0x300;
lpPal->palNumEntries = *lpiNumColors;
for (i = 0; i < *lpiNumColors; i++)
{
lpPal->palPalEntry[i].peRed = lpbmi->bmiColors[i].rgbRed;
lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen;
lpPal->palPalEntry[i].peBlue = lpbmi->bmiColors[i].rgbBlue;
lpPal->palPalEntry[i].peFlags = 0;
}
hPal = CreatePalette(lpPal);
GlobalUnlock(hLogPal);
GlobalFree(hLogPal);
}
return hPal;
}
HBITMAP LoadResourceBitmap(HINSTANCE hInstance, LPSTR lpString, HPALETTE FAR* lphPalette)
{
HRSRC hRsrc;
HGLOBAL hGlobal;
HBITMAP hBitmapFinal = NULL;
LPBITMAPINFOHEADER lpbi;
HDC hdc;
int iNumColors;
if (hRsrc = FindResource(hInstance, lpString, RT_BITMAP) )
{
hGlobal = LoadResource(hInstance, hRsrc);
lpbi = (LPBITMAPINFOHEADER)LockResource(hGlobal);
hdc = GetDC(NULL);
*lphPalette = CreateDIBPalette ((LPBITMAPINFO)lpbi, &iNumColors);
if (*lphPalette)
{
SelectPalette(hdc,*lphPalette,FALSE);
RealizePalette(hdc);
}
hBitmapFinal = CreateDIBitmap( hdc,
(LPBITMAPINFOHEADER)lpbi,
(LONG)CBM_INIT,
(LPSTR)lpbi + lpbi->biSize + iNumColors *
sizeof(RGBQUAD), (LPBITMAPINFO)lpbi, DIB_RGB_COLORS );
ReleaseDC(NULL,hdc);
UnlockResource(hGlobal);
FreeResource(hGlobal);
}
return (hBitmapFinal);
}
// CVc7AutomationHelper hides away the grot of talking to the COM interfaces
// exposed by Visual Studio...
CVc7AutomationHelper Workspace;
extern CAddInModule _AtlModule;
CConnect::CConnect(void)
{
m_hwndResOrg = NULL;
}
CConnect::~CConnect(void)
{
}
// When run, the Add-in wizard prepared the registry for the Add-in.
// At a later time, the Add-in or its commands may become unavailable for reasons such as:
//
// 1) You moved this project to a computer other than which is was originally created on.
// 2) You chose 'Yes' when presented with a message asking if you wish to remove the Add-in.
// 3) You add new commands or modify commands already defined.
//
// You will need to re-register the Add-in by building the ResOrgNETAddInSetup project,
// right-clicking the project in the Solution Explorer, and then choosing install.
// Alternatively, you could execute the ReCreateCommands.reg file the Add-in Wizard generated in
// the project directory, or run 'devenv /setup' from a command prompt.
// CConnect
STDMETHODIMP CConnect::OnConnection(IDispatch* pApplication,
AddInDesignerObjects::ext_ConnectMode eConnectMode,
IDispatch* pAddInInst,
SAFEARRAY** ppCustom)
{
UNREFERENCED_PARAMETER(ppCustom);
pApplication->QueryInterface(__uuidof(EnvDTE::_DTE), (LPVOID*)&m_pDTE);
pAddInInst->QueryInterface(__uuidof(EnvDTE::AddIn), (LPVOID*)&m_pAddInInstance);
Workspace.SetDTE(m_pDTE);
// Initialise the document manager and create the mainframe
ConnectApp();
if (SUCCEEDED( CreateMainToolWindow(pApplication) ) )
{
ASSERT(m_pwndMain != NULL);
m_pwndMain->put_Visible(VARIANT_TRUE);
}
switch ( (int)eConnectMode)
{
case 0: // AddInDesignerObjects::ext_cm_AfterStartup
#ifdef _DEBUG
// AJM 13.8.2002 in the debug build we remove and reconfigure our commands when
// the user loads the add-in explicitly in the add-in manager for ease of testing
//
// Since this will cause the IDE to crash if the add-in is loaded as a result of
// activating an existing ResOrg.NET command, this is NOT done in the release version!
if (FAILED(ConfigureCommands() ) )
{
::AfxMessageBox(IDP_ADDIN_CONFIGURE_CMD_FAILED, MB_OK | MB_ICONEXCLAMATION);
}
#endif
break;
case 5: // AddInDesignerObjects::ext_cm_UISetup
if (FAILED(ConfigureCommands() ) )
{
::AfxMessageBox(IDP_ADDIN_CONFIGURE_CMD_FAILED, MB_OK | MB_ICONEXCLAMATION);
}
break;
case 1: // AddInDesignerObjects::ext_cm_Startup
case 2: // AddInDesignerObjects::ext_cm_External
case 3: // AddInDesignerObjects::ext_cm_CommandLine
case 4: // AddInDesignerObjects::ext_cm_Solution
default:
break;
}
ConfigureEvents();
return S_OK;
}
STDMETHODIMP CConnect::OnDisconnection(AddInDesignerObjects::ext_DisconnectMode eRemoveMode,
SAFEARRAY** ppCustom)
{
UNREFERENCED_PARAMETER(eRemoveMode);
UNREFERENCED_PARAMETER(ppCustom);
UnadviseEvents();
Workspace.SetDTE(NULL);
DisconnectApp();
m_pDTE = NULL;
return S_OK;
}
STDMETHODIMP CConnect::OnAddInsUpdate(SAFEARRAY** ppCustom)
{
UNREFERENCED_PARAMETER(ppCustom);
return S_OK;
}
STDMETHODIMP CConnect::OnStartupComplete(SAFEARRAY** ppCustom)
{
UNREFERENCED_PARAMETER(ppCustom);
return S_OK;
}
STDMETHODIMP CConnect::OnBeginShutdown(SAFEARRAY** ppCustom)
{
UNREFERENCED_PARAMETER(ppCustom);
return S_OK;
}
STDMETHODIMP CConnect::QueryStatus(BSTR bstrCmdName,
EnvDTE::vsCommandStatusTextWanted eNeededText,
EnvDTE::vsCommandStatus* pStatusOption,
VARIANT* pvarCommandText)
{
UNREFERENCED_PARAMETER(pvarCommandText);
if (eNeededText == EnvDTE::vsCommandStatusTextWantedNone)
{
if (!_wcsicmp(bstrCmdName, L"ResOrgNETAddIn.Open") )
{
*pStatusOption = (EnvDTE::vsCommandStatus)(EnvDTE::vsCommandStatusEnabled + EnvDTE::vsCommandStatusSupported);
}
else if (!_wcsicmp(bstrCmdName, L"ResOrgNETAddIn.Options") )
{
*pStatusOption = (EnvDTE::vsCommandStatus)(EnvDTE::vsCommandStatusEnabled + EnvDTE::vsCommandStatusSupported);
}
else if (!_wcsicmp(bstrCmdName, L"ResOrgNETAddIn.AboutBox") )
{
*pStatusOption = (EnvDTE::vsCommandStatus)(EnvDTE::vsCommandStatusEnabled + EnvDTE::vsCommandStatusSupported);
}
else if (!_wcsicmp(bstrCmdName, L"ResOrgNETAddIn.ViewToolWin") )
{
QueryStatusToggleViewToolWinCmd(eNeededText,
pStatusOption,
pvarCommandText);
}
}
return S_OK;
}
void CConnect::QueryStatusToggleViewToolWinCmd( EnvDTE::vsCommandStatusTextWanted eNeededText,
EnvDTE::vsCommandStatus* pStatusOption,
VARIANT* pvarCommandText)
{
UNREFERENCED_PARAMETER(eNeededText);
UNREFERENCED_PARAMETER(pvarCommandText);
if (m_pwndMain != NULL)
{
VARIANT_BOOL bVisible = VARIANT_FALSE;
m_pwndMain->get_Visible(&bVisible);
if (bVisible)
{
*pStatusOption = (EnvDTE::vsCommandStatus)( EnvDTE::vsCommandStatusEnabled +
EnvDTE::vsCommandStatusSupported +
EnvDTE::vsCommandStatusLatched);
}
else
{
*pStatusOption = (EnvDTE::vsCommandStatus)( EnvDTE::vsCommandStatusEnabled +
EnvDTE::vsCommandStatusSupported);
}
}
else
{
*pStatusOption = (EnvDTE::vsCommandStatus)( EnvDTE::vsCommandStatusUnsupported);
}
}
STDMETHODIMP CConnect::Exec(BSTR bstrCmdName,
EnvDTE::vsCommandExecOption eExecuteOption,
VARIANT* pvarVariantIn,
VARIANT* pvarVariantOut,
VARIANT_BOOL* pvbHandled)
{
UNREFERENCED_PARAMETER(pvarVariantIn);
UNREFERENCED_PARAMETER(pvarVariantOut);
*pvbHandled = VARIANT_FALSE;
if (EnvDTE::vsCommandExecOptionDoDefault == eExecuteOption)
{
if (!_wcsicmp(bstrCmdName, L"ResOrgNETAddIn.Open") )
{
OpenResOrgMainWindow();
*pvbHandled = VARIANT_TRUE;
return S_OK;
}
else if (!_wcsicmp(bstrCmdName, L"ResOrgNETAddIn.ViewToolWin") )
{
ToggleProjectViewCmd();
}
else if (!_wcsicmp(bstrCmdName, L"ResOrgNETAddIn.Options") )
{
DoOptionsCmd();
*pvbHandled = VARIANT_TRUE;
return S_OK;
}
else if (!_wcsicmp(bstrCmdName, L"ResOrgNETAddIn.AboutBox") )
{
DoAboutBoxCmd();
*pvbHandled = VARIANT_TRUE;
return S_OK;
}
}
return S_OK;
}
HRESULT CConnect::FinalConstruct(void)
{
return S_OK;
}
void CConnect::FinalRelease(void)
{
}
/////////////////////////////////////////////////////////////////////////////
// CConnect implementation
void CConnect::ConnectApp(void)
{
AFX_MANAGE_STATE( ::AfxGetStaticModuleState() );
::GetApp()->OnConnection();
m_hwndResOrg = ::AfxGetMainWnd()->GetSafeHwnd();
}
void CConnect::DisconnectApp(void)
{
AFX_MANAGE_STATE( ::AfxGetStaticModuleState() );
::GetApp()->OnDisconnection();
}
HRESULT CConnect::ToggleProjectViewCmd(void)
{
VARIANT_BOOL bVisible = VARIANT_FALSE;
m_pwndMain->get_Visible(&bVisible);
m_pwndMain->put_Visible(!bVisible);
return S_OK;
}
// Use with care - this will fail if Visual Studio is not the active window
HWND CConnect::FindVisualStudioMainWnd(void) const
{
HWND hWndDevStudio = NULL;
HWND hWndDesktop = ::GetDesktopWindow();
HWND hWnd = ::GetActiveWindow();
while ( (NULL != hWnd) && (hWnd != hWndDesktop) )
{
hWndDevStudio = hWnd;
hWnd = ::GetParent(hWnd);
}
return hWndDevStudio;
}
const UINT WM_RESORG_OPEN = ::RegisterWindowMessage( _T("ResOrgShowMainWnd") );
HRESULT CConnect::OpenResOrgMainWindow(void)
{
AFX_MANAGE_STATE( ::AfxGetStaticModuleState() );
if (::IsWindow(m_hwndResOrg) )
{
#ifdef _RESORG_EXPIRY_DATE
// Because this add-in is implemented as a Regular DLL with a
// mainframe window the usual trick of calling EnableModeless()
// isn't enough to ensure that modal dialogs behave correctly -
// we also have to ensure that any dialogs are parented by
// DevStudio's main window rather than our own...
HWND hWndDevStudio = FindVisualStudioMainWnd();
CWnd* pWnd = CWnd::FromHandle(hWndDevStudio);
::DoVersionExpiryCheck(pWnd);
#endif
::SendMessage(m_hwndResOrg, WM_RESORG_OPEN, 0, (LPARAM)0);
return S_OK;
}
return E_FAIL;
}
HRESULT CConnect::DoOptionsCmd(void)
{
AFX_MANAGE_STATE( ::AfxGetStaticModuleState() );
// Because this add-in is implemented as a Regular DLL with a
// mainframe window the usual trick of calling EnableModeless()
// isn't enough to ensure that modal dialogs behave correctly -
// we also have to ensure that any dialogs are parented by
// DevStudio's main window rather than our own...
HWND hWndDevStudio = FindVisualStudioMainWnd();
CWnd* pWnd = CWnd::FromHandle(hWndDevStudio);
Options.Configure(pWnd);
return S_OK;
}
HRESULT CConnect::DoAboutBoxCmd(void)
{
AFX_MANAGE_STATE( ::AfxGetStaticModuleState() );
// Because this add-in is implemented as a Regular DLL with a
// mainframe window the usual trick of calling EnableModeless()
// isn't enough to ensure that modal dialogs behave correctly -
// we also have to ensure that any dialogs are parented by
// DevStudio's main window rather than our own...
HWND hWndDevStudio = FindVisualStudioMainWnd();
CWnd* pWnd = CWnd::FromHandle(hWndDevStudio);
CResOrgAboutBox aboutDlg(pWnd);
aboutDlg.SetModuleState( ::AfxGetStaticModuleState() );
aboutDlg.DoModal();
return S_OK;
}
HRESULT CConnect::ConfigureEvents(void)
{
HRESULT hr = S_OK;
{
CComPtr<EnvDTE::Events> pEvents;
CComPtr<EnvDTE::_SolutionEvents> pSolutionEvents;
if (SUCCEEDED(m_pDTE->get_Events(&pEvents) ) )
{
if (SUCCEEDED(pEvents->get_SolutionEvents(&pSolutionEvents) ) )
{
m_SolutionEventsSink.DispEventAdvise( (IUnknown*)pSolutionEvents.p);
// Tell our event sink where the main tool window lives
m_SolutionEventsSink.Initialise(m_pwndMainCtrl);
hr = S_OK;
}
}
}
return hr;
}
HRESULT CConnect::UnadviseEvents(void)
{
HRESULT hr = S_OK;
{
CComPtr<EnvDTE::Events> pEvents;
CComPtr<EnvDTE::_SolutionEvents> pSolutionEvents;
if (SUCCEEDED(m_pDTE->get_Events(&pEvents) ) )
{
if (SUCCEEDED(pEvents->get_SolutionEvents(&pSolutionEvents) ) )
{
m_SolutionEventsSink.DispEventUnadvise( (IUnknown*)pSolutionEvents.p);
//pSolutionEvents = NULL;
hr = S_OK;
}
}
}
return hr;
}
HRESULT CConnect::RemoveCommands(void)
{
HRESULT hr = S_OK;
CComPtr<EnvDTE::Commands> pCommands;
hr = m_pDTE->get_Commands(&pCommands);
if (FAILED(hr) )
{
return hr;
}
CComPtr<Office::_CommandBars> pCommandBars;
hr = m_pDTE->get_CommandBars(&pCommandBars);
if (FAILED(hr) )
{
return hr;
}
// Find and remove (if found) our menu bar & toolbar
CComPtr<Office::CommandBar> pResOrgMenuBar;
CComPtr<Office::CommandBar> pResOrgToolBar;
if (SUCCEEDED(pCommandBars->get_Item(CComVariant("Resource ID Organiser .NET"), &pResOrgMenuBar) ) )
{
pCommands->RemoveCommandBar(pResOrgMenuBar);
}
if (SUCCEEDED(pCommandBars->get_Item(CComVariant("ResOrg.NET"), &pResOrgToolBar) ) )
{
pCommands->RemoveCommandBar(pResOrgToolBar);
}
// Find and remove (if found) our named commands
CComPtr<EnvDTE::Command> pCommand;
pCommands->Item( CComVariant("ResOrgNETAddIn.Open"), 0, &pCommand);
if (pCommand != NULL)
{
pCommand->Delete();
pCommand = NULL;
// pCommand.Release();
}
pCommands->Item(CComVariant("ResOrgNETAddIn.ViewToolWin"), 0, &pCommand);
if (pCommand != NULL)
{
pCommand->Delete();
pCommand = NULL;
// pCommand.Release();
}
pCommands->Item(CComVariant("ResOrgNETAddIn.Options"), 0, &pCommand);
if (pCommand != NULL)
{
pCommand->Delete();
pCommand = NULL;
// pCommand.Release();
}
pCommands->Item(CComVariant("ResOrgNETAddIn.AboutBox"), 0, &pCommand);
if (pCommand != NULL)
{
pCommand->Delete();
pCommand = NULL;
// pCommand.Release();
}
return hr;
}
// Load a newline delimited specification for a command
bool CConnect::LoadCommandSpec( UINT uResourceID,
CString& rsCmd,
CString& rsButtonText,
CString& rsToolTip,
CString& rsKeyBinding,
BOOL& rbHasBitmap) const
{
AFX_MANAGE_STATE( ::AfxGetStaticModuleState() );
CString sSpecification;
sSpecification.LoadString(uResourceID);
if (!sSpecification.IsEmpty() )
{
rsCmd = ::Chomp( sSpecification,_T("\n") );
rsButtonText = ::Chomp( sSpecification,_T("\n") );
rsToolTip = ::Chomp( sSpecification,_T("\n") );
rsKeyBinding = ::Chomp( sSpecification,_T("\n") );
// Work out whether this command has a bitmap icon
CBitmap bitmap;
rbHasBitmap = bitmap.LoadBitmap(uResourceID);
return !rsCmd.IsEmpty();
}
return false;
}
HRESULT CConnect::AddCommand(CComPtrBase<EnvDTE::Commands>& pCommands,
CComPtr<EnvDTE::Command>& rpCommand,
UINT uCmdSpecID,
bool bEnabled)
{
CString sCmd, sButtonText, sToolTip, sKeyBinding;
BOOL bHasBitmap = FALSE;
if (!LoadCommandSpec( uCmdSpecID,
sCmd,
sButtonText,
sToolTip,
sKeyBinding,
bHasBitmap) )
{
return E_FAIL;
}
LONG lStatus = bEnabled ? EnvDTE::vsCommandStatusSupported | EnvDTE::vsCommandStatusEnabled :
EnvDTE::vsCommandStatusSupported;
bHasBitmap = true;
// Add the command...note that I haven't got the bitmaps to appear yet. No idea why
HRESULT hr = pCommands->AddNamedCommand(m_pAddInInstance,
CComBSTR(sCmd),
CComBSTR(sButtonText),
CComBSTR(sToolTip),
bHasBitmap ? VARIANT_FALSE : VARIANT_TRUE,
bHasBitmap ? uCmdSpecID : 0,
NULL,
lStatus,
&rpCommand);
if (FAILED(hr) )
{
// Failed to add command
ASSERT(FALSE);
return hr;
}
if (!sKeyBinding.IsEmpty() )
{
rpCommand->put_Bindings(CComVariant(sKeyBinding));
}
return hr;
}
HRESULT CConnect::AddCommandToCommandBar(CComPtrBase<Office::CommandBar>& pCommandBar,
CComPtr<EnvDTE::Command> pCommand,
LONG lPos,
bool bBeginGroupBefore /*= false*/)
{
CComPtr<Office::CommandBarControl> pControl;
HRESULT hr = pCommand->AddControl(pCommandBar, lPos, &pControl);
if (FAILED(hr) )
{
// Failed to add command
ASSERT(FALSE);
return hr;
}
if (bBeginGroupBefore)
{
pControl->put_BeginGroup(VARIANT_TRUE);
}
CComQIPtr<Office::_CommandBarButton> pButton(pControl);
if (pButton != NULL)
{
pButton->put_Style(Office::msoButtonAutomatic);
}
return S_OK;
}
HRESULT CConnect::ConfigureCommands(void)
{
AFX_MANAGE_STATE( ::AfxGetStaticModuleState() );
RemoveCommands();
HRESULT hr = E_FAIL;
{
CComPtr<EnvDTE::Commands> pCommands;
CComPtr<Office::_CommandBars> pCommandBars;
CComPtr<Office::CommandBar> pToolsMenuBar;
IfFailGoCheck(m_pDTE->get_Commands(&pCommands), pCommands);
IfFailGoCheck(m_pDTE->get_CommandBars(&pCommandBars), pCommandBars);
// Add the commands
CComPtr<EnvDTE::Command> pOpenCmd;
CComPtr<EnvDTE::Command> pToggleToolwinCmd;
CComPtr<EnvDTE::Command> pOptionsCmd;
CComPtr<EnvDTE::Command> pAboutBoxCmd;
HRESULT hrAddOpenCmd = AddCommand( pCommands,
pOpenCmd,
IDR_OPEN_RESORG_CMD,
false);
HRESULT hrAddToggleCmd = AddCommand(pCommands,
pToggleToolwinCmd,
IDR_VIEW_MAIN_TOOLWIN_CMD,
false);
HRESULT hrOptionsCmd = AddCommand( pCommands,
pOptionsCmd,
IDR_OPTIONS_CMD,
false);
HRESULT hrAboutBoxCmd = AddCommand( pCommands,
pAboutBoxCmd,
IDR_ABOUTBOX_CMD,
false);
// Create the toolbar...
CComQIPtr<Office::CommandBar> pResOrgToolBar;
IfFailGoCheck(pCommands->AddCommandBar( CComBSTR("ResOrg.NET"),
EnvDTE::vsCommandBarTypeToolbar,
NULL,
0,
(IDispatch**)&pResOrgToolBar), pResOrgToolBar);
//...and menu bar
CComPtr<Office::CommandBar> pResOrgMenuBar;
// Add a submenu to the "Tools" menu
CString sTitle;
VERIFY( sTitle.LoadString(IDS_ADDIN_MENU_NAME) );
IfFailGoCheck(pCommandBars->get_Item( CComVariant(L"Tools"),
&pToolsMenuBar), pToolsMenuBar);
IfFailGoCheck(pCommands->AddCommandBar( CComBSTR(sTitle),
EnvDTE::vsCommandBarTypeMenu,
pToolsMenuBar,
1,
(IDispatch**)&pResOrgMenuBar), pResOrgMenuBar);
// Now add the commands to the toolbar & menubar
AddCommandToCommandBar( pResOrgToolBar,
pOpenCmd,
1);
AddCommandToCommandBar( pResOrgToolBar,
pToggleToolwinCmd,
2,
true);
AddCommandToCommandBar( pResOrgToolBar,
pOptionsCmd,
3);
AddCommandToCommandBar( pResOrgToolBar,
pAboutBoxCmd,
4,
true);
AddCommandToCommandBar( pResOrgMenuBar,
pOpenCmd,
1);
AddCommandToCommandBar( pResOrgMenuBar,
pToggleToolwinCmd,
2);
AddCommandToCommandBar( pResOrgMenuBar,
pOptionsCmd,
3);
AddCommandToCommandBar( pResOrgMenuBar,
pAboutBoxCmd,
4,
true);
pResOrgToolBar->put_Visible(VARIANT_TRUE);
hr = (SUCCEEDED(hrAddOpenCmd) && SUCCEEDED(hrAddToggleCmd) ) ? S_OK : E_FAIL;
}
Error:
return hr;
}
HRESULT CConnect::CreateMainToolWindow(IDispatch* pApplication)
{
USES_CONVERSION;
AFX_MANAGE_STATE( ::AfxGetStaticModuleState() );
CComPtr<EnvDTE::Windows> pWindows;
m_pDTE->get_Windows(&pWindows);
// First create the window using Visual Studio's Automation Interface...
HRESULT hr = pWindows->CreateToolWindow(m_pAddInInstance,
CComBSTR("RESORGNETADDINTO.ResOrgNETAddInToCtrl.1"),
CComBSTR("ResOrg.NET"),
CComBSTR("{7542A9D7-23A4-4ADD-BD8B-9BF76BBD82D6}"),
&m_pwndMainCtrl,
&m_pwndMain);
if (FAILED(hr) )
{
::AfxMessageBox(_T("Unable to create ResOrg.NET tool window. The add-in may be incorrectly installed"),
MB_OK | MB_ICONSTOP);
return hr;
}
CComBSTR bsSatelliteDll;
m_pAddInInstance->get_SatelliteDllPath(&bsSatelliteDll.m_str);
//TRACE( _T("ResOrg.NET Satellite DLL path: %s\n"), OLE2T(bsSatelliteDll.m_str) );
HMODULE hModule = ::LoadLibrary( OLE2T(bsSatelliteDll.m_str) );
CComVariant varPic;
CComPtr<IPictureDisp> pPictureDisp;
PICTDESC pd;
pd.cbSizeofstruct = sizeof(PICTDESC);
pd.picType = PICTYPE_BITMAP;
pd.bmp.hbitmap = LoadResourceBitmap(hModule,
MAKEINTRESOURCE(IDR_VIEW_MAIN_TOOLWIN_CMD),
&pd.bmp.hpal ) ;
OleCreatePictureIndirect( &pd,
IID_IPictureDisp,
FALSE,
(LPVOID*)&pPictureDisp);
pPictureDisp->QueryInterface(IID_IUnknown, (LPVOID*)&varPic.punkVal ) ;
varPic.vt = VT_UNKNOWN;
m_pwndMain->SetTabPicture(varPic) ;
::FreeLibrary(hModule);
//...and now we need to configure it. The ActiveX Control within the window
// has an Initialise DISP method, and all we need to do is pass the IDispatch
// pointer to the application to it (from which it will find the DTE interface)
//
// Until I tried to get this working I had NO idea how much of a bodge ActiveX/IDispatch
// is - it took me ages to get this working!
//
// First, obtain the DISPID of the property/method.
OLECHAR FAR* szName = A2W("Initialise");
DISPID dispid;
hr = m_pwndMainCtrl->GetIDsOfNames( IID_NULL,
&szName,
1,
LOCALE_SYSTEM_DEFAULT,
&dispid);
if (FAILED(hr) )
{
ASSERT(FALSE); // Very, very bad - and should never happen
return hr;
}
// This is the bit that really got me - how much work it is to make a simple
// method call. Roll on .NET.....
VARIANTARG args[2];
memset(args, 0, sizeof args);
args[1].vt = VT_DISPATCH;
args[1].pdispVal = pApplication;
args[0].vt = VT_UI4;
args[0].ulVal = (unsigned long)m_hwndResOrg;
DISPPARAMS params;
memset(¶ms, 0, sizeof params);
params.rgvarg = &(args[0]);
params.cArgs = 2;
params.cNamedArgs = 0;
VARIANT varResult;
hr = m_pwndMainCtrl->Invoke(dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
¶ms,
&varResult,
NULL,
NULL);
return hr;
}
HRESULT CConnect::RefreshMainToolWindow(void)
{
return m_SolutionEventsSink.RefreshMainToolWindow();
}
// This is a bit of a bodge...since the event handler needs to call this it makes
// sense to make it a member of this class, although it really doesn't make sense!
HRESULT CConnect::SolutionEventsSink::RefreshMainToolWindow(void)
{
USES_CONVERSION;
ASSERT(m_pwndMainCtrl != NULL);
if (m_pwndMainCtrl == NULL)
{
return E_FAIL;
}
//...and now we need to configure it. The ActiveX Control within the window
// has an Initialise DISP method, and all we need to do is pass the IDispatch
// pointer to the application to it (from which it will find the DTE interface)
//
// Until I tried to get this working I had NO idea how much of a bodge ActiveX/IDispatch
// is - it took me ages to get this working!
//
// First, obtain the DISPID of the property/method.
OLECHAR FAR* szName = A2W("Refresh");
DISPID dispid;
HRESULT hr = m_pwndMainCtrl->GetIDsOfNames( IID_NULL,
&szName,
1,
LOCALE_SYSTEM_DEFAULT,
&dispid);
if (FAILED(hr) )
{
ASSERT(FALSE); // Very, very bad - and should never happen
return hr;
}
DISPPARAMS params;
memset(¶ms, 0, sizeof params);
params.rgvarg = NULL;
params.cArgs = 0;
params.cNamedArgs = 0;
return m_pwndMainCtrl->Invoke( dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
¶ms,
NULL,
NULL,
NULL);
}
void __stdcall CConnect::SolutionEventsSink::Opened(void)
{
TRACE( _T("SolutionEvents::Opened\n") );
RefreshMainToolWindow();
}
void __stdcall CConnect::SolutionEventsSink::BeforeClosing(void)
{
TRACE( _T("SolutionEvents::BeforeClosing\n") );
}
void __stdcall CConnect::SolutionEventsSink::AfterClosing(void)
{
TRACE( _T("SolutionEvents::AfterClosing\n") );
RefreshMainToolWindow();
}
void __stdcall CConnect::SolutionEventsSink::QueryCloseSolution(VARIANT_BOOL *fCancel)
{
UNREFERENCED_PARAMETER(fCancel);
TRACE( _T("SolutionEvents::QueryCloseSolution\n") );
}
void __stdcall CConnect::SolutionEventsSink::Renamed(BSTR OldName)
{
UNREFERENCED_PARAMETER(OldName);
TRACE( _T("SolutionEvents::Renamed\n") );
}
void __stdcall CConnect::SolutionEventsSink::ProjectAdded(EnvDTE::Project* pProject)
{
UNREFERENCED_PARAMETER(pProject);
#ifdef _DEBUG
TRACE( _T("SolutionEvents::ProjectAdded\n") );
CComBSTR bsName;
pProject->get_Name(&bsName);
TRACE( _T("\tProject name: %s\n"), bsName);
#endif
RefreshMainToolWindow();
}
void __stdcall CConnect::SolutionEventsSink::ProjectRemoved(EnvDTE::Project* pProject)
{
UNREFERENCED_PARAMETER(pProject);
#ifdef _DEBUG
TRACE( _T("SolutionEvents::ProjectRemoved\n") );
CComBSTR bsName;
pProject->get_Name(&bsName);
TRACE( _T("\tProject name: %s\n"), bsName);
#endif
RefreshMainToolWindow();
}
void __stdcall CConnect::SolutionEventsSink::ProjectRenamed(EnvDTE::Project* pProject, BSTR bsOldName)
{
UNREFERENCED_PARAMETER(pProject);
UNREFERENCED_PARAMETER(bsOldName);
TRACE( _T("SolutionEvents::ProjectRenamed\n") );
RefreshMainToolWindow();
}