views:

360

answers:

0

I have written a winforms client app that uses remoting to monitor and control a .Net Windows Service. The Service fires events for which the client registers to a handler, using this (simplified) code...

Server creates Server-side instance of Message Manager and registers it with Remoting using this code:

    // MessageManager is singleton class defined on server...
    // which is registered on server using this code
    MessageManager mgr = MessageManager.Instance;
    RemotingServices.Marshal(mgr, MesgURI);

Client registers transparent proxy to Server using RegisterwithServer() code; 2nd method below:

    private static void RegisterChannel()
    {
        BinaryClientFormatterSinkProvider clientProvider =
            new BinaryClientFormatterSinkProvider();
        BinaryServerFormatterSinkProvider serverProvider =
            new BinaryServerFormatterSinkProvider();
        serverProvider.TypeFilterLevel = TypeFilterLevel.Full;

        IDictionary props = new Hashtable();
        props["port"] = 0;
        string s = Guid.NewGuid().ToString();
        props["name"] = s;
        props["typeFilterLevel"] = TypeFilterLevel.Full;
        TcpChannel chan = new TcpChannel(
            props, clientProvider, serverProvider);

        ChannelServices.RegisterChannel(chan, true);
    }


    public void RegisterwithServer(DIServer svr, string oldServerAndPort)
    {        
        Type T = typeof(MessageManager); 
        RegisterChannel();   // private method - above... 
        string url = "tcp://" + svr.ServerName + ":" +
             svr.TcpPort + "/" + svr.MessagingUri;
        BroadcastMsgEventWrapper bew = 
             svr.BroadcastMessageEventWrapper;
        try
        {
            IMessageMgr ServerMsgMgr =
                (IMessageMgr)Activator.GetObject(T, url);

            // Create Broadcast Event Wrapper --------------------------
            bew = new BroadcastMsgEventWrapper();

            // Register "Local" handler with this wrapper's contained event
            bew.MsgArrivedClientHandler += HandleMessage;

            // Now, register local eventwrapper with Server MessageManager
            ServerMsgMgr.MessageArrivedClientHandler += 
                             bew.ClientMessageArrivalHandler;
            /* **************************************************
               bew.ClientMessageArrivalHandler runs in 
               Message Manager on Server, and calls HandleMessage, 
               which will run here on client   
             ************************************************** */
        }

In RegisterWithServer(), When the last line runs,

ServerMsgMgr.MessageArrivedClientHandler += 
                         bew.ClientMessageArrivalHandler;

it registers the client-side handler with the server event. So here's the question... This client app needs to be able to connect/register to multiple servers, and because the connections can get destroyed or lost or timeout, this code gets called multiple times, based upon the user selecting a UI element that requires a connection to a specific server... So because of that, it is being called at times when the event registration is currently still ok, and re-registering the client handler again, causing the server event to have 2 or more delegates to the same client Handler. Now the server can, (and should), allow multiple handlers to be registered for this event, It just should not allow more than one from the SAME client. I have modified the code in the server to deal with this as follows: When the server calls each delegate in the server's invocation list, using a hascode of the callback delegate, it checks to see if it has already called that delegate, and if so, it skips it and removes it from the invocation list.

private void CallEachClientSubscriber(MessageBase msg)
{
     string logMsg;
     // No registered Listeners...
     if (MessageArrivedClientHandler == null) 
     {
         logMsg = "Received " + msg.MessageType +
                  ", - No registered subscribers.";
         DALLog.Write(DALLog.Level.Debug, logMsg, sourceName);
         return;
     }
     int Cnt = MessageArrivedClientHandler.GetInvocationList().Length;
     logMsg = "Publishing msg {" + msg.MessageType + 
                    "} to " + Cnt + " client subscriber" +
                    ((Cnt == 1) ? "." : "s.");
     DALLog.Write(DALLog.Level.Debug, logMsg, sourceName);
     MessageArrived mAH = null;
     Delegate[] clientList = MessageArrivedClientHandler.GetInvocationList();
     List<int> dels = new List<int>();
     foreach (Delegate d in clientList)
        try
        {
           mAH = (MessageArrived)d;
           int delHC = mAH.GetHashCode();
           if (dels.Contains(delHC))
                MessageArrivedClientHandler -= mAH;
           else
           {
                    dels.Add(delHC);
                    mAH(msg);
           }
        }

But I would like, instead, to modify the client code to not even create the duplicate registration if the server already has one for that client in it's invocation List. But I can't figure out how to access the invocation List from the client... Is there any way to do that? or is there another mechanism by which I can stop the duplicate event handler registrations?