d.sunnyone.org
sunnyone.org

ページ

2013-12-22

PowerShellにMSBuildLauncherで簡易GUIをつける

今日のPowerShell勉強会第1回の運営の方々、発表者の方々、そして参加者の方々お疲れ様でした。どれも面白かったり勉強になったりでよかったです。

さて、Lightning Talkで掲題の話をしたのですが、実用するには速すぎてわからなかったと思うので読み物としてわかるようにまとめたいと思います。

概要

MSBuild(.csprojなんかで使われている)はビルドには必要なんだけど、ちょっとコマンドライン叩くの面倒だよね、適当なGUIないよね。
  →MSBuild Launcherの誕生。

あれ、このGUIなら逆にビルドじゃない用途でも(スクリプト実行の用途でも)MSBuildにExec書いて使ったら便利なんじゃね?
  →MSBuild LauncherありきのMSBuildの邪道な使い方の誕生。

というお話です。

前提として、MSBuild Launcherをインストーラでインストール(関連付けオプション有効)を想定しています。

---

1. シンプルな使い方

単純に.ps1ファイルを置いて、それを実行するだけ。これでも十分に便利。一応、コマンドが長くなりすぎちゃうので、プロパティ(※1)としてpowershellコマンドを別出しにしておくのがおすすめ。

※1: パラメータのようなもの。PropertyGroup要素に好きな名前の要素を置くと、それがプロパティになる。

<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PSCmd>powershell -ExecutionPolicy RemoteSigned -NonInteractive -Command </PSCmd>
</PropertyGroup>
<Target Name="Build">
<Exec Command='$(PSCmd) .\simple.ps1' />
</Target>
</Project>


なお、-NonInteractiveは常に入れておくのがおすすめ。発表時も話したけども、例えば必須引数が足りなかったりすると通常のコンソールでは入力待ちで止まるけども、こいつはそんなことできないので、無言で待ちに入る。そして「進まない!」と悩む。なので-NonInteractiveで即座にエラーにする。

対応するスクリプトは好きな処理を書けばいいのだけど、一応例として。

Write-Output "Hello, World."
view raw simple.ps1 hosted with ❤ by GitHub


動かすとこんな感じ。


2. プロパティを持たせる使い方

パラメータが欲しいときは、MSBuildにプロパティを持たせるとよい。そしてコマンドの中で$(Name)で使う。
ただし、Exec Command=""の中身の文字列はcmd解釈?のようなので注意。Command=をくくるのは""にして、プロパティを'$(Name)'という感じにくくるのがおすすめ。

<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Msg>こんにちは</Msg>
<!-- 選択肢にしたいとき -->
<Select>おはよう</Select>
<Select Condition=" '$(Select)' == 'おやすみ' "></Select>
<PSCmd>powershell -ExecutionPolicy RemoteSigned -NonInteractive -Command </PSCmd>
</PropertyGroup>
<Target Name="Build">
<Exec Command="$(PSCmd) .\property.ps1 -Msg '$(Msg)'" />
</Target>
</Project>
view raw gistfile1.xml hosted with ❤ by GitHub


プロパティはLTでは話さなかったけども、候補としてConditionを追加してあげると、コンボボックスの選択になるので、選択のときはそうしてあげるとよいかも。

スクリプトはこんな感じ。paramで受けてあげればいい。

param($Msg)
Write-Output "$Msg, World."
view raw gistfile1.ps1 hosted with ❤ by GitHub


動かすとこんな感じ。右側で設定できる。


3. 複数のターゲットを用意する便利な使い方

複数のやりたいことを一つの画面で行うには、もちろんスクリプトを複数用意してもよいのだけど、処理自体は少ない場合は、モジュール(.psm1)に関数を定義して、それをMSBuildから使う方式がおすすめ。

具体的には、PSCmdの中にImport-Moduleまで入れてしまって、Execの中では関数名を書く形にする。

<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PSCmd>powershell -ExecutionPolicy RemoteSigned -NonInteractive -Command Import-Module .\module.psm1; </PSCmd>
</PropertyGroup>
<Target Name="GetService">
<Exec Command='$(PSCmd) Get-MyService' />
</Target>
<Target Name="GetProcess">
<Exec Command='$(PSCmd) Get-MyProcess' />
</Target>
</Project>
view raw gistfile1.xml hosted with ❤ by GitHub


スクリプトはこんな感じ。

function Get-MyService {
Get-Service | Select-Object -First 5 | Format-List
}
function Get-MyProcess {
Get-Process | Select-Object -First 5 | Format-List
}
view raw gistfile1.ps1 hosted with ❤ by GitHub


動かすとこんな感じ。


4. PowerShellの便利機能を活用する

PowerShellを簡易GUIっぽくしちゃうことで有名なOut-GridViewやShow-Commandはここでも便利。
ただ1点注意があって、Execで実行するとコンソールと違ってホストが残らないので、そのままだとすぐ画面がいなくなってしまう。
なので、以下のように終了を待ってあげるのがいい。
Get-Service | Out-GridView -Passthru | Out-Null
view raw gistfile1.ps1 hosted with ❤ by GitHub


---
今度こそぜひ活用してあげてください。

2013-12-16

Markdown.XAMLを使ってWPFでMarkdownテキストをレンダリングする

この記事は、XAML Advent Calendar 2013用です。今日は、MarkdownをWPFアプリケーションに表示する方法についてです。
---

Windows Phoneでmarkdownで書いたテキストをhtmlに変換して表示する を見て、そういえばMarkdownを表示するためのライブラリがあったことを思い出した。

その名もMarkdown.XAML。これは、MarkdownテキストをFlowDocumentに変換してくれる。使い方はざっくり言うと、TextToFlowDocumentConverterというコンバータが使えるようになるので、FlowDocumentScrollViewerのようなFlowDocumentを表示してくれるコントロールに、Markdownテキストが入ったプロパティとこのコンバータをセットでバインドしておくだけでOK.

詳しい使い方は以下の通り。

1. 下準備

Markdown.XAMLをclone or zipでダウンロードして、Visual Studioで開き、コンパイルする。

そうすると、build\Markdown.Xaml\(Configuration)のあたりにMarkdown.Xaml.dllができるので、これを、Markdownを表示させたいプロジェクトに持っていって、参照させる。

NuGetになさそうなのが残念だけど、すんなりコンパイルできるのでまぁ。

2. Markdownテキストを適当なプロパティに入れておく

ファイルから読んでとか、いろいろありそうだが、ここの本題ではないので、ここでは適当な文字列を返すプロパティを作っておく。
public class TestData
{
public string MarkdownText
{
get { return "* Get-Host\n* Get-Job\n"; }
}
}
view raw gistfile1.cs hosted with ❤ by GitHub


3. リソースに、MarkdownとTextToFlowDocumentConverterを定義する

TextToFlowDocumentConverterを作り、そこに実体であるMarkdownオブジェクトを紐づける。とりあえずこんなかんじ:
<Window x:Class="MarkdownTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:md="clr-namespace:Markdown.Xaml;assembly=Markdown.Xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<md:Markdown x:Key="Markdown" />
<md:TextToFlowDocumentConverter x:Key="TextToFlowDocumentConverter" Markdown="{StaticResource Markdown}" />
</Window.Resources>
view raw gistfile1.xml hosted with ❤ by GitHub


4. FlowDocumentをレンダリングするコントロールを配置する

FlowDocumentScrollViewerを配置して、さきほどのプロパティとコンバータをバインドする。

<DockPanel>
<FlowDocumentScrollViewer Document="{Binding MarkdownText, Converter={StaticResource TextToFlowDocumentConverter}}" />
</DockPanel>
view raw gistfile1.xml hosted with ❤ by GitHub

シンプルに表示したいだけなら、これだけ。

できたものはこんなかんじ:

活用編

FlowDocumentになっているので、もう少しWPFに寄った形で使える。

1. パネルの見た目を変更する

ちゃんとパネルも見えているので、たとえばパネルの背景色を変えたりすることができる。FlowDocumentScrollViewerのプロパティも当然使える。
<DockPanel Background="#012456">
<FlowDocumentScrollViewer Document="{Binding MarkdownText, Converter={StaticResource TextToFlowDocumentConverter}}" VerticalScrollBarVisibility="Disabled" IsHitTestVisible="False" />
</DockPanel>
view raw gistfile1.xml hosted with ❤ by GitHub


2. スタイルを設定する

たとえば、文字色を変えたければ、このようにいつもの通りStyleを定義することで、変更することができる。
<md:Markdown x:Key="Markdown">
<md:Markdown.DocumentStyle>
<Style TargetType="FlowDocument">
<Setter Property="Foreground" Value="#EEEDF0" />
</Style>
</md:Markdown.DocumentStyle>
</md:Markdown>
view raw gistfile1.xml hosted with ❤ by GitHub


結果はこんなかんじ:


詳しくはデモがあるので見るとわかりやすいと思う。
https://github.com/theunrepentantgeek/Markdown.XAML/blob/master/src/Markdown.Xaml.Demo/MainWindow.xaml


----

どうでしょうか。わりと簡単に使えると思うので、適当なテキストを出したいときなんかにどうぞ。

2013-12-11

PowerDbg でデバッガ操作をオートメーションする

この記事は、PowerShell Advent Calendar 2013用です。本当は、誰も触れてないPowerShell v4.0の新機能について触れる予定だったのですが、新機能以外の部分で挫折したので実用的な内容に方針転換します。

今回の話題は、WinDbgの操作をオートメーションするスクリプト、PowerDbgの使い方です。これはなにかというと、WinDbgのコンソール版、cdb.exeのフロントエンドです。PowerShellがデバッガになるというよりは、windbgをコントロールできる感じです。

インストール方法

http://powerdbg.codeplex.com/ に行き、Downloadボタンを押すと、zipが落ちてくるので開きます。その中に「Install_PowerDbg.bat」というファイルがあるので、実行するとインストールされます。

といっても、横に置いてある「PowerDbg.psm1」を「%USERPROFILE%\Documents\WindowsPowerShell\Modules\PowerDbg」にコピーするだけなので、手でやっても変わらないです(もっと言うと、好きな場所に置いて毎回Import-Module .\PowerDbg.psm1でもいいです)。

あとは、いくつかの方法のどれかでcdb.exeのあるフォルダを指定します。でも一番簡単なのは.psm1の頭、param(...)のあとに以下のように記述することなので、ここではそれだけ書きます(どうせスクリプトは更新されなさそうだし)。
$debuggerRoot = "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64"


使い方

「New-DbgSession」コマンドレットで、デバッガを起動できます。-command "コマンド..."で新コマンド起動、-process "プロセス名" でアタッチ、-dump "dumpファイルのパス" でダンプ読み込みです。

PS> New-DbgSession -command "C:\Program Files (x86)\MsbuildLauncher\MsbuildLauncher.exe"

そして、「Invoke-DbgCommand」でデバッガコマンドを送れます。
PS> Invoke-DbgCommand 'bp $exentry'

「g」は「Send-DbgGo」というコマンドレットが用意されているので使えます。
PS> Send-DbgGo
Send-DbgGoすると、止まるまでブロックします。Ctrl+Cで抜けることも可能。

終わりたいときは、「Exit-DbgSession」で終われます。
PS> Exit-DbgSession

.loadby sos clrみたいに拡張をロードしたいときは、Load-DbgExtensionが使えます。
PS> Load-DbgExtension sos clr

Invoke-DbgCommandで何を送るかが肝で、PowerShell固有の操作はほとんどありません。(引数名がcamelCaseなのがいけてない感じですがスルーでおねがいします…)

実用例1: 異常終了したときに.NETのスタックトレース等を表示するスクリプト

前にやった「WinDbgとSOS拡張でVSを使わずに.NETアプリをデバッグ - 異常終了時の調査」を自動でやれます。

param($Command)
New-DbgSession -command $Command
Invoke-DbgCommand sxe ld clr
Send-DbgGo # to clr loaded
Load-DbgExtension sos clr # .loadby sos clr
Send-DbgGo # to CorExeMain
Send-DbgGo # to go!
# app crashes
Invoke-DbgCommand "!pe"
Invoke-DbgCommand "!clrstack"
Exit-DbgSession


実行するとこんな感じ。
PS> > .\PrintExceptionSecondChance.ps1 -Command "C:\temp\MsbuildLauncher-0.1.1\MsbuildLauncher\MsbuildLauncher.exe"
ModLoad: 000007fe`f7200000 000007fe`f7b60000   C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
ntdll!ZwMapViewOfSection+0xa:
00000000`7755153a c3              ret
(1340.8e8): Unknown exception - code 04242420 (first chance)
ModLoad: 000007fe`f5a60000 000007fe`f5b8e000   C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clrjit.dll
ntdll!ZwMapViewOfSection+0xa:
00000000`7755153a c3              ret
(1340.8e8): C++ EH exception - code e06d7363 (first chance)
(1340.8e8): C++ EH exception - code e06d7363 (first chance)
(1340.8e8): C++ EH exception - code e06d7363 (first chance)
(1340.8e8): C++ EH exception - code e06d7363 (first chance)
(1340.8e8): C++ EH exception - code e06d7363 (first chance)
(1340.8e8): CLR exception - code e0434352 (first chance)     ←ここでエディタパスをおかしくして「Edit」ボタンをクリック
(1340.16c0): Unknown exception - code 000006ba (first chance)
(1340.8e8): CLR exception - code e0434352 (first chance)
(1340.8e8): CLR exception - code e0434352 (!!! second chance !!!)
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\system32\KERNELBASE.dll -
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\Microsoft.NET\Framework64\v4.0.3
0319\clr.dll -
KERNELBASE!RaiseException+0x3d:
000007fe`fd6a940d 4881c4c8000000  add     rsp,0C8h
PDB symbol for clr.dll not loaded
Exception object: 0000000002b0eeb8
Exception type:   System.ComponentModel.Win32Exception
Message:          指定されたファイルが見つかりません。
InnerException:   
StackTrace (generated):
(略)


実用例2: .NETなプログラムのプロセスのダンプファイルを読み取ってスタックトレースを出力するスクリプト

実はいつもやってる「clr.dllが読み込まれるまで進める」という処理が必要なければ、New-DbgSessionは-sosというオプションでsos.dllを自動で読んでくれるのです。それを利用すると、これだけ簡潔にできます。

param($Dump)
New-DbgSession -dump $Dump -sos
Invoke-DbgCommand "~*e !clrstack"
Exit-DbgSession



どうでしょうか?今回の例はとくにPowerShellらしさはないですが、特定のブレイクポイントを一気に仕掛けるとか、結果に応じてどうこうするなんて用途には便利かと思います。

蛇足

WDK 8.1についてるWinDbgでSOS拡張をロードして、SOS系のコマンドを打つと、一発目がExceptionになって、二発目以降に成功するようになる。8.0ではそんなことはないので、WDK 8.0についてるWinDbgがおすすめ。