I got a little further, as I have been having the same problem manually connecting events when exposing a .NET control as an ActiveX using COM interop.
If you dig a little into the UserControl class using Reflector (e.g. Redgate Reflector), you'll see an 'ActiveXImpl' nested class member, which contains another nested static class called 'AdviseHelper', which has members ComConnectionPointContainer and ComConnectionPoint. It also has helper functions to connect points as you require.
There's a problem. When an event is hooked up by COM interop from your control (the event source) to the connection point container (which contains the event sink for your control's events), the IQuickActivate.QuickActivate function gets called, which then calls AdviseHelper class' 'AdviseConnectionPoint' function.
An IUnknown interface pointer to the event sink, on your client (i.e. not your control, the thing containing it) is passed to this QuickActivate function (parameter 'pUnkEventSink'. In reflector this function looks like this, and I've highlighted where it does the actual event hookup:
internal void QuickActivate(UnsafeNativeMethods.tagQACONTAINER pQaContainer, UnsafeNativeMethods.tagQACONTROL pQaControl)
{
int num;
this.LookupAmbient(-701).Value = ColorTranslator.FromOle((int) pQaContainer.colorBack);
this.LookupAmbient(-704).Value = ColorTranslator.FromOle((int) pQaContainer.colorFore);
if (pQaContainer.pFont != null)
{
Control.AmbientProperty ambient = this.LookupAmbient(-703);
IntSecurity.UnmanagedCode.Assert();
try
{
Font font2 = Font.FromHfont(((UnsafeNativeMethods.IFont) pQaContainer.pFont).GetHFont());
ambient.Value = font2;
}
catch (Exception exception)
{
if (ClientUtils.IsSecurityOrCriticalException(exception))
{
throw;
}
ambient.Value = null;
}
finally
{
CodeAccessPermission.RevertAssert();
}
}
pQaControl.cbSize = UnsafeNativeMethods.SizeOf(typeof(UnsafeNativeMethods.tagQACONTROL));
this.SetClientSite(pQaContainer.pClientSite);
if (pQaContainer.pAdviseSink != null)
{
this.SetAdvise(1, 0, (IAdviseSink) pQaContainer.pAdviseSink);
}
IntSecurity.UnmanagedCode.Assert();
try
{
((UnsafeNativeMethods.IOleObject) this.control).GetMiscStatus(1, out num);
}
finally
{
CodeAccessPermission.RevertAssert();
}
pQaControl.dwMiscStatus = num;
if ((pQaContainer.pUnkEventSink != null) && (this.control is UserControl))
{
Type defaultEventsInterface = GetDefaultEventsInterface(this.control.GetType());
if (defaultEventsInterface != null)
{
IntSecurity.UnmanagedCode.Assert();
try
{
**AdviseHelper.AdviseConnectionPoint(this.control, pQaContainer.pUnkEventSink, defaultEventsInterface, out pQaControl.dwEventCookie);**
}
catch (Exception exception2)
{
if (ClientUtils.IsSecurityOrCriticalException(exception2))
{
throw;
}
}
finally
{
CodeAccessPermission.RevertAssert();
}
}
}
if ((pQaContainer.pPropertyNotifySink != null) && UnsafeNativeMethods.IsComObject(pQaContainer.pPropertyNotifySink))
{
UnsafeNativeMethods.ReleaseComObject(pQaContainer.pPropertyNotifySink);
}
if ((pQaContainer.pUnkEventSink != null) && UnsafeNativeMethods.IsComObject(pQaContainer.pUnkEventSink))
{
UnsafeNativeMethods.ReleaseComObject(pQaContainer.pUnkEventSink);
}
}
The 'pUnkEventSink' variable is passed in to this function via the tagQACONTROL structure, but as you can see, unlike the IAdviseSink, control container, font style etc., this variable is not set to any member of the 'ActiveXImpl' class, and hence you cannot get to it after this function has initially been called by the framework.
You need to get this IUnknown pUnkEventSink variable to call AdviseHelper.AdviseConnectionPoint() function, which will do the manual event hookup. And this is the problem I've had - sadly you simply can't seem to get hold of it.
Anyone else had any further developments with this do let me know!