[originally posted in framework.interop. . . refered here]
source: http://www.obj-tec.com/msdnforums/lcedemo2.zip
I am getting "An event was unable to invoke any of the subscribers (Exception
from HRESULT: 0x80040201)" on what appears to have been a properly subscribed
transient event.
I have an Event Class in a signed library, LceEvent.dll, registered in COM+
via
regsvcs LceEvent.dll
--------------------------------------------------------------------------------
Imports System.Runtime.InteropServices
Imports System.EnterpriseServices
<ComVisible(True)> _
Public Interface ITheEvent
Sub Fire()
End Interface
<ComVisible(True), _
EventClass()> _
Public Class TheEvent
Inherits ServicedComponent
Implements ITheEvent
Public Sub Fire() Implements ITheEvent.Fire
End Sub
End Class
--------------------------------------------------------------------------------
above class registers appropriately
I have a sink library, LceSink.dll with an Event Sink :
--------------------------------------------------------------------------------
Imports System.EnterpriseServices
Imports System.Runtime.InteropServices
Imports LceEvent
<ComVisible(True)> _
Public Class TheEventSink
Implements ITheEvent
Public Event Fired As EventHandler
Private _subId As String
Public Sub New()
End Sub
Public Sub Fire() Implements LceEvent.ITheEvent.Fire
RaiseEvent Fired(Me, EventArgs.Empty)
End Sub
End Class
--------------------------------------------------------------------------------
I have a helper class for transient subscriptions:
--------------------------------------------------------------------------------
Public Class LceHelper
Public Shared Function CreateTransientSubscription( _
ByVal clsid As String, ByVal subscriber As Object) As String
Dim catalog As New COMAdmin.COMAdminCatalog
Dim catalogCollection As COMAdmin.COMAdminCatalogCollection = _
catalog.GetCollection("TransientSubscriptions")
catalogCollection.Populate()
Dim subscription As COMAdmin.ICatalogObject = catalogCollection.Add()
subscription.Value("SubscriberInterface") = subscription
subscription.Value("EventCLSID") = "{" & clsid & "}"
subscription.Value("Name") = "TransientSubscription"
catalogCollection.SaveChanges()
' POINT 1
Return subscription.Value("ID").ToString()
End Function
Public Shared Sub DeleteTransientSubscription(ByVal subid As String)
Dim catalog As New COMAdmin.COMAdminCatalog
Dim catalogCollection As COMAdmin.COMAdminCatalogCollection = _
catalog.GetCollection("TransientSubscriptions")
catalogCollection.Populate()
' POINT 5
For i As Integer = 0 To catalogCollection.Count - 1
Dim subscription As COMAdmin.COMAdminCatalogObject =
catalogCollection.Item( i )
If subscription.Value("ID").ToString() = subid Then
catalogCollection.Remove( i )
Return
End If
Next
End Sub
End Class
--------------------------------------------------------------------------------
I am trying to use in a form:
--------------------------------------------------------------------------------
Public Class Form1
Private WithEvents _TheSink As LceSink.TheEventSink
Private _thesub As String
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
Dim t As Type
t = Type.GetTypeFromProgID("LceEvent.TheEvent")
_TheSink = New LceSink.TheEventSink()
Try
_thesub = LceSink.LceHelper.CreateTransientSubscription( _
t.GUID.ToString(),
_TheSink)
' POINT 2
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
MyBase.OnLoad(e)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim evt As LceEvent.ITheEvent = New LceEvent.TheEvent
Try
' POINT 3
evt.Fire()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub Form1_Disposed(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Disposed
' POINT 4
LceSink.LceHelper.DeleteTransientSubscription(_thesub)
End Sub
End Class
--------------------------------------------------------------------------------
At POINT 1 (LceHelper.CreateTransientSubscription), catalogCollection.Count
has increased by 1
At POINT 2 (Form1 above), it appears the subscription succeeds as the call
to CreateTransientSubscription assigns a giud string to _subID
At POINT 3 (Form1 above), I get exception An event was unable to invoke any
of the subscribers (Exception from HRESULT: 0x80040201)
At POINT 4 (Form1 above), trace into LceHelper.DeleteTransientSubscription
At POINT 5 (LceHelper.DeleteTransientSubscription )CatalogCollection.Count >
1, the subid is found and the subscription is released
What am i doing wrong? Why isnt the subscription being called?
You do not use EventHandler nor RaiseEvent with COM+ LC Events
Here's some sample code we use for ours. Sorry, it's in C++
THE INTERFACE AND PUBLISHER CLASS
#region ISNRWriteEvents
/// <summary>
/// Summary description for ISNRWriteEvents
/// </summary>
[GuidAttribute("115D79D5-6CB8-4954-ABAF-CB973A164629"), InterfaceQueuing]
public interface ISNRWriteEvents
{
void SNRCreated(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID, int PDOID, int PurchasedCRID, SqlInt32
SuppliedCRID, SqlInt32 ProgramID, bool IsLead);
void SNRPurchasedCRIDChanged(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID, int OldPurchasedCRID, int NewPurchasedCRID);
void SNRSuppliedCRIDChanged(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID, SqlInt32 OldSuppliedCRID, SqlInt32
NewSuppliedCRID);
void SNRProgramChanged(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID, SqlInt32 OldProgramID, SqlInt32
NewProgramID);
void SNRLeadProgramSet(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID);
void SNRLeadProgramCleared(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID);
void SNRDeleted(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID);
}
#endregion
#region SNRWriteEvents
/// <summary>
/// Summary description for PDOWriteEvents
/// </summary>
[GuidAttribute("7BF8C3FF-F394-493a-BA53-4BD96FE04EE5"), EventClass]
public class SNRWriteEvents : RequiredTransServicedComponent,
ISNRWriteEvents
{
public void SNRCreated(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID, int PDOID, int PurchasedCRID, SqlInt32
SuppliedCRID, SqlInt32 ProgramID, bool IsLead){}
public void SNRPurchasedCRIDChanged(Guid EventTrackingGuid,
QCObjectWrapper PWSecurityIDWrapper, int SNRID, int OldPurchasedCRID, int
NewPurchasedCRID) {}
public void SNRSuppliedCRIDChanged(Guid EventTrackingGuid,
QCObjectWrapper PWSecurityIDWrapper, int SNRID, SqlInt32 OldSuppliedCRID,
SqlInt32 NewSuppliedCRID) {}
public void SNRProgramChanged(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID, SqlInt32 OldProgramID, SqlInt32
NewProgramID) {}
public void SNRLeadProgramSet(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID) {}
public void SNRLeadProgramCleared(Guid EventTrackingGuid,
QCObjectWrapper PWSecurityIDWrapper, int SNRID) {}
public void SNRDeleted(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID) {}
}
#endregion
#endregion
A SUBSCRIBER
[GuidAttribute("964B7002-5873-4cee-8C58-493B1902E064")]
public class CR_SNRWriteSink : RequiredTransServicedComponent,
ISNRWriteEvents
{
#region ISNRWriteEvents Members
#region SNRCreated
public void SNRCreated(Guid EventTrackingGuid, QCObjectWrapper
PWSecurityIDWrapper, int SNRID, int PDOID, int PurchasedCRID, SqlInt32
SuppliedCRID, SqlInt32 ProgramID, bool IsLead)
{
PWSecurityID Identity;
PropertyInstanceWrite WritePropertyInstance = null;
try
{
// reconstitute the objects
Identity = PWSecurityIDWrapper.Target as PWSecurityID;
if (! IsLead)
return;
WritePropertyInstance = new PropertyInstanceWrite();
WritePropertyInstance.SetMappings(Identity,
PurchasedCRID,
CommonDataTypes.ObjectType.CRCustomer,
PDOID,
new SqlInt32((int) CommonDataTypes.ObjectType.PDO),
SNRID,
CommonDataTypes.ObjectType.SNR,
PDOID,
new SqlInt32((int) CommonDataTypes.ObjectType.PDO),
true);
ContextUtil.SetComplete();
}
catch (Exception Ex)
{
ContextUtil.SetAbort();
ExceptionManager.Publish(Ex);
}
finally
{
if (WritePropertyInstance != null) try {
WritePropertyInstance.Dispose(); }
catch{}
}
}
#endregion
#endregion
}
FIRING THE EVENT
WriteSNREvent = new SNRWriteEvents();
WriteSNREvent.SNRCreated(Guid.NewGuid(), new
QCObjectWrapper(PWAdminUser), SNRID, PDOID, PurchasedCRID, SuppliedCRID,
ProgramID, bIsLead);
REGISTERING THE SUBSCRIBERS INTO COM+ PROGRAMMATICALLY - We do this in
Installer classes included in the subscriber assemblies by calling
InstallUtil from Visual Build (our build system)
public static void RegisterComponentSubscriptions(string appName, string
componentName, string [] subscriberMethodNames, string interfaceGUID, string
eventCLSID, bool bQueued)
{
COMAdminCatalog oCatalog = new COMAdminCatalog();
COMAdminCatalogCollection oApplications =
(COMAdminCatalogCollection)oCatalog.GetCollection("Applications");
oApplications.Populate();
foreach(COMAdminCatalogObject oApp in oApplications)
{
if (oApp.Name.ToString() == appName)
{
COMAdminCatalogCollection oComponents =
(COMAdminCatalogCollection)oApplications.GetCollection("Components",
oApp.Key);
oComponents.Populate();
foreach (COMAdminCatalogObject oComp in oComponents)
{
// replace component name with your subscriber component name
if (oComp.Name.ToString() == componentName)
{
COMAdminCatalogCollection oSubscriptions =
(COMAdminCatalogCollection)oComponents.GetCollection("SubscriptionsForComponent",
oComp.Key);
for (int SubscriberMethodIndex = 0; SubscriberMethodIndex <
subscriberMethodNames.GetLength(0); SubscriberMethodIndex++)
{
oSubscriptions.Populate();
bool bExists = false;
foreach (COMAdminCatalogObject oSub in oSubscriptions)
{
// register each of your subscriptions if they are not
already registered
if (bExists = (oSub.Name.ToString() ==
subscriberMethodNames[SubscriberMethodIndex]))
break;
}
if (!bExists)
{
COMAdminCatalogObject oSubscription =
(COMAdminCatalogObject) oSubscriptions.Add();
oSubscription.set_Value("Name",
subscriberMethodNames[SubscriberMethodIndex]);
oSubscription.set_Value("MethodName",
subscriberMethodNames[SubscriberMethodIndex]);
oSubscription.set_Value("Queued", bQueued);
// this is the GUID from the interface definition
oSubscription.set_Value("InterfaceID", interfaceGUID);
// this is the guid from the event class definition
oSubscription.set_Value("EventCLSID", eventCLSID);
oSubscriptions.SaveChanges();
}
}
break;
}
}
break;
}
}
}
}
If you want to run your subscribers asyncronously, there's a little more you
need to do
E-mail me for more info.
Kevin
> [originally posted in framework.interop. . . refered here]
>
[quoted text clipped - 154 lines]
>
> What am i doing wrong? Why isnt the subscription being called?
Kevin Jackson - 22 Feb 2006 14:26 GMT
I mean c#. ;)
> You do not use EventHandler nor RaiseEvent with COM+ LC Events
>
[quoted text clipped - 469 lines]
>>
>> What am i doing wrong? Why isnt the subscription being called?
Blair Allen Stark - 23 Feb 2006 01:49 GMT
> > You do not use EventHandler nor RaiseEvent with COM+ LC Events
Yes i know you dont use events to sink the LCE.
TheEventSink is a wrapper that fires its own event when the LCE method is
called.
lets look a bit more closely -
TheEventSink has its own field "Fired" which is a .NET event delegate which
will notify the application that the LCE method was invoked.
<ComVisible(True)> _
Public Class TheEventSink
Implements ITheEvent
Private _subId As String
// EVENT BELONGING TO THE CLASS
Public Event Fired As EventHandler
// LCE METHOD IMPLEMENTATION
Public Sub Fire() Implements LceEvent.ITheEvent.Fire
// RAISE EVENT TO APPLICATION THAT 'Fire' was invoked
RaiseEvent Fired(Me, EventArgs.Empty)
End Sub
End Class
Blair Allen Stark - 23 Feb 2006 01:51 GMT
oh yeah, the impetus is that I only want one subscription per application.
the sink will notify other clients.
news.microsoft.com - 23 Feb 2006 05:32 GMT
It all works for us just dandy... Even loosely couple events that use
queued components.
Good luck
>> > You do not use EventHandler nor RaiseEvent with COM+ LC Events
>
[quoted text clipped - 23 lines]
> End Sub
> End Class
Blair Allen Stark - 23 Feb 2006 12:39 GMT
> It all works for us just dandy... Even loosely couple events that use
> queued components.
are you saying my code works for you? or your code works for you???
what code works for you. . . Kevin Jacksons? I dont think thats a "transient
subscription", is it? thats a full fledged serviced component subscription
Kevin Jackson - 23 Feb 2006 20:56 GMT
My code works for me and yes we don't use transient subscriptions... Full
blown component subscriptions...
>> It all works for us just dandy... Even loosely couple events that use
>> queued components.
[quoted text clipped - 4 lines]
> "transient
> subscription", is it? thats a full fledged serviced component subscription
UGHHHH!!! I'm an idiot!!!
I found my problem
subscription.Value("SubscriberInterface") = subscription
should have been
subscription.Value("SubscriberInterface") = subscriber
frigging intellisense!!!
Yeah guys. .. no need to make your subscriber a ServicedComponent
And my event firing is the proper pattern.
Kevin Jackson - 24 Feb 2006 02:26 GMT
It's always the little things ;)
> UGHHHH!!! I'm an idiot!!!
>
[quoted text clipped - 11 lines]
>
> And my event firing is the proper pattern.
Blair Allen Stark - 24 Feb 2006 05:08 GMT
well I am making progress. . .
Here is where I was going with this. . .
I want an ASP.NET application to fire the event. I want users to be able to
run an app and get a real-time view of certain activities.
whith these set in the COM+ Event Class assembly:
<Assembly: ApplicationActivation(ActivationOption.Library)>
<Assembly: ApplicationAccessControl(Value:=False)>
I can get the event to fire between stand-alone windows applications. But
trying to fire from ASP.NET to windows app throws "unable to invoke any
subscribers"
But It doesnt fire from ASP.NET to windows app, or from windows to windows,
when I have these assembly attributes set in the COM+ Event Class assembly:
<Assembly: ApplicationActivation(ActivationOption.Server)>
<Assembly: ApplicationAccessControl(Value:=False)>
Nothing happens - ASP.NET page works as expected, no event fired in Windows
app.
I have just in time activation set up on the COM+ Event Class class, too.
any ideas???