Hi Stoitcho,
You are absolutely right; I also find it to be the problem.
Yet there are two kind of solution, the one you mentioned, which I tried as
follow:
I set all the Subscriber members during the regular constructor call new, so
when the ISerializable.GetObjectData() is called, all members was initialized
correctly, making the mentioned problem to go away.
*** There is a much simpler solution:
--------------------------------------------
I used Activator.GetObject(...) in order to get a local reference for the
remoting object, this is not necessary, it’s causing for a proxy object
creation which I do not need. Instead I have changed the call to
RemotingServices.Marshal(...) for the remoting object that I created locally
using a regular new operator.
When I did that, the Subscriber class that uses the remoting object locally,
does not need to be marked as Serialized any more, and the above
ISerializable is not used at all and the bug has disappeared.
BUT now I have some different questions:
--( 1 )--
The Form application wish to delete the Subscriber object, so it the
following:
(a) Unregister from the publisher.
(b) Calls Subscriber Dispose, and this Dispose is doing:
RemotingServices.Unmarshal(m_SubscriberObjRef);
RemotingServices.Disconnect(m_SubscriberMarshal);
ChannelServices.UnregisterChannel(m_Channel);
m_SubscriberObjRef = null;// returned by
RemotingServices.Marshal(m_SubscriberMarshal)
m_SubscriberMarshal = null;
m_Channel = null;// The registered channel of the m_SubscriberMarshal
(c) Remove any reference of the Subscriber.
(d) Call GC.Collect();
But still the Subscriber does not die.
Note: I’m doing the same at the Publisher, and it does die.
Why the Subscriber does not die ???
--( 2 )–
In case both Subscriber and Publisher are at the same computer, Is there a
way that the Subscriber will obtain a reference to the Publisher in a way
that does not require Proxy? I mean instead of calling Activator.GetObject()
???
--( 3 )–
As you can see, the subscriber is only a client of the Publisher. For that I
also made the Subscriber Marshaled (derived from MarshalByRefObject). Is
there any way that only the Publisher will be Marshaled ???
I tried it by that the Subscriber is not derived from the
MarshalByRefObject, and the Subscriber will only set a callback function
through a Publisher delegate. But then I get an Exception that the TcpCahnnel
is not Serializable.
As there a way for that???
----
Many thanks
Sharon G.
---------------
:
Hi Sharon,
The only reason for that, as far as I can see without to be able to compile
and debug your sample, would be that the Subscriber is actually marshaled by
value (It doesn't inherit form MarshalByRef).
In this case when you subscribe for m_SubscriberMarshal.notificationDelegate
what actually happens is your object is serialized and deserialized on the
server side (the server gets totally new copy of the object) At this point
you haven't subscribed for
subscriber.appNotificationDelegate event yet. So the server may fire its
event, but the copy of the subscriber object on the server has no event
handlers attached. So what you may try to do is:
1. Make the subscriber marshal by reference object
-- or --
2. Attach subscriber.appNotificationDelegate event handler before hooking on
m_SubscriberMarshal.notificationDelegate. It might work as long as the
object that handles subscriber.appNotificationDelegate event is declared as
marshal by ref.
---------------
HTH
Stoitcho Goutsev (100) [C# MVP]
"Sharon" <
[email protected] in message
Well, here is the code example with some remark describing the bug.
Note: The classes: Subscriber, SubscriberMarshal are in a DLL and the
SubscriberUserForm in a EXE using this DLL.
public delegate void NotificationDelegate(object notification);
public delegate void AppNotificationDelegate(object notification, int
port);
public class SubscriberMarshal : MarshalByRefObject
{
public NotificationDelegate notificationDelegate = null;
public SubscriberMarshal() {}
~SubscriberMarshal() {}
// The notification message does arrive to this point from the publisher
public void Notify(object notification)
{
// The notificationDelegate is set and the notification
// is forwarded to the Subscriber
if( notificationDelegate != null ) {
notificationDelegate.BeginInvoke(notification, null, 0);
}
}
}
[Serializable()]
public class Subscriber : ISponsor, IDisposable
{
public AppNotificationDelegate appNotificationDelegate = null;
private SubscriberMarshal m_SubscriberMarshal = null;
public Subscriber(string subscriberHost,
int subscriberPort,
string subscriberUri,
string publisherHost,
int publisherPort)
{
BinaryClientFormatterSinkProvider clientProvider = new
BinaryClientFormatterSinkProvider();
BinaryServerFormatterSinkProvider serverProvider = new
BinaryServerFormatterSinkProvider();
serverProvider.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["name"] = "tcp" + subscriberPort.ToString();
props["port"] = subscriberPort;
props["typeFilterLevel"] = TypeFilterLevel.Full;
TcpChannel chan = new TcpChannel(props, clientProvider, serverProvider);
ChannelServices.RegisterChannel(chan);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemoteSubscriber.SubscriberMarshal),
subscriberUri,
WellKnownObjectMode.Singleton);
m_SubscriberMarshal =
(SubscriberMarshal)Activator.GetObject(typeof(RemoteSubscriber.SubscriberMarshal),
"tcp://" + subscriberHost + ":" + subscriberPort.ToString() + "/" +
subscriberUri);
m_SubscriberInfo = new SubscriberInfo(subscriberHost, subscriberUri,
subscriberPort);
m_SubscriberMarshal.notificationDelegate += new
NotificationDelegate(NotifyHandlerFwr);
ILease leas = (ILease)m_SubscriberMarshal.GetLifetimeService();
leas.Register(this);
}
// The notification message does arrive to this point from the publisher
public void NotifyHandlerFwr(object notification)
{
// The appNotificationDelegate became null, there for the application
// form is not informed about the notification. ==THE BUG
if( appNotificationDelegate != null ) {
appNotificationDelegate.BeginInvoke(notification, m_SubscriberInfo.Port,
null, 0);
}
}
// ISponsor Implementation
public TimeSpan Renewal(ILease lease)
{
ILease leas = (ILease) m_SubscriberMarshal.GetLifetimeService();
if( m_Disposed )
{ leas.Unregister(this); }
return leas.InitialLeaseTime;
}
// IDisposable Implementation
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if( !m_Disposed ) {
if( disposing ) {
ILease leas = (ILease)m_SubscriberMarshal.GetLifetimeService();
leas.Unregister(this);
IChannel [] channels = ChannelServices.RegisteredChannels;
foreach( IChannel channel in channels )
{ ChannelServices.UnregisterChannel(channel); }
}
}
m_Disposed = true;
}
}
public class SubscriberUserForm : System.Windows.Forms.Form
{
private int m_SubscriberPortEnum;
private ArrayList m_Subscribers = new ArrayList();
.. . .
private void SubscribeButton_Click(object sender, System.EventArgs e)
{
m_SubscriberPortEnum = Convert.ToInt32(SuscriberPortTextBox.Text);
int PublishererPortNum = Convert.ToInt32(PublisherPortTextBox.Text);
Subscriber subscriber = new Subscriber (Environment.MachineName,
m_SubscriberPortEnum,
"RemoteSubscriberURI",
PublisherHostTextBox.Text,
PublishererPortNum);
subscriber.appNotificationDelegate += new
AppNotificationDelegate(NotificationHandler);
m_Subscribers.Add(subscriber);
}
// This method is never called !!!!!! ==THE BUG
public void NotificationHandler(object notification, int port)
{
MessageBox.Show(notification.ToString(),
"subscriber Form Notification Handler - Port " + port.ToString(),
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
}
---------------------------------------------------------------------------------
:
Sharon,
Actually I couldn't get the problem. Is it possible to write simple
example
that demonstrates the problem?
---------------
Stoitcho Goutsev (100) [C# MVP]
---------------------------------------------------------------------------------
"Sharon" <
[email protected] wrote in message
Halleluiah! The Security Exception is gone and I can use non static
delegates in the remoting object.
BUT now, the object that holds this remote object locally (let's call it
Holder) that also has a non static delegate used by the application Form,
Become null whenever a message arrived from the channel.
For sure these delegates are set correctly after the local reference is
obtained by calling new.
Note that the changes you asked me to do, forced me to set the Holder
Class as Serializable ( [Serializable()] )
It did work when the delegate were static, but not correctly as you already
now, I mean that they did not turned to null.
Note also the Holder class is also the Sponsor of the remote object for
renewing the leas of the remote object so it will not die and up again when I
want him to stay alive with my settings.
What can I do so the Holder delegates will remain valid all the way?
---------------------------------------------------------------------------------
:
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
Hi Sharon,
Since .NET v1.1 Microsoft demand more security restrictions on remoting
serialization. As a result in .NET v1.0 it is was possible to create a
channel as ChannelServices.RegisterChannel(new TcpChannel(XXX));
However, with v1.1 we need to do more work to relax those restrictions and
make handling events possible.
On the server site you relax those restrictions as follows
BinaryClientFormatterSinkProvider clientProvider = null;
BinaryServerFormatterSinkProvider serverProvider = new
BinaryServerFormatterSinkProvider();
serverProvider.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = 4000;
props["typeFilterLevel"] = TypeFilterLevel.Full;
TcpChannel chan = new TcpChannel(props,clientProvider,serverProvider);
ChannelServices.RegisterChannel(chan);
On the client site:
BinaryClientFormatterSinkProvider clientProvider = new
BinaryClientFormatterSinkProvider();
BinaryServerFormatterSinkProvider serverProvider = new
BinaryServerFormatterSinkProvider();
serverProvider.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = 0;
props["typeFilterLevel"] = TypeFilterLevel.Full;
TcpChannel chan = new TcpChannel(
props,clientProvider,serverProvider);
ChannelServices.RegisterChannel(chan);
---
HTH
Stoitcho Goutsev (100) [C# MVP]
-----------------------------------------------------------------------------
"Sharon" <
[email protected] wrote in message
I'm trying to build a generic Publisher-Subscriber that will work over the
net, so I'm using the Remoting.
I wish that the subscriber user will be notify about the messages sent by
the remote publisher, so I used delegate that the user will be able to set on
it his own function for that purpose.
The trouble is that this delegate must not be static because there may be
many subscribers, and each subscriber may have different function hooking on
that delegate.
But if I don't set this delegate to static I get an exception:
"An unhandled exception of type 'System.Security.SecurityException' occurred
in mscorlib.dll
Additional information: Type System.DelegateSerializationHolder and the
types derived from it (such as System.DelegateSerializationHolder) are not
permitted to be deserialized at this security level."
How can I fix it???