Libreria EmiNBC - Classe NoBlockingCall

Utilizzo della classe

Vediamo come si esegue una chiamata a dei metodi in modalità di No Bloching aiutandoci con il seguente codice autocommentato:

Option Explicit
' Se un oggetto deve utilizzare delle chiamate che ritornino il più velocemente
' possibile indipendentemente da ciò che la chiamata debba fare a monte,
' è necessario utilizzare delle chiamate non bloccanti che poi
' eseguano in background le chiamate alle funzioni effettivamente da chiamare.
' La libreria EmiNBC gestisce appunto tale problematica.
' Affichè un oggetto posso utilizzare la classe NoBlockingCall è necessario che
' l'oggetto implementi  l'interfaccia INoBlockingCall che, tramite il suo metodo
'
' Private Sub INoBlockingCall_MethodCalled(ByVal NBCObj As EmiNBC.NoBlockingCall)
'
' gestisce una chiamata di callback che viene utilizzata per la chiamata della
' funzione effettiva da chiamare.

Implements INoBlockingCall

' E' poi necessario definire, a livello di modulo un oggetto di tipo NoBlockingCall.
' E' possibile gestire piu' oggetti di tale tipo ma e' conveniente generare una
' array di tali oggetti per ogni chiamata NB (No Blocking) che si desidera gestire
' BNC(0) gestirà la chiamata NB alla funzione Test1
' BNC(1) gestirà la chiamata NB alla funzione Test2
Dim BNC(0 To 1) As NoBlockingCall
 

' Chiamiamo la sub Test1 in modalità NB

Private Sub Command1_Click()
  ' Impostiamo a Nothing l'oggetto NB delegato 
  ' a gestire la chiamata alla funzione
  ' Test1. Questa cosa è utile qualora avessimo
  ' chiamato command1_click velocemente
  ' (molto velocemente :) una seconda volta mentre 
  ' la prima chiamata NB ancora
  ' non ha chiamato la Test1. BNC(0) infatti NON 
  ' muore finche non ha fatto il suo dovere
  ' chiamando la Test1. Siamo così sicuri che, 
  ' una volta superata questa riga, la
  ' precedente chiamata a BNC(0) ha compiuto 
  ' completamente il suo lavoro, ossia ha
  ' già inoltrato al client la volonta di chiamare Test1
  Set BNC(0) = Nothing
  ' Inizializzo l'oggetto di NB
  Set BNC(0) = New NoBlockingCall
  With BNC(0)
  ' Imposto la funzione da chiamare in modalità NB.
  ' La funzione da chiamare è "Test1"
    .MethodNameToCall = "Test1"
  ' Associo una chiave univoca di tipo Long a questo oggetto e, guarda caso,
  ' scelgo l'indice dell'array a cui l'oggetto appartiene. Questo sarà
  ' utile nella chiamata di callback per distruggere l'oggetto
    .Index = 0
  ' Gli associo me stesso per usufruire della chiamata di callback tramite
  ' l'interfaccia da me implementata INoBlockingCall
    Set .Parent = Me
  ' Lancio il metodo in modalità NB
    .CallMethod
  ' Esco. Qui il metodo "Test1" NON è stato ancora eseguito. E' questo il bello
  ' delle chiamate NB: Test1 può anche essere una chiamata che durerà un tempo molto
  ' lungo per essere eseguita ma intanto il flusso del programma è gia uscito
  ' dal metodo Command1_Click
  End With
End Sub


Private Sub Command2_Click()
  Dim i&
  ' Chiamiamo il metodo Test2 in modalità NB e, per giunta, chiamiamolo dieci volte
  ' di fila di modo da verificare che non ci siano problemi qualora cercassimo di
  ' terminare un oggetto che non ha ancora eseguito la chiamata di callback.
  ' In sostanza, vogliamo verificare che Test2 venga lanciato dieci volte e non
  ' magari una volta sola e che non ci siano buchi nella libreria che potrebbero
  ' causare errori di runtime
  ' En-passan, Test2 ha bisogno di due parametri da passare. vedremo 
  ' così come si faccia questo.
  For i& = 1 To 10
    CallTest2WithNoBlockingMethod i&, "Pippo"
  Next
End Sub

Private Sub CallTest2WithNoBlockingMethod(ByVal FirstArgument As Long, _
  ByVal SecondArgument As String)
  ' Qui ora è veramente utile impostare a 
  ' Nothing BNC(1). La seconda chiamata a questa
  ' funzione avviene sicuramente prima che BNC(1) 
  ' chiamata in precedenza abbia utilizzato
  ' l'interfaccia di callback 
  ' INoBlockingCall_MethodCalled(ByVal NBCObj As EmiNBC.NoBlockingCall)
  ' per notificarmi che è stato espresso il 
  ' desiderio di lanciare la funzione "Test2"
  ' Se si commenta tale riga si ottiene o un errore 
  ' di runtime o si esegue l'esecuzione
  ' di "Test2" UNA SOLA VOLTA alla decima chiamata, 
  ' quando finalmente le acque si calmano.
  ' Così facendo, questa riga non ritorna finchè BNC(1) 
  ' non ha compiuto tutto il suo lavoro.
  ' Come effetto si ha però che, con questa libreria di 
  ' NB non è possibile eseguire una funzione
  ' più di una volta ogni 10-20 millisecondi. 
  ' E' uno svantaggio tanto grosso ? :)
  Set BNC(1) = Nothing
  Set BNC(1) = New NoBlockingCall
  With BNC(1)
    .MethodNameToCall = "Test2"
  ' Aggiungiamo qui i valori da passare alla funzione Test2. 
  ' Test2 ha bisogno di due parametri
  '
  ' Private Sub Test2(ByVal Index As Long, ByVal Key As String)
  '
  ' Passiamo i valori corrispondenti stando attenti a inserirli nell'ordine
  ' in cui vengono richiesti da Test2. Prima il primo e poi il secondo
    .AddParameter FirstArgument
    .AddParameter SecondArgument
    .Index = 1
    Set .Parent = Me
    .CallMethod
  End With
End Sub

Private Sub Test1()
  ' Viene eseguita la funzione Test1
  Debug.Print "Test1 Sub Called"
End Sub

Private Sub Test2(ByVal Index As Long, ByVal Key As String)
  ' Viene eseguita la funzione Test2
  Debug.Print "Test2 Sub Called with arg1=" & Index & " and arg2=" & Key
End Sub

Private Sub INoBlockingCall_MethodCalled(ByVal NBCObj As EmiNBC.NoBlockingCall)
  ' Definisco una variabile array di tipo variant 
  ' qualora la chiamata alla funzione NB
  ' necessiti di parametri aggiuntivi che saranno 
  ' stati passati in precedenza
  Dim C() As Variant
  With NBCObj
    ' A seconda del metodo da chiamare eseguo una funzione differente
    Select Case .MethodNameToCall
    Case "Test1"
    ' Se il metodo e' Test1 lo eseguo
      Call Test1
    Case "Test2"
    ' Se il metodo è Test2, che ha bisogno di due parametri, allora estraggo 
    ' i due parametri e poi eseguo il metodo passandogli i due parametri
      C = .ParametersArray
      Call Test2(C(0), C(1))
    End Select
    ' Grazie al fatto che la proprieta Index dell'oggetto NB corrisponde proprio
    ' all'indice dell'array a cui appartiene NBC posso distruggere semplicemente
    ' l'oggetto relativo a questa chiamata di callback
    Set BNC(.Index) = Nothing
  End With
End Sub