Tutti coloro che si stanno cimentando con l’utilizzo delle Speech API di Windows Phone 8, avranno notato, probailmente con qualche improperio, che le eccezioni generate dalle classi utilizzate non sono sempre “parlanti”.
Per fare un esempio, proviamo a utilizzare un Voice Command Definition file (per un’ottima spiegazione riguardo l’utilizzo dei VCD vi rimando ai posts Michele Locuratolo) non correttamente formato.
Potremmo, ad esempio, eliminare il CommandName in un Command del CommandSet:
- <?xml version="1.0" encoding="utf-8"?>
- <VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.0">
- <CommandSet xml:lang="en-US">
- <CommandPrefix>Contoso Rodeo</CommandPrefix>
- <Example> play a new game </Example>
- <Command >
- <Example> play a new game </Example>
- <ListenFor> [and] play [a] new game </ListenFor>
- <ListenFor> [and] start [a] new game </ListenFor>
- <Feedback> Starting a new game... </Feedback>
- <Navigate />
- </Command>
- <PhraseList Label="number">
- <Item> one </Item>
- <Item> two </Item>
- <Item> three </Item>
- </PhraseList>
- </CommandSet>
- </VoiceCommands>
In questo caso, nel momento in cui cerchiamo di installare il file tramite il VoiceCommandService otteniamo:
Come possiamo vedere, l’eccezione che otteniamo è una System.Exception e, per capire di cosa si tratta, dobbiamo prendere l’HResult (in questo caso 0x80045560) e, armati di santa pazienza e della funzione Search di Internet Explorer, andare alla pagina MSDN “Handling errors in speech apps for Windows Phone” e scoprire che:
In alcuni casi siamo fortunati e abbiamo delle eccezioni specifiche ma nella stragrande maggioranza dei casi siamo di fronte ad delle System.Exception con il solo HResult.
I codici di errore disponibili (SPERR_VC_INVALID_COMMAND_ATTRIBUTES in questo caso) sono un centinaio e sarebbe carino poter avere, all’interno dela nostra app, delle costanti che ci permettano di eseguire facilmente il test per capire cosa si è verificato.
Per fare questo possiamo utilizzare la semplice classe:
- Public NotInheritable Class SpeechHResults
- Private Sub New()
- End Sub
- Public Const SPERR_RECOERROR_NOSPEECH As Integer = &H80045501
- Public Const SPERR_RECOERROR_NOMATCH As Integer = &H80045502
- Public Const SPERR_RECOERROR_NETWORK_TIMEOUT As Integer = &H80045503
- Public Const SPERR_RECOERROR_NETWORK_UNAVAILABLE As Integer = &H80045504
- Public Const SPERR_GRAMMAR_DUP_NAME As Integer = &H80045505
- Public Const SPERR_GRAMMARSET_CANT_ADD As Integer = &H80045506
- Public Const SPERR_GRAMMARSET_LOAD_CANCELED As Integer = &H80045507
- Public Const SPERR_SYSTEM_CALL_INTERRUPTED As Integer = &H80045508
- Public Const SPERR_SPEECH_PRIVACY_POLICY_NOT_ACCEPTED As Integer = &H80045509
- Public Const SPERR_AUDIO_LIMIT_EXCEEDED As Integer = &H8004550A
- Public Const SPERR_NO_RULES_TO_ACTIVATE As Integer = &H8004550B
- Public Const SPERR_WINRT_INTERNAL_ERROR As Integer = &H800455A0
- Public Const SPERR_WINRT_ALREADY_IN_LEX As Integer = &H800455A1
- Public Const SPERR_WINRT_NOT_IN_LEX As Integer = &H800455A2
- Public Const SPERR_WINRT_RULE_NOT_DYNAMIC As Integer = &H800455A3
- Public Const SPERR_WINRT_DUPLICATE_RULE_NAME As Integer = &H800455A4
- Public Const SPERR_WINRT_DUPLICATE_RESOURCE_NAME As Integer = &H800455A5
- Public Const SPERR_WINRT_TOO_MANY_GRAMMARS As Integer = &H800455A6
- Public Const SPERR_WINRT_CIRCULAR_REFERENCE As Integer = &H800455A7
- Public Const SPERR_WINRT_INVALID_IMPORT As Integer = &H800455A8
- Public Const SPERR_WINRT_RULE_NAME_ID_CONFLICT As Integer = &H800455A9
- Public Const SPERR_WINRT_NO_RULES As Integer = &H800455AA
- Public Const SPERR_WINRT_CIRCULAR_RULE_REF As Integer = &H800455AB
- Public Const SPERR_WINRT_NOT_DYNAMIC_GRAMMAR As Integer = &H800455AC
- Public Const SPERR_WINRT_AMBIGUOUS_PROPERTY As Integer = &H800455AD
- Public Const SPERR_WINRT_EXPORT_DYNAMIC_RULE As Integer = &H800455AE
- Public Const SPERR_WINRT_WORDFORMAT_ERROR As Integer = &H800455AF
- Public Const SPERR_WINRT_LANGID_MISMATCH As Integer = &H800455B1
- Public Const SPERR_WINRT_NO_WORD_PRONUNCIATION As Integer = &H800455B2
- Public Const SPERR_WINRT_SML_GENERATION_FAIL As Integer = &H800455B3
- Public Const SPERR_WINRT_ROOTRULE_ALREADY_DEFINED As Integer = &H800455B4
- Public Const SPERR_WINRT_UNSUPPORTED_PHONEME As Integer = &H800455B5
- Public Const SPERR_WINRT_PHONEME_CONVERSION As Integer = &H800455B6
- Public Const SPERR_WINRT_NO_RULES_TO_ACTIVATE As Integer = &H800455B7
- Public Const SPERR_WINRT_LEX_INVALID_DATA As Integer = &H800455B8
- Public Const SPERR_WINRT_CFG_INVALID_DATA As Integer = &H800455B9
- Public Const SPERR_WINRT_SISR_ATTRIBUTES_NOT_ALLOWED As Integer = &H800455BA
- Public Const SPERR_WINRT_SISR_MIXED_NOT_ALLOWED As Integer = &H800455BB
- Public Const SPERR_WINRT_UNSUPPORTED_LANG As Integer = &H800455BC
- Public Const SPERR_WINRT_STRING_TOO_LONG As Integer = &H800455BD
- Public Const SPERR_WINRT_STRING_EMPTY As Integer = &H800455BE
- Public Const SPERR_WINRT_NO_MORE_ITEMS As Integer = &H800455BF
- Public Const SPERR_VC_EXPECTED_VOICE_COMMANDS_ELEMENT As Integer = &H80045550
- Public Const SPERR_VC_INVALID_VOICE_COMMANDS_ELEMENT As Integer = &H80045551
- Public Const SPERR_VC_EXPECTED_XML_DECLARATION As Integer = &H80045552
- Public Const SPERR_VC_INVALID_XML_ATTRIBUTES As Integer = &H80045553
- Public Const SPERR_VC_INVALID_XML_VERSION As Integer = &H80045554
- Public Const SPERR_VC_INVALID_XML_ENCODING As Integer = &H80045555
- Public Const SPERR_VC_INVALID_NAMESPACE As Integer = &H80045556
- Public Const SPERR_VC_EXPECTED_COMMANDSET As Integer = &H80045557
- Public Const SPERR_VC_INVALID_COMMANDSET As Integer = &H80045558
- Public Const SPERR_VC_INVALID_COMMANDSET_ATTRIBUTES As Integer = &H80045559
- Public Const SPERR_VC_EXPECTED_PREFIX_OR_EXAMPLE As Integer = &H8004555A
- Public Const SPERR_VC_INVALID_PREFIX As Integer = &H8004555B
- Public Const SPERR_VC_EXPECTED_COMMANDSET_EXAMPLE As Integer = &H8004555C
- Public Const SPERR_VC_INVALID_COMMANDSET_EXAMPLE As Integer = &H8004555D
- Public Const SPERR_VC_EXPECTED_COMMAND As Integer = &H8004555E
- Public Const SPERR_VC_INVALID_COMMAND As Integer = &H8004555F
- Public Const SPERR_VC_INVALID_COMMAND_ATTRIBUTES As Integer = &H80045560
- Public Const SPERR_VC_EXPECTED_COMMAND_OR_PHRASELIST As Integer = &H80045561
- Public Const SPERR_VC_EXPECTED_COMMAND_EXAMPLE As Integer = &H80045562
- Public Const SPERR_VC_INVALID_COMMAND_EXAMPLE As Integer = &H80045563
- Public Const SPERR_VC_EXPECTED_LISTENFOR As Integer = &H80045564
- Public Const SPERR_VC_INVALID_LISTENFOR As Integer = &H80045565
- Public Const SPERR_VC_EXPECTED_FEEDBACK As Integer = &H80045566
- Public Const SPERR_VC_INVALID_FEEDBACK As Integer = &H80045567
- Public Const SPERR_VC_EXPECTED_NAVIGATE As Integer = &H80045568
- Public Const SPERR_VC_INVALID_NAVIGATE As Integer = &H80045569
- Public Const SPERR_VC_INVALID_NAVIGATE_ATTRIBUTES As Integer = &H8004556A
- Public Const SPERR_VC_EXPECTED_PHRASELIST As Integer = &H8004556B
- Public Const SPERR_VC_INVALID_PHRASELIST As Integer = &H8004556C
- Public Const SPERR_VC_INVALID_PHRASELIST_ATTRIBUTES As Integer = &H8004556D
- Public Const SPERR_VC_EXPECTED_PHRASELIST_ITEM As Integer = &H8004556E
- Public Const SPERR_VC_INVALID_PHRASELIST_ITEM As Integer = &H8004556F
- Public Const SPERR_VC_TOO_MANY_PHRASELIST_ITEMS As Integer = &H80045570
- Public Const SPERR_VC_INVALID_NODE As Integer = &H80045571
- Public Const SPERR_VC_TOO_MANY_PHRASELISTS As Integer = &H80045572
- Public Const SPERR_VC_UNEXPECTED_END_OF_FILE As Integer = &H80045573
- Public Const SPERR_VC_EXPECTED_END_OF_FILE As Integer = &H80045574
- Public Const SPERR_VC_INVALID_STRING_LENGTH As Integer = &H80045575
- Public Const SPERR_VC_INVALID_XML_DOCUMENT As Integer = &H80045576
- Public Const SPERR_VC_INVALID_FEEDBACK_LABEL_NO_SEMANTICS As Integer = &H80045577
- Public Const SPERR_VC_INVALID_PATTERN_REFERENCED_LABEL_NOT_FOUND As Integer = &H80045578
- Public Const SPERR_VC_INVALID_PHRASELIST_NEVER_USED As Integer = &H80045579
- Public Const SPERR_VC_INVALID_FEEDBACK_LABEL_NOT_IN_ALL_LISTENFORS As Integer = &H80045580
- Public Const SPERR_VC_TOO_MANY_LISTENFORS As Integer = &H80045581
- Public Const SPERR_VC_TOO_MANY_COMMANDSETS As Integer = &H80045582
- Public Const SPERR_VC_TOO_MANY_COMMANDS As Integer = &H80045583
- Public Const SPERR_VC_INVALID_COMMANDSET_LANGUAGE As Integer = &H80045584
- Public Const SPERR_VC_DUPLICATE_COMMANDSET_LANGUAGE As Integer = &H80045585
- Public Shared Function GetCodeByHResult(hresult As Integer) As String
- Dim meType = GetType(SpeechHResults)
- Dim fieldName = (From f In meType.GetFields(Reflection.BindingFlags.Public Or Reflection.BindingFlags.Static)
- Where f.FieldType Is GetType(System.Int32) _
- AndAlso CType(f.GetValue(Nothing), Integer) = hresult
- Select f.Name).FirstOrDefault()
- Return fieldName
- End Function
- End Class
Utilizzando questa classe possiamo scrivere:
- Private Async Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
- Try
- Await VoiceCommandService.InstallCommandSetsFromFileAsync(New Uri("ms-appx:///VoiceCommandDefinition.xml",
- UriKind.RelativeOrAbsolute))
- Catch ex As Exception
- If ex.HResult = SpeechHResults.SPERR_VC_INVALID_COMMAND_ATTRIBUTES Then
- MessageBox.Show("The Command element does not specify a Name attribute; or, any attribute within the Command element is invalid.")
- End If
- End Try
- End Sub
E’ evidente che l’ideale sarebbe avere un toolkit che, a partire dall’eccezione generica generasse un eccezione specifica che contenesse il codice di errore, l’HResult e il messaggio che abbiamo nella sopra citata pagina (magari anche tradotto nella lingua dell’app).
Esiste un piccolissimo toolkit da me realizzato (sono veramente 4 classi in croce), chiamato Speech Exception Toolkit e che potete trovare all’indirizzo https://set.codeplex.com/.
Attualmente supporta le descrizioni delle eccezioni (tradotte automaticamente con il Multilingual App Toolkit di Microsoft) in diverse lingue tra cui l’italiano.
E’ possibile utilizzare NuGet per referenziare il package: https://www.nuget.org/packages/SpeechExceptionsToolkit/
Grazie a questo stupidissimo toolkit possiamo scrivere:
- Private Async Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
- Try
- Await VoiceCommandService.InstallCommandSetsFromFileAsync(New Uri("ms-appx:///VoiceCommandDefinition.xml",
- UriKind.RelativeOrAbsolute))
- Catch ex As Exception
- Dim speechException = ExceptionFactory.CreateException(ex)
- MessageBox.Show(speechException.Message)
- End Try
- End Sub
La ExceptionFactory permette di creare, a partire dall’eccezione generica, una eccezione tipizzata che contiene tutte le informazioni utili a capire cosa è successo. La proprietà Message contiene la descrizione (quella presente alla pagina MSDN) tradotta nella lingua del thread corrente.
Se l’eccezione non è un eccezione relativa alle Spech API, l’ExceptionFactory la lascia inalterata.
Il toolkit è distribuito con licenza Ms-PL, quindi potete utilizzarlo senza alcun problema e modificarlo come volete.
Commenti