cortana在后台任务中拥有自己的命令
cortana own command in backgroundtask
我正在尝试编写一个 c# 程序来为 cortana 定义自定义命令!
首先我创建一个默认的应用程序并加载 VCD 文件...
我创建了第二个项目 link 到主项目并在其中创建一个后台任务来处理 cortana 请求!
现在 cortana 知道我的命令,但如果我告诉她一些事情,她就会回答 "Es ist etwas schief gelaufen"(英语:"it fails")。
我认为 BackgroundTask 调用不正确。
App.cs
namespace CortanaCommandsStart
{
/// <summary>
/// Stellt das anwendungsspezifische Verhalten bereit, um die Standardanwendungsklasse zu ergänzen.
/// </summary>
sealed partial class App : Application
{
/// <summary>
/// Initialisiert das Singletonanwendungsobjekt. Dies ist die erste Zeile von erstelltem Code
/// und daher das logische Äquivalent von main() bzw. WinMain().
/// </summary>
public App()
{
Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync(Microsoft.ApplicationInsights.WindowsCollectors.Metadata | Microsoft.ApplicationInsights.WindowsCollectors.Session);
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Wird aufgerufen, wenn die Anwendung durch den Endbenutzer normal gestartet wird. Weitere Einstiegspunkte
/// werden z. B. verwendet, wenn die Anwendung gestartet wird, um eine bestimmte Datei zu öffnen.
/// </summary>
/// <param name="e">Details über Startanforderung und -prozess.</param>
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
// App-Initialisierung nicht wiederholen, wenn das Fenster bereits Inhalte enthält.
// Nur sicherstellen, dass das Fenster aktiv ist.
if (rootFrame == null)
{
// Frame erstellen, der als Navigationskontext fungiert und zum Parameter der ersten Seite navigieren
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Zustand von zuvor angehaltener Anwendung laden
}
// Den Frame im aktuellen Fenster platzieren
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// Wenn der Navigationsstapel nicht wiederhergestellt wird, zur ersten Seite navigieren
// und die neue Seite konfigurieren, indem die erforderlichen Informationen als Navigationsparameter
// übergeben werden
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
try {
StorageFile vcdStorageFile = await Package.Current.InstalledLocation.GetFileAsync(@"LEDControll.xml");
await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(vcdStorageFile);
System.Diagnostics.Debug.WriteLine("There was no error registering the Voice Command Definitions");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("There was an error registering the Voice Command Definitions", ex);
}
// Sicherstellen, dass das aktuelle Fenster aktiv ist
Window.Current.Activate();
}
/// <summary>
/// Wird aufgerufen, wenn die Navigation auf eine bestimmte Seite fehlschlägt
/// </summary>
/// <param name="sender">Der Rahmen, bei dem die Navigation fehlgeschlagen ist</param>
/// <param name="e">Details über den Navigationsfehler</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Wird aufgerufen, wenn die Ausführung der Anwendung angehalten wird. Der Anwendungszustand wird gespeichert,
/// ohne zu wissen, ob die Anwendung beendet oder fortgesetzt wird und die Speicherinhalte dabei
/// unbeschädigt bleiben.
/// </summary>
/// <param name="sender">Die Quelle der Anhalteanforderung.</param>
/// <param name="e">Details zur Anhalteanforderung.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Anwendungszustand speichern und alle Hintergrundaktivitäten beenden
deferral.Complete();
}
}
}
应用程序清单:
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
<Identity Name="32ed3ec5-2e4a-4517-ae2b-842653ab8a8e" Publisher="CN=Flo" Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="32ed3ec5-2e4a-4517-ae2b-842653ab8a8e" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>CortanaCommandsStart</DisplayName>
<PublisherDisplayName>Flo</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="CortanaCommandsStart.App">
<uap:VisualElements DisplayName="CortanaCommandsStart" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="CortanaCommandsStart" BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">
</uap:DefaultTile>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
<Extensions>
<uap:Extension Category="windows.appService" EntryPoint="CortanaCommand.CortanaCommandService">
<uap:AppService Name="CortanaCommandService" />
</uap:Extension>
</Extensions>
</Application>
</Applications>
</Package>
CortanaCommand.cs
namespace CortanaCommand
{
public sealed class CortanaCommandService : IBackgroundTask
{
private VoiceCommandServiceConnection voiceServiceConnection;
private BackgroundTaskDeferral _deferral;
public async void Run(IBackgroundTaskInstance taskInstance)
{
_deferral = taskInstance.GetDeferral();
voiceServiceConnection.VoiceCommandCompleted += VoiceCommandCompleted;
AppServiceTriggerDetails triggerDetails = taskInstance.TriggerDetails as AppServiceTriggerDetails;
if (triggerDetails != null && triggerDetails.Name.Equals("CortanaCommandService"))
{
try
{
voiceServiceConnection = VoiceCommandServiceConnection.FromAppServiceTriggerDetails(triggerDetails);
VoiceCommand voiceCommand = await voiceServiceConnection.GetVoiceCommandAsync();
// Perform the appropriate command depending on the operation defined in VCD
VoiceCommandUserMessage userMessage = new VoiceCommandUserMessage();
switch (voiceCommand.CommandName)
{
case "LedChangeColor":
var destination = voiceCommand.Properties["color"][0];
SendCompletionMessageForDestination(destination);
break;
default:
LaunchAppInForeground();
break;
}
}
finally
{
_deferral.Complete();
}
}
}
private void VoiceCommandCompleted(VoiceCommandServiceConnection sender, VoiceCommandCompletedEventArgs args)
{
if (this._deferral != null)
{
this._deferral.Complete();
}
}
private async void SendCompletionMessageForDestination(string destination)
{
// Take action and determine when the next trip to destination
// Inset code here
// Replace the hardcoded strings used here with strings
// appropriate for your application.
// First, create the VoiceCommandUserMessage with the strings
// that Cortana will show and speak.
var userMessage = new VoiceCommandUserMessage();
userMessage.DisplayMessage = "Here’s your trip.";
userMessage.SpokenMessage = "Your trip to Vegas is on August 3rd.";
// Optionally, present visual information about the answer.
// For this example, create a VoiceCommandContentTile with an
// icon and a string.
var destinationsContentTiles = new List<VoiceCommandContentTile>();
// Create the VoiceCommandResponse from the userMessage and list
// of content tiles.
var response = VoiceCommandResponse.CreateResponse(userMessage, destinationsContentTiles);
// Ask Cortana to display the user message and content tile and
// also speak the user message.
await voiceServiceConnection.ReportSuccessAsync(response);
}
private async void LaunchAppInForeground()
{
var userMessage = new VoiceCommandUserMessage();
userMessage.SpokenMessage = "Launching Adventure Works";
var response = VoiceCommandResponse.CreateResponse(userMessage);
// When launching the app in the foreground, pass an app
// specific launch parameter to indicate what page to show.
response.AppLaunchArgument = "showAllTrips=true";
await voiceServiceConnection.RequestAppLaunchAsync(response);
}
}
}
VCD:
<?xml version="1.0" encoding="utf-8"?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
<CommandSet xml:lang="de-de" Name="LEDControll">
<CommandPrefix>LED</CommandPrefix>
<Example>Steuere die LEDs</Example>
<Command Name="LedChangeColor">
<Example>LED Farbe grün</Example>
<ListenFor>farbe {color}</ListenFor>
<Feedback>Farbe wird geändert in {color}</Feedback>
<VoiceCommandService Target="CortanaCommandService"/>
</Command>
<PhraseList Label="color">
<Item> grün </Item>
<Item> blau </Item>
<Item> rot </Item>
</PhraseList>
</CommandSet>
</VoiceCommands>
感谢您的帮助!
只需在此处添加答案,这样就清楚了:
为确保后台任务正确添加到应用程序包中,您可以在VS中添加从启动项目到后台任务项目的引用:
- 右键单击启动项目中的引用
- 转到添加参考
- Select"Projects"左边
- 勾选包含后台任务的项目旁边的框。
- 点击确定。
这样可以确保 VS 在您部署时构建后台任务并将其复制到包中。否则,您 运行 会遇到像这样难以调试的问题。
通常,当您三重检查包清单 uap:Extension 条目是否包含正确的详细信息时,您可以判断发生了这种情况,但是当您尝试时无法在后台任务中设置断点激活它。我还将检查是否有任何可以查找的事件日志类型条目,以便更轻松地查看。
我正在尝试编写一个 c# 程序来为 cortana 定义自定义命令!
首先我创建一个默认的应用程序并加载 VCD 文件...
我创建了第二个项目 link 到主项目并在其中创建一个后台任务来处理 cortana 请求!
现在 cortana 知道我的命令,但如果我告诉她一些事情,她就会回答 "Es ist etwas schief gelaufen"(英语:"it fails")。
我认为 BackgroundTask 调用不正确。
App.cs
namespace CortanaCommandsStart
{
/// <summary>
/// Stellt das anwendungsspezifische Verhalten bereit, um die Standardanwendungsklasse zu ergänzen.
/// </summary>
sealed partial class App : Application
{
/// <summary>
/// Initialisiert das Singletonanwendungsobjekt. Dies ist die erste Zeile von erstelltem Code
/// und daher das logische Äquivalent von main() bzw. WinMain().
/// </summary>
public App()
{
Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync(Microsoft.ApplicationInsights.WindowsCollectors.Metadata | Microsoft.ApplicationInsights.WindowsCollectors.Session);
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Wird aufgerufen, wenn die Anwendung durch den Endbenutzer normal gestartet wird. Weitere Einstiegspunkte
/// werden z. B. verwendet, wenn die Anwendung gestartet wird, um eine bestimmte Datei zu öffnen.
/// </summary>
/// <param name="e">Details über Startanforderung und -prozess.</param>
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
// App-Initialisierung nicht wiederholen, wenn das Fenster bereits Inhalte enthält.
// Nur sicherstellen, dass das Fenster aktiv ist.
if (rootFrame == null)
{
// Frame erstellen, der als Navigationskontext fungiert und zum Parameter der ersten Seite navigieren
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Zustand von zuvor angehaltener Anwendung laden
}
// Den Frame im aktuellen Fenster platzieren
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// Wenn der Navigationsstapel nicht wiederhergestellt wird, zur ersten Seite navigieren
// und die neue Seite konfigurieren, indem die erforderlichen Informationen als Navigationsparameter
// übergeben werden
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
try {
StorageFile vcdStorageFile = await Package.Current.InstalledLocation.GetFileAsync(@"LEDControll.xml");
await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(vcdStorageFile);
System.Diagnostics.Debug.WriteLine("There was no error registering the Voice Command Definitions");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("There was an error registering the Voice Command Definitions", ex);
}
// Sicherstellen, dass das aktuelle Fenster aktiv ist
Window.Current.Activate();
}
/// <summary>
/// Wird aufgerufen, wenn die Navigation auf eine bestimmte Seite fehlschlägt
/// </summary>
/// <param name="sender">Der Rahmen, bei dem die Navigation fehlgeschlagen ist</param>
/// <param name="e">Details über den Navigationsfehler</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Wird aufgerufen, wenn die Ausführung der Anwendung angehalten wird. Der Anwendungszustand wird gespeichert,
/// ohne zu wissen, ob die Anwendung beendet oder fortgesetzt wird und die Speicherinhalte dabei
/// unbeschädigt bleiben.
/// </summary>
/// <param name="sender">Die Quelle der Anhalteanforderung.</param>
/// <param name="e">Details zur Anhalteanforderung.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Anwendungszustand speichern und alle Hintergrundaktivitäten beenden
deferral.Complete();
}
}
}
应用程序清单:
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
<Identity Name="32ed3ec5-2e4a-4517-ae2b-842653ab8a8e" Publisher="CN=Flo" Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="32ed3ec5-2e4a-4517-ae2b-842653ab8a8e" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>CortanaCommandsStart</DisplayName>
<PublisherDisplayName>Flo</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="CortanaCommandsStart.App">
<uap:VisualElements DisplayName="CortanaCommandsStart" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="CortanaCommandsStart" BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">
</uap:DefaultTile>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
<Extensions>
<uap:Extension Category="windows.appService" EntryPoint="CortanaCommand.CortanaCommandService">
<uap:AppService Name="CortanaCommandService" />
</uap:Extension>
</Extensions>
</Application>
</Applications>
</Package>
CortanaCommand.cs
namespace CortanaCommand
{
public sealed class CortanaCommandService : IBackgroundTask
{
private VoiceCommandServiceConnection voiceServiceConnection;
private BackgroundTaskDeferral _deferral;
public async void Run(IBackgroundTaskInstance taskInstance)
{
_deferral = taskInstance.GetDeferral();
voiceServiceConnection.VoiceCommandCompleted += VoiceCommandCompleted;
AppServiceTriggerDetails triggerDetails = taskInstance.TriggerDetails as AppServiceTriggerDetails;
if (triggerDetails != null && triggerDetails.Name.Equals("CortanaCommandService"))
{
try
{
voiceServiceConnection = VoiceCommandServiceConnection.FromAppServiceTriggerDetails(triggerDetails);
VoiceCommand voiceCommand = await voiceServiceConnection.GetVoiceCommandAsync();
// Perform the appropriate command depending on the operation defined in VCD
VoiceCommandUserMessage userMessage = new VoiceCommandUserMessage();
switch (voiceCommand.CommandName)
{
case "LedChangeColor":
var destination = voiceCommand.Properties["color"][0];
SendCompletionMessageForDestination(destination);
break;
default:
LaunchAppInForeground();
break;
}
}
finally
{
_deferral.Complete();
}
}
}
private void VoiceCommandCompleted(VoiceCommandServiceConnection sender, VoiceCommandCompletedEventArgs args)
{
if (this._deferral != null)
{
this._deferral.Complete();
}
}
private async void SendCompletionMessageForDestination(string destination)
{
// Take action and determine when the next trip to destination
// Inset code here
// Replace the hardcoded strings used here with strings
// appropriate for your application.
// First, create the VoiceCommandUserMessage with the strings
// that Cortana will show and speak.
var userMessage = new VoiceCommandUserMessage();
userMessage.DisplayMessage = "Here’s your trip.";
userMessage.SpokenMessage = "Your trip to Vegas is on August 3rd.";
// Optionally, present visual information about the answer.
// For this example, create a VoiceCommandContentTile with an
// icon and a string.
var destinationsContentTiles = new List<VoiceCommandContentTile>();
// Create the VoiceCommandResponse from the userMessage and list
// of content tiles.
var response = VoiceCommandResponse.CreateResponse(userMessage, destinationsContentTiles);
// Ask Cortana to display the user message and content tile and
// also speak the user message.
await voiceServiceConnection.ReportSuccessAsync(response);
}
private async void LaunchAppInForeground()
{
var userMessage = new VoiceCommandUserMessage();
userMessage.SpokenMessage = "Launching Adventure Works";
var response = VoiceCommandResponse.CreateResponse(userMessage);
// When launching the app in the foreground, pass an app
// specific launch parameter to indicate what page to show.
response.AppLaunchArgument = "showAllTrips=true";
await voiceServiceConnection.RequestAppLaunchAsync(response);
}
}
}
VCD:
<?xml version="1.0" encoding="utf-8"?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
<CommandSet xml:lang="de-de" Name="LEDControll">
<CommandPrefix>LED</CommandPrefix>
<Example>Steuere die LEDs</Example>
<Command Name="LedChangeColor">
<Example>LED Farbe grün</Example>
<ListenFor>farbe {color}</ListenFor>
<Feedback>Farbe wird geändert in {color}</Feedback>
<VoiceCommandService Target="CortanaCommandService"/>
</Command>
<PhraseList Label="color">
<Item> grün </Item>
<Item> blau </Item>
<Item> rot </Item>
</PhraseList>
</CommandSet>
</VoiceCommands>
感谢您的帮助!
只需在此处添加答案,这样就清楚了:
为确保后台任务正确添加到应用程序包中,您可以在VS中添加从启动项目到后台任务项目的引用:
- 右键单击启动项目中的引用
- 转到添加参考
- Select"Projects"左边
- 勾选包含后台任务的项目旁边的框。
- 点击确定。
这样可以确保 VS 在您部署时构建后台任务并将其复制到包中。否则,您 运行 会遇到像这样难以调试的问题。
通常,当您三重检查包清单 uap:Extension 条目是否包含正确的详细信息时,您可以判断发生了这种情况,但是当您尝试时无法在后台任务中设置断点激活它。我还将检查是否有任何可以查找的事件日志类型条目,以便更轻松地查看。