all groups > dotnet remoting > january 2008 >
You're in the

dotnet remoting

group:

Events stop working when interface assembly is signed (strong-named).



Events stop working when interface assembly is signed (strong-named). GeorgeK
1/25/2008 12:17:05 PM
dotnet remoting: 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)
Re: Events stop working when interface assembly is signed (strong-named). GeorgeK
1/25/2008 1:43:54 PM
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
AddThis Social Bookmark Button