sabato 26 gennaio 2008

Net Remoting in un Windows Service con IPC

Net Remoting…se ne parla poco, ma recentemente l'ho usato per un mio applicativo e devo dire che mi è risultato molto utile ed ha permesso di risolvere parecchi problemi progettuali: in sintensi, si doveva creare un Windows service che compiva determinate operazioni ad intervalli di tempo regolari ma anche che fossero lanciate da un applicativo esterno. La soluzione più semplice è stata implementare un semplice applicativo in Net remoting con Visual Studio 2005 e Net 2.0: come breve introduzione, ricordo che il Net remoting non è altro che un metodo di comunicazione tra applicazioni attraverso canali aperti all'interno della stessa macchina (come l' IPC) o tra la macchina dove risiede l'applicativo server e i vari client che vogliono interagire con queste funzionalità via internet o via rete LAN (quindi attraverso il protocollo TCP e HTTP). Dato che la nostra applicazione sarà un Windows service che dovrà girare su un PC locale utilizzeremo il canale IPC, evitando, quindi, di aprire porte indisiderate che potrebbero rendere vulnerabile il sistema operativo.

Andiamo ad analizzare ora in dettaglio il metodo più semplice, efficace ma soprattutto veloce per sviluppare il nostro applicativo. Per prima cosa, creeremo una nuova soluzione con Visual Studio 2005, al proprio interno creeremo 3 progetti che elenco:

  1. Class Library Interface: in questa class library creeremo la nostra interfaccia per rendere pubblici i metodi a noi interessati attraverso Net Remoting. Per fare ciò, creare il nuovo progetto, cancellare la classe di default creata ed aggiungere un nuovo oggetto interfaccia.Il codice dovrà essere come segue:
    Namespace IRemotingObject
        Public Interface IRemoteObject
            Sub Task()
        End Interface
    End Namespace

  2. Class Library MarshalByRefObject: in questa class library svilupperemo l'interfaccia precedentemente creata e inoltre dovremo ereditare MarshalByRefObject dalla reference System.Runtime.Remoting. Difatti, solo ereditandolo possiamo rendere una classe "ereditabile".
    In ogni caso andremo a sviluppare il nostro applicativo come segue:

    Namespace RemotingObject
        Public Class RemoteObject
            Inherits MarshalByRefObject
            Implements IRemotingObject.IRemoteObject

            Public Sub New()
            End Sub

            Public Sub ThreadProc ()
            '...
            End Sub        

Public Sub Task() Implements IRemotingObject.IRemoteObject.Task             '....
        End Sub
    End Class
End Namespace

  1. Windows service: tutti i dettagli per la realizzazione di un windows service magari li metterò in un altro post, ora basta mettere le basi. Creato il nostro progetto, entriamo nel design del componente, premiamo tasto destro all'interno e vedremo che tra le varie opzioni ci sarà "Add Installer…": in questo modo, verrà aggiunto al nostro progetto un altro oggetto che ci consentirà di dare la configurazione generale al nostro servizio.
    Prima di tutto, ricordiamoci anche qui di includere tra le referenze il System.Runtime.Remoting e di importare i seguenti naspaces: System.Runtime.Remoting.Channels e System.Runtime.Remoting.Channels.Ipc.
    Il codice del nostro servizio, in ogni caso, sarà il seguente:
    Public Class ServiceComponent
    Private ServiceThread As Thread
    Private srv As New RemotingObject.RemoteObject
    Private ipcchannel as IpcChannel

    Protected Overrides Sub OnStart(ByVal args() As String)
        ServiceThread = New Thread(New ThreadStart(AddressOf srv.ThreadProc))
        ServiceThread.Start()
        ServiceThread.IsBackground = True

    Dim props As New System.Collections.Hashtable()
         props("portName") = " IPCChannelForExample"
        props("exclusiveAddressUse") = False
    props("authorizedGroup") = "Everyone"
    ipcchannel = New IpcChannel(props, Nothing, Nothing)

    ChannelServices.RegisterChannel(ipcchannel, False) RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("RemotingObject.RemoteObject,RemoteObject"),"RemotingServer",WellKnownObjectMode.SingleCall)

         End Sub

Protected Overrides Sub OnStop()
    ChannelServices.UnregisterChannel(ipcchannel)
    ServiceThread.Abort()
End Sub

End Class

E con questi 3 semplici passi (logicamente espandibili a piacimento).

Per concludere, andiamo a vedere come chiamare il nostro metodo "Task" da un altro applicativo collegandoci via IPC al Windows service da, per esempio, un Windows Form.
Prima, però, ricordiamoci anche qui di includere tra le referenze il System.Runtime.Remoting e l'interfaccia creata precedentemente e di importare System.Runtime.Remoting.Channels e System.Runtime.Remoting.Channels.Ipc. Di seguito metto un semplice esempio di chiamata da un pulsante:

Private Sub TransmissionButton_ItemClick(ByVal sender As System.Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles TransmissionButton.ItemClick
Dim ipcCh As New IpcChannel("myClient")
ChannelServices.RegisterChannel(ipcCh, False)

Dim obj As IRemotingObject.IRemoteObject = DirectCast(Activator.GetObject(GetType(IRemotingObject.IRemoteObject), "ipc://IPCChannelForExample/RemotingServer"), IRemotingObject.IRemoteObject)

obj.Task()

ChannelServices.UnregisterChannel(ipcCh)
End Sub

E con questo abbiamo creato il nostro semplice applicativo. Logicamente per la creazione dei vari canali IPC nel servizio è da ricordare che ci sono molte altre opzioni, anche per aumentare il livello di sicurezza, ma penso che il metodo più semplice utilizzo è come l'ho dimostrato: inoltre, impostandolo che deve "impersonalizzare" il gruppo "Everyone", tutti possono accedervi senza problemi di permissioni, problemi che mi son scontrato più volte prima di terminare il progetto.

Nessun commento: