Ureader.com  
Microsoft software help and Community
   home   |   control panel login   |   archive   |  
 
DotNet
acad.assignment.mngr
academic
adonet
aspnet
aspnet.announcements
aspnet.build.controls
aspnet.caching
aspnet.datagridcontrol
aspnet.mobile
aspnet.security
aspnet.webcontrols
aspnet.webservices
clr
compactframework
component_services
datatools
distributed_apps
drawing
faqs
framework
framework.wmi
general
internationalization
interop
languages.csharp
languages.jscript
languages.vb
languages.vb.controls
languages.vb.data
languages.vb.upgrade
languages.vc
languages.vc.libraries
myservices
odbcnet
performance
remoting
scripting
sdk
security
setup
vjsharp
vsa
webservi.enhancements
webservices
windowsforms
windowsforms.controls
winforms.databinding
winforms.designtime
xml
  
 
date: Fri, 25 Jan 2008 12:17:05 -0800 (PST),    group: microsoft.public.dotnet.framework.remoting        back       


Events stop working when interface assembly is signed (strong-named).   
Our csharp Windows forms application uses .Net remoting as a way for
third party applications to integrate with us. This API includes
methods, properties and events that client applications can do to
share context with us. We've also provided a COM wrapper so that non-
dotnet apps can use it too.

Problem: When we recently attempted to sign the shared assembly and
access it from the GAC, certain events stopped working. Events that
have parameters derived from EventArgs, and are defined in the shared
interface assembly, were broken. Other events that simply passed
EventArgs still worked. Here is a derived event argument:

[Serializable]
public class LoginEventArgs : EventArgs
{
   public string UserName;
   public LoginEventArgs(string userName)
   {
      UserName = userName;
   }
}

Our application (server) would fire the events, but they would never
get to the client, and there were no exceptions or errors or anything
to indicate a problem.

I created a simplified test program that duplicates the problem
without even having to include methods and properties, just two
events. One event uses an EventArgs parameter, and the other uses the
derived LoginEventArgs parameter. The one that uses the EventArgs
parameter works. Here is how the test solution it is layed out
(similar to all the remoting samples you have probably seen):

RemoteTest - solution
   Interface - C# class libary containg the shared remoting interface
   Server - Windows form app that fires the events
   Client - Windows forms app that receives fired events

The Server app has a simple form containing a button that will fire
the UserLoggedIn event. When closing the form, it will fire the
Terminated event. NOTE: the "event wrapper" and ISynchronize.Invoke
method is used to get the events to the client in a thread-safe
manner.

The Client app has a simple form containing a "Connect" button, which
simply connects to Server's remoting port.

I broke down the problem one step further: I signed the shared
assembly, but did NOT install it in the GAC. I simply let it be copied
to each applications local directory. IT STILL DID NOT WORK. I then
unsigned the assembly, and sure enough, all events worked.

So, it would seem that signing an assembly somehow breaks the
reference to the shared interface for the Server and/or Client
application(s). Has anyone seen this behavior? Is there some kind of
security thing going on here that I don't know about? Any chance that
this is a problem with .Net serialization? Any advice is
appreciated...

The source code starts here...

-----------------------------------------------------
// The shared "Interface" assembly
using System;
using System.Runtime.Remoting.Messaging;
using System.ComponentModel;

namespace RemoteTest.Interface
{
   //EventArg derivative for Login
   [Serializable]
   public class LoginEventArgs : EventArgs
   {
      public string UserName;
      public LoginEventArgs(string userName)
      {
         UserName = userName;
      }
   }

   // event delegates
   public delegate void UserLoggedInHandler(object sender,
LoginEventArgs e);
   public delegate void TerminatedHandler(object sender, EventArgs e);

   // event wrappers needed for remoting
   public class UserLoggedInEventWrapper : MarshalByRefObject
   {
      public event UserLoggedInHandler UserLoggedInArrivedLocally;
      [OneWay]
      public void LocallyHandleUserLoggedIn(object sender,
LoginEventArgs e)
      {
         foreach (UserLoggedInHandler singleCast in
UserLoggedInArrivedLocally.GetInvocationList())
         {
            ISynchronizeInvoke syncInvoke = singleCast.Target as
ISynchronizeInvoke;
            try
            {
               if ((null != syncInvoke) &&
(syncInvoke.InvokeRequired))
                  syncInvoke.Invoke(singleCast, new object[] { sender,
e });
               else
                  singleCast(sender, e);
            }
            catch { }
         }
      }
      public override object InitializeLifetimeService()
      {
         return null;
      }
   }
   [Serializable]

   public class TerminatedEventWrapper : MarshalByRefObject
   {
      public event TerminatedHandler TerminatedArrivedLocally;
      [OneWay]
      public void LocallyHandleTerminated(object sender, EventArgs e)
      {
         // forward the message to the client
         foreach (TerminatedHandler singleCast in
TerminatedArrivedLocally.GetInvocationList())
         {
            ISynchronizeInvoke syncInvoke = singleCast.Target as
ISynchronizeInvoke;
            try
            {
               if ((null != syncInvoke) &&
(syncInvoke.InvokeRequired))
                  syncInvoke.Invoke(singleCast, new object[] { sender,
e });
               else
                  singleCast(sender, e);
            }
            catch { }
         }
      }
      public override object InitializeLifetimeService()
      {
         return null;
      }
   }

   public interface IRemoteTest
   {
      event UserLoggedInHandler UserLoggedIn;
      event TerminatedHandler Terminated;
   }
}

-----------------------------------------------------
// The Server, which fires the events
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Text;
using System.Windows.Forms;
using RemoteTest.Interface;

namespace RemoteTest.Server
{
   public partial class Form1 : Form, IRemoteTest
   {
      public event UserLoggedInHandler UserLoggedIn;
      public event TerminatedHandler Terminated;

      TcpChannel _tcpChannel = null;

      public Form1()
      {
         InitializeComponent();
      }

      private void Form1_Load(object sender, EventArgs e)
      {
         this.FormClosed += new
FormClosedEventHandler(Form1_FormClosed);

         //initialilze Remoting for Slave
         BinaryServerFormatterSinkProvider serverProv = new
BinaryServerFormatterSinkProvider();
         serverProv.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
         IDictionary props = new Hashtable();
         props["port"] = 9090;
         _tcpChannel = new TcpChannel(props, new
BinaryClientFormatterSinkProvider(), serverProv);
         ChannelServices.RegisterChannel(_tcpChannel, true);
         RemotingServices.Marshal(this, "RemoteTest.Interface",
typeof(IRemoteTest));
      }

      void Form1_FormClosed(object sender, FormClosedEventArgs e)
      {
         if (null != Terminated)
            Terminated("RemoteTest_Server", new EventArgs());
      }

      private void btnLogin_Click(object sender, EventArgs e)
      {
         if (null != UserLoggedIn)
            UserLoggedIn("RemoteTest_Server", new
LoginEventArgs("george"));
      }
   }
}

------------------------------------------------------
// Client app - receives events

using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Text;
using System.Windows.Forms;
using RemoteTest.Interface;

namespace RemoteTest.Client
{
   public partial class Form1 : Form
   {
      TcpChannel _channel = null;
      IRemoteTest _remoteTest = null;

      public Form1()
      {
         InitializeComponent();
      }

      private void btnConnect_Click(object sender, EventArgs e)
      {
         IDictionary props = new ListDictionary();
         props["port"] = 0; // have the Remoting system pick a unique
listening port (required for callbacks)
         props["name"] = String.Empty; // have the Remoting system
pick a unique name
         _channel = new TcpChannel(props, null, null);
         ChannelServices.RegisterChannel(_channel, true);

         _remoteTest =
(IRemoteTest)Activator.GetObject(typeof(IRemoteTest), "tcp://localhost:
9090/RemoteTest.Interface");

         //set up event handlers
         UserLoggedInEventWrapper userLoggedIn_w = new
UserLoggedInEventWrapper();
         userLoggedIn_w.UserLoggedInArrivedLocally += new
UserLoggedInHandler(UserLoggedIn);
         _remoteTest.UserLoggedIn += new
UserLoggedInHandler(userLoggedIn_w.LocallyHandleUserLoggedIn);
         TerminatedEventWrapper terminated_w = new
TerminatedEventWrapper();
         terminated_w.TerminatedArrivedLocally += new
TerminatedHandler(Terminated);
         _remoteTest.Terminated += new
TerminatedHandler(terminated_w.LocallyHandleTerminated);
      }

      //IRemoteTest Events
      public void UserLoggedIn(object sender, LoginEventArgs e)
      {
         MessageBox.Show("UserLoggedIn: " + e.UserName);
      }
      public void Terminated(object sender, EventArgs e)
      {
         MessageBox.Show("Terminated");
      }
   }
}
date: Fri, 25 Jan 2008 12:17:05 -0800 (PST)   author:   GeorgeK

Re: Events stop working when interface assembly is signed (strong-named).   
I think I just answered my own question:

When you sign the shared assembly, both the remoting client as well as
the server need to set their respective server providor's
typeFilterLevel property to full. If you look at my code, the Client
was using the default service provider, which apparently defaults to
"Low". So, the following code fixed the problem in my test app:

BinaryServerFormatterSinkProvider serverProv = new
BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
_channel = new TcpChannel(props, new
BinaryClientFormatterSinkProvider(), serverProv);
date: Fri, 25 Jan 2008 13:43:54 -0800 (PST)   author:   GeorgeK

Google
 
Web ureader.com


    COPYRIGHT 2007, YARDI TECHNOLOGY LIMITED, ALL RIGHT RESERVE  |   contact us