Dispinterface vs. Events and Runtime Sinks

In the normal COM scheme of things, the communication between client and server is driven by the client. The client creates the server's COM object, and makes calls into the object as it needs to. The object generally sits there passively waiting for calls from clients. However, sometimes, things happen which make the server want to notify the client, and these things might be asynchronous to anything the client is doing, and perhaps only peripherally associated with any client activity. For example, if your control has a user interface, and the user clicks on it, you might want to notify the container. Or, if your object encapsulates some business rule that has been broken, you might want to tell your client. Or, say an object that looks after a database might be updating its table indexes, and connected clients need to know when this processing is finished so that they can continue retrieving rowsets.

To code a solution to these requirements, you only really have two choices: client-initiated polling; and object-initiated notification. With polling, the client decides when to ask if some condition has been met (control clicked, business rule broken, indexes updated, etc). Clearly, this is inefficient. Much better is the solution where the object initiates the notification - since, after all, the object knows immediately when the notification is required, and only the one call is needed, instead of the iterative polling calls.

In order for this to work in a COM universe, it requires the client to implement an interface that the object makes calls on. This hooks into all the usual advantages of specifying COM interfaces, and logically represents the model since for the purposes of the event notification, the object is acting like a client, and the client is acting like a server.

However, the object still specifies the interface - this, too, is reasonable, since the object must clearly know about and support the interface event though it doesn't actually implement it. Also, the interface can be described in the server's IDL code and built into its type library, while the client on the other hand is unlikely to have any IDL code of its own. Since the object is the source of the calls on this outgoing interface, this interface is called a source interface. The client is the sink for calls on this interface.

The source interface

Writing a source interface in IDL is very simple, eg:

[ uuid(1F8C0130-CDF5-11d3-B77E-00104BDC292F),
 helpstring("_IRobotEvents Interface")
dispinterface _IRobotEvents
 [id(1), helpstring("method Finished")] HRESULT Finished();

The Finished event will be fired by the COM object whenever it deems appropriate. There's nothing in the IDL for the interface itself that tells us whether the interface is a source or sink interface. It can be either, depending on how it's used. Whether it's a source interface is determined by our particular object, so we specify it in the coclass section of our IDL:

coclass Robot
 [default] interface IMotion;
 [default, source] dispinterface _IRobotEvents;

Note that you can have two default interfaces, so long as one is a source interface and the other isn't. Also, remember that the source object does NOT implement the source interface - it merely defines it through its type library. The client will actually implement the source interface, having read the server's type library to find out the details of this interface.

IConnectionPoint and IConnectionPointContainer

On the other hand, the source object will implement a connection point. Your source object will have exactly one connection point for each outgoing interface. Connection points are separate COM objects, but they're created by your object, not by CoCreateInstance, and typically implement only two interfaces: IUnknown and IConnectionPoint.

COM objects can support more than one connection point. In order to do this, the source object must implement the IConnectionPointContainer interface. This interface allows the client object to obtain a connection point.

IConnectionPointContainer is a simple interface, with only two methods: FindConnectionPoint returns a pointer to the connection point specified by the IID passed by the client object, and EnumConnectionPoints returns an IEnumConnectionPoints enumerator that allows the holder to walk through all of the connection points supported by the source object.

So, the source object implements IConnectionPointContainer. The object also maintains a collection of connection points that can be searched and enumerated via the methods in IConnectionPointContainer. Each connection point maintains a list of active connections, set up by calls to the connection point's Advise method and ended by calls to the connection point's Unadvise method.

Sequence of calls to set up an event

  • The client queries the source object for IConnectionPointContainer. If an object implements IConnectionPointContainer, it indicates that it does fire events of some kind.
  • If the QueryInterface succeeds, the client passes IConnectionPointContainer::FindConnectionPoint the IID of an event interface it wants to receive. If the connectable object supports that interface, it returns a pointer to the server's connection point object for that interface. Alternatively, the client can call IConnectionPointContainer::EnumConnectionPoints to get an enumerator, so it can examine all of the supported connection points (and see whether it understands any of them).
  • Assuming that we've got a pointer to a connection point, the client calls IConnectionPoint::Advise on that pointer, passing an IUnknown pointer pointing to the mini-object that will actually receive the events (that is, the object in the client code that implements the specified event interface). The server object's implementation of Advise stores this interface pointer internally, and returns a cookie to the client, which the client in turn stores internally. This cookie is a DWORD with a unique value that represents this connection.
  • From this point, the server's source object can fire an event by getting the interface pointer from the connection point object and calling a method on that pointer. In other words, the server object calls through its internally stored sink object pointer to the client's sink object method. Since there may be multiple clients, the source object maintains a list of connected clients, enumerates these connections and calls the implemented event interface method on each one.
  • When it wants to stop receiving events (such as before it shuts down), the client calls IConnectionPoint::Unadvise, passing the cookie it stored previously. This causes the connection point object to delete the associated interface pointer.

Modifying a Server to Support Events

The following steps detail the modifications necessary to an existing ATL object in order to support firing an event through a dispinterface. If you were creating a new COM object from scratch, you'd make it support connection points by choosing that option in the ATL Object Wizard. In this case, you'll duplicate the effects of choosing that option by adding that support manually.
  1. Add a dispinterface to the library section of the .idl file and get a new GUID, as indicated below. Also add the dispinterface to the coclass as the [default, source] interface.
     helpstring("_IRobotEvents Interface")
    dispinterface _IRobotEvents
  2. In ClassView, right-click the interface _IRobotEvents and add an arbitrary method named Finished to the interface. For simplicity, specify void return, no arguments.
  3. Now, generate code to fire the event. First, run the MIDL compiler on the .idl file to get the built type library. Then, in ClassView, right-click the COM object class and choose Implement Connection Point. Select the _IRobotEvents interface and click OK. The wizard then makes the following changes to your COM object class (but, be warned, there are bugs in the wizard, so check that all the changes have actually been made correctly):
    • Adds the interface IConnectionPointContainerImpl to the multiple inheritance list
    • Adds IConnectionPointContainer to the COM map.
    • Adds a connection point map with an entry for the dispinterface.
    • (MSVC 6.0 sometimes incorrectly inserts IID__IRobotEvents as the argument to the CONNECTION_POINT_ENTRY macro. Be sure to verify that DIID__IRobotEvents was entered - the first 'D' is the convention to indicate a dispinterface, and there are two underscores because there was already one at the beginning of the interface name).
    • Includes the connection point header file ATLRobotCP.h.
    • Adds the connection point proxy class (CProxy_IRobotEvents) to the multiple inheritance list. Examine this proxy class:
    template <class T>
    class CProxy_IRobotEvents : 
    public IConnectionPointImpl<T, &DIID__IRobotEvents, 
     HRESULT Fire_Finished()
      CComVariant varResult;
      T* pT = static_cast(this);
      int nConnectionIndex;
      int nConnections = m_vec.GetSize();
      for (nConnectionIndex = 0; 
           nConnectionIndex < nConnections; 
       CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
       IDispatch* pDispatch = 
       if (pDispatch != NULL)
        DISPPARAMS disp = { NULL, NULL, 0, 0 };
        pDispatch->Invoke(0x1, IID_NULL,
                          &disp, &varResult, 
                          NULL, NULL);
      return varResult.scode;
  4. Next, fire the event: in any method in the object: at some appropriate point, call the implemented Fire_Finished method of the inherited proxy class, bearing in mind that this will actually walk the list of connected sinks and call the Finished method on each client.

Modifying a Client to Support Events

Again, suppose you already have an MFC client. The next section lists the steps needed to add event sink support, so that you can clearly see what the extra code involved should be. In this exercise, you will add a new class and additional code so that the client can receive events from the COM object server that you modified in the previous exercise. You will use an ATL class as the base class for your sink object.
  1. For example, start with a regular MFC dialog-based application, and #import the server's type library. Add an interface pointer as a member in the dialog class, using the #import-generated _com_ptr_t smart pointer class, (eg ISomethingPtr m_pIS). Initialize this in the OnInitDialog, eg:
     m_pIS = ISomethingPtr(CLSID_Robot);
    catch (_com_error &e)
  2. Put a button on the dialog, and get a BN_CLICKED handler. Code this handler to call any of the object's methods, eg:
    void CMfcClientDlg::OnButton1() 
      int j = 0;
      char s[8];
      wsprintf(s, "%d", j);
     catch (_com_error &e)
  3. We now need to add support for the ATL: add the following lines of code to the stdafx.h:
    #include <atlbase.h>
    extern CComModule _Module;
    #include <atlcom.h>
  4. Add this line to the stdafx.cpp
    #include <atlimpl.cpp>
  5. Now, create, initialize, and terminate a global instance of the CComModule class. First, at file-level scope in the implementation file of the application object (say, just above the constructor), declare a CComModule object:
    CComModule _Module;
  6. In the InitInstance, initialize and terminate the _Module object around the declaration and use of the dialog object. Force the dialog object's destructor to execute before the call to CComModule::Term by placing the declaration and use of the dialog object within a new block - this is because our dialog is going to use the COM object with ATL support, and we can't have its destructor doing any such work after the CComModule::Term has erased ATL COM support.
    _Module.Init(NULL, m_hInstance);
    // declaration of dialog object, and DoModal, etc
  7. Now add a new class to implement the event interface. First, add a new C++ source file to the project. Name the file EventHandler.cpp. Also add a new C++ header file to the project. Name the file EventHandler.h. In the header file EventHandler.h, add a declaration for a class named CEventHandler that publicly inherits from IDispEventImpl. Add the six arguments to this template class. You can get the IID of the dispinterface from the .tlh file. Add a SINK map to the class CEventHandler by using BEGIN_SINK_MAP() AND END_SINK_MAP. To the SINK map, add a SINK_ENTRY_EX macro for the event method Finished. Add a prototype for the Finished method. Remember that the function must use the __stdcall calling convention. You can find this function's signature in the .tlh file.
  8. Add the following lines of code to the file EventHandler.cpp:
    #include "stdafx.h"
    #include "EventHandler.h"
  9. Write code for the Finished function - for the exercise, just call AfxMessageBox to display a suitable message.
  10. Now to incorporate the EventHandler class within the dialog application. First, add a CEventHandler pointer as aprivate member variable in the dialog class - call it m_pHandler. Include the header file EventHandler.h in the dialog class header file. Add code to the try block in the OnInitDialog to create a sink object and to make the connection point in the server aware of this sink with the function DispEventAdvise, as shown below.
    m_pHandler = new CEventHandler;
    IUnknownPtr pUnk = m_pIS;
  11. Add a destructor to the dialog class. In the destructor, call the DispEventUnadvise function and delete the CEventHandler object, as shown below:
    IUnknownPtr pUnk = m_pIS;
    delete m_pHandler;
  12. Build and test the project. When you click the button, you should get a message box from the Finished event.

Dispinterface vs VTBL Events

Recall that the type of interface for our events is a dispinterface. Why, you might ask? Well, despite the fact that dispatch interfaces are slower, they do simplify things into just an implementation of IDispatch. Also, with a dispatch interface, the object receiving the calls doesn't have to provide implementations of all the methods in the interface. Remember that when you implement a regular custom (or, for that matter, dual) interface, you must implement all of the methods on that interface (at least to return E_NOTIMPL).

For a dispatch interface, you have to implement all of the methods of IDispatch, but your implementation of Invoke doesn't have to implement every method in the dispatch interface -- it can just return an error (DISP_E_MEMBERNOTFOUND) for methods it doesn't support (in other words, events it doesn't care to handle).

So, because it's easier for a language such as VB or VBA to get event calls on a dispatch interface rather than on a custom interface, that's all they support. If you're writing your own sink in C++ and you don't care about other clients, you can use a custom interface for improved performance. But in most cases, events are relatively rare, so usually the performance isn't a big issue.

If you want to have an event interface that can be high performance and compatible with VB, implement two equivalent source interfaces (one custom and one dispatch) with different interface IDs (IIDs), and make the dispatch interface the default so VB will be happy.

Server with VTBL Event

We'll continue with our ATL object, and modify the current version so that its event interface is also exposed by a VTBL (custom) interface, ie an interface derived from IUnknown.
  1. Create a new GUID and add an interface named IFinishedEvent, which is derived from IUnknown, to the library section of the .idl file, as shown below. Add the interface IFinishedEvent to the coclass as a source interface. Copy the Finished method from the dispinterface to the IFinishedEvent interface. Make sure the return type is HRESULT.
    [uuid(Fresh guid here),
    interface IFinishedEvent : IUnknown
  2. Now run the MIDL compiler on the .idl file to generate the type library. Then, in ClassView, right-click the COM object class and choose Implement Connection Point. Select both the _IRobotEvents interface and the IFinishedEvent interface, and click OK. This will regenerate the ATLRobotCP.h file with proxy classes for each event interface. The one based on _IRobotEvents will be the same as before, but note the code for the new one:
    template <class T>
    class CProxyIFinishedEvent 
    : public IConnectionPointImpl<T, 
     HRESULT Fire_Finished()
      HRESULT ret;
      T* pT = static_cast<T*>(this);
      int nConnectionIndex;
      int nConnections = m_vec.GetSize();
      for (nConnectionIndex = 0; 
           nConnectionIndex < nConnections;
       CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
       IFinishedEvent* pIFinishedEvent = 
       if (pIFinishedEvent != NULL)
        ret = pIFinishedEvent->Finished();
      return ret;
  3. Replace the event-firing call that you previously put into one of the object's methods with a pair of calls to both versions of the Finished event. You must use the full method name of the event from both source interfaces, as shown below. Then build the server again.

Client supporting VTBL Events

Now modify your current client (with dispinterface event support), so that it implements the custom event interface on the ATL Robot server instead of the dispinterface event.
  1. First take a copy of your current MFC dialog-based client project. We need all the ATL support as before, including the various #includes, the CComModule object, and the Init and Term in the InitInstance.
  2. Now the event handler class. Comment out the previous dispinterface-based CEventHandler class declaration, and declare a replacement. This class will also be called CEventHandler, but will inherit from CComObjectRoot and IFinishedEvent. Add a COM map to the class, and a COM_INTERFACE_ENTRY for the IFinishedEvent interface. Use the STDMETHOD macro to add a prototype to the function that will receive the event. If you are unsure of the function name, look at the primary type library (TLH) file. In the EventHandler.cpp, comment out the previous implementation of the function, and replace it with this:
    STDMETHODIMP CEventHandler::raw_Finished()
     AfxMessageBox("Got a VTBL event");
     return S_OK;
  3. In the dialog class, remove the CEventHandler pointer member, and replace it with a DWORD for the cookie. In the OnInitDialog, add code to create an instance of the event handler class, and use AtlAdvise to connect to the source, eg:
    CComObject<CEventHandler> *pHandler;
    HRESULT rc = AtlAdvise(m_pIM, pHandler->GetUnknown(),
                           IID_IFinishedEvent, &m_dwCookie);
  4. In the WM_DESTROY handler, call AtlUnadvise, as shown below. Then built and test.
    if (m_pIM)
     AtlUnadvise(m_pIM, IID_IFinishedEvent, m_dwCookie);

Multiple Clients, Single Server

If your COM object resides in a DLL server, it will be loaded into the address space of a client - if there are multiple clients, there will be multiple instances of the DLL, one for each client. However, in some situations, your server will be performing some central, poolable activity such that multiple clients need to connect to the same instance of the server's COM object(s). In this case, you need to set up your server as an EXE server, and register the class factory or factories for multiple use (the default with the ATL COM AppWizard generated code). Also, you need to set your COM object class factory for single object creation.
  1. Create a new ATL COM AppWizard project, and make it an EXE server. Insert a new ATL object. Make sure the "Support connection points" box is checked. Add whatever methods you want to the interface, and implement them in some simple way.
  2. Add the DECLARE_CLASSFACTORY_SINGLETON macro to the object's C++ class. Add a method to the event interface, called Finished, no parameters.
  3. Compile the IDL. Right-click on the CRobot class and select "Implement connection point". Select the _IMotionEvents dispinterface and OK everything. Then check that the wizard has done its job properly. Build the server.
  4. Take a copy of the previous mfcClient project, and make the following changes. First, change the #import to use the new LocalRobot type library. Double-check the interface ID used throughout, especially in the CEventHandler class - it should probably be DIID__IMotionEvents. Also change the LIBID. Build and test the client - everything should work as before.
  5. Now run multiple clients - they should all be using the same instance of the server object - so when any one of them triggers the event, it will be sent to all of them.

Runtime Sinks

Some clients - notably scripting clients - might wish to implement a connection point sink at runtime. In order to support this, your component needs to support IProvideClassInfo2. This extends IProvideClassInfo with the GetGUID method, which allows a client to retrieve an object's outgoing interface IID for its default event set. In an ATL object, you can use IProvideClassInfo2Impl to support this.

You may also add the IObjectSafety interface - this marks a component as safe for scripting. Again, the ATL supports this with the IObjectSafetyImpl class. Finally, you must add an entry for IProvideClassInfo to the COM map; however, because this interface serves as the base for IProvideClassInfo2, you need not add IProvideClassInfo to the multiple inheritance list.

  1. Create a new ATL COM AppWizard project: call it SimpleSink, all defaults. Insert a new ATL object, with the short name Simple. Make sure the "Support connection points" box is checked. Then #include to the top of your Simple.h. Add IProvideClassInfo2Impl and IObjectSafetyImpl to the multiple inheritance:
    public IProvideClassInfo2Impl<&CLSID_Simple,
    public IObjectSafetyImpl<CSimple, 
  2. Update the COM map:
  3. Add a method to the _SimpleObject event interface, called e1. Compile the IDL, right-click the CSimple class and Implement Connection Point. Add a method to the ISimple interface, called m1. Implement this to call Fire_e1. Build. File|New|File: add an HTML file called TestSimpleSink, and insert the following code where the comment is:
    <OBJECT ID="Simple"
     VALUE="Invoke Event-Firing Method">
    sub b1_OnClick()
    end sub
    sub Simple_e1()
     MsgBox "Received Event"
    end sub
  4. Save the HTML file, and double-click it in Explorer, or right-click in the Visual Studio editor, and select Preview.

OK, we've had a quick review of the basics of events in ATL, we've considered why you might want a dispinterface event instead of a custom event interface, we've seen how to implement a 'one-object-to-many-clients' event solution, and implemented a scriptable runtime sink.


  • Queries on connection points

    Posted by edjamesx on 04/23/2004 05:49am

    1.How to build a proxy/stub dll with connection points ?.
      2. Connection points works only for an interface. If I remove singleton class It's firing for current interface 
           which invokes that method. Is there anyway to track the connected interfaces in COM server and
           fire the event to all conneted clients. 
          [I feel the disadvantage of using SINGLETON class because It makes one client to wait until It finishes
           the process]
    3.  Is there anyway to fire an event for a particular connection .[Example  Like in chat program If I want 
           to send a message to a particular client  ]
      3. Is there any way to detect IP of the remote machine using connection point interface.
    If any one know how to do, please let me know. you can contact me at edjamesx@hotmail.com

  • You Rule

    Posted by Legacy on 02/13/2004 12:00am

    Originally posted by: Todd Clark

    Great Article. Saved me many hours of frustration.

  • How can I fire a event for Drop-Down List

    Posted by Legacy on 12/21/2003 12:00am

    Originally posted by: Khondker Ashif

    I am new in IE programming..i am able to put a click event for a button element...but i could not fire a event for a drop-down(list)to expand list from a using MFC..i have the drop-down element...plz some body help me to fire the drop down element..i need the project or helping code..Thanks

  • Some important DCOM issues

    Posted by Legacy on 10/03/2003 12:00am

    Originally posted by: Konstantin Lvov

    Most likely these issues are obvious for experience programmer, but I would like to accent them.

    It's related to case when COM server is implemented as executable, and client that catch events implemented as console application with ATL support.


    1) In stdafx.h should be #define _WIN32_DCOM

    2) COM must be initialized via CoInitializeEx(NULL,COINIT_MULTITHREADED) - if simple CoInitialize or CoInitializeEx with COINIT_APARTMENTTHREADED flag used, Invoke call didn't return in server Fire_Finished() function.

    3) ATL _Module must be initialized with HINSTANCE value different from zero. I couldn't find how to obtain module HINSTANCE for console application, but _Module.Init(NULL,(HINSTANCE) 0x400000) worked for me.


    1) If server is implemented as multithreaded application and it fires events from thread different from it's main thread, then following conditions must be met:

    - CoInitializeEx(NULL,COINIT_MULTITHREADED) must be called by firing thread

    - server class must derive from CComMultiThreadModel

    - ATL_FREE_THREADED must be defined in stdafx.h instead of ATL_APARTMENT_THREADED

    - Multithreaded version of runtime library must be used (Project -- Settings -- C++ -- Category: Code generation)

    Security issues:

    1) Server launch and access permissions must be properly configured via dcomcnfg.exe utility.

    2) It's better to use short server name (in 8.3 format) - when Service Control Manager (SCM) creates server process it calls CreateProcessAsUser - it seems that process will be created with proper credentials only if name of server in 8.3 format.

    I didn't dig deep into security, in my version client obtains server interface pointer via CoCreateInstanceEx with default security attributes (COSERVERINFO::pAuthInfo = 0), and it worked when server and client were run under the same account.

  • How to get the control in the dialog

    Posted by Legacy on 08/13/2003 12:00am

    Originally posted by: sway

    Hello, I used the knowledge in this article to update my program. However, I have a question here: my client dialog has a CProgressCtrl control, I want to manupilate the control in my event handler so that I can see the progress while the COM object is doing long time caculation, but I don't know how to acquire the pointer.

    Thanks for the answer,


  • Event with data

    Posted by Legacy on 06/19/2003 12:00am

    Originally posted by: Arvind K. Chaodhary


    Is it possible to fire an event on multiple clients with data.
    I tried this with some data as parameter in event funtion but this doesn't work.

    If it is possible in ATL com pls let me know how.

    Thanks and Regards

  • Connection Points Over DCOM

    Posted by Legacy on 06/18/2003 12:00am

    Originally posted by: Raj

    Please let me know, how the above illustrated connection point example could be used over DCOM.

    Have a SINGLETON EXE Server(Out of proc) and multiple Clients some MFC & some ATL connecting to this SINGLETON server, Connection points work perfectly fine on the LOCAL SERVER with multiple clients, but when tried over DCOM, the DispEventAdvise fails on the remote client.

    m_pHandler = new CEventHandler;
    IUnknownPtr pUnk = m_pIS;

    Have registered the proxy stub dll and could call into the interface methods but could get the connection point or events/DispEventAdvise to work though.

    Any help or downloadle code is much appreciated,
    Thanking you much,

    Best Regards, -Raj.

    • Connection point works if COM server is registered in client machine

      Posted by edjamesx on 04/23/2004 05:57am

         We also encounter same problem. But When we register COM server exe in client machine with 
        (/Regserver) option Connection points started working. I think Connection point implementation for this Interface is not  packed in proxy/stub dll.

  • Dispinterface vs. Events and Runtime Sinks

    Posted by Legacy on 03/14/2003 12:00am

    Originally posted by: Lindsey Eastburn

    I'm a neophyte in the COM world and I'm really enjoying the new-found freedoms, but I'm also a bit overwhelmed by all of the new terms, the objects and macros, and everything else. I really appreciate people such as yourself, who take the time to share their knowledge. To me, that's one of the finest characteristics of the best of those in our profession.

    I just read your article, "Dispinterface vs. Events and Runtime Sinks". It was well-writen and concise. You saved me a ton of research.

    Thanks again,
    Lindsey Eastburn

  • 5 times better

    Posted by Legacy on 03/13/2003 12:00am

    Originally posted by: Zhifang Zhao

    At least 5 times better than Chapter 8 of "ATL Internals".
    One little point to mention, if you have a lagecy COM server and want to add connection point support. Define a event interface and add it to coclass with "[default, source]" in xxserver.idl, the Wizard still can generate event support code after this xxserver.idl get compiled.


  • What about returning value from events?

    Posted by Legacy on 01/20/2003 12:00am

    Originally posted by: Vitaly

    How to invoke an event that has a return parameter, i.e. parameter [out, retval] ?

  • Loading, Please Wait ...

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

Top White Papers and Webcasts

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • When it comes to desktops – physical or virtual – it's all about the applications. Cloud-hosted virtual desktops are growing fast because you get local data center-class security and 24x7 access with the complete personalization and flexibility of your own desktop. Organizations make five common mistakes when it comes to planning and implementing their application management strategy. This eBook tells you what they are and how to avoid them, and offers real-life case studies on customers who didn't let …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds