secretbase.log

.NET/C#/Pythonなど

Windows 10 のライフサイクル

Windows 10 の Fall Creators Update がリリースされました。 過去の更新プログラムのサポート終了を調べましたのでメモします。

https://support.microsoft.com/ja-jp/help/13853/windows-lifecycle-fact-sheet

更新プログラムと Service Pack

デスクトップ オペレーティング システム 提供日 サポート終了
Windows Vista SP1 2008年2月4日 2011年7月12日
Windows Vista SP2 2009年5月26日 2017年4月11日
Windows 7 SP1 2011年2月22日 2020年1月14日
Windows 8.1 2013年10月18日 N/A
Windows 10 Version 1507 (初期バージョン) 2015年7月29日 2017年5月9日
Windows 10 Version 1511 (November Update) 2015年11月10日 2017年10月10日
Windows 10 Version 1607 (Anniversary Update) 2016年8月2日 2018年3月(仮)
Windows 10 Version 1703 (Creators Update) 2017年4月5日 2018年9月(仮)
Windows 10 Version 1709 (Fall Creators Update) 2017年10月17日 2019年3月(仮)

Windows 10 サポートのロードマップ

f:id:cointoss1973:20171019111231p:plain

Anniversary Update は最近出たような気がしていましたが、来年3月にはサポート終了します。

.NET Framework と WPF についてバージョン毎の機能

.NET FrameworkWPF について主にバージョン毎の機能についてまとめた資料をメモします。

.NET

タイトル URL
.NET Framework のバージョン対応表 .NET Framework のバージョン対応表 - Qiita
.NET Framework のバージョンおよび依存関係 docs.ja-jp/versions-and-dependencies.md at master · dotnet/docs.ja-jp · GitHub
.NET Framework の新機能 .NET Framework の新機能 | Microsoft Docs

WPF

タイトル URL
WPF バージョン履歴 (WPF: Versions, History and Major Enhancements) https://www.codeproject.com/Articles/1035800/WPF-Versions-History-and-Major-Enhancements
WPF Version 4.5 の新機能 WPF Version 4.5 の新機能

 

プログラミング.NET Framework 第4版 (Microsoft Press)

プログラミング.NET Framework 第4版 (Microsoft Press)

         

NuGet クイックスタート | nupkg をつくる

NuGet の公式ドキュメント からたどれる場所に nupkg を作るための Quickstart がおいてありまして参考になりました。

TL;DR

  • nugetコマンドがある
  • nuget spec hoge.csproj にて .nuspec のひな形がつくれる
  • nuget pack hoge.csproj にてパッケージ(.nupack)がつくれる
  • nuget push hoge.1.0.0.0.nupkg にて パッケージを公開できる

   

Quickstart の場所

まずは、 Quickstartへの行き方です。

nuget.org の Documentation をクリックします。

f:id:cointoss1973:20170313133100p:plain

Create a Package をクリックします。

f:id:cointoss1973:20170313133110p:plain

ここです。(最初は Create a Package だけだったのかもしれませんね。 Publish まで含まれています)

f:id:cointoss1973:20170313133125p:plain

では、Quickstart に沿って、簡単にメモしながら実行していきたいと思います。

   

事前準備 必要なインストール

  1. VS2015 を準備してください
  2. nuget.exe の 最新 をダウンロードして、好きな場所においてください。

f:id:cointoss1973:20170313094030p:plain

nuget.exe をダウンロードしてパスの通っている箇所におきました。

現時点(2017/3/15)では v3.5.0 が推奨の最新版 (Recommended latest)です。

クラスライブラリ プロジェクトの作成

  1. クラスライブラリのプロジェクトを作成してください

f:id:cointoss1973:20170313133138p:plain

AppLogger というクラスライブラリをつくりました。    

.nuspec パッケージマニフェストファイルを作成する

NuGetパッケージは、内容と依存関係を記述するためのマニフェストファイル(.nuspec)が必要です。 nugetコマンドラインでファイルをつくれます。カスタマイズしてください。

  1. コマンドプロンプトを開いて、csproj の場所に移動してください
  2. nuget spec を実行してね ( AppLogger.nuspec がつくられます)
  3. 好みのテキストエディタで開いて編集してください
    • $<token>$ で囲まれた箇所は Properties/AssenblyInfo.cs ファイルの内容に置き換わります
  4. パッケージIDを選んでね。名前空間のネーミング規約を適用することをお勧めします。

手順に沿って、nuspecファイルのひな形を作成します。

f:id:cointoss1973:20170314155405p:plain

AppLogger.nuspec ファイルが生成されました。

エディタで開いてみます。(使ったエディタは VSCodeです。)

f:id:cointoss1973:20170314160223p:plain

  • 下記のような感じで nuspec ファイルを編集します。
 <?xml version="1.0"?>
 <package>
     <metadata>
     <id>MyCompanyName.MyProductName.MyPackageName</id>
     <version>$version$</version>
     <title>$title$</title>
     <authors>kraigb</authors>
     <owners>kraigb</owners>
     <requireLicenseAcceptance>false</requireLicenseAcceptance>
     <description>Awesome application logging utility</description>
     <releaseNotes>First release</releaseNotes>
     <copyright>Copyright 2017</copyright>
     <tags>application app logger logging logs</tags>
     </metadata>
 </package>

 

パッケージをつくる

  • nuget pack AppLogger.csproj のコマンドを実行してください。
    • AppLogger.1.0.0.0.nupkg がつくられます。
  • 注意: デフォルト値から更新していない場合、警告が表示されることに注意してください。

nuget pack コマンドの実行にて、無事、nupkg ができました。

   

パッケージの公開

nuget.org の場合

nuget.org にパッケージを公開する場合の手順です。

  1. nuget.org のフリーアカウントをとるか持っている人はログインしてください。
  2. ログインしたらユーザー名をクリックすると設定画面に飛びます。
  3. API Key を Copy to Clipboard を押してコピー (API Key は秘密にしてください)してください。
  4. nuget push AppLogger.1.0.0.0.nupkg 47be3377-c434-4c29-8576-af7f6993a54b -Source https://www.nuget.org/api/v2/package で公開できます。

以上が、Quickstart の手順となります。

     

おまけ

ここからは、次のステップに向けていくつか調べた内容を記載していきます。

ローカル環境でパッケージを利用する場合

マシンのローカルにおいて利用(インストール)することもできます。

C:\data\NuGetPackeges を作成し、そこに パッケージを追加する方法です。

以下のバッチファイルを用意しました。ビルド後に実行することで パッケージを追加します。

nuget pack foo.csproj -Prop Configuration=Release

set PkgDir=C:\data\NuGetPackages
if EXIST %PkgDir% (
    for %%N in ( *.nupkg ) do (
        %NUGET% add %%N -source %PkgDir%
    )
)

Visual Studio のソリューションのパッケージ管理のパッケージソースを追加することで NuGet経由で参照することができます。

f:id:cointoss1973:20170313093435p:plain

ありそうな質問

クイックガイドを行った時点でありそうな質問をまとめます。

Debug ビルドの成果物がPack対象になっているみたい。Releaseにできない?

nuget pack foo.csproj は、 bin/Debug 配下の DLL を Pack します。

bin/Release をPackしたい場合は、下記オプション指定でOKです。

> nuget pack Foo.csproj -Prop Configuration=Release`

まとめ

  • nuget spec hoge.csproj にて .nuspec のひな形がつくれる
  • nuget pack hoge.csproj にてパッケージ(.nupack)がつくれる
  • nuget push hoge.1.0.0.0.nupkg にて パッケージを公開できる

参考

nginx を Windows サービスとしてインストールする

はじめに

常時動作しているWindowsマシンがあり、 nginx と Webアプリケーション(nodejsまたはGoやPythonで記述)の組み合わせて提供することができそうで環境構築をしたのでメモします。

nginx は Windows の場合コマンドラインアプリケーションとして動作させることができます。しかしサービス化は、将来の予定にはあるようですが、現時点ではまだ提供されていません。

winswを使って実現しているblogがいくつかありましたので、最新のバージョンの手順です。

  • nginx 1.11.10 (現時点のmainlineバージョン)
  • winsw 2.0.2.0 (現時点の最新)

f:id:cointoss1973:20170306124538p:plain

nginx のインストールと動作確認

まずは、単体アプリケーションとして起動を確認します。

  1. 公式サイトから zip をダウンロードして、C:\nginx に展開します
  2. コマンドプロンプトを開きます。
  3. start nginx にて起動します
  4. Windows セキュリティの重要な警告 ダイアログが表示されますので「アクセスを許可する(A)」を選択します

f:id:cointoss1973:20170306123939p:plain

  1. http://localhost をブラウザで開いてWelcome to nginx! ページが表示されることを確認します

f:id:cointoss1973:20170306123923p:plain

正しくインストールされていることを確認できました。

Windowsサービス化

次に、Windowsサービス化をします。

サービス化には、任意のプロセスをサービス化することができる winsw を利用します。

  1. WinSW をダウンロードします

    • WinSW 2.x系は、.NET Framework 2系(~3.5) と、 .NET Framework 4系が提供されています
    • 現時点では、 V2.0.2 が 最新です
  2. ダウンロードした WinSW.exe を nginxsvc.exe にリネームします。(この名前は好きな名前で良いです)

  3. exe と同じ名前の xml を作成します (ここでは nginxsvc.xml を作成します)

<service>
  <id>nginx</id>
  <name>nginx</name>
  <description>nginx</description>
  <executable>c:\nginx\nginx.exe</executable>
  <logpath>c:\nginx\</logpath>
  <logmode>roll</logmode>
  <depend></depend>
  <startargument>-p c:\nginx</startargument>
  <stopargument>-p c:\nginx -s stop</stopargument>
</service>
  1. 管理者のコマンドプロンプトを開きます

  2. C:\nginx\nginxsvc.exe install を実行します

起動

f:id:cointoss1973:20170306123956p:plain

  • nginx がサービスとして登録されていますので右クリックして起動します

エラー

あれ、エラーになりました。

nginxsvc.err.log にエラーが出力されていますので、中身を確認します。

nginx: [alert] could not open error log file: CreateFile() " c:\nginx/logs/error.log" failed (123: The filename, directory name, or volume label syntax is incorrect)
2017/03/06 09:35:40 [emerg] 10528#10192: CreateFile() " c:\nginx/conf/nginx.conf" failed (123: The filename, directory name, or volume label syntax is incorrect)

ファイル、ディレクトリ名が不正のようです。

起動オプションの修正

nginxsvc.xml の定義にて、nginx を起動時の引数として -p オプションで指定しているのですが、このあたりが影響してそうです。 削除して試してみます。

C:\nginx\nginxsvc.exe uninstall にて、一度アンインストールします。

<service>
  <id>nginx</id>
  <name>nginx</name>
  <description>nginx (powered by WinSW)</description>
  <executable>c:\nginx\nginx.exe</executable>
  <logpath>c:\nginx\</logpath>
  <logmode>roll</logmode>
  <depend></depend>
  <startarguments></startarguments>
  <stoparguments>-s stop</stoparguments>
</service>

xmlファイル修正 【2017/3/6 15時追記】

  • startarguments 、 stoparguments (複数形にする)必要があります
    • これにしておかないと Windowsサービスが停止できない状況が発生します

実行

C:/nginx/nginxsvc.exe install を再度実行します。

うまく起動しました。 ブラウザで http://localhost を開いてみます。

logs\access.log にもアクセス記録が追記されていますので、 conf フォルダや logs フォルダにもアクセスできています。

参考

nginx

winsw

Windows インストーラー: Versionに設定できる数字の範囲

WIndowsインストーラーのバージョン

プログラムのアンインストールまたは変更で、インストールしたプログラムのバージョンを表示することができます。 下記は、 1.0.0.0 と設定しているプログラムの例です。

f:id:cointoss1973:20170301135609p:plain

WiXでは、下記 Product Element の Version で定義します。

 <Product Id="{AAEE70F7-54D1-46C4-B351-A7FFADD67405}"
           Name="Acme FunctionSample"
           Language="1033" Version="1.0.0.0" Manufacturer="Acme Ltd."
           UpgradeCode="d3ade0eb-6e9a-4cb5-bd00-4282ef774434">

インストーラーのバージョン番号の範囲

           Version="1.400.0.0"

ここで、1.400.0.0 を設定してみました。

しかし、Candle でエラー error CNDL0242 になります。

error CNDL0242: Invalid product version ‘1.400.0.0’. Product version must have a major version less than 256, a minor version less than 256, and a build version less than 65536.

  • メジャー番号と、マイナーバージョンは、 256 より小さい数字
  • またビルド番号は、 65536 より小さい数字

でないとダメとのことです。

調べたところ、これは、Windowsインストーラーの仕様のようです。

ProductVersion property (Windows)

しかし、アセンブリバージョンでは、 65536まで設定できるので、同じ番号をつけたかったのですが、しかたないですね(´・ω・`)

WPFアプリケーションをEXEひとつにまとめる

はじめに

WPFアプリケーションをインストーラーなどで配置する場合、Prismなどライブラリを使うとDLLを複数配置する必要があります。 WiXを用いる場合は、heat でまとめて wxsソースを自動生成して…といったアプローチが常套手段となりますが、EXEにDLLをマージすることで、EXEひとつを配布する方法を調べました。

ILMergeを使うとDLLをマージできるのですが、XAMLのリソースに対応していないためWPFでは使えないようです。

そこで、さらに調べたところ、下記ブログに記載されている方法がズバリな方法だったので、ご紹介します。 http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application

参照しているDLLをリソースファイルとして埋め込むことで、まとめるというアプローチです。

手順

csproj に追記

csprojをエディタで開きます。 最後の方に追加できるような形でコメントが記載されています。

  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

このコメントの後に下記を追記します。

<Target Name="AfterResolveReferences">
  <ItemGroup>
    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>
</Target>

ビルド結果の確認

一度ビルドしてみましょう。 ビルドされた実行ファイルを 逆コンパイラのILSpy でみると、リソースファイルとして埋め込まれていることがわかります。

http://ilspy.net/

DLLの読み込み処理の追加

実行時にDLLを読み込むための処理を追加します。 まず Program.cs を追加します。 下記処理を実装します。

    public class Program
    {
        /// <summary>
        /// Main
        /// </summary>
        [STAThread]
        public static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
            App.Main();
        }

        private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
        {
            Assembly executingAssembly = Assembly.GetExecutingAssembly();
            AssemblyName assemblyName = new AssemblyName(args.Name);

            string path = assemblyName.Name + ".dll";
            if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
            {
                path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
            }

            using (Stream stream = executingAssembly.GetManifestResourceStream(path))
            {
                if (stream == null)
                    return null;

                byte[] assemblyRawBytes = new byte[stream.Length];
                stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
                return Assembly.Load(assemblyRawBytes);
            }
        }
    }

次に、アプリケーションのスタートアップオブジェクトを .Program に設定してください。

これでDLLを埋め込んだ状態で起動したりデバッグ実行したりすることができます。

参考

WPF 4 Unleashed

WPF 4 Unleashed

例外処理は重いのでEnum.TryParseを用いたほうが良い

定義した Enum のメンバーに例えば入力した文字列が合致した場合に変換するといったケースはよくあります。

そういった場合に使えるメソッドが標準で用意されています。

文字列表現を Enum に変換するメソッド

  • Enum.Parse

    • Parse は戻り値として変換結果を返します。変換出来ない場合は、例外を返すメソッドです。
  • Enum.TryParse

    • TryParse は変換出来ない場合は、戻り値として bool を返します。

使用例

ここでは Enum として元素記号(H, He, Li, Be...と元素番号順)を定義します。

  • 入力
    • 入力はなんでも良いのですが、あらかじめAからZのアルファベットの文字列配列を用意し、AからZまで順番に元素記号に変換できるかを試みます。
    • つまり、Aに該当する元素記号は存在せず、Bに該当する元素記号は存在するといった具合です。
      • 半分くらいが存在しないケースになります。
  • 出力
    • 存在した場合はそのメンバーと存在しない場合は 0 を返す、という仕様とします。

ChemicalElementsクラスは下記となります。

static class ChemicalElements
{
    /// <summary>
    /// 元素記号
    /// </summary>
    public enum SymbolsOfElements
    {
        H = 1, He,
        Li, Be, B, C, N, O, F, Ne,
        Na, Mg, Al, Si, P, S, Cl, Ar,
        K, Ca, Sc, Ti, V, Cr, Mn, Fe, Co, Ni, Cu, Zn, Ga, Ge, As, Se, Br, Kr,
        Rb, Sr, Y, Zr, Nb, Mo, Tc, Ru, Rh, Pd, Ag, Cd, In, Sn, Sb, Te, I, Xe,
        Cs, Ba, La, Ce, Pr, Nd, Pm, Sm, Eu, Gd, Tb, Dy, Ho, Er, Tm, Yb, Lu, Hf, Ta, W, Re, Os, Ir, Pt, Au, Hg, Tl, Pb, Bi, Po, At, Rn,
        Fr, Ra, Ac, Th, Pa, U, Np, Pu, Am, Cm, Bk, Cf, Es, Fm, Md, No, Lr, Rf, Db, Sg, Bh, Hs, Mt, Ds, Rg, Cn, Nh, Fl, Mc, Lv, Ts, Og,
    };

    /// <summary>
    /// Enum.Parse による実装
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    static public SymbolsOfElements GetSymbolParse(string name)
    {
        SymbolsOfElements symbol;
        try
        {
            symbol = (SymbolsOfElements)Enum.Parse(typeof(SymbolsOfElements), name);
        }
        catch
        {
            symbol = 0;
        }
        return symbol;
    }


    /// <summary>
    /// Enum.TryParse による実装
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    static public SymbolsOfElements GetSymbolTryParse(string name)
    {
        SymbolsOfElements symbol;
        Enum.TryParse(name, out symbol);
        return symbol;
    }
}

上記クラスを利用するクラスは下記のような形です。

class Program
{
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
    
        // A から Z までの文字列配列を作成する
        var alphabets = Enumerable.Range(0, 26).Select( n => ((char)('A' + n)).ToString() );

        // TryParse による処理
        Console.WriteLine("処理開始 [TryParse実装] ...");
        sw.Start();
        for (int i = 0; i < 100; i++)
        {
            foreach (var alphabet in alphabets)
            {
                var symbol = ChemicalElements.GetSymbolTryParse(alphabet);
            }
        }
        sw.Stop();
        Console.WriteLine($"処理時間: {sw.Elapsed}\n");


        // Parse による処理
        Console.WriteLine("処理開始 [Parse実装] ...");
        sw.Restart();
        for (int i = 0; i < 100; i++)
        {
            foreach (var alphabet in alphabets)
            {
                var symbol = ChemicalElements.GetSymbolParse(alphabet);
            }
        }
        sw.Stop();
        Console.WriteLine($"処理時間: {sw.Elapsed}\n");

        Console.Read();

    }
}

実行結果

処理開始 [TryParse実装] ...
処理時間: 00:00:00.0036813

処理開始 [Parse実装] ...
処理時間: 00:00:05.8251481

いずれも同じ結果となる処理が、TryParseの場合は、4ms 未満に対し、Parseの場合は、5.8秒となりました。 1500倍くらい差がありますね。

例外処理は重いので Enum.TryParse を用いましょう。