20200824のC#に関する記事は4件です。

Serilogを使ってUWPアプリでログを出力できるようにする

背景

UWPアプリでログを出力できるようにしようとNLogを使おうとしたのですが、出力できませんでした。
いろいろ調べてみたところ、以下が候補として上がりました。

MetroLogは更新履歴が数年前で止まっていて、使えなさそうだと判断し、Serilogを使うことにしました。
また、日本語での情報がほとんどなかったので、記事として残そうと思います。
(UWPアプリ開発されている人が少ないんですね。。)

Serilogとは

公式サイトによると、

.NET用の他の多くのライブラリと同様に、Serilogはファイル、コンソール、その他の場所への診断ロギングを提供します。セットアップが簡単で、APIがクリーンで、最近の.NETプラットフォーム間での移植性があります。

他のロギングライブラリとは異なり、Serilogは強力な構造化イベントデータを念頭に置いて構築されています。

とのことです。
プラットフォームとしては、NET 4.5+, Windows (8/WinRT/Universal+) and Windows Phone 8+をサポートしています。

インストール方法

必要なのは、

  • Serilog
  • Serilog.Sinks.File

になります。

[ツール] > [NuGet パッケージ マネージャー] > [パッケージ マネージャー コンソール]で下記のコマンドを実行するか、

PM> Install-Package Serilog
PM> Install-Package Serilog.Sinks.File

ソリューション エクスプローラーで [参照] を右クリックし、 [NuGet パッケージの管理] を選択し、「Serilog」で検索して最新版をインストールしてください。

設定方法

以下のようにLoggerConfigurationクラスを使って設定を行います。
カスタムのロガークラスなどを作成するとよいでしょう。

string logFilePath = Path.Combine(Path.Combine(ApplicationData.Current.LocalFolder.Path, "logs/log.txt"));
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.File(logFilePath, rollingInterval: RollingInterval.Day)
    .CreateLogger();

MinimumLevelはログ出力の最小レベル、WriteTo.Fileは出力先ファイルを示しています。
ApplicationData.Current.LocalFolder.Pathはローカルストアを示していて、その中にlogsフォルダを掘ってさらにその下にlogが保存される仕組みです。
また、rollingIntervalはファイルの分割単位を示していて、RollingInterval.Dayは日毎にログファイルを分割する設定です。
ちなみに、ファイル容量はデフォルトで1GB、ファイル数は直近の31ファイルまで(1ヶ月分)保存されます。
(ファイル容量とファイル数の設定を変更することも可能ですが、今回は割愛します)

上記のように設定すると、c:\users<user name>\AppData\Local\Packages<app package identifier>\LocalState\logsに、以下のようにログファイルが保存されていきます。

log20180631.txt
log20180701.txt
log20180702.txt

他にも様々な設定ができるようなので、必要あればまた調べてみたいと思います。

使用方法

単純に出力したいログレベルの関数に文字列を渡すだけです。
ここは他のライブラリと同様かと思います。

Log.Information("Informationレベルで出力されます");
Log.Debug("Debugレベルで出力されます");
Log.Warning("Warningレベルで出力されます");
Log.Error("Errorレベルで出力されます");

参考URL

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

.NET Core で RIPEMD160 を使おう! with Bouncy Castle

ハッシュアルゴリズムの RIPEMD160 を Bouncy Castle を使って C# で実装した時のメモです。
このアルゴリズムは Bitcoin 等に使われてますね!

環境

  • .NET Core 3.1

RIPEMD160 が消えた

公式ドキュメント を見るとわかる通り、 .NET Framework では RIPEMD160 アルゴリズムが公式にサポートされていましたが、.NET Core では削除されていました。
リポジトリの Issue を見るに、(私の拙い英語力で解釈すると)
「OS 側で提供されないアルゴリズムは .NET Core では実装しないよ!」ということでしょうか?
困りました。

Bouncy Castle を使おう

Bouncy Castle は暗号化ライブラリの一つです。言語は Java と C# がサポートされています。
.NET Core でも使えます。
Nuget からサクっといれちゃいましょう。
執筆時点(2020/08/24)ではバージョンが最新ではないみたいなので、気になるようであればリポジトリの Release からどうぞ。

使い方

Org.BouncyCastle.Crypto.Digests 名前空間の RipeMD160Digest クラスを使います。
コード自体は簡単なものになります。

class Program
{
    static void Main(string[] args)
    {
        var data = Encoding.UTF8.GetBytes("abcdefg");

        var digest = new RipeMD160Digest();
        var result = new byte[digest.GetDigestSize()];
        digest.BlockUpdate(data, 0, data.Length);
        digest.DoFinal(result, 0);

        WriteBytes(result);
        //874f9960c5d2b7a9b5fad383e1ba44719ebb743a
    }

    private static void WriteBytes(IEnumerable<byte> bytes)
    {
        Console.WriteLine(string.Join("", bytes.Select(x => $"{x:x2}")));
    }
}
  1. RIPEMD160 のインスタンスを生成して、
  2. BlockUpdate を呼び出して、
  3. DoFinal で結果を得る。

という単純な使い方です。
(RipeMD160 の仕様は長さが20バイトなので、digest.GetDigestSize() の値も 20 で固定です。)

参考にしたもの

RipeMD160Digest.cs
GeneralDigest.cs

Bouncy Castle は C# に対するドキュメントが無いため、学ぶにはソースコードを読む必要があるなど一苦労かかりますね!
私の記事がなにかお役に立てれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[VisualStudio/ダンプ] ProcDumpを使ったダンプファイルの作成と解析

もくじ
https://qiita.com/tera1707/items/4fda73d86eded283ec4f

やりたいこと

自分が作成したあるアプリが、他の人のPCで動かしたときに例外を吐いてクラッシュしてしまう。
その原因を調べたい。できれば自分のPCでVisualStudioでデバッグ実行して、落ちた部分でbreakさせて調査をしたいが、自分のPCでは起きてくれず、リモートデバッグも出来ない状況だが、なんとかして手がかりを得たい。

やること

ダンプを採取して、VisualStudio(2019)で、落ちた時の状況を再現する。
ダンプの採取には、Microsoftのツール「Proc Dump」を使用する。

前提

今回ダンプのやり方を調べるきっかけとなった、アプリがクラッシュしてこまった時の状況は下記のようなものだった。

  • 状況
    • アプリは、VisualStudio2019、C#、.netFramework4.0で作成している。
    • アプリで特定の操作を、かなりの回数行ったときにアプリがクラッシュする。(BSODではない)
    • 起きている例外は「AccessViolationException」だった。
    • 自分の(開発用の)PCでは起きない。
    • テスターの方のPCでのみ起きる。
    • 最初、起きた時はReleaseビルドだったが、試した結果Debugビルドでも起きる。
    • テスターの方のPCで、Debugビルドで再現させてもらうことは可能。

そのため、この状況に対してなにか情報を得られるようなProcDumpの使い方を調べる。
ProcDumpは、ここで調べたやり方以外にもいろいろなことができる様子。

サンプルプログラム

調べるきっかけになったことは上記のようなことだが、実験的にアプリをクラッシュさせて、どういう動きをするのかを実験したかったので、実験の際は下記のサンプルプログラムでヒープの0番地にデータをコピーする、ということを行って、「System.AccessViolationException」でクラッシュするようにした。
このプログラムで、だいたい同じようなシチュエーションになっているはず。

AccessViolationExceptionでクラッシュ.cs
using System.Runtime.InteropServices;
using System.Windows;

namespace WpfApp48
{
    public unsafe partial class MainWindow : Window
    {
        internal class NativeMethods
        {
            [DllImport("kernel32.dll")]
            public static extern unsafe void CopyMemory(void* dst, void* src, int size);
        }
        public MainWindow()
        {
            InitializeComponent();
        }
        private unsafe void Button_Click(object sender, RoutedEventArgs e)
        {
            byte[] realsrc = new byte[3] { 1, 2, 3};
            byte[] realdest = new byte[3];

            fixed (void* src = realsrc)
            fixed (void* dest = realdest)
            {
                // ここで無理やり0番地にデータをコピーするようにした
                NativeMethods.CopyMemory((void*)0, src, realsrc.Length);
            }
        }
    }
}

やり方

何の例外で落ちたかを調べる

アプリが落ちた時に、何の例外で落ちたか?を確認する。

一般的な、クラッシュ原因の例外が何だったかの調べ方

イベントログに、該当のアプリがなぜクラッシュしたかが残るので、それを見て調べる。

  • イベントログの開き方
    • [Windowsキー + R]を押す
    • eventvwrと入力し、Enterを押す
    • [Windowsログ]の中の[Application]を開く

上のサンプルプログラムがクラッシュした際には、下記のようなイベントログが残る。
image.png
これで、
System.AccessViolationException」でクラッシュしたのだ、とわかる。

ProcDumpを使ってダンプファイルを作成する

ProcDumpをダウンロード

ProcDumpとは、指定したアプリが、指定した例外で落ちた時にダンプファイルを作成してくれるツール。
(色々な指定をして、ダンプを作成できる。詳しくは下記のページを参照)

下記のMicrosoftのページの「Downliad ProcDump」をクリックして、ProcDumpをダウンロードしてくる。
https://docs.microsoft.com/ja-jp/sysinternals/downloads/procdump

image.png
※調べた時点では、バージョンはv9.0。

ダウンロードしたzipを解凍すると、下記のようなファイルがある。
ここにある「procdump.exe」がツール本体。

ProcDump実行のためのパラメータを決める

こいつを実行する際に渡すパラメータで、何のアプリの、何の例外を取るか、というのを指定できる。
下記の欄に詳しく書かれている。
https://docs.microsoft.com/ja-jp/sysinternals/downloads/procdump#using-procdump
image.png

今回の、上の「前提」のところに書いた状況を鑑みて、ProcDumpには下記のパラメータを渡してダンプ作成させることにした。(動かす、クラッシュするアプリは実験用コードのものを使う)

  • ProcDump実行のためのコマンド
    • procdump -w -ma -e -f *Violation* WpfApp48.exe
パラメータ 説明
-w 対象のプロセスが起動するまで待つ。
指定しないと、procdump起動時「そんなプロセス起動してないよ」と言ってprocdump終わってまう
-e 例外が起きた時にダンプファイルを取るようにする。※
-f <例外の名前> ダンプをとる例外を指定する。
指定の際は「*」も使える。上記のように指定すると、AccessViolationExceptionも対象になってくれる。
-ma フルダンプ。
プロセスの仮想アドレス空間をすべて含む。※

その他のパラメータについては、こちらを参照。

-eパラメータについて
-e 1などとして、1を付けるとファーストチャンス例外をとる、とか公式に説明があるが、要するに、
1を付けるとtry catchで自前の例外処理をしてる部分でもダンプをとる、ということをする。
(1がないと、try catchしてるとこではダンプしない)
今回は、trycatchできていない(アプリがクラッシュしてしまう)例外のときにダンプしたいので、1は付けない。

-maフルダンプについて
-maのほかに、-mm-mpがある様子だが、msdocsにある説明だけではそれらがどう異なるのかよくわからないので別途実験をしてみた。→こちら参照。

ProcDumpを実行する

コマンドプロンプトから、上で決めたパラメータを渡してProcDumpを実行する。
※毎回コマンドプロンプトを開いてコマンド入力したくないので、バッチファイルを作って行う。

同じ階層にgodebug.batというファイルを作成し、下記のように書く。
image.png

godebug.bat
procdump -w -ma -e -f *Violation* WpfApp48.exe
pause

このバッチファイルをダブルクリックして実行すると、下記のような画面が開く。
※コマンドプロンプト中のWpfApp48.exeというのは、今回の実験のためのクラッシュさせるアプリの名前

image.png

これで、対象のアプリが動き出すのを待っている状態になった。

対象アプリを起動し、例外が起きる操作を行う

対象のアプリを輝度すると、コマンドプロンプトのの表示が下記のように変化する。
image.png
※対象のアプリのexeパスや、指定した例外の名前(Violation)、出力するダンプの名前などいろんな情報が載っている。

さらに、その状態で指定の例外が発生すると、下記のような表示になる。
image.png

上記表示がでると、例外が発生したのでダンプを出力した状態。
ProcDump.exeのフォルダに、ダンプファイルが出力されている。
image.png

VisualStudio2019でダンプのデバッグを行う

開く

作成されたダンプファイルをVisualStudio2019で開く。
VisualStudio2019のメニューから、
- [ファイル]
- [開く]
- [ファイル]
- →作成されたダンプファイルを選択する。

image.png
※.dmpファイルがVisualStudio2019に関連づけられているのなら、ダブルクリックするだけでもOK。

ダンプファイルを開くと、下記のような画面になる。
image.png

ここで、右上のほうの「マネージドのみでデバッグ」をクリックする。
もし開発を行ったPCで、ソースコードが開発(コードを書いてビルドした)ときと同じパスに置いてあるなら、下記のように、クラッシュしたときのコード上の位置が表示される。
image.png

※もし開発用のPCではなく、ソースコードがビルド時のパスに無い場合は、下記のような画面になる。
image.png
これは、ダンプファイルの中に実装時のコードのパスや、実行時のexeのパスの情報を覚えていて、そのパスにコードやexeが見つからないから出ているエラーっぽい。そのあたり、別ページで実験してみたので参照。

デバッグする

これで、VisualStudioでデバッグ実行中にbreakを貼って、そこで止めたときと似たことができる。
つまり、クラッシュしたときに、プロパティにどういう値が入っていたのか、とか、スタックフレームを遡ったりできる。(さかのぼった先で、そこのプロパティがどういう値なのか、も見ることができる)

こんな感じ
image.png
ただし、デバッグ実行の時のように、F5を押して続きを実行するということはできない。

注意点

今回はDebugビルドのexeを実行して、そのダンプファイルを作成し解析したが、Releaseビルドだと、できることに制限がある。

今回試した限りでは、最適化がかかっているために、クラッシュ箇所で止まった時のプロパティの値が見れないものがあった。(メソッド内のローカル変数は見れなかった。クラスのプロパティは見れるっぽい)

image.png

以上

以上が、ProcDunpを使ったダンプファイルの取得とVisualStudio2019での解析のやり方。
アンマネージドのコードで同じことをしたいときなどはまた別の手順があるかもしれないが、それはそういう場面に出会ったらまた調べる。

参照

procdumpの仕様(コマンドのパラメータなど)
https://docs.microsoft.com/ja-jp/sysinternals/downloads/procdump

VisualStudioでのダンプについて
https://docs.microsoft.com/ja-jp/visualstudio/debugger/using-dump-files?view=vs-2019

-mm、-ma、-mpの違い
https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2011/december/sysinternals-procdump-v4-0-writing-a-plug-in-for-sysinternals-procdump-v4-0#%E7%94%A8%E8%AA%9E

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#文法メモ

配列

配列の宣言
型名[] = 変数名;

要素数の指定
変数名 = new 型名[要素数];

配列の宣言と要素数の指定
型名[] 変数名 = new 型名[要素数];

配列の初期化(配列の宣言と値代入)
型名[] 変数名 = {値, 値 , 値};

foreach文

foreach (型名 変数名 in 配列変数名) 
{
}
foreach (KeyValuePair<型名, 型名> 変数名 in Dictionary型の変数名)
{
//変数名.Key、変数名.ValueでKey、Valueを取得できる
}

List

List型の変数をつくる
List<型名> 変数名 = new List<型名>();

Listに要素を追加
変数名.Add(追加する値);

Listの要素数取得
変数名.Count;

Listから要素を削除
変数名.RemoveAt(インデックス)
変数名.RemoveAt(削除したい値) 
  *同じ値を持つ要素が複数ある場合、インデックスが一番小さい要素が削除される

Listを昇順に並び替える
変数名.sort();

Dictionary

Dictionary型の変数を作る
Dictionary<キーの型、値の型> 変数名 = new Dictionary<キーの型、値の型>();

Dictionaryに要素を追加
変数名.Add(キー、値);

Dictionaryからキーに対応する値を取得
変数名[キー]

int型

TryParse(string型の値、変換後の値が代入される変数)メソッド

string型をint型に変換し、正しく変換できればtrueを返す。

Parse(文字列型の値)メソッド


string型からint型に変換する。

string型

Split('文字')メソッド

引数に与えた文字で区切り、string型の配列にする。

StreamReader型のメソッド

ReadLineメソッド

ファイルから一行読み取り文字列として返す。

DataSet型

データセット名(※小文字始まり).データテーブル名.Addデータテーブル名Row(登録する1行分のデータ)メソッド

DataTableに値を追加

値型の引数を参照型として渡す

ref修飾子・・引数に渡す変数に予め値を代入すべし
out修飾子・・引数に渡す変数に予め値を代入せんでいい

DataGridView型

currentRow.Indexプロパティ

選択している行のインデックスを取得

プロパティ

アクセサ(getter, setter)を作る仕組み
アクセサを直接使うよりも簡潔にコードが書ける

public プロパティの型名 プロパティ名
{
  set
  {
    変数名 = value;
  }
  get
  {
    return 変数名;
  }
}

LINQ

配列やコレクションの中から条件を満たす値だけ取り出したり、各データに特定の処理をしたりできる。

配列やコレクションの中化エアラムダ式の条件に合うデータだけを取り出す
var 変数名 = 配列やコレクションの変数名.Where(ラムダ式)
※var・・代入された値によってコンパイラが自動的に型を判断

配列やコレクションのデータにラムダ式の処理を加えて新しいデータを作る
var 変数名 = 配列やコレクションの変数名.Select(ラムダ式)

要素数を取得する
var 変数名 = 配列やコレクションの変数名.Count()

オーバーロード

引数の型と個数が違うとき同じ名前のメソッドを複数定義できる

オーバーライド

オーバーライドされる基本クラスのメソッドの書き方

virtual 戻り値の型 メソッド名()
{
  メソッドの処理内容
}

オーバーライド(override)する派生クラスのメソッド書き方

override 戻り値の型 メソッド名()
{
  メソッドの処理内容
}

抽象クラスと抽象メソッド

抽象メソッド・・メソッドの処理は派生クラスで実装することを前提とした中身のないメソッド
抽象クラス・・抽象メソッドを1つ以上もつクラス

基本クラス

abstract class クラス名 //abstarctをつけるとこのクラスのインスタンスは生成できない
{
  public abstract 戻り値の型 メソッド名(); 
}

派生クラス

class クラス名 : 基本クラス名
{
//抽象メソッドの中身を書かないとエラー
  public override 戻り値の型 メソッド名()
  {
    処理内容
  }
}

static

static・・クラスのメンバ変数やメンバメソッドにつけると、それらはインスタンスを作らずに呼び出せる

class Employee
{
public static string companyName;
}
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Employee.companyName);
        }
    }

partial

2つのファイルに分けてクラスを記述する場合、クラスの前につける。

public partial class Form1 : Form
{
  省略
}

構造体

構造体:値型 ↔︎ クラス:参照型
ex)2次元座標を表すPoint型、ベクトルを表すVector型、色を表すColor型

struct 構造体名
{
  メンバ変数
 メンバメソッド
}

usingステートメント

usingのブロックを抜けるタイミングでそのクラスのDisposeメソッドを呼ぶ。
以下の場合は、Disposeメソッドによってファイルを閉じている。

            using (System.IO.StreamReader file =
                new System.IO.StreamReader(@"..\..\data.txt"))
            {
                while (!file.EndOfStream)
                {
                    string line = file.ReadLine();
                    string[] data = line.Split(',');
                    this.phonebook.Add(data[0], data[1]);
                }
            }

//@をつけた文字列は、特殊な記号(エスケープシーケンス\nなど)を使っていない文字列として扱われる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む