Start Listening
Riprendiamo la figura che abbiamo lasciato nella prima parte di questo tutorial:
1) Al click di un bottone presente sulla form, si genererà un altro thread che si occuperà di rimanere in ascolto delle chiamate provenienti dalla parte unmanaged. Queste chiamate indicheranno che un certo evento è successo nella parte unmanaged e dovremo quindi informare l'utente dell'interfaccia
2) Il "Listener thread" comunicherà al suo omologo unmanaged un delegate. In questo delegate c'è il nome di un metodo che sarà successivamente chiamato in corrispondenza dell'evento atteso. Per effettuare questa comunicazione si utilizza la tecnica del Platform Invoke. Un delegate sarà interpretato nella parte unmanaged come un puntatore a funzione.
3) Adesso siamo nella DLL non gestita. il Listener unmanaged crea un altro thread e proprio in questo thread va inserita tutta la logica relativa all'ascolto degli eventi a basso livello. Qui per esempio potremmo utilizzare un driver della porta seriale, oppure possiamo aprire una socket per rimanere in ascolto su una certa porta secondo un protocollo proprietario.
4) Appena accade una certa condizione, il thread comunica all'unmanaged thread che è accaduto quello che ci aspettavamo. E' il caso di avvertire la parte managed.
5) Si invoca il metodo che ci era stato passato nello step 2. Qui passiamo due parametri alla parte managed: un intero e una stringa. Dobbiamo fare qui sempre molta attenzione affinchè le cose tornino in maniera corretta visto che il marshalling dei parametri non è mai una cosa semplice e soprattutto immediata. Il metodo che invochiamo NON può aggiornare lui l'interfaccia grafica. Cioè non posso inserire i valori dei paramentri provenienti dalla parte unmanaged direttamente nelle textbox, per esempio, della GUI. Devo delegare la cosa ad un'altra classe (chiamata "ControlInvoke") di cui parleremo più avanti
6)A questo punto non rimane che invocare il metodi di una particolare classe che penserà lei a invocare il metodo della classe Form (passandogli i parametri provenienti dalla parte unmanaged) deputato all'aggiornamento delle textbox. Questo metodo appartenendo alla classe Form, quindi stesso thread della form, può aggiornare la GUI. E' una questione di "apartment".
Creazione del "Listener thread"
Vediamo come si presenta innanzitutto la windows form:
Al click del bottone creiamo il thread gestito:
Il nuovo thread eseguirà il metodo ListenCall() dentro il quale faremo una chiamata alla DLL tramite Platform Invoke. In questa chiamata come input passiamo il nome del metodo di callback (ReceivedCall) che vedremo più avanti. Vediamo come abbiamo definito la Classe NativeClass contenente i metodi corrispondenti alle funzioni esposte dalla DLL:
Buona norma è quella di racchiudere in un unica classe tutte le dichiarazioni Platform Invoke, come è stato fatto qui con la classe NativeClass. Come vediamo è stato dichiarato prima un delegate che sarà l'argomento passato alla parte unmanaged. Questo delegate contiene la firma di un metodo che sarà invocato successivamente al verificarsi dell'evento atteso. Da qui si deduce cha dalla parte unmanaged ci aspettiamo un intero e una stringa. La classe ListenState è un oggetto sul quale ci si sincronizza tra i thread managed (in questo caso ne abbiamo uno solo) per aggiornare eventuali variabili globali all'applicazione. Dalla parte unmanaged ci sarà quindi la funzione ReadyToReceiveCall esposta nella DLL che avrà questa firma:
Come risulta chiaro la funzione accetta un puntatore a funzione. Quest'ultimo sarà il metodo managed invocato successivamente.
Nella prossima sessione vedremo cosa succede nella DLL e come dobbiamo impostare il codice nativo C++