Common Add-Ins

Introduction

This article describes a way to write add-ins such that a single binary can be hosted across multiple versions of DevStudio, Visual Studio, and Office. It uses C++ and ATL, but the principles should carry over to other languages and frameworks.

Background

I've been frustrated by the changes in the add-in programming model across versions of Microsoft's IDEs over the years. I invested a fair amount of effort in writing some add-ins for DevStudio 6, and found I had to completely re-write them when I upgraded to Visual Studio 2003. Then, the object model changed again (albeit slightly) when I moved to Visual Studio 2005, requiring another update. I still use all three IDEs on different projects, so I can't just abandon the older versions; I have to maintain all three versions of any given add-in I have.

This irritated me enough that I worked out a scheme for writing Add-Ins in such a way that the same binary that can be loaded into DevStudio 6, Visual Studio 2003 and 2005, and even Office 2003 and hosted as an add-in for each. Because I suspect I'm not the only one to go through this, I decided to write up what I'd done.

General Layout of the Add-In

The core idea is very simple; the bulk of the effort involved working out a lot of implementation details. For the rest of this article, I'll refer to the sample add-in I wrote to illustrate this, SampleCAI (see below). Before digging into the details, however, let me lay out my general scheme.

I'm going to build an In-Process COM Server (that is, a .dll) that exports a single component that implements all the interfaces required by the target hosts. This component will present itself to each host as an add-in in terms it recognizes. For example, when DevStudio instantiates the component, it will ask for the interface IDSAddIn. As long as you implement IDSAddIn, DevStudio will treat you as a DevStudio-compliant add-in. You could implement a hundred other interfaces, present a UI, service HTTP requests, whatever—DevStudio will neither know nor care.

Likewise, when VisualStudio 2003, 2005, or Office instantiate your component, they'll ask your component for the IDTEExtensibility2 and IDTECommandTarget interfaces. Again, as long as you implement these two interfaces, the host application will treat it like an add-in, no matter what else you can do.

Put simply, if you whip up a COM component that implements these three interfaces, all your hosts will happily load that component as an add-in that conforms to their respective models:

As an aside, there are name clashes between DevStudio 6 and Visual Studio interfaces (for example, ITextDocument). I dealt with that by #includeing the DevStudio 6 header files (out of 'objmodel') and left those identifiers in the global namespace. I then #imported the typelibs describing the Visual Studio extensibility models, taking advantage of import's default behavior of creating a new namespace for all the entities defined in the imported typelib.

The sample is a standard Win32 DLL project, implemented in C++ on top of ATL. I'll walk through the implementation step by step and try to explain what I did and why.

Class CAdd-In

SampleCAI.cpp is boilerplate ATL code; it implements DLLMain and the four exports required by all COM In-Process Servers. The story really starts at SampleCAI.dll's sole COM component, CoAddIn. Here's the IDL for CoAddIn:

[
   uuid(5f4e04a1-1a92-11db-89d7-00508d75f9f1),
   helpstring("Common Add-In Sample Add-In Object")
]
coclass CoAddIn
{
   [default] interface IUnknown /*IDSAddIn*/;
}

The DLL can export only a single component due to the way DevStudio discovers new add-ins. When you point the DevStudio IDE at a DLL and ask it to load it as an add-in, DevStudio appears to call LoadLibrary, and then RegisterClassObjects on that DLL. It seems to spy on the Registry to figure out what COM components are registered by that call. I say "seems to" and "appears to" because this mechanism is undocumented; various add-in authors have deduced it empirically (see [1], for instance).

In any event, DevStudio determines what COM components are exported by your DLL and calls CoCreateInstance on each. After it creates the component, it calls QueryInterface, looking for IDSAddIn. If QI failes, DevStudio will notify the user and refuse to load the DLL as an add-in. Consequently, if you implemented, say, two COM objects, one for DevStudio and one for Visual Studio 2003 and 2005, DevStudio would find that its QI fails for the second component and will refuse to load the entire DLL.

Therefore, SampleCAI.dll exports one and only one COM component, CoAddIn, and that component implements IDSAddIn. Note that I don't actually advertise IDSAddIn in the IDL: That's just because IDSAddIn is only defined in a C/C++ header file (ADDAUTO.H); you have no IDL for it.

Now, take a look at CAddIn, the C++ class that incarnates the COM component CoAddIn:

class ATL_NO_VTABLE CAddIn :
   // Standard ATL parent classes...
{
public:
   /// ATL requires a default ctor
   CAddIn();
   /// ATL-defined initialization routine
   HRESULT FinalConstruct();
   /// ATL-defined cleanup routine
   void FinalRelease();

# ifndef DOXYGEN_INVOKED    // Shield the macros from doxygen...
   // Stock ATL macros...
   // Tell ATL which interfaces we support
   BEGIN_COM_MAP(CAddIn)
      COM_INTERFACE_ENTRY(ISupportErrorInfo)
      COM_INTERFACE_ENTRY_AGGREGATE(IID_IDSAddIn, m_pDSAddIn)
      COM_INTERFACE_ENTRY_AGGREGATE(EnvDTE::IID_IDTCommandTarget,
                                    m_pDTEAddIn)
      COM_INTERFACE_ENTRY_AGGREGATE(AddInDO::IID__IDTExtensibility2,
                                    m_pDTEAddIn)
      COM_INTERFACE_ENTRY_AGGREGATE(IID_IDispatch, m_pDTEAddIn)
   END_COM_MAP()
# endif    // not DOXYGEN_INVOKED

// ...

public:
   /// Display our configuration dialog
   void Configure();
   /// Carry out our command
   void SayHello();

private:
   /// Reference on our aggregated instance of CoDSAddIn
   CComPtr<IUnknown> m_pDSAddIn;
   /// Reference on our aggregated instance of CDTEAddIn
   CComPtr<IUnknown> m_pDTEAddIn;

};

As you can see, CAddIn is a plain-jane ATL class implementing a COM component. The first point of interest is the interface map. As explained above, class CAddIn exports IDSAddIn. However, you support it through an aggregate, m_pDSAddIn. This is a subordinate COM object:

[
   uuid(5f4e04a2-1a92-11db-89d7-00508d75f9f1),
   helpstring("Common AddIn Sample DevStudio 6 AddIn Object"),
   noncreatable
]
coclass CoDSAddIn
{
   [default] interface IUnknown /*IDSAddIn*/;
}

Note the "noncreatable" tag; you don't register it, either. It's not strictly necessary to do this; I created this class solely for purposes of code readability. Adding the code for all the object models (that is, DevStudio's, Visual Studio's, and Office's) to CAddIn made for an overly large class. This is strictly a matter of my personal preference; externally, callers won't know the difference.

The upshot is that when DevStudio calls QueryInterface looking for IDSAddIn, you'll delegate to m_pDSAddIn for it.

m_pDTEAddIn will hold a reference on a CoDTEAddIn. This is another aggregate analagous to CoDSAddin, but supporting the interfaces needed for Visual Studio and Office 2003. Here's the IDL for this component:

[
   uuid(5f4e04a3-1a92-11db-89d7-00508d75f9f1),
   helpstring("Common AddIn Sample DTE-Compatible AddIn"),
   noncreatable
]
coclass CoDTEAddIn
{
   [default] interface IDispatch;
}

The next touchy part is the fact that both IDTEExtensibility2 and IDTECommandTarget are duals; what should you do if it gets a QI for IDispatch? This is one of the many reasons I don't like duals, but that's another article! In this case, it turns out that the IDE expects you to return IDTEExtensibilty2. Fortunately, IDSAddIn is custom, so there's no conflict there.

The next points of interest are the public methods Configure and SayHello. The sample can do only two things: configure itself and say hello. This functionality resides in CAddIn. The idea is to concentrate the add-in's "core functionality" in CAddIn and delegate to aggregated helper classes for dealing with each host app. When they detect that the hosting application has invoked a command, they'll just call on CAddIn to do the work. After all, the whole point of this undertaking is to eliminate the need for code duplication in multiple add-ins.

DevStudio 6 Hosting

I've done a few non-standard things with respect to hosting within DevStudio. These aren't, strictly speaking, necessary in terms of loading the add-in into multiple hosts; they just make the add-in a little nicer.

When you point the DevStudio IDE at a .dll and ask to load it as an AddIn, DevStudio makes Registry entries for the new AddIn under:

HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\AddIns

However, a component can "self-register" under that key as part of its installation, sparing the user the hassle of doing so. However, this means that the first time the new add-in is loaded, the vfFirstTime parameter to OnConnect won't be set to VARIANT_TRUE.

This complicates toolbar creation, because a) you need to keep track yourself whether or not it's been created, and b) calling AddCommandBarButton when vfFirstTime is *em* 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 a hidden message window (it turns out that AddCommandBarButton will succeed if you call it outside the context of OnConnection).

I learned how to do this from Nick Hodapp's article "Undocumented Visual C++" [1]. Here's the Registrar script the sample uses:

HKCU
{
   NoRemove Software
   {
      NoRemove Microsoft
      {
         NoRemove DevStudio
         {
             NoRemove '6.0'
             {
                NoRemove AddIns
                {
                   ForceRemove 'SampleCAI.CoAddin.1' = s '1'
                   {
                      val Description = s 'Sample Common AddIn
                                           'Developer Studio Add-in'
                      val DisplayName = s 'SampleCAI'
                      val Filename    = s '%MODULE%'
                   }
                }
            }
         }
      }
   }
}

Another tip from the same article is a way to name your toolbar. When using the standard add-in APIs, your new toolbar will be named "Toolbar ", where <n> is the number of un-named toolbars (which is irritating and ugly). The Sample add-in instead hooks the toolbar creation and changes the Window name to something a little nicer.

Note: The new name must be no longer than the intended one (usually eight characters).

With that, look at CDSAddIn, the C++ class that incarnates the DevStudio AddIn:

class ATL_NO_VTABLE CDSAddIn :
      public CComObjectRootEx<CComSingleThreadModel>,
      public CComCoClass<CDSAddIn, &CLSID_CoDSAddIn>,
      public ISupportErrorInfo,
      public IDSAddIn
{
public:
   ...
   /// Private initialization routine
   void SetParam(CAddIn *pParent);

   ...
   /// Tell the ATL Registrar *not* to register us
   DECLARE_NO_REGISTRY();
   /// This component may only be created as an aggregate
   DECLARE_ONLY_AGGREGATABLE(CDSAddIn)
   ...
   /// Tell ATL which interfaces we support
   BEGIN_COM_MAP(CDSAddIn)
      COM_INTERFACE_ENTRY(IDSAddIn)
      COM_INTERFACE_ENTRY(ISupportErrorInfo)
   END_COM_MAP()

   ...
private:
   ...
   /// Non-owning reference to our parent CAddIn instance
   CAddIn *m_pParent;
   ...

};

For the most part, this is a straightforward ATL COM class implementing IDSAddIn. In fact, the implementation should be familiar to DevStudio AddIn developers. Its implementation of IDSAddIn::OnConnection gives DevStudio a dispinterface exposing your commands, and sinks any DevStudio events it's interested in. In IDSAddIn::OnDisconnection, it severs that connection.

Notes:
  1. The COM coclass CoDSAddIn is not directly creatable. It's not even registered, and, in any event, creation will fail without an aggregator.
  2. CDSAddIn maintains a (non-owning) back-pointer to its parent CAddIn. This enables it to delegate to its parent's command implementations. I suppose this isn't the most generic design: having one COM object so intimately aware of another's implementation. However, given that this component will never see use elsewhere, the convenience seemed worth it.

Visual Studio and Office Hosting

As described above, your add-in will provide implementations of IDTEExtensibility2 and IDTECommandTarget through another COM aggregate, CoDTEAddIn. This co-class is incarnated by CDTEAddIn:

class ATL_NO_VTABLE CDTEAddIn :
   // Stock ATL parent classes...
{
public:
   /// Application host flavors
   enum Host
   {
      /// Sentinel value
      Host_Unknown,
      /// Visual Studio 2003
      Host_VS2003,
      /// Visual Studio 2005
      Host_VS2005,
      /// Excel 2003
      Host_Excel2003,

      // Add new hosts here...

   };

public:
   ...
   /// Private initialization routine
   void SetParam(CAddIn *pParent);
   ...
   /// Tell the ATL Registrar *not* to register us
   DECLARE_NO_REGISTRY();
   /// This component may only be created as an aggregate
   DECLARE_ONLY_AGGREGATABLE(CDTEAddIn)
   /// Tell ATL which interfaces we support
   BEGIN_COM_MAP(CDTEAddIn)
     COM_INTERFACE_ENTRY(ISupportErrorInfo)
     COM_INTERFACE_ENTRY(EnvDTE::IDTCommandTarget)
     COM_INTERFACE_ENTRY(AddInDO::_IDTExtensibility2)
     COM_INTERFACE_ENTRY2(IDispatch, AddInDO::IDTExtensibility2)
   END_COM_MAP()
   ...

private:
   ...
   /// Reference to our host's Application object
   CComPtr<EnvDTE::_DTE> m_pApp;
   /// Reference to our host's Application object
   CComPtr<Excel::_Application> m_pExcel;
   /// Which host are we loaded into
   Host m_nHost;
   /// Non-owning reference to our parent CAddIn instance
   CAddIn *m_pParent;
   ...
};    // End CDTEAddIn.

The first thing that should jump out at you is that the class knows the host into which it's been loaded. Although Visual Studio and Office 2003 use this Add-In programming model, hosting applications themselves offer different interfaces to you. You need to take this into account when requesting services from our host. You guess the host type in the OnConnection method of the IDTExtensibility2 interface:

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 add-in object representing us,
   m_pAddIn = com_cast<EnvDTE::AddIn>(pAddInInst);

   // and try to figure out what DTE-compatible host we're
   // currently loaded into:
   m_nHost = GuessHostType(pApplication);

   ATLTRACE2(atlTraceHosting, 2, "CoDTEAddIn has been loaded with a c"
             "onnect mode of %d (our host is %d).\n", nMode, m_nHost);

   ...

After validating your parameters, the first real work you do is wrapped up in the call to GuessHostType; here is where you figure out what sort of environment you've been loaded into. Now, I've seen some Add-Ins that call GetModuleFileName(NULL,...) to get the name of the executable they've been loaded into, but I took a different approach. My thinking was that as long as the host implements the interfaces I expect, I can talk to it. For example, an application suite like Open Office could host Microsoft Office add-ins by implementing the appropriate COM interfaces.

GuessHostType works by QI'ing the application pointer you were given in OnConnection for various sorts of interfaces:

CDTEAddIn::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;
   }

   // Okay -- maybe it's Visual Studio 2003...
   ...

Note that we make no distinction between Visual Studio 2005 & 2008. It turns out that VS2008 implements interface EnvDTE80::IID_DTE2, and implements in a manner close enough to VS2005 that, for your purposes, you don't need to distinguish between them.

The goal is to fill in m_nHost with a member of the Host enumeration so that the rest of the logic "knows" how to behave. For instance, the next thing you do in OnConnection is call AddCommands:

void CDTEAddIn::AddCommands(AddInDO::ext_ConnectMode nMode)
{
   switch (m_nHost)
   {
   case Host_VS2003:
        AddCommandsVS2003(nMode);
        break;
   case Host_VS2005:
        AddCommandsVS2005(nMode);
        break;
   ...
Notes:
  1. The COM coclass CoDTEAddIn is not directly creatable; it's not even registered, and, in any event, creation will fail without an aggregator.
  2. Like CDSAddIn, CDTEAddIn maintains a (non-owning) back-pointer to its parent CAddIn, for the same reasons.

Common Add-Ins

Conclusion

These are the broad strokes; as I mentioned at the start, most of the work was in the details. I've attached a fully functional sample add-in that will load into DevStudio, Visual Studio 2003, Visual Studio 2005, and Excel 2003. It's a Visual Studio 2005 solution that contains the add-in itself, as well as its associated satellite DLL. To install it, just build either the Debug or Release configuration; there's a post-build step that will automatically register the DLL appropriately.

There's certainly more work that could be done; see Appendix A.

Enjoy. Questions, feedback, and suggestions are welcome.

Appendix A: Future Work

Cache COM Component Creation

The primary COM component, CoAddIn, implements different hosting models in terms of aggregated components. Today, both those components are instantiated in 'FinalConstruct'; it would be nice to move to some sort of cached scheme to avoid instantiating an instance of, say, CoDTEAddIn when you're loaded into DevStudio...

Property Pages

Visual Studio 2003 and 2005 allow their add-ins to add pages to the dialogs they display in response to Tools | Options. You can tell Visual Studio about your page, or pages, by adding some additional Registry entries (take a look at 'vs2003.rgs' or 'vs2005.rgs' in the sample, or see here).

I had thought it would be nice to add a new property page to DevStudio 6 and Excel 2003, but I wasn't able to figure out how. My scheme was to install a CBT hook and catch the creation of the Tools | Options Property Sheet. There, I'd post a message back to a private, message-only window that would create my Property Page and send a PSM_ADDPAGE message to the Property Sheet, along with my new page.

For whatever reason, I got that to work in a little test app, but not in either Dev Studio 6 or in Excel 2003. In both cases, the Tools | Options Property Sheet does not have a Windows Class of "Dialog" (like this one does), so perhaps these apps have some kind of non-standard implementation.

If anyone has any thoughts on this, or more success than I did, I'd love to hear about it.

It would also be cool to have the sheets that Visual Studio 2003 and 2005 display set their selection to your page when Configure is invoked. Currently, they open to the last page viewed. I've got some thoughts in terms of again installing a CBT hook to catch the sheet's creation and sending a message to its child tree control, but I haven't done anything on it. Jeff Paquette tells me he's used this successfully in his VisEmacs Add-In, however.

Other Applications

I don't have a copy of Visual Studio 2002, so I couldn't test that. I implemented support for Excel 2003, but that's it. It would be cool to build out support for the whole suite.

Project Template

A project template for generating code for a common add-in would be nice.

Appendix B: Visual Studio Commands and Command Bars

Getting the commands added and command bars set up was probably the most irritating part of writing this sample. In wading through this mess, I relied heavily on the article "HOWTO: Adding buttons, commandbars and toolbars to Visual Studio .NET from an add-in", by Carlos J. Quintero [2].

Carlos describes two distinct flavors of Visual Studio Command Bar: permanent and temporary.

Permanent Command Bars:

  1. Remain visible in the IDE even if the add-in is unloaded through the Add-in Manager
  2. Are created once (when the OnConnection method receives the value ext_ConnectMode.ext_cm_UISetup... which happens only once after an add-in is installed on a machine)
  3. Are added through DTE.Commands.AddCommandBar()
  4. Are removed only when the add-in is uninstalled (not when it is unloaded) through a custom action in the uninstaller using the using the DTE.Commands.RemoveCommandBar() function

Temporary Command Bars:

  1. Are created each time that the add-in is loaded using the DTE.CommandBars.Add() or CommandBar.Controls.Add() functions (depending on the type of commandbar: Toolbar or CommandBarPopup)
  2. Are removed by the add-in each time it is unloaded, using the CommandBar.Delete() method

According to Carlos, the fact that Permanent Command Bars remain even when the user unloads the add-in "will be confusing for many users" and consequently, "most add-ins don't use this approach". He lays out the following approach:

If ext_cm_AfterStartup or ext_cm_Startup

   Check for the command's existence through Commands::Item
   If not there, create it via Commands::AddNamedCommand for
   both 2003 and 2005

   Create a new (temporary) command bar by calling
   pTempCmdBar = ICommandBars::Add() (both 2003 and 2005!)

   Add a button:
   pTempCmdBar->AddControl

   pTempCmdBar->Visible = true;

Then call pTempCmdBar->Delete() in OnDisconnect

Note that he just ignores ext_cm_UIStartup entirely.

Now, to me, temporary toolbars seem fine, except for two problems:

  1. The toolbar re-appears every time VS is started, no matter if the user turned it off
  2. The commands are still there, even if you un-register the add-in; that's not so bad... the IDE detects this and removes the commands the next time they're invoked

Permanent toolbars respect the user's decision to turn them off, but if you un-register the add-in and delete the commands, the stupid thing is still there and you can't delete it!

I still haven't settled on the "right" solution. The sample add-in can use a few different approaches, depending on the setting of the #define SAMPLECAI_COMMAND_BAR_STYLE. It can take one of three values:

  1. SAMPLECAI_COMMAND_BAR_TEMPORARY: Uses temporary command bars
  2. SAMPLECAI_COMMAND_BAR_PERMANENT: Uses permanent command bars
  3. SAMPLECAI_COMMAND_BAR_SEMIPERMANENT: Uses permantent command bars, but deletes the Command Bar programmatically on un-install

Appendix C: Resetting Visual Studio

During the course of developing your add-in, you'll likely have times where you want to just re-set all the UI changes your add-in's made to Visual Studio.

For Visual Studio 2005, you can run 'devenv /resetaddin <AddInNamespace.Connect>'. Unfortunately, Visual Studio 2003 only offers 'devenv.exe /setup', which will re-set all your UI customizations (including your keybindings!). Because that's a bit draconian, I whipped up this little VBS script:

Dim objDTE
Dim objCommand
Dim objTb

On Error Resume Next

Set objDTE = CreateObject("VisualStudio.DTE.7.1")
If objDTE Is Nothing Then
   MsgBox "Couldn't find VS 2003"
Else

   Set objCommand =
      objDTE.Commands.Item("SampleCAI.CoAddIn.Configure")
   If objCommand Is Nothing Then
      MsgBox "The Configure command has already been deleted."
   Else
      objCommand.AddControl(objDTE.CommandBars.Item("Tools"))
      objCommand.Delete
   End If

   Set objCommand = objDTE.Commands.Item("SampleCAI.CoAddIn.SayHello")
   If objCommand Is Nothing Then
      MsgBox "The SayHello command has already been deleted."
   Else
      objCommand.AddControl(objDTE.CommandBars.Item("Tools"))
      objCommand.Delete
   End If

   Set objTb = objDTE.CommandBars.Item("SampleCAI")
   If objTb Is Nothing Then
      MsgBox "No (permanent) command bar named SampleCAI."
   Else
      objDTE.Commands.RemoveCommandBar(objTb)
      objTb.Delete
      set objTb = Nothing
   End If

   objDTE.Quit
   set objDTE = Nothing

End If

Set objDTE = CreateObject("VisualStudio.DTE.8.0")
If objDTE Is Nothing Then
   MsgBox "Couldn't find VS 2005"
Else

   Set objCommand =
      objDTE.Commands.Item("SampleCAI.CoAddIn.Configure")
   If objCommand Is Nothing Then
      MsgBox "The Configure command has already been deleted."
   Else
      'objCommand.AddControl(objDTE.CommandBars.Item("Tools"))
      objCommand.Delete
   End If

   Set objCommand = objDTE.Commands.Item("SampleCAI.CoAddIn.SayHello")
   If objCommand Is Nothing Then
      MsgBox "The SayHello command has already been deleted."
   Else
      'objCommand.AddControl(objDTE.CommandBars.Item("Tools"))
      objCommand.Delete
   End If

   Set objTb = objDTE.CommandBars.Item("SampleCAI")
   If objTb Is Nothing Then
      MsgBox "No (permanent) command bar named SampleCAI."
   Else
      objDTE.Commands.RemoveCommandBar(objTb)
      objTb.Delete
      set objTb = Nothing
   End If

   objDTE.Quit
   set objDTE = Nothing

End If

References

  1. Undocumented Visual C++ by Nick Hodapp (http://www.codeproject.com/macro/openvc.asp)
  2. Adding buttons, commandbars, and toolbars to Visual Studio .NET from an add-in (http://mztools.com/articles/2005/MZ003.htm
  3. Building an Office2K COM addin with VC++/ATL (http://www.codeproject.com/com/outlookaddin.asp)


Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

Most Popular Programming Stories

More for Developers

RSS Feeds