2017年11月5日日曜日

Fall CU - コマンドライン・Win+R からのUWP App 起動

Windows 10 Fall Creators Update (以下FCU)から、UWP App 側に少し変更を入れることでコンソールからUWP App の起動が可能になりました。
Package.appxmanifest で定義するエイリアス名での起動、またパラメータ・カレントディレクトリのパスの取得ができます。




コンソールから起動と言われても…そんなに使わないんじゃん?僕らヤングはGUI世代じゃん?と思われるかもですが、実は「ファイル名を指定して実行」でも使う事が可能です。
Win+R、で名前入れればApp一発起動!というのは割と魅力と思うのですがどうでしょう?
あそこは履歴も残るので個人的にはスタートメニュー本体より使用頻度が高いです。


みんな大好きWin+R
ちなみにdevmgmt.mscはデバイスマネージャ、
appwiz.cplはアプリケーションの追加と削除



使い方

ここでは、 Visual Studio 2017 でUWP App をBlank テンプレートから作り始めた想定でコンソールからの起動を追加してみます。

1 プロジェクトのMinVersion を16299以上に設定する





 今回の機能はFall CU以降のみで使える機能です。MinVersionをFCU, 16299 に設定する必要があります。

2 Package.appxmanifest でアプリケーションのエイリアス名・エントリポイントを設定する



<?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"
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" IgnorableNamespaces="uap mp">
<Identity Name="55884DDLG.FCUTrial" Publisher="CN=0ADFD460-31D2-4A92-A4A8-DF66FF78B5F8" Version="1.1.1.0" />
<mp:PhoneIdentity PhoneProductId="f0696861-c4dc-44b1-87f7-97601f0c45c3" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>FCU Trial</DisplayName>
<PublisherDisplayName>DDLG</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="App1.App">
<uap:VisualElements DisplayName="FCU Trial" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="App1" BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">
</uap:DefaultTile>
<uap:SplashScreen Image="Assets\SplashScreen.png" uap5:Optional="true" />
</uap:VisualElements>
<Extensions>
<uap5:Extension
Category="windows.appExecutionAlias"
Executable="App1.exe"
EntryPoint="App1.App"
>
<uap5:AppExecutionAlias>
<uap5:ExecutionAlias Alias="App1.exe"/>
<uap5:ExecutionAlias Alias="hogehoge.exe"/>
</uap5:AppExecutionAlias>
</uap5:Extension>
</Extensions>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
</Capabilities>
</Package>
※今回の内容はVisual Studio のマニフェスト デザイナからは設定できません。ソリューション エクスプローラー上で Pacakge.appxmanifest を右クリック → 「コードを表示」 からXML を直で編集します。

Package.appxmanifest を全部貼りました。一部切り出されてもエレメントの関係が分かりづらいので。
まず、一番上のPacakge エレメントにネームスペースuap5 を追加します。 xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" です。
次、下の方のApplication エレメント を探します。この子要素にExtensions を追加し、その中に今回のuap5:Extension Category="windows.appExecutionAlias" を追加します。26~37行です。

  • Executable は、このアプリの実行ファイル名…普通はプロジェクトのアセンブリ名です。
  • EntryPoint は、このアプリのアプリケーションクラス名です。テンプレそのままだと大体「アプリ名.app」ですね。
  • uap5:AppExecutionAlias Alias は エイリアス名です。ここで「App1.exe」とすると、Win+Rやコンソールでは「App1」で起動します。また、このエイリアス名はuap:AppExecutionAlias の中で複数指定できます。


3 App.xaml.cs にコマンドライン起動の受けを追加



ここまでで、「起動」はするようになります。ただ、スプラッシュスクリーンで止まってしまいます。
これは、まだコンソール起動時のハンドラを書いていないためです。


protected override void OnActivated(IActivatedEventArgs args)
{
Frame rootFrame = Window.Current.Content as Frame;
if(ActivationKind.CommandLineLaunch == args.Kind)
{
var commandArgs = args as CommandLineActivatedEventArgs;
var operation = commandArgs.Operation;
string arguments = operation.Arguments;
string activationPath = operation.CurrentDirectoryPath;
//operation.ExitCode = arguments.Length;
if (null == rootFrame)
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
}
Window.Current.Content = rootFrame;
}
rootFrame.Navigate(typeof(MainPage), arguments);
Window.Current.Activate();
}
}
view raw Appx.xaml.cs hosted with ❤ by GitHub

App.xaml.cs に、この OnActivated を追加します。
UWP App ではApp起動時 OnLaunched にまず飛んでくるのはご存知と思います。ただこれは「通常起動」…スタートメニューからポチっと起動した場合の話です。

そうでは無い各種起動はOnActivated で扱います。今回のCommandlineやCortanaのVoiceCommandのような別口…裏口?起動は全部こちらに書きます。

上のコードにあるように、

  • Arguments ... 起動時に渡されたパラメータ文字列
  • CurrentDirectoryPath ... 起動時のカレントディレクトリ(後述)
  • ExitCode ... 呼び側に渡す終了コード

等を使用・設定できます。
Arguments は、そのとおりパラメータなのですが…一つ注意として、おしりに必ずスペースが一つ入ります。"abc" と渡すと、Argumentsに入ってくるのは"abc "です。
ExitCode は呼び側に即返ります。以下のスクリーンキャプチャは、上のExitCodeのコメントを外した上で.cmd から起動し、ErrorLevelを表示している例です。

最後に、渡されたパラメータを表示するためにMain.Xaml と Main.xaml.cs をちょっと弄りましょう。

<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48" x:Name="tbArguments" />
</Grid>
</Page>
view raw Main.xaml hosted with ❤ by GitHub
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ProcessPassedCommandlineArguments(e.Parameter);
}
public void ProcessPassedCommandlineArguments(object arguments)
{
tbArguments.Text = arguments as string;
}
view raw Main.xaml.cs hosted with ❤ by GitHub

これで完成です(*´▽`*)!
コマンドラインやWin+Rから起動してみてください。
また、すでに起動している所にもう一度実行しても表示が更新されるはずです。OnActivated は起動・アクティベートどちらでも通り、どちらもOnNavigatedToを通すのでこうなっています。



古の%ErrorLevel%とかそういう世界



「カレントディレクトリ」?


少し気を付けたいのは、取得できる「カレントディレクトリ」のパス名です。
例えばコンソールでc:\hogehoge に居る場合にアプリを起動すると「c:\hogehoge」が返ります。
じゃあそのディレクトリ内のファイルをFindFirstで列挙して云々、とついやりたくなるのが人情ですが…
UWP App の場合、それは基本出来ません。

UWP App の場合、Appがアクセスできるのは
  • App固有のフォルダ
  • ユーザーがFolderPickerで指定したフォルダ
  • その他Manifestで指定された特定のフォルダ

のみである!という鉄の掟があります。App Container のサンドボックスですね。
このため、文字列でフォルダの名前を渡されてもあんまり使いどころがありません。
他のWin32 Appにパススルーで渡すとかその程度でしょうか。





0 件のコメント:

コメントを投稿