2016/04/22


コルタナさん、とても便利なのですが Vim を起動する事が出来ない。どこかで見たショートカットファイルを所定の場所に置く方法もうまく動かない。

C:\ProgramData\Microsoft\Windows\Start Menu\Programs

調べると、ボイスコマンドという API が出ているのだけど、Universal Application でないといけない。Universal Application だと System.Diagnostic からプロセスを起動できない。そこで別に立てたウェブサーバから Vim を起動させる。サーバのコードは至って簡単。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            HttpListener listener = new HttpListener();
            listener.Prefixes.Add("http://localhost:8888/");
            listener.Start();
            while (true)
            {
                HttpListenerContext context = listener.GetContext();
                HttpListenerRequest req = context.Request;
                HttpListenerResponse res = context.Response;

                if (req.RawUrl == "/vim")
                {
                    Process.Start("gvim");
                    res.StatusCode = 200;
                }
                else
                {
                    res.StatusCode = 404;
                }
                res.Close();
            }
        }
    }
}

次に空の Universal Application を作り、OnLaunched イベントでボイスコマンドAPIにコマンド定義を食わせる。

        protected async override void OnLaunched(LaunchActivatedEventArgs e)
        {
#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                this.DebugSettings.EnableFrameRateCounter = true;
            }
#endif
            Frame rootFrame = Window.Current.Content as Frame;

            // ウィンドウに既にコンテンツが表示されている場合は、アプリケーションの初期化を繰り返さずに、
            // ウィンドウがアクティブであることだけを確認してください
            if (rootFrame == null)
            {
                // ナビゲーション コンテキストとして動作するフレームを作成し、最初のページに移動します
                rootFrame = new Frame();

                rootFrame.NavigationFailed += OnNavigationFailed;

                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: 以前中断したアプリケーションから状態を読み込みます
                }

                // フレームを現在のウィンドウに配置します
                Window.Current.Content = rootFrame;
            }

            if (e.PrelaunchActivated == false)
            {
                if (rootFrame.Content == null)
                {
                    // ナビゲーション スタックが復元されない場合は、最初のページに移動します。
                    // このとき、必要な情報をナビゲーション パラメーターとして渡して、新しいページを
                    //構成します
                    rootFrame.Navigate(typeof(MainPage), e.Arguments);
                }
                // 現在のウィンドウがアクティブであることを確認します
                Window.Current.Activate();

                try
                {
                    // 俺コマンドの登録
                    StorageFile vcdStorageFile = await Package.Current.InstalledLocation.GetFileAsync(@"MyCommands.xml");
                    await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(vcdStorageFile);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("ボイスコマンドの定義でエラーが発生しました", ex);
                }
            }
        }

食わせるコマンド定義は XML で記述し、以下のようにした。

xml version="1.0" encoding="utf-8" ?>

<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
  <CommandSet xml:lang="ja-jp" Name="HayCortanaLaunchVim">
    <CommandPrefix>ねぇ
    <Example>ビムを起動して
    <Command Name="OpenVim">
      <Example>ビムを起動して
      <ListenFor>ビムを起動して[下さい]
      <Feedback>ビムを起動しています
      <Navigate/>
    
  

これで「コルタナさん、ねぇ Vim を起動して」と喋るだけで http://localhost:8888/vim にリクエストが飛び、サーバを経由して Vim が起動する。

サーバ起動しておかないといけないのが面倒臭い。ちなみにちゃんと作るならクライアントサーバ間でトークンか何かをやりとりする必要があるし、サーバも localhost からのアクセスに絞るべきです。

ソースは GitHub に置いておきます。

Posted at by | Edit