開発備忘録

WPF(Xaml + C#)の話題を中心に.Net関連についてのブログです。

Windows上のJenkinsを特定のユーザーで実行する

Windows上でJenkinsを起動しているときに実行しているユーザーを切り替える必要があったのでメモ。
環境は以下の通り。

JenkinsはWindows上ではサービスで起動していて、各ジョブはOSのシステムが実行しています。
試しにジョブで"Windowsバッチコマンドの実行"で以下のコマンドを入力します。

whoami /user

するとコンソール出力にこのようなログが出力されます。

ユーザー名          SID     
=================== ========
nt authority\system <SID>

これが現在Jenkinsを実行しているユーザーです。
このユーザーを切り替えるには、以下の手順を実行します。

  1. [コントロールパネル] - [管理ツール] - [サービス] を開く。
  2. 一覧から[Jenkins]を探し、右クリック[プロパティ]を選択する。
  3. [ログオン]タブから[アカウント]を選択し、ユーザーとパスワードを入力する。
  4. [OK]をクリックしてサービスを再起動する。

これで指定したユーザーで実行することができます。

JenkinsでNuGetパッケージの復元メモ

Jenkinsを使用してプロジェクトをビルドするとNuGetパッケージの復元が実行されなかったので、その解決方法をメモメモ
環境は以下の通り

Visual Studio 2015からはNuGet.exeがプロジェクトに配置されなくなり、ビルド時に自動的に復元されるようになったためJenkins側で復元してやる必要があります。

1.NuGet.exeをダウンロードする。

以下のページからNuGet.exeをダウンロードして任意の場所に保存します。
Installing NuGet | Microsoft Docs

2.Windowsバッチコマンドを実行する。

ジョブのビルドから[Windowsバッチコマンドの実行]を選択して以下のコマンドを設定します。

cd "<NuGet.exeの配置パス>"
nuget.exe restore "%WORKSPACE%<復元対象のソリューション ファイルパス>"

Visual Studio 2013以前は、プロジェクトに配置されているNuGet.exeを参照すればOKです。

これでジョブを実行すればパッケージの復元ができます。
復元のためのバッチコマンド実行は、必ずMSBuildの実行よりも前に設定してください。
また、NuGet.exeのオプションなどは以下を参照してください。
NuGet Command-Line Interface (CLI) Reference | Microsoft Docs

TFS構成DBからプロジェクト コレクション名を列挙する

またまたTFSネタです。
TFSのDBをバックアップする上で、プロジェクト コレクション名が必要だったのですが、TFSが現在管理しているプロジェクト コレクション名を全て取得する方法って無いのかな―と調べていました。
結果から言うとそれっぽい情報は無かったので、色々試してみた結果、以下の方法でそれっぽく取得することができました。

動作環境には"Team Foundation Server 2012"を使用しています。

まずは、SQL Server Management Studio を起動してTFSの管理しているインスタンスに接続します。
そこでクエリエディタを表示して以下のSQLクエリを入力して実行すると、TFSの持つリソース情報が表示されます。

SELECT * FROM [Tfs_Configuration].[dbo].[tbl_CatalogResource]

実行結果
f:id:iyemon018:20170111190710p:plain

”DisplayName”列にところどころプロジェクト コレクション名が見えるのですが、どうやって取得するか…
しばし悩んだところで、以下のテーブルに[Identifier]列の定義があったのでこれを使います。

SELECT * FROM [Tfs_Configuration].[dbo].[tbl_CatalogResourceType]

実行結果
f:id:iyemon018:20170111191024p:plain

上図の赤枠線で囲っているところがチーム プロジェクト コレクションのリソース識別子っぽいです。
では、以下のSQLを実行してみましょう。

SELECT * FROM [Tfs_Configuration].[dbo].[tbl_CatalogResource] WHERE [tbl_CatalogResource].[ResourceType]=<Identifier>

これでチーム プロジェクト コレクションの一覧を全て取得することができました。
tbl_CatalogResourceTypeテーブルの[Identifier]列は、リソース種別の識別子なのでtbl_CatalogResourceテーブルの[Identifier]列とは一致しません。
[ResourceType]列と比較することでリソースの種別を識別することが可能になっています。

TFS構成ツールを使用してコレクションのアタッチ/デタッチを実行する方法

今日もTFSネタです。
TFSには”TfsConfig.exe"という構成ツールが含まれていますが、これを使用してコマンドラインからプロジェクト コレクションのアタッチ/デタッチ/削除を行うことができます。

  • アタッチ
C:\Program Files\Microsoft Team Foundation Server 11.0\Tools>TfsConfig Collection /attach /CollectionName:<コレクション名> /CollectionDb:<インスタンス名>;<コレクションDB名>
  • デタッチ
C:\Program Files\Microsoft Team Foundation Server 11.0\Tools>TfsConfig Collection /detach /CollectionName:<コレクション名>
  • 削除
C:\Program Files\Microsoft Team Foundation Server 11.0\Tools>TfsConfig Collection /delete /CollectionName:<コレクション名>

デタッチと削除はコレクション名だけでいいですが、アタッチの場合はDB名も指定する必要があります。
DB名は、通常は"Tfs_<コレクション名>"で作成されているのでご確認を。

他にもコマンドラインからいろいろ制御できるので以下のURLを確認しながらバッチを書いてもいいかもしれません。
www.visualstudio.com

Team Foundation Serverデータベースの復元方法メモ

新年早々にサーバー機の新調に伴い、Team Foundation Serverのバックアップと復元を行ったのですが、かなり手こずったので手順をメモメモ
オチ的には、「TFS 管理コンソールのバックアップ・復元機能は使用しないほうがいい」ということでした。


使用する環境はこんな感じです。

バックアップサーバーを"サーバー A"、復元サーバーを"サーバー B"とします。
どちらも以下の環境を使用しています。

・OS:Windows Server 2012 R2
Team Foundation Server 2012 Update4
SQL Server 2012 Standard SP3

1. TFS 復元手順

  1. Team Foundation Server 管理コンソールから"サーバー A" の全てのプロジェクトコレクションを[停止]→[デタッチ]する。
  2. SQL Server Management Studio から上記のプロジェクトコレクションに該当するDBをバックアップする。(※1)
  3. "サーバー B"のTeam Foundation Server 管理コンソールから[インストール構成]-[詳細]を選択する。
  4. 構成(Tfs_Configuration)のみ作成し、"レポート"、"Share Point"、"デフォルトコレクション"は作成しない。
  5. "サーバー B"からSQL Server Management Studio を使用してバックアップしたDBを復元する。
  6. Team Foundation Server 管理コンソールの[チーム プロジェクト コレクション]-[コレクションのアタッチ]を選択して復元したDBへアタッチする。

この手順を実行してVS上でプロジェクトが表示されない場合は、【セキュリティの管理設定】でユーザーorグループを追加する。

※1.プロジェクトコレクションのDBは、"Tfs_プロジェクトコレクション名"という形式で作成されている。

2. セキュリティの管理設定

Team Foundation Server 管理コンソールからTFSのコレクション操作権限を設定するための機能です。
コレクション全体の設定は、[アプリケーション層]-[セキュリティの管理]から追加します。
コレクションごとの設定は、[チーム プロジェクト コレクション]-[コレクションを選択]-[セキュリティの管理]から追加します。

3. TFSの復元でうまく行かなかったこと

TFS 管理コンソールのバックアップ・復元機能を使用して復元を行う。

[現象]
まず、TFS 管理コンソールからバックアップすることは可能だった。
しかし、バックアップしたデータ(DB)を復元することができなかった。
復元を実行してもTFS 管理コンソールの[チーム プロジェクト コレクション]にはコレクションが表示されないが、SQL ServerにはDBが復元されているという現象が発生した。

[原因]
現時点では不明。
ログを見たりエラー番号で検索する限り、"DBの接続文字列が異なっている"や"SQL Serverインスタンス バージョンとDBバージョンが一致していないため"などが挙げられていた。
TFS のDBを見る限り、SQL Serverの接続文字列やTFSの構成情報を持っているので環境が変わるとそのあたりの情報が整合性が取れなくなっているのでは???

[対策]
上記の【復元手順】の通りに実行するとエラーが発生することなく実行できた。
TFS 管理コンソールのバックアップ・復元機能は使用せず、バッチでコレクションの停止、デタッチ、バックアップ、アタッチ、開始を行う必要がある。
このあたりはJenkinsなどのCIツールを使いたい。


WPFのCalendarコントロールをカスタマイズ

WPFのCalendarコントロールは日付を選択する場合に便利ですが、標準ではサポートされていない機能があります。
例えば土、日、祝日の場合に文字色を変更するなど。
日本国内限定の使用とはいえ、地味に欲しい機能ではあります。
ただ、Calendarコントロールは便利な半面、sealed されているため機能を拡張することはできません。
かと言って1からカスタムCalendarコントロールを作るのも面倒です。
今回はWPFのメリットを活かして、コンバーターとスタイルを使って土日のボタンを色替えをします。

ちなみにデフォルトではこのようになっています。
f:id:iyemon018:20160510223154p:plain

1. カレンダーコントロールの構成

CalendarのControlTemplateはいくつかのパーツによって構成されています。
CalendarControlを構成するパーツについては以下を参照してください。

Calendar のスタイルとテンプレート

各パーツは以下の様に配置、使用されています。

f:id:iyemon018:20160510223246p:plainf:id:iyemon018:20160510223249p:plain

Calendarには年月を指定したMonthViewと月を指定する、もしくは表示する年数を指定するYearViewが存在します。
上記の図にもあるように、MonthViewで表示されている日付ボタンには"CalendarDayButtonStyle"が使用され、YearViewで表示されているボタンには"CalendarButtonStyle"が使用されています。
今回はこの"CalendarDayButtonStyle"にコンバーターを設定していきます。

2. CalendarDayButtonStyleの構造

CalendarDayButtonStyleもCalendarItem同様、少々複雑な構造になっています。

f:id:iyemon018:20160510223404p:plain

このように7つのレイヤーに分かれており、それぞれ以下の特性を持ちます。

①DayButtonFocusVisual

フォーカスを取得した時に表示するRectangle
表示の切り替えはViewStateManagerで行う。

②Blackout

選択不可能な日付であることを表す✕マークを表示する。
CalendarのBlockoutDatesに設定した日付はこのマークを表示する。

③NormalText

日付の日数分を表示する。
このボタンコントロールのContentPresenter。

④HighlightBackground

MouseOver,Pressed,Disable状態に表示するRectangle

⑤(Border)

ボタンの枠線

⑥SelectedBackground

選択された場合に表示するRectangle

⑦TodayBackground

DataContextの日付がDateTime.Todayの場合に表示する背景Rectangle

そして、CalendarDayButtonStyleのTemplateには、DataContextにCalendarのDateTimeがバインドされています。

3. コンバーターの実装

コンバーターはバインドしたDateTimeのDayOfWeekの値によってブラシリソースを切り替えます。

namespace CalendarSample1
{
    using System;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Media;

    /// <summary>
    /// <see cref="T:DateTime"/>の値から曜日の<see cref="T:Brush"/>リソースへのコンバータークラスです。
    /// </summary>
    /// <seealso cref="System.Windows.Data.IValueConverter" />
    [ValueConversion(typeof(DateTime), typeof(Brush))]
    public class DateTimeToDayOfWeekBrushConverter : IValueConverter
    {
        /// <summary>
        /// 日曜日用のブラシリソース
        /// </summary>
        public static Brush SundayBrush
        {
            get { return Brushes.Red; }
        }

        /// <summary>
        /// 土曜日用のブラシリソース
        /// </summary>
        public static Brush SaturdayBrush
        {
            get { return Brushes.Blue; }
        }

        /// <summary>値を変換します。</summary>
        /// <returns>変換された値。 メソッドが null を返す場合は、有効な null 値が使用されています。</returns>
        /// <param name="value">バインディング ソースによって生成された値。</param>
        /// <param name="targetType">バインディング ターゲット プロパティの型。</param>
        /// <param name="parameter">使用するコンバーター パラメーター。</param>
        /// <param name="culture">コンバーターで使用するカルチャ。</param>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(value is DateTime))
            {
                return DependencyProperty.UnsetValue;
            }

            var dayOfWeek = ((DateTime)value).DayOfWeek;
            switch (dayOfWeek)
            {
                case DayOfWeek.Sunday:
                    return SundayBrush;
                case DayOfWeek.Saturday:
                    return SaturdayBrush;
                default:
                    return DependencyProperty.UnsetValue;
            }
        }

        /// <summary>値を変換します。</summary>
        /// <returns>変換された値。 メソッドが null を返す場合は、有効な null 値が使用されています。</returns>
        /// <param name="value">バインディング ターゲットによって生成される値。</param>
        /// <param name="targetType">変換後の型。</param>
        /// <param name="parameter">使用するコンバーター パラメーター。</param>
        /// <param name="culture">コンバーターで使用するカルチャ。</param>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

これをリソースディクショナリに追加します。

    <local:DateTimeToDayOfWeekBrushConverter x:Key="DateTimeToDayOfWeekBrushConverter" />

あとは、CalendarDayButtonStyleに以下のように設定します。

    <Style x:Key="DefaultCalendarDayButtonStyle" TargetType="{x:Type CalendarDayButton}">
        <Setter Property="Foreground" Value="{Binding Converter={StaticResource DateTimeToDayOfWeekBrushConverter}, Mode=OneWay}" />
        <!--  省略  -->
        <Setter Property="Template">
                    <!--  省略  -->
                        <!--  TextElement.Foreground で直接文字色を設定しているのでコメントアウト or 削除します。  -->
                        <ContentPresenter x:Name="NormalText"
                                          Margin="5,1,5,1"
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                                          <!--TextElement.Foreground="#FF333333" />-->
                    <!--  省略  -->
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

これで実行するとこのようになります。
f:id:iyemon018:20160510224330p:plain

日付ボタンを色替えすることはできましたが、曜日ヘッダー部分が変わっていません。
ついでにこれも色替えしましょう。

曜日ヘッダー部分の文字列はCalendarItem.DayTitleTemplateResourceKeyのDataTemplateによってCalendarItemに定義されています。
表示している曜日の文言はOSの言語設定に依存していて、日本語の場合、"日"~"土"までを表示します。
これに以下のように手を加える事で曜日の文字色も色替えすることが可能です。

    <Style x:Key="DefaultCalendarItemStyle" TargetType="{x:Type CalendarItem}">
        <Setter Property="Margin" Value="0,3,0,3" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type CalendarItem}">
                    <ControlTemplate.Resources>
                        <DataTemplate x:Key="{x:Static CalendarItem.DayTitleTemplateResourceKey}">
                            <!--  ForegroundはStyleへ移動させる。  -->
                            <TextBlock Margin="0,6,0,6"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center"
                                       FontFamily="Verdana"
                                       FontSize="9.5"
                                       FontWeight="Bold"
                                       Text="{Binding}">
                                <TextBlock.Style>
                                    <Style TargetType="{x:Type TextBlock}">
                                        <Setter Property="Foreground" Value="#FF333333" />
                                        <Style.Triggers>
                                            <!--  バインドされている値によって文字色を切り替える。  -->
                                            <DataTrigger Binding="{Binding }" Value="日">
                                                <Setter Property="Foreground" Value="{Binding Source={StaticResource DateTimeToDayOfWeekBrushConverter}, Path=SundayBrush, Mode=OneWay}" />
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding }" Value="土">
                                                <Setter Property="Foreground" Value="{Binding Source={StaticResource DateTimeToDayOfWeekBrushConverter}, Path=SaturdayBrush, Mode=OneWay}" />
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBlock.Style>
                            </TextBlock>
                        </DataTemplate>
                    </ControlTemplate.Resources>
                    <Grid x:Name="PART_Root">
                   <!--  省略  -->
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

これを実行すると以下のようになります。
f:id:iyemon018:20160510230444p:plain

曜日も日付ボタンも色替えができましたね。

このようにCalendarのスタイルをカスタマイズするだけで簡単にボタンの色替えなどが実現できます。


今回使用したソースはこちら↓↓↓
github.com

Staticなフィールドをバインドするには

WPFではViewのDataContextからプロパティをバインドしてデータの入出力ができますが、
staticフィールドのバインドも可能になっています。

例えば、DateTime.Todayをバインドする場合は次のようになります。

<TextBlock Text="{Binding Source={x:Static system:DateTime.Today}, Mode=OneWay, StringFormat={}{0:yyyy/MM/dd(dddd)}, ConverterCulture=ja}" />

Binding Source= の部分をx:Static system:DateTime.Today とかにしてしまいがちなので注意。
こういうのってよく忘れて毎回StackOverflowに辿り着くんですよね…