NGWS SDK Documentation  

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

Synthesizing Selected Interfaces

In addition to the interfaces that are explicitly implemented by a NGWS class, the CCW can synthesize an implementation of the interfaces listed below on demand. The implementations of these interfaces are based on the metadata provided by the class and are completely implemented by the CCW on behalf of the object. While it is possible to override or completely replace the runtimes implementation of any interface, a NGWS class is never required to implement any of the following interfaces.

The IUnknown Interface

Even though NGWS objects are garbage collected, a standard implementation of IUnknown is provided to the classic COM client so that it can continue to use the AddRef, Release and QueryInterface. The reference counting is done on the CCW, which, in turn maintains a traced reference to the NGWS object. The NGWS runtime object is not reference counted directly

There are issues relating to determinism lifetime with this approach. The NGWS class is not destroyed until it is actually garbage collected. This differs from the classic COM model where objects are destroyed immediately upon the final call to Release.

As already mentioned, the wrapper’s implementation of QueryInterface creates tear-off for any interfaces explicitly implemented by the class as the client requests them. Given a class Foo that implements interfaces IFoo1 and IFoo2 and extends class Bar that implements IBar1, QueryInterface will respond to requests for IFoo, IFoo2 and IBar with interfaces that delegate calls to the classes implementation. In addition, the CCW will also synthesize a vtable and implementation of any of the interfaces shown Table 1 on demand. The sections that follow describe each of these interfaces in more detail.

Table 1. Synthesized Interfaces Table

Interface ID Interface Description
_Foo
The class interface for Foo
The class interface for Foo. Contains the methods of the class Foo, the class Bar, and of the NGWS frameworks class Object since all NGWS classes extend object. In addition, this interface has all methods of IFoo1, IFoo2 and IBar1.
_Bar
The class interface for Bar
The class interface for Bar. Contains the methods of the class Bar, and of the NGWS frameworks class Object since all NGWS classes extend object. In addition, this interface has all methods of IBar1.
_Object

The class interface for Object

The class interface for Object. Contains the methods of the class Object. This interface is defined in runtime class library
IDispatch A standard dispinterface that contains all the same methods of _Foo.
IProvideClassInfo A standard IProvideClassInfo interface that provides the CLSID of Foo
ISupportErrorInfo   
IErrorInfo A standard interface for conveying detailed error information from callee to caller
IDispatchEx A standard interface that allows callers to dynamically add and remove methods from a dispinterface among other things.
IConnectionPointContainer
IConnectionPoint
A standard interface for dealing with events
IObjectSafety   A standard interface that allows caller to determine if a particular object is safe for scripting with.

The wrapper may, for some NGWS classes, also provide implementation of the following interfaces:

Interface ID Interface Description
IEnumVariant: For collection type classes, an interface for enumerating the objects in the collection.
IPersistStream: For persistent objects an interface for reading and writing the object to a stream.

IDispatch Interface

The runtime often needs to provide an implementation of the IDispatch interface for COM clients to use. This can either be in response to a direct request for an IDispatch (through QueryInterface) or by calling through the IDispatch methods in a dual interface.

There are two separate IDispatch implementations that the runtime can use.

For the highest level of backward compatibility, the Ole Automation compatible implementation should be used. This is the same IDispatch implementation that earlier versions of Visual Basic used and it is the one that most COM applications are designed to use (see Appendix F for known compatibility issues). The downside of using this implementation is that it requires static type information (even for managed types). The runtime can, and will, automatically generate the type information for managed types as necessary, but the process is time consuming. See the section below on on-the-fly typelib generation.

As an alternative, the runtime provides its own internal implementation of IDispatch. This implementation does not require static type information unless it is specifically asked for by calling IDispatch.GetTypeInfo. Other methods in the IDispatch interface can be called without requiring any form of static type information. If IDispatch.GetTypeInfo or IDispatch.GetTypeInfoCount is called, the same on-the-fly typelib generation process described below is used.

The IDispatch implementation can be controlled with the custom attribute System.InteropServices.IDispatchImplAttribute. The attribute can be set at the assembly level or on an individual class. When applied to an individual class, the attribute overrides any assembly level setting for that class. If the attribute is missing, the internal implementation is used. For a description of the differences between the Internal and Ole Automation implementation see Error! Reference source not found..

Individual interfaces can also use the System.InteropServices.InterfaceTypeAttribute to control whether the interface is exposed to COM as IUnknown derived or IDispatch derived. The table below shows how the interfaces InterfaceTypeAttribute attribute and the classes IDispatchImplAttribute attributes can be combined to create the desired effect.

InterfaceTypeAttribute on Interface IDispatchImplAttribute on Class Effect on Interface
ComInterfaceType.InterfaceIsDual or
ComInterfaceType.InterfaceIsIDispatch
IDispatchImplType.CompatibleImpl Dual interface using compatible implementation.
ComInterfaceType.InterfaceIsDual or
ComInterfaceType.InterfaceIsIDispatch
IDispatchImplType.InternalImpl Dual interface using internal implementation.
ComInterfaceType.InterfaceIsDual or
ComInterfaceType.InterfaceIsIDispatch
IDispatchImplType.SystemDefinedImpl
or Missing
Dual interface using internal implementation.
ComInterfaceType.InterfaceIsUnknown IDispatchImplType.CompatibleImpl IUnknown derived interface
ComInterfaceType.InterfaceIsUnknown IDispatchImplType.InternalImpl IUnknown derived interface
ComInterfaceType.InterfaceIsUnknown IDispatchImplType.SystemDefinedImpl
or Missing
IUnknown derived interface.
Missing IDispatchImplType.SystemDefinedImpl
or Missing
Dual interface using internal implementation.

Responding to Queries for IDispatch

The attribute NoIDispatchAttribute is used to control how the class responds to queries for IID_IDispatch. By default all managed classes implement the IDispatch interface because the runtime can provide an implementation of the interface on behalf of the class. This behavior can be overridden with then NoIDispatchAttribute attribute. Attributing a class with this attribute causes the class to always respond to such queries for IID_IDispatch with E_NOINTERFACE.

This attribute has no effect on other interfaces that are dual (like the class interface). It only effects how the class responds to QueryInterface. Clients may still use the dispatch portion of other interfaces to late bind to the class. The class interface in particular is always dual and would still be available if a client queried for that particular interface.

On-The-Fly Type Library Generation

The downside of exposing IDispatch is that, depending on how the interface is used; static type information may be needed. For example, using the Ole Automation compatible IDispatch always requires COM type library for the managed type that’s being exposed. The internal implementation of IDispatch also requires a type library but only if the IDispatch.GetTypeInfo or IDispatch.GetTypeInfoCount methods are called.

The type information can be generated in advance and packaged with the application or it can be generated on the fly by the runtime when the IDispatch is utilized. There are several things to note about this automatic type library generation:

  1. Type libraries are only generated if the client of a managed class actually uses the IDispatch methods of an interface. If the client never calls the IDispatch methods, the type library is not be generated.

If a type library is needed, the runtime will first check for an existing type library for the given type. The typelib can be located in 3 ways:

If the runtime does need to generate a type library on the fly, the type library will be generated in its entirety. Partial type libraries cannot be generated. In other words, if type information is needed for one type that is packaged in a assembly along with 99 other types, type information is generated for all 100 types at that time. The type library is then registered so that it can be used again later.

Generated type libraries are named with the same name as the managed assembly but with a .tlb extension.

To avoid type library generation all together, there are three things you must do when defining new types:

The Class Interface

In order for COM client to be able to access the members of a managed class, a new interface is defined when a class is exported to a type library with TlbExp. This interface is commonly referred to as the “class interface” and carries the same name as the class itself but is prefixed with an underscore. The class interface contains all the members of the managed class so that unmanaged users have access to the class members directly. The interface is automatically generated by TlbExp and is exposed to COM in addition to any other interfaces that the class implements explicitly. The class interface is always dual so COM clients can either early bind or late bind to the interface.

Managed clients never use (or even know about) the class interface. Instead, managed clients can simply access the class methods directly.

The example below shows the class interface for a simple managed class called Mammal.

In Managed C++:

__managed class Mammal
{
public:
   short Weight;
   Mammal (){   };
   __property short Height{
      get {return m_height;};
      set {m_height = value;};
   };
   void  Eat(){;};
   void  Breathe(){;};
   void  Sleep(){;};

protected:
   short m_height;  
private:
   void Walk(){;};
};

In the COM type library produced by TlbExp:

    [odl, 
    uuid(890A3E4A-B640-36A4-9F67-194D4B5BDA8A),
     hidden, dual, 
     nonextensible, 
     oleautomation ]
    interface _Mammal : IDispatch {
        [id(0x60020000)] HRESULT GetType([out, retval] _Type** pRetVal);
        [id(0x00000000)] HRESULT ToString([out, retval] BSTR* pRetVal);
        [id(0x60020002)] HRESULT Equals([in] IUnknown* obj, 
                        [out, retval] VARIANT_BOOL* pRetVal);
        [id(0x60020003)] HRESULT GetHashCode([out, retval] long* pRetVal);
        [id(0x60020004)] HRESULT Eat();
        [id(0x60020005)] HRESULT Breathe();
        [id(0x60020006)] HRESULT Sleep();
        [id(0x60020007), propget] HRESULT Weight([out, retval] short* pRetVal);
        [id(0x60020007), propput] HRESULT Weight([in] short pRetVal);
        [id(0x60020008), propget] HRESULT Height([out, retval] short* pRetVal);
        [id(0x60020008), propput] HRESULT Height([in] short pRetVal);
};

    [
      uuid(C77C754F-EAC8-3092-82F3-3EDC33A77755)
    ]
    coclass Mammal {
        [default] interface _Mammal;
        interface _Object;
   };

There are several things to notice about the class interface:

There are several things to notice about the Mammal coclass:

Methods

Methods on the class interface are treated the same as methods on any other interface. They undergo the same translation described in section 4.1.4.5.1 above. The class interface includes public method of the managed class as well as any methods on the managed base class. The methods can be accessed through the dispatch portion of the interface or through the v-table directly. When late binding to the interface with IDispatch, it’s also possible to call methods found on the derived class as well. For example, if the Monkey class was created that extend the class Mammal in the example above, methods of the Monkey class would be callable through the _Mammal.Invoke().

DispIds

The CCW will generate unique dispids on demand for the methods and properties of the class. The dispids will be guaranteed to always be the same for any instance of the same version of the same class. In order to explicitly set the dispid of a member to a specific value, the DispIdAttribute can be set on an individual member (see the Interop Metadata Spec). If a classic COM type library with predefined dispids is converted into NGWS runtime metadata with the type library importer, the importer will retain the dispids of the imported dispinterface within the metadata. The DispIdAttribute is set for each member that has an explicit DispId in the type library. The runtime’s IDispatch implementation will honor the DISPIDs stored as custom attribute in the objects metadata. These dispids are preserved only to ensure that the interface can be exposed to COM in a compatible way.

Special Dispids – There are some Dispids that have special meaning associated with them. For example:

DISPID_VALUE (0) - The default member for the dispinterface—that is, the property or method invoked if the object name is specified by itself without a property or a method in a controller script.

DISPID_PROPERTYPUT(-3) - Indicates the parameter that receives the value of an assignment in a property put

DISPID_NEWENUM(-4) - The _NewEnum method of a collection.

DISPID_EVALUATE (-5) - A method named Evaluate that a controller implicitly invokes when it encounters arguments in square brackets. For example, the following two lines are equivalent:

x.[A1:C1].value = 10
x.Evaluate("A1:C1").value = 10

Managed classes that need to expose methods with any of these “special” dispids should that use the DispIdAttribute to explicitly assign the correct dispid to the member. The runtime makes no association between the members and these special Dispids.

In situations where multiple members of the same type end up with the same dispid, the member defined first will respond to the assigned dispid. Members defined subsequently will essentially be inaccessible. Duplicate dispids can result from collisions with the dispids that are automatically generated by the typelib exporter (in the range of 0x6002000 –0x6003000) or by inadvertently assigning the same Dispid with the DispIdAttribute attribute. Both situations should be avoided.

EXCEPINFO

Dispinterfaces return error information in a separate structure passed in on every call to Invoke. In a late bound call, a pointer to an EXCEPINFO structure is passed in to the call to IDispatch::Invoke. When an exception is encounter, the CCW packs additional details about the exception, including the HRESULT, into the EXCEPINFO structure and returns DISP_E_EXCEPTION from the call to Invoke. In order to find out more information about the error that occur during the call to Invoke, the classic COM client checks the EXCEPINFO structure passed in on the call.

Default Interface

COM has the notion of a default interface. Since classes cannot contain members, the members of the default interface are used as the members of the class by late bound languages like Visual Basic. In the NGWS runtime there is no need for a default interface because classes themselves can have members. However, when exposing managed classes to COM, they are much easier to use if they have a default interface.

By default, the runtime creates a class interface for each class and marks that interface as the default when generating type information for the class. This process can be overridden with the HasDefaultInterfaceAttribute attribute.

If the HasDefaultInterfaceAttribute attribute is specified for a class, the normal class interface is not created. Instead the first implemented interface supplied when the type was created is assumed to be the default. That interface will be marked as the default when the type information for the class is generated. For example, the following class does not have an explicit default interface.

interface IFoo {…}

public class Foo : public IFoo {
…
}

And therefore would a class interface would be generated when the class is exported and marked as the default interface.

interface IFoo {…}
interface _Foo {…}
coclass Foo {
   [default] interface _Foo;
   interface IFoo;
}

By specifying an explicit default interface with the HasDefaultInterfaceAttribute the class interface generation will be omitted:

interface IFoo {…}

[HasDefaultInterface] public class Foo : public IFoo {
…
}

The class would be exported as:

interface IFoo {…}
coclass Foo {
   [default] interface IFoo;
}

Default interfaces are defined separately at each level of an objects inheritance hierarchy. In other words, an object with an explicit default interface may derive from an object that uses the class interface as its default. In that case each object has a distinct default interface.

Default interface are not required to be dual interface when exposed to COM.

IEnumVariant

A custom marshaler is provided by the runtime that provides a bridge between the NGWS frameworks interface IEnumerator and the COM interface IEnumVariant. The custom marshaler causes method parameters and properties of type IEnumerator to be exposed to COM as IEnumVariant interface. So for example, the GetEnumerator method of the System.Collections.SortedList class whose managed signature is:

IEnumerator GetEnumerator(bool allowRemove);

would have the following signature when exported to a COM type library.

HResult GetEnumerator(bool allowRemove, IEnumVariant** ppEnum);

The marshaler automatically delegates the calls from the COM interface to the NGWS runtime interface with the following behavior:

HResult IEnumVariant.Next(ulong celt, Variant *rgVar, ulong, *pCeltFetched)
Calls IEnumerator.MoveNext and IEnumerator.Current celt times and marshals each object returned from GetObject into an element of the rgVar array. Sets pceltFetched to the number of elements loaded in rgVar. Return S_OK if celt elements were copied into rgVar otherwise returns S_FALSE.
HResult IEnumVariant.Skip(ulong celt)
Calls IEnumerator.MoveNext() celt times. Return S_OK if celt elements were skipped otherwise returns S_FALSE.
HResult IEnumVariant.Reset()
Calls IEnumerator.Reset()
HResult IEnumVariant.Clone(IEnumVARIANT **ppEnum)
Attempts to case the enumerator to the ICloneable interface. If successful calls IClonable.Clone to create a new enumerator which is returned in ppEnums

Locating Enumerators

In COM, dispinterfaces that provide enumerators do so through a property with a DISPID of -4. There are no rules defining the name or signature of this property. There is no standard way of identifying enumerators provided by interfaces that are not dispinterfaces. For example, in the following type library, the interface _Collection provides and enumerator through its _NewEnum member:

interface _Collection : IDispatch 
{
   [id(0xfffffffc)] HRESULT _NewEnum([out, retval] IUnknown** ppvObj);
}

In the NGWS runtime, classes or interfaces that provide enumerators do so by implementing the IEnumerable interface. The recommended way of determining whether a class provides an enumerator is to cast the type to IEnumerable. If the cast succeeds, the enumerator is provided by the GetEnumerator method on the interface.

In order for a managed class to expose an enumerator to COM clients it must still expose a method with a dispid of 0xfffffffc. The Enumerator method on the IEnumerable interface will always have the proper DISPID of 0xfffffffc. However, DISPID’s on interfaces are not propagated to the implementation of the member on the class. The author of the class is therefore responsible for explicitly setting the DISPID on the method that returns the enumerator to the proper value of 0xfffffffc.

Consider the following example. The interface Fields extends the interface IEnumerable. The Collection class then implements the Fields interface.

In Managed C++

public interface Fields : public System.Collections.IEnumerable {
public void SomeOtherMethod();
}

public class Collection : public Fields {
   public IEnumerator GetEnumerator();
public void SomeOtherMethod();
}

When exported to a type library:

interface Fields : public IDispatch {
   [id(-4),  propget] HResult GetEnumerator([out retval] Unknown**ppv);
   HResult SomeOtherMethod();
}

interface _Collection : public IDispatch {
   [id(-4),  propget] HResult GetEnumerator([out retval] Unknown**ppv);
   HResult SomeOtherMethod();
}

coclass Collection {
   interface _Collection;
   interface Fields;
}

ISupportErrorInfo/IErrorInfo

Classic COM clients that use interfaces other than IDispatch typically use the ISupportErrorInfo and IErrorInfo interfaces in combination for finding out the details of an exception. When an errant HRESULT is returned from a method call on an interface, the client can query for an ISupportErrorInfo interface on the object. ISupportErrorInfo is used to determine if the current object supports the IErrorInfo interface. If the object does support the IErrorInfo interface, it returns S_OK from ISupportErrorInfo::InterfaceSupportsErrorInfo. The S_OK return value indicates that the client can reliably call the GetErrorInfo API to obtain an IErrorInfo interface on the latest exception object. ISupportErrorInfo::InterfaceSupportsErrorInfo only needs to be called once for the lifetime of the object. The classic COM client will then use the methods of the IErrorInfo interface to get additional details about the nature of the error. Through the IErrorInfo interface, a textual description of the error can be obtained as well as the source or the error, help file, help context, and GUID of class that generated the error.

interface IErrorInfo: IUnknown {
   HRESULT GetGUID([out] GUID *pGUID);
   HRESULT GetSource([out] BSTR *pBstrSource);
   HRESULT GetDescription([out] BSTR *pBstrDescription);
   HRESULT GetHelpFile([out] BSTR *pBstrHelpFile);
   HRESULT GetHelpContext([out] DWORD pdwHelpContext);
}

Within the COM callable wrapper, NGWS runtime exceptions thrown during any method call are caught so they can be returned to the client through IErrorInfo. The wrapper implements the ISupportErrorInfo interface and returns S_OK form any calls to ISupportErrorInfo::InterfaceSupportsErrorInfo indicating that the wrapper will call the SetErrorInfo API providing a valid IErrorInfo interface on the last thrown exception.

With the IErrorInfo interface, the client can get to all the information available about the Exception object. The following table describes how each of the IErrorInfo methods obtains it from the exception.

IErrorInfo Method Source of Information
GetGuid Always returns NULL.
GetSource Exception.Source
GetDescription Exception.Message or, if empty, the string returned from Exception.ToString().
GetHelpFile

And

GetHelpContext

The Exception.HelpLink is parsed into the Helpfile and HelpContext As follows:

If Exception.HelpLink does contain a “#” character and, if the string to the right of the “#” character contains a valid numeric value, then GetHelpContext returns the numeric string converted to a DWORD and GetHelpFile returns the string to the left of the “#” character.

Otherwise, GetHelpFile returns the complete Exception.HelpLink and GetHelpContext returns zero. Note that Exception.HelpLink is intended to be a URL as opposed to a file name.

For example:

HelpLink: http://www.microsoft.com/msdn/22k343.htm

GetHelpFile: http://www.microsoft.com/msdn/22k343.htm

GetHelpContext: 0

HelpLink: http://www.microsoft.com/msdn/22k343.htm#top

GetHelpFile: http://www.microsoft.com/msdn/22k343.htm#top

GetHelpContext: 0

HelpLink: http://www.microsoft.com/msdn/22k343.htm#5534

GetHelpFile: http://www.microsoft.com/msdn/22k343.htm

GetHelpContext: 5534

HelpLink: c:\winnt\system32\corhelp.hlp#5534

GetHelpFile: c:\winnt\system32\corhelp.hlp

GetHelpContext: 5534

In order to get a reference to the actual exception object, the client can query through the IErrorInfo interface for the class interface of a specific exception class. For example, the client could query for the _SystemException interface or the _ArithmeticException interface. Members that are unique to a specific exception class are only available through the exceptions class interface.

IObjectSafety

All managed objects implement the IObjectSafety interface to indicate that they are safe for scripting. Container that host managed types can query the managed object for the IObjectSafety interface. The runtime implementation has the following behavior:

GetInterfaceSafetyOptions()
   Supported : INTERFACESAFE_FOR_UNTRUSTED_DATA | INTERFACESAFE_FOR_UNTRUSTED_CALLER
   Enabled : INTERFACESAFE_FOR_UNTRUSTED_DATA | INTERFACESAFE_FOR_UNTRUSTED_CALLER
SetInterfaceSafetyOptions
   Error if any other option is specified.

ITypeInfo Interface

All managed objects that are exposed to COM through a CCW provide an implementation of the ITypeInfo interface. The interface can be obtained through the Object’s IDispatch interface, through the objects IProvideClassInfo interface or by calling the GetType method on the objects class interface. The type information provided through the interface is exactly the same as the information found for that class in the type library produced by the TlbExp utility. In fact, the interface is produced by loading that exact type library. A side effect of requesting this interface is that the type library may be generated as a result of the request. Generating this type library can be time consuming and should be done sparingly as the type library is never saved and never registered. In order to avoid generating the type library, consider shipping the tlb along with you assembly.

In addition to providing type information on request as described above, the runtime also provides a custom marshaler for marshaling the System.Type class to the ITypeInfo interface. Any managed method that uses the System.Type class will automatically use the marshaler when the method is exposed to COM. The TlbExp utility will convert all references to System.Type to ITypeInfo on export.

For example, the following managed type:

interface TypeProvider {
   void SetType(System.Type t);
   void SetTypeRef(ref System.Type t);
   System.Type GetType();
}

Is converted to:

interface TypeProvider {
   HRESULT SetType([in] ITypeInfo *t);
   HRESULT SetTypeRef([in, out] ITypeInfo **t);
   HRESULT GetType([out, retval] ITypeInfo **t);
}

IProvideClassInfo Interface

The IProvideClassInfo interface is provided by the CCW for all managed objects in order to gain access to the individual ITypeInfo interface. See the section 4.9.7 for details.

IDispatchEx Interface

IConnectionPointContainer and IConnectionPoint Interface

Managed types that source events do so using the NGWS frameworks and runtime delegate based event model. The delegate model is quite different from the connection point model traditionally used by COM but the CCW is able to bridge the two models.

In order to sink managed events with COM connection points the managed class must identify an event interface. The event interface is only required if the events are to be sink from unmanaged code using connection points. The event interface can be defined in either managed code or in a COM type library (or IDL which is used to produce a type library). The code below shows the Button class along with the ButtonEvents event interface defined in IDL.

using System.Runtime.InteropServices

delegate void ClickDelegate(int x, int y);
delegate int ResizeDelegate();


[ComSourceInterfaces(“ButtonEvents”)]
public class Button
{
   public event ClickDelegate Click;
   public event ResizeDelegate Resize;
   public void OnClick(int x, int y) { Click(x, y); }
   public int OnResize() { return Resize(); }
   public void Init() {;}
}
library ButtonEventsLib
{
    [uuid(…)]
    interface ButtonEvents : IDispatch {
        [id(0x60020000)] HRESULT Click([in] int x, [in] int y);
        [id(0x60020001)] HRESULT Resize([out, retval] int *pRetVal);
    };

};

The event interface must meet the following requirements:

By identifying an event interface for a managed class, the CCW for that class will automatically implement IConnectionPointContainer interface. A connection point is provided for each source interface identified with the ComSourceInterfaces attribute. The managed object does not explicitly implement the interface.

Unmanaged clients can call IConnectionPointContainer.FindConnectionPoint passing the IID of the source interface to obtain an IConnectionPoint interface. A connection point interface can be obtained for any interface identified in the ComSourceInterfaces attribute for a given class.

Unmanaged clients wanting to sink with managed events should then provide an implementation of the event interface. As events are fired from managed code, the CCW will automatically call the corresponding methods of the event interface on any client that has connected to the connection points provided by the CCW. Clients can connect to the CCW by calling IConnectionPoint.Advise passing the unmanaged implementation of the source interface. The event interface is actually connected to the managed delegates on a per-event basis. In other words, the event interface may contain only a subset of the actual events sourced by the managed class. Events and methods are matched by name and signature when IConnectionPoint.Advise is called. If an event is fired that has no matching method on the source interface, the source will not receive the event.