SAMPLE: Helper Functions to Add Event Support to Server

ID: Q159041

The information in this article applies to:

- Microsoft COM libraries included with:

    - Microsoft Windows NT, version 4.0
    - Microsoft Windows 95

SUMMARY

The Eventh.exe sample provides helper functions that allow easy implementation of event-handling support in a COM server. It also provides a COM server that uses these functions and a COM client that uses the server.

MORE INFORMATION

The following file is available for download from the Microsoft Software Library:

 ~ Eventh.ex

For more information about downloading files from the Microsoft Software Library, please see the following article in the Microsoft Knowledge Base:

   ARTICLE-ID: Q119591
   TITLE     : How to Obtain Microsoft Support Files from
               Online Services

After downloading the file, run it in an empty directory with the -d switch to set up the directory structure:

   EVENTH.EXE -d

This sample provides helper functions that allow easy implementation of event-handling support in a COM server that fires events. A COM server must implement the following interfaces to fire events:

   IConnectionPointContainer
   IConnectionPoint
   EnumConnectionPoints
   EnumConnections

This sample provides a function called CreateStdCPContainer() that implements the above interfaces and a function called FireEvent() that the server can use to fire events to all clients who have connected to a connection point. The helper functions can be found in the Event.cpp and Event.h files and can be used by 32-bit applications only. These files are self-contained and can be added to any 32-bit project.

This sample also provides a COM server that uses the helper functions to implement event-handling support and a COM client that uses the server. The COM server is the HELLO automation server sample that ships with the Win32 SDK with added event-handling support. The COM client is the HELCTRL automation client sample that ships with the Win32 SDK with added event-handling support.

To implement event-handling support in a COM server, the server must aggregate with the IConnectionPointContainer implementation provided by the helper function, CreateStdCPContainer. Following is the documentation of this function:

   HRESULT CreateStdCPContainer(
       IUnknown* punkController,
       ITypeInfo* aptinfo[],
       ULONG cTypeInfos,
       DWORD dwFlags,
       IUnknown** ppunkCPC)

The function creates a connection-point container object with connection points corresponding to the array of typeinfos passed in. The newly created object, which supports IConnectionPointContainer, is aggregated with punkController. The implementation uses SAFEARRAYs as the data structure to store the connection-point objects and the event sink pointers provided by the COM clients.

Parameters

punkController

 [in] Points to the server's IUnknown. The connection-point container
created by this function aggregates with this IUnknown.

aptinfo

 [in] Array of pointers to typeinfos. Each one corresponds to an event
interface/dispinterface that the object supports. Typically, an object has only one event interface/dispinterface.

cTypeInfos

 [in] Size of aptinfo array. This is typically 1, unless the object has
multiple event interfaces.

dwFlags

 [in] Indicates the type of connection-point container to create. The two
values allowed are CPTYPEFLAG_MULTICAST and CPTYPEFLAG_SINGLECAST. CPTYPEFLAG_MULTICAST indicates that multiple clients can connect their sinks to each connection point and the event will be fired to all those clients. CPTYPEFLAG_SINGLECAST indicates that only one client can connect its sink to each connection point and the event will be fired only to that client. These constants are defined in Event.h in this sample.

ppunkCPC

 [out] Returns IUnknown* of the created connection-point container object.
(This is the private unknown of the aggregate.)

Return Value

S_OK              Success.
E_INVALIDARG      Invalid argument.
E_OUTOFMEMORY     Memory allocation failed.

Example

In this code example, ptinfo is the typeinfo of the event interface (obtained from the type library) and riidEvents is the GUID of that interface. The COM server should call this code in its creation routine. This server has one event interface. It saves the returned pointer to the IUnknown* of the connection-point container object in the m_punkCPContainer data member. It also saves the pointer to the connection point in the m_pcp data member:

   CreateStdCPContainer(this, &ptinfo, 1, CPTYPEFLAG_MULTICAST,
   &m_punkCPContainer);
   m_punkCPContainer->QueryInterface(IID_IConnectionPointContainer,
   (void**)&pcpc);
   pcpc->FindConnectionPoint(riidEvents, &m_pcp);

The COM server should implement its IUnknown::QueryInterface. Following is an example of how to handle a request for IID_IConnectionPointContainer:

   if (iid == IID_IConnectionPointContainer)
        return m_punkCPContainer->QueryInterface(iid, ppv);

After the server has aggregated with the connection-point container object provided by CreateStdCPContainer, it can use the FireEvent() helper function to fire events to the clients who have connected to a connection point. Following is the documentation of FireEvent():

   STDAPI FireEvent(IConnectionPoint FAR *pcp,
         DISPID dispidMember,
         REFIID riid,
         LCID lcid,
         WORD wFlags,
         DISPPARAMS FAR* pdispparams,
         VARIANT FAR* pvarResult,
         EXCEPINFO FAR* pexcepinfo,
         UINT FAR* puArgErr)

This fires the event specified by dispidMember to all sinks connected to the pcp connection point. IDispatch::Invoke fires the event. Consequently, the event sink in the client must implement IDispatch for this function to work. If you do not want to use IDispatch to fire events, you must rewrite this function but you can still use CreateStdCPContainer(). This function does not stop firing events if the call to a specific event sink fails.

Parameters

pcp

 [in] The connection point for which events are going to be fired. This
connection point must be obtained from the connection-point container created by CreateStdCPContainer().

The other parameters are the same as the IDispatch::Invoke parameters and include the DISPID of the event to be fired and the parameters of the event.

Return Value

S_OK              Success
E_OUTOFMEMORY     Memory Allocation failure
Errors returned by failure of SafeArrayCopy and SafeArrayAccessData.

Example

In the following example, m_pcp is the variable referenced in the example section of the CreateStdCPContainer documentation:

   FireEvent(m_pcp, dispid, IID_NULL, LOCALE_USER_DEFAULT,
             DISPATCH_METHOD, &dispparamsNoArgs, NULL, NULL, NULL);

To Compile the Samples

To compile an ANSI-debug version of the sample for Windows NT and Windows 95, use the following command:

   nmake

To compile a Unicode-debug version of the sample for Windows NT only, use the following command:

   nmake HOST=NT

See the makefile header comments for other make options.

Compile the HELLO sample before compiling the HELCTRL sample because HELCTRL uses HELLO's Tlb.h file. In this sample, the HELCTRL client application uses the server's type library to implement the IDispatch interface of the event sink. Note that the client can also use its own type library for this purpose.

To Run the Samples

Change Hello.reg to provide the full path of Hello.exe and Hello.tlb. Register Hello.reg in the registration database by double-clicking it in Explorer. Run Helctrl.exe and use its menu to control the HELLO server. You can use CreateHello to create a new instance of HELLO. You can use GetHello to bind to a running instance of HELLO. Use the Visible menu item to make the server visible if required. Use InvokeSayHello to invoke the SayHello method. The server fires the SaidHello event when SayHello is invoked. The client displays a message box indicating that it received the fired event.

Windows NT 4.0 and Windows 95 with DCOM have system-provided marshaling code for the event interfaces (IConnectionPointContainer, IConnectionPoint, EnumConnectionPoints and EnumConnections). Without DCOM, Windows NT 3.51 and Windows 95 do not have system-provided marshaling code for these interfaces. If you want to run these samples on Windows 95 without DCOM and Windows NT 3.51, install the marshaling code provided in the following sample from the Microsoft Knowledge Base. Make sure that you carefully read the installation instructions in the sample before installing the marshaling code:

   Article ID: Q149231
   TITLE:      Marshaling Code for Connection Point Interfaces

If you would like to run the server on a remote computer and you want to gain access to it from the client using DOCM, read the following article in the Microsoft Knowledge Base. If you do this, make sure that the client application provides access permission to the user on the server computer, which is where the server application runs. Otherwise, the server application cannot fire events to the client application.

   Article ID: Q158582
   TITLE     : Configuring a non-DCOM server and client to use DCOM

Additional reference words: kbfile
Version           : 4.00
Platform          : NT WINDOWS

Last Reviewed: December 7, 1998