
Signature
Bruce E. Stemplewski
GarXface OCX and C++ Class Library for the Garmin GPS
www.stempsoft.com
Hi Bruce,
> Thanks again Anna,
>
> Sounds like a really interesting former job. I love writing programs that
> interface to hardware.
It was...I was there for 6 years, which says something! In the end my
circumstances changed unexpectedly, an opportunity presented itself, and the
rest is now rapidly becoming history. Before that I was working for Racal
Instruments developing C++ Virtual Instrument and Virtual Super Instrument
software (my concept, BTW) for VXIbus avionic test systems.
The hardware bias shouldn't be too surprising as my first foray into
industry was as an RF design engineer and I've spent most of my working
career working with instrumentation of one form or another. <grin>
> I was going to write this plugin in C# but I guess I shouldn't try to
> learn plugins along with C#!
Actually I think there's no reason not to. Although you'll be learning a new
language, the majority of add-in examples and tutorials are in either C# or
VB.NET. Writing in C++ (especially unmanaged) brings additional complexity,
but also flexibility and power.
Besides, if you know C++ already I think you'll find C# a pleasant surprise.
:)
We've chosen to stick with unmanaged code to make support of multiple
versions easier too (that's a story in its own right!). At the moment we're
also seriously considering back porting our latest add-in (Visual Lint) to
VC6 as a result of the download profile we're seeing on our ResOrg add-in -
which of course makes managed code even harder to use.
> What is the base class of CVsCommandHandler?
It's one of our own:
/////////////////////////////////////////////////////////////////////////////
// VsCommandHandler.h
#pragma once
/// CVsCommandHandler is a base class for handling adding commands in the
/// Visual Studio environment.
///
/// To add additional commands derive your own classes from this one
/// and override the QueryStatus() and Execute() methods to implement
/// the command actions required.
///
/// The add-in's CConnect class is responsible for maintaining a map
/// (command name => CVsCommandHandler pointer) of command handler objects
/// and routing QueryStatus() and Execute() calls to the correct one.
///
class CVsCommandHandler
{
// Construction/destruction
public:
CVsCommandHandler( CComPtr<EnvDTE::_DTE> pDTE,
_bstr_t bsCommandName);
virtual ~CVsCommandHandler(void);
// Data members
protected:
CComPtr<EnvDTE::_DTE> m_pDTE;
_bstr_t m_bsCommandName;
// Operations
public:
_bstr_t GetCommandName(void) const
{ return m_bsCommandName; }
HRESULT RemoveCommand(void);
static HRESULT RemoveCommand(CComPtr<EnvDTE::_DTE> pDTE, _bstr_t
bsCommandName);
// Virtual overrides
public:
virtual HRESULT QueryStatus(BSTR bstrCmdName,
EnvDTE::vsCommandStatusTextWanted NeededText,
EnvDTE::vsCommandStatus* pStatusOption,
VARIANT* pvarCommandText);
virtual HRESULT Exec( BSTR bstrCmdName,
EnvDTE::vsCommandExecOption ExecuteOption,
VARIANT* pvarVariantIn,
VARIANT* pvarVariantOut,
VARIANT_BOOL* pvbHandled);
};
typedef std::map<_bstr_t, CVsCommandHandler*> CVsCommandHandlerMap;
typedef std::pair<_bstr_t, CVsCommandHandler*> CVsCommandHandlerMapPair;
typedef std::vector<CVsCommandHandler*> CVsCommandHandlerVector;
/////////////////////////////////////////////////////////////////////////////
// VsCommandHandler.cpp
#include "StdAfx.h"
#include "VsCommandHandler.h"
/////////////////////////////////////////////////////////////////////////////
// CVsCommandHandler construction/destruction
CVsCommandHandler::CVsCommandHandler(CComPtr<EnvDTE::_DTE> pDTE,
_bstr_t bsCommandName)
{
m_pWindowManager = NULL;
m_pDTE = pDTE;
ATLASSERT(m_pDTE != NULL);
m_bsCommandName = bsCommandName;
ATLASSERT(m_bsCommandName.length() > 0);
}
CVsCommandHandler::~CVsCommandHandler(void)
{
m_pDTE = NULL;
}
/////////////////////////////////////////////////////////////////////////////
// CVsCommandHandler operations
HRESULT CVsCommandHandler::RemoveCommand(void)
{
return RemoveCommand(m_pDTE, m_bsCommandName);
}
HRESULT CVsCommandHandler::RemoveCommand(CComPtr<EnvDTE::_DTE> pDTE, _bstr_t
bsCommandName)
{
try
{
EnvDTE::CommandsPtr ptrCommands = pDTE->GetCommands();
EnvDTE::CommandPtr ptrCommand = ptrCommands->Item( CComVariant(
CComBSTR(bsCommandName.GetBSTR() ) ), 0);
IfNullIssueError(ptrCommand);
ptrCommand->Delete();
}
catch (_com_error& e)
{
return e.Error();
}
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// CVsCommandHandler virtual overrides
HRESULT CVsCommandHandler::QueryStatus( BSTR bstrCmdName,
EnvDTE::vsCommandStatusTextWanted NeededText,
EnvDTE::vsCommandStatus* pStatusOption,
VARIANT* pvarCommandText)
{
UNREFERENCED_PARAMETER(bstrCmdName);
UNREFERENCED_PARAMETER(NeededText);
UNREFERENCED_PARAMETER(pStatusOption);
UNREFERENCED_PARAMETER(pvarCommandText);
return E_NOTIMPL;
}
HRESULT CVsCommandHandler::Exec(BSTR bstrCmdName,
EnvDTE::vsCommandExecOption ExecuteOption,
VARIANT* pvarVariantIn,
VARIANT* pvarVariantOut,
VARIANT_BOOL* pvbHandled)
{
UNREFERENCED_PARAMETER(bstrCmdName);
UNREFERENCED_PARAMETER(ExecuteOption);
UNREFERENCED_PARAMETER(pvarVariantIn);
UNREFERENCED_PARAMETER(pvarVariantOut);
UNREFERENCED_PARAMETER(pvbHandled);
return E_NOTIMPL;
}
> What calls CVsCommandHandler::RemoveCommand? Do I do it directly or does
> the framework call it? If I call it directly what do I supply for
> CComPtr<EnvDTE::_DTE> pDTE?
There are actually two overloads of RemoveCommand() - one of which is a
static, so it needs the DTE pointer to be specified. The other one is a
class member and takes its DTE pointer from member data supplied by CConnect
during construction. The static version is pretty erroneous now and we'll
probably remove it shortly.
RemoveCommand() should be called for each command supported by your add-in
by your CConnect::OnConnection() immediately before trying to add commands
when the connect mode is ext_cm_UISetup.
Pointers to all of the CVsCommandHandler objects are stored in a
name=>pointer map within CConnect, so it just iterates through them all and
removes the command for each (as well as removing the add-ins toolbar and
menubar of course).
Then it adds its commands as usual. We're left that at a higher level for
now, which keeps the command handler classes simple.
> Is all of this in the book you recommended? Does it cover ATL?
The book covers all of these concepts, as well as the project/soluton model,
macros, the code model, installers, help integration etc. It's pretty good.
:)
All the examples are in C# which is another reason to learn the language. ;)
Incidentally we're seriously considering writing an article on
codeproject.com using some of the code I've described. The premise is that
the code generated by the add-in wizard is ghastly, and since we have a
better (read: more OO) design for a skeleton C++ add-in we'd like to share,
what better way to do that than to show others how to change the code
generated by the wizard to something more friendly and maintainable?
As ever, the one thing in the way is time. I am quite keen to do this one
though, so hopefully we'll find time to do it in the near future.
Kind Regards,
Anna-Jayne Metcalfe
Software/Product Development Consultant,
Riverblade Limited.
http://www.riverblade.co.uk