読者です 読者をやめる 読者になる 読者になる

secretbase.log

コードはすべてNYSLです

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

C# .NET NuGet

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 サービスとしてインストールする

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に設定できる数字の範囲

WiX Windows Installer

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 C#

はじめに

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を用いたほうが良い

C# .NET

定義した 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 を用いましょう。

コレクションをソートして取り出す際にインデックスつける

C# Python

コレクションから要素を取り出していく際に、連番をつけたいことがあります。

例えば下記のような文字列の配列を、ソートしてインデックスとともに要素を取得したい、といった場合のTipsです。

"Banana", "Apple", "Melon"

ソートし取り出す際にインデックスつける

C#

C# の場合は、 Selectメソッドにオーバーロードがあり、それを用いることで実現できます。

string[] fruits = { "Banana", "Apple", "Melon" };

foreach(var item in fruits.OrderBy(n => n).Select((v, i) => new {v, i}))
{
    Console.WriteLine($"{item.i} {item.v}");
}
0 Apple
1 Banana
2 Melon

Python

Pythonでは 組み込み関数である enumerate を使います。 enumerateはインデックスと要素をタプルで返してくれます。

fruits = ["Banana", "Apple", "Melon"]

for index , item in enumerate(sorted(fruits)):
    print(index, item)
0 Apple
1 Banana
2 Melon

いずれも、シンプルに実装できますね。

参考

名前空間の名前

C# .NET

C# の場合、namespace はどのように付けたら良いか、以前調べた。

https://msdn.microsoft.com/ja-jp/library/ms229026(v=vs.100).aspx

から抜粋。

一般的な形式

 <Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]

説明

  • 名前空間名には、プリフィックスとして企業名を付け、各企業の名前空間の名前とプリフィックスが重複しないようにしてください。
  • 名前空間の第 2 レベルには、バージョンに依存しない、安定した製品名を使用してください。
  • 企業内のグループ名は変更されることが多いため、名前空間の階層構造での命名基準として組織の階層構造を使用することは避けてください

なるほど、名前空間に、企業内の部署名をつけてしまうのは悪手のようだ。

説明(2016 10月更新版)

更新されていたことに気づいたので追記。

https://msdn.microsoft.com/ja-jp/library/ms229026(v=vs.110).aspx

✓は守るべき内容、Xは避けるべき内容。 機械翻訳があれなので、意訳して掲載。

  • ✓ 異なる会社が名前空間を同じにすることを防ぐために、名前空間名に会社名を付けること。
  • 名前空間名の第2レベルでは、バージョンに依存しない安定した製品名を使用してください。
  • X 企業内のグループ名は有効期間が短い傾向があるため、名前空間の階層内の名前の基準として組織階層を使用しない。
    • 関連技術のグループの周囲の名前空間の階層で整理すると良い。
  • pascal 表記を使用し、名前空間をピリオドで区切る (例: Microsoft.Office.PowerPoint)。 ブランド名はこの限りではなく逸脱しても良い。
  • ✓ 適切な場所に複数系の名前空間名を使用することを検討してください。
    • 例えば、System.Collection の代わりに System.Collections を使用します。ただし、ブランド名や頭字語はこの規則の例外です。例えば、System.IOs ではなく System.IO を使用します。
  • X 名前空間とその名前空間の型に同じ名前を使用しないでください。
    • たとえば、名前空間名としてDebugとして、同じ名前空間にDebugという名前のクラスも指定しないようにする。コンパイラに対し、型を完全修飾する必要がでてきます。