[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

microsoft.public.dotnet.framework.remoting

simple communication btw 2 apps - use events?

shonend

6/3/2004 9:35:00 PM

Hi,
my idea is to establish a simple communication between appA, app1 and
app2, or better to say CONTROL the app1 and app2 from applicationA.
All of those are WinForm apps.
AppA should be able to query app1 and app2 for some simple info, like
the status of those applications, and also to shut them down if
needed, i.e. issue the command to applications to shutdown themselves.

The research on how to accomplish this brought me to .NET Remoting, so
I've decided to try it out.

First thing I learned about Remoting is that initiating application
should be the client, while the application that "listens" for remote
calls, and responds to them is the server. In my case that would make
the appA the client and app1 and app2 the servers, since the appA will
be the one always initiating the remote contact. And that's what I
did, allthough already here I think I made mistake, because it's
unusual to have 1 client and 2 servers (and even more, because
eventually there will be app3, app4...). So what's the better solution
for the problem I need to solve?

Second problem I had was how to retreive the desired info from the
server, since the server application doesn't instantiate the remoting
class? Namely, the remoting class is defined and registered on the
server, but there is no reference from the class to the Form's
variables, and vice-versa. What I mean, I have the (single) Form code,
and separate remoting-class code, but there is no obvious connection
in executing these two, so that I can for example feed the desired
data from Form to the remoting class and return that data to the
remote-calling client.
I found a solution for this problem by sharing some data on the server
through AppDomain properties, and it's working (amazingly!). I'm sure
that this ad-hoc solution is pretty much unusual, and that there must
be some more common method and right way to do it, so I would like to
hear your expert oppinion about it, please.

The third and most important problem, I still didn't manage to solve
is how to issue a command from appA to app1 (or app2) to shutdown? I
tried with defining and raising the events, but it's not working for
some strange reason. Here's what I did:
1) server side - defined a Delegate and an Event in the remoting
class:

Public Delegate Sub ShutDownEventHandler(ByVal sender As Object, ByVal
e As EventArgs)

Public Class clsRemotingListener
Inherits System.MarshalByRefObject

...

Public Shared Event ShutDown As ShutDownEventHandler

...

I declared Event as Public Shared, because I need to catch this event
on the server, and server doesnt instantiate the remoting object.

2) defined the public method that will raise the event. This method
will be called from the client (appA). This is still in the
clsRemotingListener code:

Public Sub ShutDownServer(ByVal e As EventArgs)

RaiseEvent ShutDown(Me, e)

End Sub

3) in the server code (Form's constructor), I declare the handler for
the event:

AddHandler fRemoting.clsRemotingListener.ShutDown, AddressOf
Me.RemoteShutDown

Here you can see why the Event needed to be "Shared".
(fRemoting is just a name of the separate dll where the
clsRemotingListener is)

4) Finnaly, I define the Form's local RemoteShutDown method:

Private Sub RemoteShutDown(ByVal sender As Object, ByVal e As
EventArgs)
Me.TextBox1.Text = "I AM BEING SHUTDOWN!!!"
Me.Close()
End Sub


So when I try to invoke the "ShutDownServer" method from the client -
it doesn't work. Everything freezes, both client and server, and the
third window pops-up, obviously some error report, but it doesn't even
get to paint itself (remains blank). All that stays like that until I
kill the app1 (server), then the "third window" closes too, and appA
(client) continues to work normally.

I would like to point out that only the server shutdown attempt causes
the problem, not the remoting or event raising/receiving. At least
that's what I think, since everything works great when I comment-out
the "Me.Close()" statement in step 4). Then the server successfully
receives and processes the event, writes out the message, and
everyone's happy (both appA and app1 continue to do their jobs
uninterrupted). But the closing attempt messes everybody up!?!


So if you had enough patience to read so far, I would like to kindly
ask you to give me a couple of guidelines here.
Please don't respond with "it's obvious that you don't understand the
basic principles of .NET Remoting...". First of all that statement
couldn't be more TRUE, second of all - I already know that :)))

All I need is a couple of lines of example code with or without a
short explanation. I believe that I was clear in explaining what my
problem is and that is pretty much basic, so many of you experts in
..NET remoting are already doing that as a routine.


Thank you in advance...


Shone
4 Answers

Sunny

6/4/2004 3:03:00 PM

0

Hi Shone,
please read inline:


In article <38885f3a.0406031335.69c0a285@posting.google.com>,
shonend@yahoo.com says...
> Hi,
> my idea is to establish a simple communication between appA, app1 and
> app2, or better to say CONTROL the app1 and app2 from applicationA.
> All of those are WinForm apps.
> AppA should be able to query app1 and app2 for some simple info, like
> the status of those applications, and also to shut them down if
> needed, i.e. issue the command to applications to shutdown themselves.
>
> The research on how to accomplish this brought me to .NET Remoting, so
> I''ve decided to try it out.
>
> First thing I learned about Remoting is that initiating application
> should be the client, while the application that "listens" for remote
> calls, and responds to them is the server. In my case that would make
> the appA the client and app1 and app2 the servers, since the appA will
> be the one always initiating the remote contact. And that''s what I
> did, allthough already here I think I made mistake, because it''s
> unusual to have 1 client and 2 servers (and even more, because
> eventually there will be app3, app4...). So what''s the better solution
> for the problem I need to solve?

Actually, client is the app, which makes a request, and server is this,
which serves (executes) the request. Even if you dedicate some app for a
server, it will be so only while it servers the requests. And if this
server rises an event (i.e. invokes a method on the client) the roles
for that call are switched.

For your solution, it will depend on what is the exact scenario. You can
create a so called server (appA) to which all other apps 1, 2, etc will
register. And in that moment the roles are switched, because as appA
already knows which are connected apps, will make calls to them to get
the data or to execute something else.

>
> Second problem I had was how to retreive the desired info from the
> server, since the server application doesn''t instantiate the remoting
> class? Namely, the remoting class is defined and registered on the
> server, but there is no reference from the class to the Form''s
> variables, and vice-versa. What I mean, I have the (single) Form code,
> and separate remoting-class code, but there is no obvious connection
> in executing these two, so that I can for example feed the desired
> data from Form to the remoting class and return that data to the
> remote-calling client.
> I found a solution for this problem by sharing some data on the server
> through AppDomain properties, and it''s working (amazingly!). I''m sure
> that this ad-hoc solution is pretty much unusual, and that there must
> be some more common method and right way to do it, so I would like to
> hear your expert oppinion about it, please.

What you can do, is to create and instance of the remoting object in the
main form, passing the same form as parameter. Then the remote object
will know who is its parent and to get data and/or execute some methods.


>
> The third and most important problem, I still didn''t manage to solve
> is how to issue a command from appA to app1 (or app2) to shutdown? I
> tried with defining and raising the events, but it''s not working for
> some strange reason. Here''s what I did:
> 1) server side - defined a Delegate and an Event in the remoting
> class:
>
> Public Delegate Sub ShutDownEventHandler(ByVal sender As Object, ByVal
> e As EventArgs)
>
> Public Class clsRemotingListener
> Inherits System.MarshalByRefObject
>
> ...
>
> Public Shared Event ShutDown As ShutDownEventHandler
>
> ...
>
> I declared Event as Public Shared, because I need to catch this event
> on the server, and server doesnt instantiate the remoting object.
>
> 2) defined the public method that will raise the event. This method
> will be called from the client (appA). This is still in the
> clsRemotingListener code:
>
> Public Sub ShutDownServer(ByVal e As EventArgs)
>
> RaiseEvent ShutDown(Me, e)
>
> End Sub
>
> 3) in the server code (Form''s constructor), I declare the handler for
> the event:
>
> AddHandler fRemoting.clsRemotingListener.ShutDown, AddressOf
> Me.RemoteShutDown
>
> Here you can see why the Event needed to be "Shared".
> (fRemoting is just a name of the separate dll where the
> clsRemotingListener is)
>
> 4) Finnaly, I define the Form''s local RemoteShutDown method:
>
> Private Sub RemoteShutDown(ByVal sender As Object, ByVal e As
> EventArgs)
> Me.TextBox1.Text = "I AM BEING SHUTDOWN!!!"
> Me.Close()
> End Sub
>
>
> So when I try to invoke the "ShutDownServer" method from the client -
> it doesn''t work. Everything freezes, both client and server, and the
> third window pops-up, obviously some error report, but it doesn''t even
> get to paint itself (remains blank). All that stays like that until I
> kill the app1 (server), then the "third window" closes too, and appA
> (client) continues to work normally.
>
> I would like to point out that only the server shutdown attempt causes
> the problem, not the remoting or event raising/receiving. At least
> that''s what I think, since everything works great when I comment-out
> the "Me.Close()" statement in step 4). Then the server successfully
> receives and processes the event, writes out the message, and
> everyone''s happy (both appA and app1 continue to do their jobs
> uninterrupted). But the closing attempt messes everybody up!?!

The forms are not thread safe. Every remoting call is executed on a
different thread from the thread pool. In your remote method you have to
use Form.Invoke to transfer the execution in the main form thread. Thats
why your app freezes.


>
>
> So if you had enough patience to read so far, I would like to kindly
> ask you to give me a couple of guidelines here.
> Please don''t respond with "it''s obvious that you don''t understand the
> basic principles of .NET Remoting...". First of all that statement
> couldn''t be more TRUE, second of all - I already know that :)))
>
> All I need is a couple of lines of example code with or without a
> short explanation. I believe that I was clear in explaining what my
> problem is and that is pretty much basic, so many of you experts in
> .NET remoting are already doing that as a routine.

This is a sample pseudo code for such a scenario (take a look at
interface or abstract class approach, and SAO/CAO factory pattern on
internet):

Shared.dll (must be referenced by both clients and servers):

public interface IRegistered
{
int GetSomeFormData();
void ExecuteSomething();
void CloseApp();
}

public interface IRegistrator
{
void RegisterClient(IRegistered client)
}

// declare some delegates which you want to use to make event calls

// declare simple event wrapper to transfer the event calls (search the
// newsgroup, I have posted examples)

// you can also declare here some custom exceptions, if you wish

-----------------------------

appA (I''ll put only the remote object, you have to expose it sa SAO
Singleton, as it will contain all registered clients. You can use
Marshal method as well, so your server app can create the object and
have access to it as well):

public ClientRegistrator : MarshalByReferenceObject, IRegistrator
{
private ArrayList clients; /or some other way to store the client
references

public void RegisterClient(IRegistered client)
{
this.clients.Add(client);
// TODO whatever you want more
}

public void CloseAllClients()
{
// this is only example of a public method for which the
//clients does have no idea. But your appA, which have created
//a ClientRegistrator object, can invoke it (thats where Marshal
//approache becomes handy.

foreach (IRegistered client in this.clients)
{
client.CloseApp();
//also, you can make this call async or
//[OneWay], as it will never return (client will be
//closed

//remove the client from the list
}
}
}

----------------------------

app1,2..N

public class RemoteComunicator : MBR, IRegistered
{
//holds a ref to the main form
private Form myhostform;

//ctor
public RemoteComunicator(Form myhosthorm)
{
this.myhostform = myhostform;
}

public int GetSomeFormData()
{
return this.myhostform.Field1;
}

public void ExecuteSomething()
{
//manipulate the host form as you wish
//use Invoke !!!!
if (this.myhostform.InvokeRequired)
this.myhostform.Invoke .....
else
this.myhostform.Method1();
}

public void CloseApp()
{
//something like prev method, but call form.Close
}


}


class MainForm : Form
{
....
private RemoteComunicator comunicator;
....
....
//now in Onload event or wherever you need
//create the remoting channels, and get refference to
//the remote server IRegistrator

void RegisterWithServer()
{
//create channels
/get rem reference
IRegistrator server = Activator....

//now create our comunicator and register it to the server
this.comunicator = new RemoteComunicator(this);

server.RegisterClient(this.comunicator);
}
}


So, thats the idea, go for it :)

Hope that helps
Sunny

shonend

6/6/2004 5:57:00 PM

0

Hi Sunny,

thank you so much for your detailed response. The solution you
provided is very helpful in a way that now I have better understanding
of .NET Remoting and the approach I should take when trying to
implement these technologies. Still didn''t get it to work, allthough I
feel I''m very close. I think I need some more of the sample code on
the server part.

Here''s what I tried...

With regards to your code (if you take a look at your previous
message), I implemented Sub ShutDownClient() in the IRegistered (what
you called "CloseApp"), and my server''s remoting object RemoteListener
(what you called "ClientRegistrator") has a method to shutdown
particular client, not all of them.
So part of the code in the RemoteListener looks like this:

Public Sub RemoteShutDown(ByVal clientIdx As Integer)
If clients.Count > clientIdx Then
clients(clientIdx).ShutDownClient()
clients.Remove(clients(clientIdx))
End If
End Sub

Now the big problem for me is the server Form code. You said server
should create the object and have the access to it, also to use
Marshal method, but I don''t really know how to do that. If you
remember in my original post, that was one of the issues - how can
server instantiate the remoting object?

I tried two things:

1) Like you said, I tried with Marshal method. This is the part of the
server''s Form_Load handling method:

'' register and startup the remoting service
Dim ServerChannel As New TcpChannel(5050)
ChannelServices.RegisterChannel(ServerChannel)
RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("RemotingServer.RemotingListener,RemotingServer"),
"RemoteService", WellKnownObjectMode.Singleton)

....

MyRemoter = New RemotingListener()
RemotingServices.Marshal(MyRemoter)


So MyRemoter is the server''s local instance of the remoting object.
But this instance doesn''t "catch" any registered clients. What I mean,
when client calls:
server.RegisterClient(...)
on it''s startup (refer to your code - client form_load part), this
instance, MyRemoter is not affected; MyRemoter''s "clients" ArrayList
does not add another client. So I can''t use MyRemoter to call
ShutDownClient().

2) I tried to create an object on the server side, the same way the
client does, by using Activator.GetObject... This seems so awkward to
me, but somewhere on the groups I found that someone suggested that as
a solution.

So the server code now looks like this:

Dim ServerChannel As New TcpChannel(5050)
ChannelServices.RegisterChannel(ServerChannel)
RemotingConfiguration.RegisterWellKnownServiceType(
Type.GetType("RemotingServer.RemotingListener,RemotingServer"),
"RemoteService", WellKnownObjectMode.Singleton)

MyRemoter = CType(Activator.GetObject(GetType(RemotingListener),
_
"tcp://localhost:5050/RemoteService"), RemotingListener)


Strangely, this works (well, almost). I am getting something here, the
clients are registered and accessible in the MyRemoter. But the main
method I want to execute, doesn''t work. When I try:

MyRemoter.RemoteShutDown(index)

I get the following exception:
-------------------------------------
An unhandled exception of type ''System.MissingMemberException''
occurred in mscorlib.dll

Additional information: Public member ''ShutDownClient'' on type
''MarshalByRefObject'' not found.
------------------------------------

ShutDownClient() IS defined in the RemoteCommunicator class (client''s
MBR), as a matter of fact, here is the part of that class code:

<System.Runtime.Remoting.Messaging.OneWay()> _
Public Sub ShutDownClient() Implements IRegistered.ShutDownClient
MyHostForm.Invoke(MyHostForm.executor)
End Sub

(As you see, I applied an attribute to make this call "OneWay", like
you advised. "executor" is the delegate defined in the client''s Form
code)

Do you happend to have any ideas here? Or did I go all wrong with the
server part?

Thanks much...

Shone

Sunny

6/7/2004 3:27:00 PM

0

Hi Shone,


In article <38885f3a.0406060956.4c870a11@posting.google.com>,
shonend@yahoo.com says...

<snip>
>
> '' register and startup the remoting service
> Dim ServerChannel As New TcpChannel(5050)
> ChannelServices.RegisterChannel(ServerChannel)
> RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("RemotingServer.RemotingListener,RemotingServer"),
> "RemoteService", WellKnownObjectMode.Singleton)
>
> ....
>
> MyRemoter = New RemotingListener()
> RemotingServices.Marshal(MyRemoter)
>
>
> So MyRemoter is the server''s local instance of the remoting object.
> But this instance doesn''t "catch" any registered clients. What I mean,
> when client calls:
> server.RegisterClient(...)
> on it''s startup (refer to your code - client form_load part), this
> instance, MyRemoter is not affected; MyRemoter''s "clients" ArrayList
> does not add another client. So I can''t use MyRemoter to call
> ShutDownClient().
>
> 2) I tried to create an object on the server side, the same way the
> client does, by using Activator.GetObject... This seems so awkward to
> me, but somewhere on the groups I found that someone suggested that as
> a solution.
>
> So the server code now looks like this:
>
> Dim ServerChannel As New TcpChannel(5050)
> ChannelServices.RegisterChannel(ServerChannel)
> RemotingConfiguration.RegisterWellKnownServiceType(
> Type.GetType("RemotingServer.RemotingListener,RemotingServer"),
> "RemoteService", WellKnownObjectMode.Singleton)
>
> MyRemoter = CType(Activator.GetObject(GetType(RemotingListener),
> _
> "tcp://localhost:5050/RemoteService"), RemotingListener)
>

You do not need to call RegisterWellKnown.... Marshal should do the
trick. Then you do not have to remote to yourself as you do in the
second attempt.

Your server code should look like (C#):

MyRemoter = new RemotingListener();
RemotingServices.Marshal(MyRemoter, "RemoteService", typeof
(IRegistrator));


>
> Strangely, this works (well, almost). I am getting something here, the
> clients are registered and accessible in the MyRemoter. But the main
> method I want to execute, doesn''t work. When I try:
>
> MyRemoter.RemoteShutDown(index)
>
> I get the following exception:
> -------------------------------------
> An unhandled exception of type ''System.MissingMemberException''
> occurred in mscorlib.dll
>
> Additional information: Public member ''ShutDownClient'' on type
> ''MarshalByRefObject'' not found.
> ------------------------------------
>
> ShutDownClient() IS defined in the RemoteCommunicator class (client''s
> MBR), as a matter of fact, here is the part of that class code:
>
> <System.Runtime.Remoting.Messaging.OneWay()> _
> Public Sub ShutDownClient() Implements IRegistered.ShutDownClient
> MyHostForm.Invoke(MyHostForm.executor)
> End Sub
>
> (As you see, I applied an attribute to make this call "OneWay", like
> you advised. "executor" is the delegate defined in the client''s Form
> code)
>
> Do you happend to have any ideas here? Or did I go all wrong with the
> server part?

how is clients defined? is it Array of IRegistered, or it is Array of
MBR? As the exception shows most probably it is the latest. So it tries
to invoke a method on MBR, not on IRegistered, and fails, as MBR does
not has such a method. You may change the type of the array elements or
cast to IRegistered before you call the ShutDownClient method.

>
> Thanks much...
>
> Shone
>

Sunny

shonend

6/8/2004 3:24:00 PM

0

Excellent! Brilliant! It''s working!!!

Thanks so much!

fyi...

> You do not need to call RegisterWellKnown.... Marshal should do the
> trick. Then you do not have to remote to yourself as you do in the
> second attempt.
>
> Your server code should look like (C#):
>
> MyRemoter = new RemotingListener();
> RemotingServices.Marshal(MyRemoter, "RemoteService", typeof
> (IRegistrator));
>

.... yes, this Marshal overload was what I needed. Even the overload
without 3rd parameter works.


> how is clients defined? is it Array of IRegistered, or it is Array of
> MBR? As the exception shows most probably it is the latest. So it tries
> to invoke a method on MBR, not on IRegistered, and fails, as MBR does
> not has such a method. You may change the type of the array elements or
> cast to IRegistered before you call the ShutDownClient method.

.... "clients" is defined as simple ArrayList. Only later when I add
members to the collection (in the RemotingListener.RegisterClient),
they are added as IRegistered objects:
Public Sub RegisterClient(ByVal client As IRegistered) Implements
IRegistrator.RegisterClient
Me.clients.Add(client)
End Sub

I thought the late binding will do it''s part when I call
clients(i).ShutDownClient()
, but it seems it doesn''t work for MBR. I don''t know why and I don''t
care. I tried casting and it works like a charm.

Even after this my experimental application didn''t quite succeed, but
the errors I got, I was able to solve and get it to work. I will
describe those here just in case it might be helpful to someone...


1) for the methods that will not return, like ShutDownClient, it''s not
sufficient to put the "oneWay" attribute on the client side (in the
client''s MBR class", because server has no idea about this class.
Server knows about IRegistered, so this is where you have to put the
"OneWay":

Public Interface IRegistered
...
<System.Runtime.Remoting.Messaging.OneWay()> Sub ShutDownClient()
...
End Interface

2) not essential, since it''s just a cleanup, but...
When you successfully execute ShutDownClient, and wont to remove the
IRegistered clent from the ArrayList, this won''t work:

Public Sub RemoteShutDown(ByVal clientIdx As Integer)
If clients.Count > clientIdx Then
CType(clients(clientIdx), IRegistered).ShutDownClient()
clients.Remove(clients(clientIdx)) '' *** this will raise
excpt.
End If
End Sub

The exception raised on marked line is:
----------------------------------------
An unhandled exception of type ''System.Net.Sockets.SocketException''
occurred in mscorlib.dll

Additional information: No connection could be made because the target
machine actively refused it
----------------------------------------

And the problem is obviously that once the client is shut down, you
can;t make any reference to clients(i), since this is the MBR that is
running on the client. Not even the reference to remove it from the
ArrayList.
Luckilly, you cak go with the:
clients.RemoveAt(clientIdx)
and it will be removed.

3) Initially I defined and registered the channels the most simplest
way:

on the server:
Dim ServerChannel As New TcpChannel(5050)
ChannelServices.RegisterChannel(ServerChannel)
on the client:
Dim cliChannel As New TcpChannel
ChannelServices.RegisterChannel(cliChannel)

But this didn''t work, and threw an exception:
-------------------------------------------
An unhandled exception of type
''System.Runtime.Remoting.RemotingException'' occurred in mscorlib.dll

Additional information: This remoting proxy has no channel sink which
means either the server has no registered server channels that are
listening, or this application has no suitable client channel to talk
to the server.
--------------------------------------------

Obviously you can''t trick the .NET Framework by declaring remoting
server and clients, and hope you can get around with callback and
switching roles. The client must have it''s own port - defining the
channel port on the client side solved the problem, only you have to
make sure that each client pick a unique port #.
However, this raises another question: at this point does it really
matter who is the server and who is the client? I''m thinking to go
back to my original design and make appA client and app1,
app2,....appN remote servers. At first I thought this was bad because
I might drain the system resources by opening so many ports/channels,
but since I have to do it anyway.... ?
Also with the original design, there will be no need for callback and
creating/registering client''s MBR, because client will always initiate
the communication (sufficient for my actual project needs).
What are your thoughts here?

Thanks again,

Shone