By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
458,166 Members | 1,738 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 458,166 IT Pros & Developers. It's quick & easy.

Stupid person does not understand .NET Remoting and Delegates

P: n/a
Hello everyone,

I am a plenty silly person who is trying to
learn .NET remoting through trial and error (all articles I read are going
over my head at the moment (mostly) so I thought i'd give it a go).

What I want to do is this:

Have a server instance of the program, this server instance will receive
communication from client programs (as demonstrated in the AddMessage()
method of the RemoteObject) and then send data back to them (obviously the
true scale of this isn't really implemented here, this is proof of concept).
Unfortunately whenever I try to assign a delegate to a RemoteObject I get an
error. It used to be security error about Serialization but then I found out
about TypeFilterLevel, having fixed that I now get this error instead.

An unhandled exception of type
'System.Runtime.Serialization.SerializationExcepti on' occurred in
mscorlib.dll
Additional information: Cannot find the assembly MyRemoteClient,
Version=1.0.1843.39632, Culture=neutral, PublicKeyToken=null.

As you can probably guess this isn't the most helpful of error messages as
this happens in the MyRemoteClient.exe so I cant understand how it can't
find itself, but i'm sure i'm doing something very stupid and impeach you
all to point out my stupidity to me so that I may learn the error of my
ways.

////////////Here is the client code (this is where I get the exception)

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;

namespace MyRemoteTest
{
class RemoteClient
{
static void Main(string[] args)
{
BinaryServerFormatterSinkProvider serverProv = new
BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilter Level.Full;
BinaryClientFormatterSinkProvider clientProv = new
BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["port"] = 1000;

TcpChannel channel = new TcpChannel( props, clientProv, null );
ChannelServices.RegisterChannel( channel );

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof ( RemoteObj ),
"tcp://localhost:1099/RemoteObj" );

remObj.ReplyDel = new ReplyDelegate(WriteMessage); // This is where I get
the exception

string message = "We have connected a user. Whoo!";
remObj.AddMessage( message, 1000 );
while (message.ToLower().StartsWith( "quit" ) == false)
{
Console.WriteLine( "Enter a Message to Add to Server, 'quit' to exit
application" );
message = Console.ReadLine();
remObj.AddMessage( message, 1000 );
}
}
static void WriteMessage(string message)
{
Console.WriteLine(message);
}
}
}

//// Here is ServerCode

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;

namespace MyRemoteTest
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class RemoteServer
{
/// <summary>
/// Entry Point into this application
/// </summary>
public static void Main()
{
BinaryServerFormatterSinkProvider serverProv = new
BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilter Level.Full;
IDictionary props = new Hashtable();
props["port"] = 1099;
TcpChannel channel = new TcpChannel( props, null, serverProv );
ChannelServices.RegisterChannel( channel );
RemotingConfiguration.RegisterWellKnownServiceType ( typeof( RemoteObj ),
"RemoteObj",
WellKnownObjectMode.SingleCall );
Console.WriteLine( "The Topic Server is up and running on port {0}",
1099 );
Console.WriteLine( "Press enter to stop the server..." );
Console.ReadLine();
}
}
}

///// Here is remote obj and delegate definition

using System;
using System.Collections;
using System.Runtime.Remoting;

namespace MyRemoteTest
{
[Serializable]
public class RemoteObj: MarshalByRefObject
{
private ReplyDelegate replyDel;

public ReplyDelegate ReplyDel
{
set{replyDel = value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}", userID,message);
Console.WriteLine(announce);
replyDel(announce);
}
}
public delegate void ReplyDelegate(string announce);
}

/////////////////////////////////
Nov 16 '05 #1
Share this Question
Share on Google+
4 Replies


P: n/a
You've got a couple of issues with your code:

1) When you pass a delegate to a remote object, the method associated with
that delegate (WriteMessage in your case) cannot be a static method. It must
be a public instance method of a MarshalByRefObject-derived type.

2) Given issue #1 above, when you pass the delegate to the server, the
server will need to be able to load the definition of the class that defines
the delegate. So, if the class is defined in your client executable, then
the server will need to have access to that executable (i.e. it needs to be
in the same folder as the server).

There are ways to work around issue #2 as it's likely (and logically
reasonable) that you don't want your server to have a dependency on your
client. What you need to do then is use interfaces for all of your remoting.
For example, instead of using a delegate, you would first define an
interface in a new, shared assembly, e.g.

public interface IServerCallback
{
void OnReply(string announce);
}

This assembly would be used by both client and server processes. In your
server, your remote object class would look like:

public class RemoteObj: MarshalByRefObject
{
private IServerCallback serverCallback;

public IServerCallback Callback
{
set{serverCallback= value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}",
userID,message);
Console.WriteLine(announce);
serverCallback(announce);
}
}

Now, on your client, you'd hook into the server' callback by creating a
MarshalByRef class that implements IServerCallback, e.g.

public class ClientCallback : MarshalByRefObject, IClientCallback
{
public void OnReply(string announce)
{
Console.WriteLine(announce);
}

// This prevents the object's lifetime from being ended prematurely
public override object InitializeLifetimeLease()
{
return null;
}
}

Now hook it in as follows:

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof ( RemoteObj ),
"tcp://localhost:1099/RemoteObj" );

remObj.Callback = new ClientCallback();
string message = "We have connected a user. Whoo!";
remObj.AddMessage( message, 1000 );

Note, this is a very simple example and will only work with a single client
(because the serverCallback field can only hold a single callback), but you
can extend this so the server can hold an array of callbacks to service lots
of clients.

If you have any more questions, you may want to post to the
microsoft.public.dotnet.remoting newsgroup, which is dedicated to
remoting-related questions.

Ken
"Uchiha Jax" <i_************************@NOSPAMhotmail.com> wrote in message
news:wT***************@newsfe1-win.ntli.net...
Hello everyone,

I am a plenty silly person who is trying to
learn .NET remoting through trial and error (all articles I read are going
over my head at the moment (mostly) so I thought i'd give it a go).

What I want to do is this:

Have a server instance of the program, this server instance will receive
communication from client programs (as demonstrated in the AddMessage()
method of the RemoteObject) and then send data back to them (obviously the
true scale of this isn't really implemented here, this is proof of concept). Unfortunately whenever I try to assign a delegate to a RemoteObject I get an error. It used to be security error about Serialization but then I found out about TypeFilterLevel, having fixed that I now get this error instead.

An unhandled exception of type
'System.Runtime.Serialization.SerializationExcepti on' occurred in
mscorlib.dll
Additional information: Cannot find the assembly MyRemoteClient,
Version=1.0.1843.39632, Culture=neutral, PublicKeyToken=null.

As you can probably guess this isn't the most helpful of error messages as
this happens in the MyRemoteClient.exe so I cant understand how it can't
find itself, but i'm sure i'm doing something very stupid and impeach you
all to point out my stupidity to me so that I may learn the error of my
ways.

////////////Here is the client code (this is where I get the exception)

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;

namespace MyRemoteTest
{
class RemoteClient
{
static void Main(string[] args)
{
BinaryServerFormatterSinkProvider serverProv = new
BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilter Level.Full;
BinaryClientFormatterSinkProvider clientProv = new
BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["port"] = 1000;

TcpChannel channel = new TcpChannel( props, clientProv, null );
ChannelServices.RegisterChannel( channel );

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof ( RemoteObj ), "tcp://localhost:1099/RemoteObj" );

remObj.ReplyDel = new ReplyDelegate(WriteMessage); // This is where I get the exception

string message = "We have connected a user. Whoo!";
remObj.AddMessage( message, 1000 );
while (message.ToLower().StartsWith( "quit" ) == false)
{
Console.WriteLine( "Enter a Message to Add to Server, 'quit' to exit
application" );
message = Console.ReadLine();
remObj.AddMessage( message, 1000 );
}
}
static void WriteMessage(string message)
{
Console.WriteLine(message);
}
}
}

//// Here is ServerCode

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;

namespace MyRemoteTest
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class RemoteServer
{
/// <summary>
/// Entry Point into this application
/// </summary>
public static void Main()
{
BinaryServerFormatterSinkProvider serverProv = new
BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilter Level.Full;
IDictionary props = new Hashtable();
props["port"] = 1099;
TcpChannel channel = new TcpChannel( props, null, serverProv );
ChannelServices.RegisterChannel( channel );
RemotingConfiguration.RegisterWellKnownServiceType ( typeof( RemoteObj ), "RemoteObj",
WellKnownObjectMode.SingleCall );
Console.WriteLine( "The Topic Server is up and running on port {0}",
1099 );
Console.WriteLine( "Press enter to stop the server..." );
Console.ReadLine();
}
}
}

///// Here is remote obj and delegate definition

using System;
using System.Collections;
using System.Runtime.Remoting;

namespace MyRemoteTest
{
[Serializable]
public class RemoteObj: MarshalByRefObject
{
private ReplyDelegate replyDel;

public ReplyDelegate ReplyDel
{
set{replyDel = value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}", userID,message);
Console.WriteLine(announce);
replyDel(announce);
}
}
public delegate void ReplyDelegate(string announce);
}

/////////////////////////////////

Nov 16 '05 #2

P: n/a
Hi Ken.

Thanks for the reply, it's certainly changed my point of view in regards to
setting up remoting and i'm very thankful for being put on the right track.

Unfortunately I now have a different problem now with the same code.
I implemented it as you suggested, but i'm having really bad problems with
assignment.
In the line:

remObj.Callback = new ClientCallback();

We set the Callback property of the remote object to a new instance of the
ClientCallback type this would seem to work on the client but when the
RemoteObj.AddMessage() method is called on the server the serverCallback
field is always null.

I have since tried setting up both a property and a method to assign a
string in the RemoteObj instance but neither of these work either:

//// in the client side

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof ( RemoteObj ),
"tcp://localhost:1099/RemoteObj" );

ClientCallback myClient = new ClientCallback(); /// I tried assigning
this way too!
remObj.Callback = myClient;
remObj.Store = "Blah";
remObj.SetStore("Blah 2");

//// and then on to the while loop

/////// RemoteObj

public class RemoteObj: MarshalByRefObject
{
private IServerCallback serverCallback;
private string store;

pubic string Store
{
set{store = value;}
}

public void SetStore(string value)
{
store = value;
}

public IServerCallback Callback
{
set{serverCallback= value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}",
userID,message);
Console.WriteLine(announce);
if(serverCallback != null)
{
serverCallback.OnReply(announce);
}
else
{
Console.WriteLine("serverCallback instance is null, will attempt
other field");
Console.WriteLine("Store = {0}", store);
}
}
}

I get the following.

<message> /// whatever I wrote
serverCallback instance is null, will attempt other field
Store =
/// eg must equal null as it is not "blah" or "blah2"

Sorry to trouble you further as I really appreciate the time you have
already given me, I just don't know what to do next or even what I might be
doing wrong as I can't seem to assign anything!

Hope you can help.

Kind Regards

Jax
"Ken Kolda" <ke*******@elliemae-nospamplease.com> wrote in message
news:OX**************@TK2MSFTNGP10.phx.gbl...
You've got a couple of issues with your code:

1) When you pass a delegate to a remote object, the method associated with
that delegate (WriteMessage in your case) cannot be a static method. It must be a public instance method of a MarshalByRefObject-derived type.

2) Given issue #1 above, when you pass the delegate to the server, the
server will need to be able to load the definition of the class that defines the delegate. So, if the class is defined in your client executable, then
the server will need to have access to that executable (i.e. it needs to be in the same folder as the server).

There are ways to work around issue #2 as it's likely (and logically
reasonable) that you don't want your server to have a dependency on your
client. What you need to do then is use interfaces for all of your remoting. For example, instead of using a delegate, you would first define an
interface in a new, shared assembly, e.g.

public interface IServerCallback
{
void OnReply(string announce);
}

This assembly would be used by both client and server processes. In your
server, your remote object class would look like:

public class RemoteObj: MarshalByRefObject
{
private IServerCallback serverCallback;

public IServerCallback Callback
{
set{serverCallback= value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}",
userID,message);
Console.WriteLine(announce);
serverCallback(announce);
}
}

Now, on your client, you'd hook into the server' callback by creating a
MarshalByRef class that implements IServerCallback, e.g.

public class ClientCallback : MarshalByRefObject, IClientCallback
{
public void OnReply(string announce)
{
Console.WriteLine(announce);
}

// This prevents the object's lifetime from being ended prematurely
public override object InitializeLifetimeLease()
{
return null;
}
}

Now hook it in as follows:

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof ( RemoteObj ), "tcp://localhost:1099/RemoteObj" );

remObj.Callback = new ClientCallback();
string message = "We have connected a user. Whoo!";
remObj.AddMessage( message, 1000 );

Note, this is a very simple example and will only work with a single client (because the serverCallback field can only hold a single callback), but you can extend this so the server can hold an array of callbacks to service lots of clients.

If you have any more questions, you may want to post to the
microsoft.public.dotnet.remoting newsgroup, which is dedicated to
remoting-related questions.

Ken
"Uchiha Jax" <i_************************@NOSPAMhotmail.com> wrote in message news:wT***************@newsfe1-win.ntli.net...
Hello everyone,

I am a plenty silly person who is trying to
learn .NET remoting through trial and error (all articles I read are going over my head at the moment (mostly) so I thought i'd give it a go).

What I want to do is this:

Have a server instance of the program, this server instance will receive
communication from client programs (as demonstrated in the AddMessage()
method of the RemoteObject) and then send data back to them (obviously the true scale of this isn't really implemented here, this is proof of concept).
Unfortunately whenever I try to assign a delegate to a RemoteObject I get an
error. It used to be security error about Serialization but then I found

out
about TypeFilterLevel, having fixed that I now get this error instead.

An unhandled exception of type
'System.Runtime.Serialization.SerializationExcepti on' occurred in
mscorlib.dll
Additional information: Cannot find the assembly MyRemoteClient,
Version=1.0.1843.39632, Culture=neutral, PublicKeyToken=null.

As you can probably guess this isn't the most helpful of error messages

as this happens in the MyRemoteClient.exe so I cant understand how it can't
find itself, but i'm sure i'm doing something very stupid and impeach you all to point out my stupidity to me so that I may learn the error of my
ways.

////////////Here is the client code (this is where I get the exception)

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;

namespace MyRemoteTest
{
class RemoteClient
{
static void Main(string[] args)
{
BinaryServerFormatterSinkProvider serverProv = new
BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilter Level.Full;
BinaryClientFormatterSinkProvider clientProv = new
BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["port"] = 1000;

TcpChannel channel = new TcpChannel( props, clientProv, null );
ChannelServices.RegisterChannel( channel );

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof (

RemoteObj ),
"tcp://localhost:1099/RemoteObj" );

remObj.ReplyDel = new ReplyDelegate(WriteMessage); // This is where I

get
the exception

string message = "We have connected a user. Whoo!";
remObj.AddMessage( message, 1000 );
while (message.ToLower().StartsWith( "quit" ) == false)
{
Console.WriteLine( "Enter a Message to Add to Server, 'quit' to exit
application" );
message = Console.ReadLine();
remObj.AddMessage( message, 1000 );
}
}
static void WriteMessage(string message)
{
Console.WriteLine(message);
}
}
}

//// Here is ServerCode

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;

namespace MyRemoteTest
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class RemoteServer
{
/// <summary>
/// Entry Point into this application
/// </summary>
public static void Main()
{
BinaryServerFormatterSinkProvider serverProv = new
BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilter Level.Full;
IDictionary props = new Hashtable();
props["port"] = 1099;
TcpChannel channel = new TcpChannel( props, null, serverProv );
ChannelServices.RegisterChannel( channel );
RemotingConfiguration.RegisterWellKnownServiceType ( typeof(

RemoteObj ),
"RemoteObj",
WellKnownObjectMode.SingleCall );
Console.WriteLine( "The Topic Server is up and running on port {0}",
1099 );
Console.WriteLine( "Press enter to stop the server..." );
Console.ReadLine();
}
}
}

///// Here is remote obj and delegate definition

using System;
using System.Collections;
using System.Runtime.Remoting;

namespace MyRemoteTest
{
[Serializable]
public class RemoteObj: MarshalByRefObject
{
private ReplyDelegate replyDel;

public ReplyDelegate ReplyDel
{
set{replyDel = value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}", userID,message); Console.WriteLine(announce);
replyDel(announce);
}
}
public delegate void ReplyDelegate(string announce);
}

/////////////////////////////////


Nov 16 '05 #3

P: n/a
This is because your server object is registered as SingleCall. When you
make an object SingleCall, every property and method you invoke on the
object is actually invoked on a completely separate instance of the object.
Thus, in the lines of code:

remObj.Callback = myClient;
remObj.Store = "Blah";
remObj.SetStore("Blah 2");

you are actually accessing 3 completely separate instances of the
RemoteObject class on the server (even though from the client's pespective
it looks like the same class). SingleCall objects are meant to be
stateless -- they should not hold any data between calls (as you've seen,
any state is lost between calls anyway).

If you change your object to a Singleton, then this should work. However,
all clients will share the same instance of the object, so the code you have
would only be reasonable if you ever have a single client (since the
RemoteObject can only hold a single ClientCallback reference). If you need
different objects per client, you need to create a client-activated object
(CAO) by registering the type with
RegisterActivatedServiceType/RegisterActivatedClientType.

Good luck -
Ken
"Uchiha Jax" <i_************************@NOSPAMhotmail.com> wrote in message
news:hc***************@newsfe5-gui.ntli.net...
Hi Ken.

Thanks for the reply, it's certainly changed my point of view in regards to setting up remoting and i'm very thankful for being put on the right track.
Unfortunately I now have a different problem now with the same code.
I implemented it as you suggested, but i'm having really bad problems with
assignment.
In the line:

remObj.Callback = new ClientCallback();

We set the Callback property of the remote object to a new instance of the
ClientCallback type this would seem to work on the client but when the
RemoteObj.AddMessage() method is called on the server the serverCallback
field is always null.

I have since tried setting up both a property and a method to assign a
string in the RemoteObj instance but neither of these work either:

//// in the client side

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof ( RemoteObj ),
"tcp://localhost:1099/RemoteObj" );

ClientCallback myClient = new ClientCallback(); /// I tried assigning
this way too!
remObj.Callback = myClient;
remObj.Store = "Blah";
remObj.SetStore("Blah 2");

//// and then on to the while loop

/////// RemoteObj

public class RemoteObj: MarshalByRefObject
{
private IServerCallback serverCallback;
private string store;

pubic string Store
{
set{store = value;}
}

public void SetStore(string value)
{
store = value;
}

public IServerCallback Callback
{
set{serverCallback= value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}",
userID,message);
Console.WriteLine(announce);
if(serverCallback != null)
{
serverCallback.OnReply(announce);
}
else
{
Console.WriteLine("serverCallback instance is null, will attempt other field");
Console.WriteLine("Store = {0}", store);
}
}
}

I get the following.

<message> /// whatever I wrote
serverCallback instance is null, will attempt other field
Store =
/// eg must equal null as it is not "blah" or "blah2"

Sorry to trouble you further as I really appreciate the time you have
already given me, I just don't know what to do next or even what I might be doing wrong as I can't seem to assign anything!

Hope you can help.

Kind Regards

Jax
"Ken Kolda" <ke*******@elliemae-nospamplease.com> wrote in message
news:OX**************@TK2MSFTNGP10.phx.gbl...
You've got a couple of issues with your code:

1) When you pass a delegate to a remote object, the method associated with
that delegate (WriteMessage in your case) cannot be a static method. It must
be a public instance method of a MarshalByRefObject-derived type.

2) Given issue #1 above, when you pass the delegate to the server, the
server will need to be able to load the definition of the class that

defines
the delegate. So, if the class is defined in your client executable, then the server will need to have access to that executable (i.e. it needs to

be
in the same folder as the server).

There are ways to work around issue #2 as it's likely (and logically
reasonable) that you don't want your server to have a dependency on your
client. What you need to do then is use interfaces for all of your

remoting.
For example, instead of using a delegate, you would first define an
interface in a new, shared assembly, e.g.

public interface IServerCallback
{
void OnReply(string announce);
}

This assembly would be used by both client and server processes. In your
server, your remote object class would look like:

public class RemoteObj: MarshalByRefObject
{
private IServerCallback serverCallback;

public IServerCallback Callback
{
set{serverCallback= value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}",
userID,message);
Console.WriteLine(announce);
serverCallback(announce);
}
}

Now, on your client, you'd hook into the server' callback by creating a
MarshalByRef class that implements IServerCallback, e.g.

public class ClientCallback : MarshalByRefObject, IClientCallback
{
public void OnReply(string announce)
{
Console.WriteLine(announce);
}

// This prevents the object's lifetime from being ended prematurely
public override object InitializeLifetimeLease()
{
return null;
}
}

Now hook it in as follows:

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof (

RemoteObj ),
"tcp://localhost:1099/RemoteObj" );

remObj.Callback = new ClientCallback();
string message = "We have connected a user. Whoo!";
remObj.AddMessage( message, 1000 );

Note, this is a very simple example and will only work with a single

client
(because the serverCallback field can only hold a single callback), but

you
can extend this so the server can hold an array of callbacks to service

lots
of clients.

If you have any more questions, you may want to post to the
microsoft.public.dotnet.remoting newsgroup, which is dedicated to
remoting-related questions.

Ken
"Uchiha Jax" <i_************************@NOSPAMhotmail.com> wrote in

message
news:wT***************@newsfe1-win.ntli.net...
Hello everyone,

I am a plenty silly person who is trying to learn .NET remoting through trial and error (all articles I read are going over my head at the moment (mostly) so I thought i'd give it a go).

What I want to do is this:

Have a server instance of the program, this server instance will receive communication from client programs (as demonstrated in the AddMessage() method of the RemoteObject) and then send data back to them (obviously the true scale of this isn't really implemented here, this is proof of

concept).
Unfortunately whenever I try to assign a delegate to a RemoteObject I get
an
error. It used to be security error about Serialization but then I found out
about TypeFilterLevel, having fixed that I now get this error instead.

An unhandled exception of type
'System.Runtime.Serialization.SerializationExcepti on' occurred in
mscorlib.dll
Additional information: Cannot find the assembly MyRemoteClient,
Version=1.0.1843.39632, Culture=neutral, PublicKeyToken=null.

As you can probably guess this isn't the most helpful of error
messages as this happens in the MyRemoteClient.exe so I cant understand how it
can't find itself, but i'm sure i'm doing something very stupid and impeach

you all to point out my stupidity to me so that I may learn the error of my ways.

////////////Here is the client code (this is where I get the exception)
using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;

namespace MyRemoteTest
{
class RemoteClient
{
static void Main(string[] args)
{
BinaryServerFormatterSinkProvider serverProv = new
BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilter Level.Full;
BinaryClientFormatterSinkProvider clientProv = new
BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["port"] = 1000;

TcpChannel channel = new TcpChannel( props, clientProv, null );
ChannelServices.RegisterChannel( channel );

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof (

RemoteObj ),
"tcp://localhost:1099/RemoteObj" );

remObj.ReplyDel = new ReplyDelegate(WriteMessage); // This is where I get
the exception

string message = "We have connected a user. Whoo!";
remObj.AddMessage( message, 1000 );
while (message.ToLower().StartsWith( "quit" ) == false)
{
Console.WriteLine( "Enter a Message to Add to Server, 'quit' to
exit application" );
message = Console.ReadLine();
remObj.AddMessage( message, 1000 );
}
}
static void WriteMessage(string message)
{
Console.WriteLine(message);
}
}
}

//// Here is ServerCode

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;

namespace MyRemoteTest
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class RemoteServer
{
/// <summary>
/// Entry Point into this application
/// </summary>
public static void Main()
{
BinaryServerFormatterSinkProvider serverProv = new
BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilter Level.Full;
IDictionary props = new Hashtable();
props["port"] = 1099;
TcpChannel channel = new TcpChannel( props, null, serverProv );
ChannelServices.RegisterChannel( channel );
RemotingConfiguration.RegisterWellKnownServiceType ( typeof(

RemoteObj ),
"RemoteObj",
WellKnownObjectMode.SingleCall );
Console.WriteLine( "The Topic Server is up and running on port {0}", 1099 );
Console.WriteLine( "Press enter to stop the server..." );
Console.ReadLine();
}
}
}

///// Here is remote obj and delegate definition

using System;
using System.Collections;
using System.Runtime.Remoting;

namespace MyRemoteTest
{
[Serializable]
public class RemoteObj: MarshalByRefObject
{
private ReplyDelegate replyDel;

public ReplyDelegate ReplyDel
{
set{replyDel = value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}",

userID,message); Console.WriteLine(announce);
replyDel(announce);
}
}
public delegate void ReplyDelegate(string announce);
}

/////////////////////////////////



Nov 16 '05 #4

P: n/a
Yayyyyyyyyy!
It works now.

Thank you so much Ken your advice has been excellent.
Now to have fun with remoting........................

Jax

"Ken Kolda" <ke*******@elliemae-nospamplease.com> wrote in message
news:ed**************@TK2MSFTNGP14.phx.gbl...
This is because your server object is registered as SingleCall. When you
make an object SingleCall, every property and method you invoke on the
object is actually invoked on a completely separate instance of the object. Thus, in the lines of code:

remObj.Callback = myClient;
remObj.Store = "Blah";
remObj.SetStore("Blah 2");

you are actually accessing 3 completely separate instances of the
RemoteObject class on the server (even though from the client's pespective
it looks like the same class). SingleCall objects are meant to be
stateless -- they should not hold any data between calls (as you've seen,
any state is lost between calls anyway).

If you change your object to a Singleton, then this should work. However,
all clients will share the same instance of the object, so the code you have would only be reasonable if you ever have a single client (since the
RemoteObject can only hold a single ClientCallback reference). If you need
different objects per client, you need to create a client-activated object
(CAO) by registering the type with
RegisterActivatedServiceType/RegisterActivatedClientType.

Good luck -
Ken
"Uchiha Jax" <i_************************@NOSPAMhotmail.com> wrote in message news:hc***************@newsfe5-gui.ntli.net...
Hi Ken.

Thanks for the reply, it's certainly changed my point of view in regards to
setting up remoting and i'm very thankful for being put on the right

track.

Unfortunately I now have a different problem now with the same code.
I implemented it as you suggested, but i'm having really bad problems with
assignment.
In the line:

remObj.Callback = new ClientCallback();

We set the Callback property of the remote object to a new instance of the ClientCallback type this would seem to work on the client but when the
RemoteObj.AddMessage() method is called on the server the serverCallback
field is always null.

I have since tried setting up both a property and a method to assign a
string in the RemoteObj instance but neither of these work either:

//// in the client side

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof ( RemoteObj ), "tcp://localhost:1099/RemoteObj" );

ClientCallback myClient = new ClientCallback(); /// I tried assigning
this way too!
remObj.Callback = myClient;
remObj.Store = "Blah";
remObj.SetStore("Blah 2");

//// and then on to the while loop

/////// RemoteObj

public class RemoteObj: MarshalByRefObject
{
private IServerCallback serverCallback;
private string store;

pubic string Store
{
set{store = value;}
}

public void SetStore(string value)
{
store = value;
}

public IServerCallback Callback
{
set{serverCallback= value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}",
userID,message);
Console.WriteLine(announce);
if(serverCallback != null)
{
serverCallback.OnReply(announce);
}
else
{
Console.WriteLine("serverCallback instance is null, will

attempt
other field");
Console.WriteLine("Store = {0}", store);
}
}
}

I get the following.

<message> /// whatever I wrote
serverCallback instance is null, will attempt other field
Store =
/// eg must equal null as it is not "blah" or "blah2"

Sorry to trouble you further as I really appreciate the time you have
already given me, I just don't know what to do next or even what I might

be
doing wrong as I can't seem to assign anything!

Hope you can help.

Kind Regards

Jax
"Ken Kolda" <ke*******@elliemae-nospamplease.com> wrote in message
news:OX**************@TK2MSFTNGP10.phx.gbl...
You've got a couple of issues with your code:

1) When you pass a delegate to a remote object, the method associated with that delegate (WriteMessage in your case) cannot be a static method. It must
be a public instance method of a MarshalByRefObject-derived type.

2) Given issue #1 above, when you pass the delegate to the server, the
server will need to be able to load the definition of the class that

defines
the delegate. So, if the class is defined in your client executable, then the server will need to have access to that executable (i.e. it needs
to
be
in the same folder as the server).

There are ways to work around issue #2 as it's likely (and logically
reasonable) that you don't want your server to have a dependency on
your client. What you need to do then is use interfaces for all of your

remoting.
For example, instead of using a delegate, you would first define an
interface in a new, shared assembly, e.g.

public interface IServerCallback
{
void OnReply(string announce);
}

This assembly would be used by both client and server processes. In your server, your remote object class would look like:

public class RemoteObj: MarshalByRefObject
{
private IServerCallback serverCallback;

public IServerCallback Callback
{
set{serverCallback= value;}
}

public RemoteObj()
{
}

public void AddMessage(string message, int userID)
{
string announce = String.Format("User {0} said: {1}",
userID,message);
Console.WriteLine(announce);
serverCallback(announce);
}
}

Now, on your client, you'd hook into the server' callback by creating a MarshalByRef class that implements IServerCallback, e.g.

public class ClientCallback : MarshalByRefObject, IClientCallback
{
public void OnReply(string announce)
{
Console.WriteLine(announce);
}

// This prevents the object's lifetime from being ended prematurely public override object InitializeLifetimeLease()
{
return null;
}
}

Now hook it in as follows:

RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof (

RemoteObj ),
"tcp://localhost:1099/RemoteObj" );

remObj.Callback = new ClientCallback();
string message = "We have connected a user. Whoo!";
remObj.AddMessage( message, 1000 );

Note, this is a very simple example and will only work with a single

client
(because the serverCallback field can only hold a single callback), but you
can extend this so the server can hold an array of callbacks to
service lots
of clients.

If you have any more questions, you may want to post to the
microsoft.public.dotnet.remoting newsgroup, which is dedicated to
remoting-related questions.

Ken
"Uchiha Jax" <i_************************@NOSPAMhotmail.com> wrote in

message
news:wT***************@newsfe1-win.ntli.net...
> Hello everyone,
>
> I am a plenty silly person who is trying to > learn .NET remoting through trial and error (all articles I read are

going
> over my head at the moment (mostly) so I thought i'd give it a go).
>
> What I want to do is this:
>
> Have a server instance of the program, this server instance will receive > communication from client programs (as demonstrated in the AddMessage() > method of the RemoteObject) and then send data back to them
(obviously the
> true scale of this isn't really implemented here, this is proof of
concept).
> Unfortunately whenever I try to assign a delegate to a RemoteObject
I get
an
> error. It used to be security error about Serialization but then I found out
> about TypeFilterLevel, having fixed that I now get this error
instead. >
> An unhandled exception of type
> 'System.Runtime.Serialization.SerializationExcepti on' occurred in
> mscorlib.dll
> Additional information: Cannot find the assembly MyRemoteClient,
> Version=1.0.1843.39632, Culture=neutral, PublicKeyToken=null.
>
> As you can probably guess this isn't the most helpful of error

messages
as
> this happens in the MyRemoteClient.exe so I cant understand how it

can't > find itself, but i'm sure i'm doing something very stupid and
impeach you
> all to point out my stupidity to me so that I may learn the error of

my > ways.
>
> ////////////Here is the client code (this is where I get the exception) >
> using System;
> using System.Collections;
> using System.Runtime.Remoting;
> using System.Runtime.Remoting.Channels;
> using System.Runtime.Remoting.Channels.Http;
> using System.Runtime.Remoting.Channels.Tcp;
>
> namespace MyRemoteTest
> {
> class RemoteClient
> {
> static void Main(string[] args)
> {
> BinaryServerFormatterSinkProvider serverProv = new
> BinaryServerFormatterSinkProvider();
> serverProv.TypeFilterLevel =
> System.Runtime.Serialization.Formatters.TypeFilter Level.Full;
> BinaryClientFormatterSinkProvider clientProv = new
> BinaryClientFormatterSinkProvider();
> IDictionary props = new Hashtable();
> props["port"] = 1000;
>
> TcpChannel channel = new TcpChannel( props, clientProv, null );
> ChannelServices.RegisterChannel( channel );
>
> RemoteObj remObj = (RemoteObj) Activator.GetObject( typeof (
RemoteObj ),
> "tcp://localhost:1099/RemoteObj" );
>
> remObj.ReplyDel = new ReplyDelegate(WriteMessage); // This is
where I get
> the exception
>
> string message = "We have connected a user. Whoo!";
> remObj.AddMessage( message, 1000 );
> while (message.ToLower().StartsWith( "quit" ) == false)
> {
> Console.WriteLine( "Enter a Message to Add to Server, 'quit' to exit > application" );
> message = Console.ReadLine();
> remObj.AddMessage( message, 1000 );
> }
> }
> static void WriteMessage(string message)
> {
> Console.WriteLine(message);
> }
> }
> }
>
> //// Here is ServerCode
>
> using System;
> using System.Collections;
> using System.Runtime.Remoting;
> using System.Runtime.Remoting.Channels;
> using System.Runtime.Remoting.Channels.Http;
> using System.Runtime.Remoting.Channels.Tcp;
>
> namespace MyRemoteTest
> {
> /// <summary>
> /// Summary description for Class1.
> /// </summary>
> class RemoteServer
> {
> /// <summary>
> /// Entry Point into this application
> /// </summary>
> public static void Main()
> {
> BinaryServerFormatterSinkProvider serverProv = new
> BinaryServerFormatterSinkProvider();
> serverProv.TypeFilterLevel =
> System.Runtime.Serialization.Formatters.TypeFilter Level.Full;
> IDictionary props = new Hashtable();
> props["port"] = 1099;
> TcpChannel channel = new TcpChannel( props, null, serverProv );
> ChannelServices.RegisterChannel( channel );
> RemotingConfiguration.RegisterWellKnownServiceType ( typeof(
RemoteObj ),
> "RemoteObj",
> WellKnownObjectMode.SingleCall );
> Console.WriteLine( "The Topic Server is up and running on port {0}", > 1099 );
> Console.WriteLine( "Press enter to stop the server..." );
> Console.ReadLine();
> }
> }
> }
>
> ///// Here is remote obj and delegate definition
>
> using System;
> using System.Collections;
> using System.Runtime.Remoting;
>
> namespace MyRemoteTest
> {
> [Serializable]
> public class RemoteObj: MarshalByRefObject
> {
> private ReplyDelegate replyDel;
>
> public ReplyDelegate ReplyDel
> {
> set{replyDel = value;}
> }
>
> public RemoteObj()
> {
> }
>
> public void AddMessage(string message, int userID)
> {
> string announce = String.Format("User {0} said: {1}",

userID,message);
> Console.WriteLine(announce);
> replyDel(announce);
> }
> }
> public delegate void ReplyDelegate(string announce);
> }
>
> /////////////////////////////////
>
>



Nov 16 '05 #5

This discussion thread is closed

Replies have been disabled for this discussion.