漫坊亭

社会の底辺プログラマ

自転車 モスボール化

電アシのジェッターばかり乗るので、他は動態保存からモスボール化することにした。

1台目:カーボンロード(ジャイTCR f:id:jfactory:20160218105437j:plain

2台目:アルミ折りたたみ(ブリジストンの何か f:id:jfactory:20160218105434j:plain

3台目:フルサスMTBTREKの何か f:id:jfactory:20160218105436j:plain

この後、ビニールでくるむ予定。

CallerInfoを、.NET40 で使う

HDD整理中・・・

いまだに.NET40縛りでのコーディングが発生することがある。 制御系は古いもののメンテが多い(はやくXP消えてくれ)

開発ホストがVS2012以降なら、新しいC#の機能が使える場合もある。 もちろん、実機ではデバッグできない。

CallerInfo

ログ出力やWPFでは、もはや必須の機能である。これって、実はコンパイラの機能らしい。なので、Visual Studio 2012以降なら、ターゲットフレームワークが4.5でなくても使えないわけではない。

namespace System.Runtime.CompilerServices
{
#if !NET_45_OR_GREATER
    /// <summary>メソッドの呼び出し元のメソッドまたはプロパティの名前を取得できるようにします。</summary>
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public class CallerMemberNameAttribute : Attribute
    {
    }

    /// <summary>呼び出し元を含むソースファイルの完全パスを取得します。</summary>
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public class CallerFilePathAttribute : Attribute
    {
    }

    /// <summary>割り当てメソッドが呼び出されるソース ファイルの行番号を取得します。</summary>
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public class CallerLineNumberAttribute : Attribute
    {
    }
#endif
}

これをプロジェクトに追加すると、コンパイラが良きに計らってくれる。

class Program
{
    static void Main(string[] args)
    {
        var obj = new Foo();
        obj.Bar("テスト");

        Console.ReadLine();
    }
}

public class Foo
{
    public void Bar(
        string message,
        [CallerMemberName] string memberName = null,
        [CallerFilePath] string filePath = null,
        [CallerLineNumber] int lineNumber = 0)
    {
        Console.WriteLine("CallerMemberName = {0}", memberName);
        Console.WriteLine("CallerFilePath   = {0}", filePath);
        Console.WriteLine("CallerLineNumber = {0}", lineNumber);
        Console.WriteLine("CallerLineNumber = {0}", message);
    }
}

これで、ターゲットフレームワークが固定なら良いが、プロジェクトを.Net4.5でも使いたい場合は、ビルド時に判定する必要がある。

.NETのバージョン判定を行う。

TargetFrameworkVersionを文字列として分解し、バージョンを示すシンボルを定義する。C言語プリプロセッサのように内容を入れることができないので、個々のシンボルを作る。

  • NET_20
  • NET_30
  • NET_35
  • NET_40
  • NET_45
  • NET_451
  • NET_20_OR_GREATER
  • NET_30_OR_GREATER
  • NET_35_OR_GREATER
  • NET_40_OR_GREATER
  • NET_45_OR_GREATER

どこか(たぶんstack overflowとか)で拾ったものをベースに、4.5と4.51を追加したような記憶があるので、原著者に感謝!

<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    
    <!-- NET Frameworkのバージョンに関する定数を定義する。 -->
    <!-- NET_20, NET_30, NET_35, NET_40, NET_45, NET_451, -->
    <!-- NET_20_OR_GREATER, NET_30_OR_GREATER, NET_35_OR_GREATER, NET_40_OR_GREATER, NET_45_OR_GREATER -->

    <!-- メジャーバージョン、マイナーバージョン、ビルド番号の結果を入れる -->
    <VerMajor>0</VerMajor>
    <VerMinor>0</VerMinor>
    <VerBuild>0</VerBuild>

    <!-- Pos1:最初のピリオドの位置 / Pos2:次のピリオドの位置 / Pos1n,Pos2nは、それぞれのPosに+1している -->
    <Pos1>-1</Pos1>
    <Pos1n>-1</Pos1n>
    <Pos2>-1</Pos2>
    <Pos2n>-1</Pos2n>

    <!-- v4.5.1とかv2.0とか入っているので、まずは'v'を除去 -->
    <Tmp>$(TargetFrameworkVersion.Replace('v', ''))</Tmp>

    <!-- ピリオドがなければPos1に-1が入るので、Pos2に突入させない-->
    <Pos1>$(Tmp.IndexOf("."))</Pos1>
    <Pos1n>$([MsBuild]::Add($(Pos1), 1))</Pos1n>
    <Pos2 Condition="0 &lt;= $(Pos1)">$(Tmp.IndexOf(".", $([MsBuild]::Add($(Pos1), 1))))</Pos2>
    <Pos2n>$([MsBuild]::Add($(Pos2), 1))</Pos2n>
    
    <!-- 最初のピリオドがあれば、そこまでを VerMajor にする。ピリオドがなければ、全てを VerMajor にする。-->
    <VerMajor  Condition="0 &lt;= $(Pos1)">$(Tmp.SubString(0, $(Pos1)))</VerMajor>
    <VerMajor  Condition="0 &gt;  $(Pos1)">$(Tmp)</VerMajor>

    <!-- 次のピリオドがあれば、 最初のピリオドから次のピリオドまでを、VerMinorにする。なければ、残りをVerMinorにする-->
    <VerMinor  Condition="0 &lt;= $(Pos2)">$(Tmp.SubString($(Pos1n), $([MsBuild]::Subtract($(Pos2), $(Pos1n)))))</VerMinor>
    <VerMinor  Condition="0 &gt;  $(Pos2) And 0 != $(Pos1n)">$(Tmp.SubString($(Pos1n)))</VerMinor>
    
    <!--2つ目のピリオドがあれば、そこから後ろを VerBuild にする-->
    <VerBuild  Condition="0 &lt;= $(Pos2)">$(Tmp.SubString($(Pos2n)))</VerBuild>

    <!-- VerMajor,VerMinor,VerBuildを用いて、定数を作成する。 -->
    <DefineConstants>$(DefineConstants);NET_$(VerMajor)$(VerMinor)$(VerBuild)</DefineConstants>
    <DefineConstants Condition="2 &lt;= $(VerMajor)">$(DefineConstants);NET_20_OR_GREATER</DefineConstants>
    <DefineConstants Condition="3 &lt;= $(VerMajor)">$(DefineConstants);NET_30_OR_GREATER</DefineConstants>
    <DefineConstants Condition="3 &lt;= $(VerMajor) And 5 &lt;= $(VerMinor)">$(DefineConstants);NET_35_OR_GREATER</DefineConstants>
    <DefineConstants Condition="4 &lt;= $(VerMajor)">$(DefineConstants);NET_40_OR_GREATER</DefineConstants>
    <DefineConstants Condition="4 &lt;= $(VerMajor) And 5 &lt;= $(VerMinor)">$(DefineConstants);NET_45_OR_GREATER</DefineConstants>

  </PropertyGroup>

</Project>

これをNetFrameworkVersion.targetsという名前でプロジェクトに追加し、.csprojに下記の1行を追加する。

<Import Project="$(SolutionDir)NetFrameworkVersion.targets" />

ソース一式(拡張子をzipにして展開する)
f:id:jfactory:20160212093217j:plain

Visual Studio Installer (VS2015) カスタムアクション

visualstudiogallery.msdn.microsoft.com

  1. クラスライブラリプロジェクトを作る
  2. 参照の追加:System.Configuration.Install using System.Configuration.Install;
  3. Installerを継承したクラスを作る
namespace MyInstaller
{
    [RunInstaller(true)]
    public class MyCustomAction : Installer
    {
        /// <summary>Installs the specified state saver.</summary>
        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);
            MessageBox.Show($"id={Process.GetCurrentProcess().Id}", "MyCustomAction.Install");

            // ここに処理を書く
        }

        // public override void Uninstall(IDictionary savedState)
        // public override void Commit(IDictionary savedState)
        // public override void Rollback(IDictionary savedState)
    }
}
  1. カスタム動作に、作成したDLLを指定する f:id:jfactory:20160209111854p:plain f:id:jfactory:20160209111936p:plain

  2. デバッグするには

f:id:jfactory:20160209112149p:plain
ソリューションエクスプローラ右クリックからインストールを選択する インストールを進めるとダイアログが出るので、「出したまま」デバッグ~プロセスにアタッチ(msiexec.exeとプロセスIDを確認する)
f:id:jfactory:20160209113543p:plain f:id:jfactory:20160209113500p:plain MessageBox.Showの次の行にブレークポイントを置いて、ダイアログを閉じる。

  1. インストーラからデータを渡すには

f:id:jfactory:20160209113806p:plain カスタム動作のプロパティで、CustomActionDataを設定する。

f:id:jfactory:20160209114046p:plain

こんなのが使える。
Property Reference (Windows)

  1. インストールを失敗させるには System.Configuration.Install.InstallExceptionを投げれば良い。 (ロールバックが呼ばれる)

Debugger クラス

 こんなクラスがあったんだ(w

private void button1_Click(object sender, EventArgs e)
{
    Debugger.Launch();
}

 スタンドアロンで起動してボタンを押すと、こんなダイアログが出る。 f:id:jfactory:20160209090914p:plain

 するとDebugger.Launch();の行で停止した状態で、Visual studioが起動する。

Debugger クラス (System.Diagnostics)

 WinFormsのカスタムデザイナや、VisualStudioインストーラのカスタム動作のデバッグで活躍しそう!