ブログ

割とコンピュータよりの情報をお届けします。

コンピュータ

Bodhi LinuxでAvalonia UIアプリの使用

最近 .NET 7.0 GUIアプリケーションがWindowsで開発できるようになってきている。割と便利だ。

GUIアプリケーションの作成にxamlを使用していると、最初は大変だったが慣れてくると割と便利だ。.NET Framework 4.5程度のときはMVVMの対応に割と時間がかかってイベントハンドラを記述するなんてやっていたが、最近はMVVMの対応が楽になった。
そして.NET MAUIやAvalonia UIやUno Platformはそれをマルチプラットフォームに拡張する。
例えば、Avaloinia UIのアプリケーションをUbuntu 20.04で実行してみた。

wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt update
sudo apt install aspcorenet-runtime-7.0
LC_ALL=C dotnet AvaloniaApplication1.Desktop.dll

LC_ALL=Cが大事みたい。

ただ、.NET Framework 4.x世代の安定した互換性に比べて.NET 5.0 .NET 6.0 .NET 7.0の機能の追加・廃止の速さは少し怖い
(.NET Framework 4.xもWindows 7 の最後における.NET Framework 4.8のシステム破壊(WPFアプリがクラッシュする問題)はあったが)

≫ Read More

2023/06/05 コンピュータ   TakeMe

Mondo Rescue をUbuntu 20.04でインストール

Mondo Rescue をUbuntu 20.04でインストールしてみた。しかし、依然としてefiには非対応である。どうしても使いたい場合には、バグを自分で修正する必要がある。

Ubuntu 20.04でMondo Rescueをインストールしてみることにした。
まずは、
sudo vim /etc/apt/sources.list.d/mondorescue.sources.list
内容は以下の2行。

deb http://www.mondorescue.org/ftp/ubuntu 20.04 contrib
deb-src http://www.mondorescue.org/ftp/ubuntu 20.04 contrib

この状態で、

sudo apt update 

を実行するとkey is not available: NO_PUBKEY 6BA8C2D220EBFB0Eなどの表示がでるので、

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 6BA8C2D220EBFB0E
sudo apt update
sudo apt install mondo
sudo apt install isolinux
sudo apt install xorriso

ここまで。さらに、
sudo vim /etc/mindi/mindi.conf
として

EXTRA_SPACE=1024000
BOOT_SIZE=307200

Ubuntuのgenisoimage はefi booting のディスク作成機能を持たないために、UEFI環境でbootディスクの作成には失敗する。らしい。

どうもこの続きがあってUFEI環境では
sudo apt install syslinux-efi
とxorrisoを自分でビルドするが示されている?

ディストリビューションによってはできるらしい。

インストールしてさらに
/usr/sbin/mindi
をテキストエディタ(vimなど)で開き ../libutil.c32を探す。
その行のcp $LDLINUXE64/../libutil.c32を
cp /usr/lib/syslinux/modules/efi64/libutil.c32
と変更してしまう。

                                # cp $LDLINUXE64/../libutil.c32 $efidir 2>> $LOGFILE || Die "Cannot copy $LDLINUXE64 to $efidir). Did you run out of disk space?"
                                cp /usr/lib/syslinux/modules/efi64/libutil.c32 $efidir 2>> $LOGFILE || Die "Cannot copy $LDLINUXE64 to $efidir). Did you run out of disk space?"

今のところインストールはできるがバックアップに成功するにはmindi.confでEXTRA_SPACEを結構大きく設定する必要がある。
上の例はConoha VPSでUbuntu 20.04をバックアップしたときのものである。UEFIでは今のところ無理(2023.06.11追記)

UEFIではバックアップまではできるが、リストアに成功していない。

≫ Read More

2023/06/03 コンピュータ   TakeMe

Windows 11 「アカウント>家族」が曲者

ローカルアカウントからMicrosoftアカウントに切り替えたいなーと思ったときにOffice 365 Familyなどを使っている場合には、注意があるようだ。

ローカルアカウントからMicrosoftアカウントに切り替えたいなーと思ったときにOffice 365 Familyなどを使っている場合には、ファミリーメンバーのアカウントが待機状態になっている。いつでも「設定」から有効にできるアカウントである。

この状態でも、(ファミリーメンバーに含まれているMicrosoftアカウントに含まれていれば)「このデバイスの他のユーザーがこのMicrosoftアカウントを使用しているため、ここに追加することはできません」と表示されて自分のアカウントもMicrosoftアカウントに切り替えることができなくなるのだ。
おそらく多くの人は自分で登録した覚えがないので、「???」となる。
 lusrmgr.mscを実行してデバイスに登録されている「ユーザー」を一覧表示させてみると初めて待機状態になっているアカウントがあることがわかる。長すぎるアカウント名は一部省略名が使われる。アイコンに下向き矢印が表示されている場合には、待機状態になっていて、「設定」>「アカウント」>「家族」から「サインインを許可する」ボタンをクリックすると有効になる。

十分に注意が必要だ。しかも、日本では発売が遅かったため日本語の情報が不足している。
「このデバイスの他のユーザーがこのMicrosoftアカウントを使用しているため、ここに追加することはできません」と表示されたら、 lusrmgr.mscで いったん待機アカウントを削除してやると、ローカルアカウントをマイクロソフトアカウントに変更できるようになる。英語のMicrosoftサポートコミュニティには2019年時点でこのような情報が入っている。

≫ Read More

2023/05/28 コンピュータ   TakeMe

C#でclassのフィールドなどをインデックスで扱う方法

C#でクラスメンバを扱うとき[index]のように扱えると便利だが,Marshalの関係でメンバ数を固定にしないといけない場合がある.その場合にもインデックスで変数を扱う方法を確認.

System.Reflection.dllを使用する.
例えばフィールドならFieldInfoクラスを使う.

下の例では,
(FieldInfo)typeof(SampleA).GetField("item" + (i + 1))
で取得できるFieldInfoを使ってGetValueを使う.

代入の時には,SetValueを使用することができる(プロパティならGetProperty / SetProperty).

GetField()で取得できるのはpublic属性が付いているものに限る.privateになっているフィールドを取ろうとすると例外が発生する.実行時でないとわからないのでわかりにくいバグを発生する.ただし,「public / private」に関係なくプロパティを取得することは仕様としてはできる.デバッガーはこの仕組みを利用してデバッグを行うことができる.
一人でプログラムしている場合には気にならないが,複数人でプログラムしている場合には,ほかの人の部品の公開意図のないものを扱ってしまうのは,ややリスクがある.
この方法でだけ変数を使用していた場合,Visual Studio などでは変数の使用を検知できないのでCS0414が発生する,

using System;
using System.Reflection;

namespace Sample1
{
    class Program
    {
        static void Main(string[] args)
        {
            SampleA sampleA = new SampleA()
            {
                item1 = 1,
                item2 = 2,
                item3 = 3,
            };

            SampleA sampleA2 = new SampleA()
            {
                item1 = 11,
                item2 = 12,
                item3 = 13,
            };

            
            for (int i = 0; i < 3; i++)
            {
                Int32 item = (Int32)((FieldInfo)typeof(SampleA).GetField("item" + (i + 1))).GetValue(sampleA);
                Console.WriteLine($"{item}");
            }

            for (int i = 0; i < 3; i++)
            {
                Int32 item = (Int32)((FieldInfo)typeof(SampleA).GetField("item" + (i + 1))).GetValue(sampleA2);
                Console.WriteLine($"{item}");
            }
        }
    }

    public class SampleA
    {
        public Int32 item1;
        public Int32 item2;
        public Int32 item3;
    }
}

Tupleもこんな感じに扱える.(もちろん,Tupleはフィールド名を明示的に指定できるので状況によっては少し使い方が変わるかもしれないが)

            Tuple<double, double, double> tuple = new Tuple<double, double, double>(0, 1, 2);

            for (int i = 0; i < 3; i++)
            {
                double item = (double)((PropertyInfo)typeof(Tuple<double, double, double>).GetProperty("Item" + (i + 1))).GetValue(tuple);
                Console.WriteLine($"{item}");
            }

さてGetField以外にGetFieldsなるものがある.これが,求める「インデックスで扱う」ためのメソッドである.

≫ Read More

2022/02/13 コンピュータ   TakeMe

OxyPlotにHeatMapSeriesというのがある

OxyPlotにHeatMapSeriesというのがあるというのを知った.これまでは,Bitmapで自分で描いていた.

Bitmapで自分で描くより楽だ.例えばここのようなサンプルが良い.
下は参考ページのやり方・名前の取り方をほぼそのまま参照している.Windows Forms用に少し手直しが入っているのと,linearColorAxis1.Palette = OxyPalettes.Gray(10);を追加しているところくらいが異なる.(参考ではRainbowになっている?)
縦横に正弦波が0.5 秒おきに流れていく(感じ).

using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
using OxyPlot.WindowsForms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HeatmapTest
{
    private OxyPlot.WindowsForms.PlotView plotView1;
        private PlotModel plotModelSpectrogram = new PlotModel();
        public HeatMapSeries heatMapSeries1 = new HeatMapSeries();
        private Double[,] Data = new Double[240, 250];
        private double t = 0;

        private System.Windows.Forms.Timer timer1;

        public Form1()
        {
            InitializeComponent();

            this.plotView1 = new OxyPlot.WindowsForms.PlotView();

            this.plotView1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.plotView1.Location = new System.Drawing.Point(0, 0);
            this.plotView1.Name = "plotView1";
            this.plotView1.PanCursor = System.Windows.Forms.Cursors.Hand;
            this.plotView1.Size = new System.Drawing.Size(633, 362);
            this.plotView1.TabIndex = 0;
            this.plotView1.Text = "plotView1";
            this.plotView1.ZoomHorizontalCursor = System.Windows.Forms.Cursors.SizeWE;
            this.plotView1.ZoomRectangleCursor = System.Windows.Forms.Cursors.SizeNWSE;
            this.plotView1.ZoomVerticalCursor = System.Windows.Forms.Cursors.SizeNS;

            this.Controls.Add(this.plotView1);

            plotModelSpectrogram.Title = "TEST";

            var linearColorAxis1 = new LinearColorAxis();
            linearColorAxis1.Palette = OxyPalettes.Gray(10);
            linearColorAxis1.Position = AxisPosition.Right;
            linearColorAxis1.Minimum = 0.0;
            linearColorAxis1.Maximum = 100;
            plotModelSpectrogram.Axes.Add(linearColorAxis1);


            var linearAxis1 = new LinearAxis();
            linearAxis1.Position = AxisPosition.Bottom;
            linearAxis1.IsAxisVisible = false;
            linearAxis1.IsZoomEnabled = false;
            plotModelSpectrogram.Axes.Add(linearAxis1);

            // Dummy
            var linearAxis_a = new LinearAxis();
            linearAxis_a.Position = AxisPosition.Bottom;
            linearAxis_a.Minimum = 0.0;
            linearAxis_a.Maximum = 120.0;
            linearAxis_a.Title = "Time s";
            linearAxis_a.IsZoomEnabled = false;
            plotModelSpectrogram.Axes.Add(linearAxis_a);

            var linearAxis2 = new LinearAxis();
            linearAxis2.Position = AxisPosition.Left;
            linearAxis2.IsAxisVisible = false;
            linearAxis2.IsZoomEnabled = false;
            plotModelSpectrogram.Axes.Add(linearAxis2);

            // Dummy
            var linearAxis_b = new LinearAxis();
            linearAxis_b.Position = AxisPosition.Left;
            linearAxis_b.Minimum = 0.0;
            linearAxis_b.Maximum = 1000.0;
            linearAxis_b.Title = "Frequency kHz";
            linearAxis_b.IsZoomEnabled = false;
            plotModelSpectrogram.Axes.Add(linearAxis_b);


            heatMapSeries1.Data = new double[240, 250];
            heatMapSeries1.X0 = 0.0;
            heatMapSeries1.X1 = 100.0;
            heatMapSeries1.Y0 = 0.0;
            heatMapSeries1.Y1 = 1000.0;

            plotModelSpectrogram.Series.Add(heatMapSeries1);
            this.plotView1.Model = plotModelSpectrogram;

            this.timer1 = new Timer();
            this.timer1.Interval = 500;
            this.timer1.Enabled = true;
            this.timer1.Tick += new System.EventHandler(this.timer1_Tick);

        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            PlotSpectrogram();
        }

        private void AddLastData(Double[] data)
        {
            for (int i = 0; i < Data.GetLength(0) - 1; i++)
            {
                for (int j = 0; j < Data.GetLength(1); j++)
                {
                    Data[i, j] = Data[i + 1, j];
                }
            }
            for (int j = 0; j < Data.GetLength(1); j++)
            {
                Data[Data.GetLength(0) - 1, j] = data[j];
            }

            var pldata = new double[240, 250];
            Array.Copy(Data, pldata, Data.Length);
            heatMapSeries1.Data = pldata;
        }

        public void PlotSpectrogram()
        {
            Double[] data = new Double[Data.GetLength(1)];
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = (50.0 + 20.0 * Math.Sin(0.2 * i) + 20.0 * Math.Sin(t));

            }
            t += 0.5;
            AddLastData(data);
            plotModelSpectrogram.InvalidatePlot(true);
        }
    }
}

≫ Read More

2021/06/07 コンピュータ   TakeMe

Math.NET Numericsに機能が増えている

最近使っていないうちに Math.NET Numericsの機能が増えている.数値最小化問題を解けるようになっている.

MathNet.Numerics.Optimizationには数値最小化問題を解ける機能が備わっている.

最小化問題だが,符号を変えれば最大化問題も最小化問題になる.また,目的関数最小化(最適化)問題と呼ぶこともある.例えば以下のように使用できる.
最近の.NETには
var f1 = new Func<double, double>(x => sample(x));
というFuncという汎用のデリゲートがあるのが面白い.

using MathNet.Numerics.Optimization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            // 一次元探索
            var f1 = new Func<double, double>(x => sample(x));
            var obj = ObjectiveFunction.ScalarValue(f1);
            var r1 = GoldenSectionMinimizer.Minimum(obj, -100, 100, 1e-8, 1000, 10, 4, 4);

            Console.WriteLine($"{r1.FunctionInfoAtMinimum.Point:G16}");
            Console.WriteLine($"{r1.FunctionInfoAtMinimum.Value:G16}");

            // 多次元探索
            var f2 = new Func<MathNet.Numerics.LinearAlgebra.Vector<double>, double>(x => sample2(x));
            var obj2 = ObjectiveFunction.Value(f2);
            var init = MathNet.Numerics.LinearAlgebra.Vector<double>.Build;
            

            var r2 = NelderMeadSimplex.Minimum(obj2, init.Dense(new double[] { 0.0, 0.0 }));
            Console.WriteLine($"{r2.FunctionInfoAtMinimum.Point[0]:G16},{r2.FunctionInfoAtMinimum.Point[1]:G16}");
            Console.WriteLine($"{r2.FunctionInfoAtMinimum.Value:G16}");
        }

        static double sample(double x)
        {
            return (x - 3000) * (x - 3000);
        }

        static double sample2(MathNet.Numerics.LinearAlgebra.Vector<double> x)
        {
            return (x[0] - 1000) * (x[0] - 1000) + (x[1] - 3000) * (x[1] - 3000);
        }
    }
}

≫ Read More

2021/05/09 コンピュータ   TakeMe

netDxfの使用例

netDxfを使ってみた.
なんということはないプログラム.

netDxfでdxfファイルを開いて円だけを抜き出し,半径でソートして,中心のX座標でソートして,中心のY座標でソートして順に表示するアプリを作ってみた.

using netDxf;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DxfProc
{
    class Program
    {
        static void Main(string[] args)
        {
            bool fileMode = false;
            string fileName = "";
            for (int i = 0; i < args.Length; i++) // もしコマンドライン引数で渡されたらファイルとして処理
            {
                {
                    fileMode = true;
                    fileName = args[i];
                }
            }
            DxfDocument dxf = null;

            if (fileMode)
            {
                dxf = DxfDocument.Load(fileName);
            }
            else
            {
                Int32 length = Int32.Parse(Console.ReadLine()); // get file size
                MemoryMappedFile shared_mem = MemoryMappedFile.OpenExisting("shared_mem", MemoryMappedFileRights.Read);
                Stream stream = shared_mem.CreateViewStream(0, length, MemoryMappedFileAccess.Read);
                dxf = DxfDocument.Load(stream);
            }
            var circles = dxf.Circles;

            var a = circles.OrderBy(p => p.Center.Y).OrderBy(p => p.Center.X).OrderBy(p => p.Radius);

            Console.WriteLine("Diameter,Center X, Center Y");
            foreach (var circle in a)
            {
                Console.WriteLine($"{circle.Radius*2:0.00000},{circle.Center.X:0.00000},{circle.Center.Y:0.00000}");
            }
        }
    }
}

ちなみに共有メモリを開くようにしている理由は?netDxfのライセンスの関係で本体アプリは別に作っていたから.例えばこんな感じ.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string fileName = "Untitled.dxf";

            Process proc = new Process();
            ProcessStartInfo processStartInfo = new ProcessStartInfo()
            {
                FileName = "DxfProc.exe",
                RedirectStandardInput = true,
                RedirectStandardOutput = true,
                UseShellExecute = false,
            };

            FileInfo fileInfo = new FileInfo(fileName);
            var length = fileInfo.Length; // ファイルサイズ

            MemoryMappedFile share_mem = MemoryMappedFile.CreateFromFile(fileName, FileMode.Open, "shared_mem", length, MemoryMappedFileAccess.Read);

            proc.StartInfo = processStartInfo;
            proc.Start();

            proc.StandardInput.WriteLine(length);

            Console.Write(proc.StandardOutput.ReadToEnd());
            proc.WaitForExit();
            

            
        }
    }
}

最新のnetDxfはライセンスが変更になったのでこれがnugetに上がってくる頃にはこの対応は不要になると思う.最新訂正ではDxfDocumentの直下にCirclesはなくなりEntitiesの下に配置されるようになっている.若干の修正がある.

≫ Read More

2021/04/19 コンピュータ   TakeMe

Openclipartが復帰していた

無償のクリップアートの提供サイトであるOpenclipartが復活していた.以前に停止していると書いたのは2019年だった.

知らないうちに,Openclipartが復活していた.

停止前にできていた簡単な編集を加えてからダウンロードするような機能はなくなっていて,2017年ころの状態に戻ったような感じである.ただ,ダウンロード機能は使える.
Openclipart APIなんていうのがあるけどどんな機能なのか?
アプリにクリップアート検索機能を追加するときに便利なような機能を提供しているのかな

≫ Read More

2021/04/19 コンピュータ   TakeMe

Dokan.NET Bindingはまだまだ健在だった話

DokanはWindowsでデバイスドライバを作らなくても独自のファイルシステムを提供することができるソフト。そのDokanのユーザモードの部分を.NETで実装しやすくするのがDokan.NET Binding。Dokanの開発はずっと前に終わったと思っていた。その継続としてフォークされたDokanXも更新が止まっていた。

どうもDokanyがまだ健在である。そして,Dokan.NETもこのDokanyを使うように改修が続いている。

だいぶん前の「Dokan .netでファイルシステムを作って遊ぶ - Simple HashtableFS -(しょんぼり技術メモまいにちがしょんぼり)」というページの内容は古いがかなりその構成を維持したまま移行できるみたい(まったく同じとはいかない)。

// LinkedFS.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.Design.Serialization;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;
using DokanNet;
using Linked;

namespace Linked
{
    public class LinkedFS : IDokanOperations
    {
        LinkedRecord root;

        #region Constructor
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="root">対象</param>
        public LinkedFS(ref LinkedRecord root)
        {
            this.root = root;
        }
        #endregion

        #region IDokanOperations
        public void Cleanup(string fileName, IDokanFileInfo info)
        {
        }

        public void CloseFile(string fileName, IDokanFileInfo info)
        {
        }

        public NtStatus CreateFile(string fileName, DokanNet.FileAccess access, FileShare share, FileMode mode, FileOptions options, FileAttributes attributes, IDokanFileInfo info)
        {
            if (fileName == "\\") // root 
            {
                info.IsDirectory = true;
                return DokanResult.Success;
            }
            else
            {
                switch (mode)
                {
                    case FileMode.Open:
                        {
                            string fileName2 = fileName.TrimStart('\\');
                            return RecursiveSearch(root, fileName2, ref info);
                        }
                    case FileMode.CreateNew:
                        {
                            string fileName2 = fileName.TrimStart('\\');
                            var fileInfo = new FileInformation(); // dummy fileInfo
                            LinkedRecord result = RecursiveSearchItem(root, fileName2, ref fileInfo, ref info);
                            if (result == null)
                            {
                                string[] data = fileName2.Split('\\');
                                string fileName3 = "";
                                for (int i = 0; i < data.Length - 1; i++)
                                {
                                    fileName3 += data[i];
                                    if (i < data.Length - 2)
                                    {
                                        fileName3 += "\\";
                                    }
                                }
                                string fileName4 = data[data.Length - 1];
                                result = RecursiveSearchItem(root, fileName3, ref fileInfo, ref info);
                                if (fileName3 == string.Empty)
                                    result = root;

                                if (result == null)
                                {
                                    return DokanResult.Error;
                                }
                                else
                                {
                                    var item = new LinkedRecord();
                                    item.IsDirectory = info.IsDirectory ? true : false;
                                    if (!item.IsDirectory)
                                        item.Contents = new List<byte>();

                                    item.Name = fileName4;
                                    result.LinkedItems.Add(item);
                                }
                                return DokanResult.Success;
                            }
                            else
                            {
                                return DokanResult.AlreadyExists;
                            }
                        }
                    default:
                        {
                            string fileName2 = fileName.TrimStart('\\');
                            return RecursiveSearch(root, fileName2, ref info);
                        }
                }
            }

            // return DokanResult.FileNotFound;
        }

        public NtStatus DeleteDirectory(string fileName, IDokanFileInfo info)
        {
            return DokanResult.NotImplemented;
        }

        public NtStatus DeleteFile(string fileName, IDokanFileInfo info)
        {
            return DokanResult.NotImplemented;
        }

        public NtStatus FindFiles(string fileName, out IList<FileInformation> files, IDokanFileInfo info)
        {
            files = new List<FileInformation>();

            if (fileName == "\\")
            {
                foreach (var item in root.LinkedItems)
                {
                    var finfo = new FileInformation
                    {
                        FileName = item.Name,
                        Attributes = item.IsDirectory ? FileAttributes.Directory : FileAttributes.Normal,
                        LastAccessTime = DateTime.Now,
                        LastWriteTime = null,
                        CreationTime = null,
                        Length = item.IsDirectory ? 0 : item.Contents.Count,
                    };
                    files.Add(finfo);
                }
                return DokanResult.Success;
            }
            else
            {
                string fileName2 = fileName.TrimStart('\\');
                var fileInfo = new FileInformation(); // dummy fileInfo
                LinkedRecord itemRoot = RecursiveSearchItem(root, fileName2, ref fileInfo, ref info);

                if (itemRoot == null)
                    return DokanResult.Error;

                foreach (var item in itemRoot.LinkedItems)
                {
                    var finfo = new FileInformation
                    {
                        FileName = item.Name,
                        Attributes = item.IsDirectory ? FileAttributes.Directory : FileAttributes.Normal,
                        LastAccessTime = DateTime.Now,
                        LastWriteTime = DateTime.Now,
                        CreationTime = null,
                        Length = item.IsDirectory ? 0 : item.Contents.Count,
                    };
                    files.Add(finfo);
                }

                return DokanResult.Success;
            }
        }

        public NtStatus GetFileInformation(
            string fileName,
            out FileInformation fileInfo,
            IDokanFileInfo info)
        {
            fileInfo = new FileInformation { FileName = fileName };

            if (fileName == "\\")
            {
                fileInfo.Attributes = FileAttributes.Directory;
                fileInfo.LastAccessTime = DateTime.Now;
                fileInfo.LastWriteTime = null;
                fileInfo.CreationTime = null;

                return DokanResult.Success;
            }

            string fileName2 = fileName.TrimStart('\\');
            LinkedRecord item = RecursiveSearchItem(root, fileName2, ref fileInfo, ref info);
            
            if (item == null)
                return DokanResult.Error;

            fileInfo.Attributes = item.IsDirectory ? FileAttributes.Directory : FileAttributes.Normal;
            fileInfo.LastAccessTime = DateTime.Now;
            fileInfo.LastWriteTime = DateTime.Now;
            fileInfo.CreationTime = DateTime.Now;
            fileInfo.FileName = item.Name;
            if (item.Contents != null)
                fileInfo.Length = item.Contents.Count;

            return DokanResult.Success;
        }

        public NtStatus ReadFile(
            string fileName,
            byte[] buffer,
            out int readBytes,
            long offset,
            IDokanFileInfo info)
        {
            readBytes = 0;

            string fileName2 = fileName.TrimStart('\\');
            var fileInfo = new FileInformation(); // dummy fileInfo
            LinkedRecord item = RecursiveSearchItem(root, fileName2, ref fileInfo, ref info);
            if (item == null)
                return DokanResult.FileNotFound;

            if (item.IsDirectory)
            {
                return DokanResult.NotADirectory;
            }

            {
                try
                {
                    // バイナリ列
                    byte[] raw = item.Contents.ToArray();
                    if (raw == null)
                    {
                        return DokanResult.Success;
                    }

                    // 超過チェック
                    if (offset >= raw.Length)
                    {
                        return DokanResult.Error;
                    }

                    // offsetから読み込むバイト数
                    long read_try_byte = (raw.Length - offset < buffer.Length) ? raw.Length - offset : buffer.Length;

                    // コピー
                    int i = 0;
                    for (i = 0; i < read_try_byte; i++)
                    {
                        buffer[i] = raw[offset + i];
                    }

                    readBytes = (int)i;

                    return DokanResult.Success;
                }
                catch (Exception)
                {
                    return DokanResult.Error;
                }
            }

        }

        public NtStatus SetEndOfFile(string fileName, long length, IDokanFileInfo info)
        {
            return DokanResult.Error;
        }

        public NtStatus SetAllocationSize(string fileName, long length, IDokanFileInfo info)
        {
            return DokanResult.Success;
        }

        public NtStatus SetFileAttributes(
            string filename,
            FileAttributes attr,
            IDokanFileInfo info)
        {
            return DokanResult.Error;
        }

        public NtStatus SetFileTime(
            string filename,
            DateTime? ctime,
            DateTime? atime,
            DateTime? mtime,
            IDokanFileInfo info)
        {
            return DokanResult.Error;
        }

        public NtStatus UnlockFile(string filename, long offset, long length, IDokanFileInfo info)
        {
            return DokanResult.Success;
        }

        public NtStatus Mounted(IDokanFileInfo info)
        {
            return DokanResult.Success;
        }

        public NtStatus LockFile(
            string filename,
            long offset,
            long length,
            IDokanFileInfo info)
        {
            return DokanResult.Success;
        }

        public NtStatus MoveFile(
            string fileName,
            string newName,
            bool replace,
            IDokanFileInfo info)
        {
            var lid_s = fileName.LastIndexOf('\\');
            var lid_d = newName.LastIndexOf('\\');
            if (fileName.Substring(0, lid_s) == newName.Substring(0, lid_d))
            {
                string fileName2 = fileName.TrimStart('\\');
                var fileInfo = new FileInformation(); // dummy fileInfo
                LinkedRecord item = RecursiveSearchItem(root, fileName2, ref fileInfo, ref info);
                if (item == null)
                    return DokanResult.FileNotFound;

                string newFileName2 = newName.TrimStart('\\');
                var newFileInfo = new FileInformation(); // dummy fileInfo
                LinkedRecord itemNew = RecursiveSearchItem(root, newFileName2, ref newFileInfo, ref info);
                if (itemNew != null)
                    return DokanResult.AlreadyExists;

                string[] data = newName.Split('\\');
                item.Name = data[data.Length - 1];
                return DokanResult.Success;
            }
            else
            {
                // 移動元
                string fileName2 = fileName.TrimStart('\\');
                var fileInfo = new FileInformation(); // dummy fileInfo
                LinkedRecord item = RecursiveSearchItem(root, fileName2, ref fileInfo, ref info);
                if (item == null)
                    return DokanResult.FileNotFound;
                // 移動元の親
                string fileName2Parent = fileName.Substring(0, lid_s).TrimStart('\\');
                var fileInfoParent = new FileInformation(); // dummy fileInfo
                LinkedRecord itemP = RecursiveSearchItem(root, fileName2Parent, ref fileInfoParent, ref info);
                if (itemP == null)
                {
                    if (fileName2Parent == string.Empty)
                    {
                        itemP = root;
                    }
                    else
                        return DokanResult.FileNotFound;
                }

                // 移動先
                string newFileName2 = newName.TrimStart('\\');
                var newFileInfo = new FileInformation(); // dummy fileInfo
                LinkedRecord itemNew = RecursiveSearchItem(root, newFileName2, ref newFileInfo, ref info);
                if (itemNew != null)
                    return DokanResult.AlreadyExists;

                // 移動先の親
                string newFileName2Parent = newName.Substring(0, lid_d).TrimStart('\\');
                var newFileInfoParent = new FileInformation(); // dummy fileInfo
                LinkedRecord itemNewP = RecursiveSearchItem(root, newFileName2Parent, ref newFileInfoParent, ref info);
                if (itemNewP == null)
                {
                    if (newFileName2Parent == string.Empty)
                    {
                        itemNewP = root;
                    }
                    else
                        return DokanResult.FileNotFound;
                }

                string[] data = newName.Split('\\');
                item.Name = data[data.Length - 1];
                itemNewP.LinkedItems.Add(item);
                itemP.LinkedItems.Remove(item);

                return DokanResult.Success;
            }
        }

        public NtStatus Unmounted(IDokanFileInfo info)
        {
            return DokanResult.Success;
        }

        public NtStatus GetDiskFreeSpace(
            out long freeBytesAvailable,
            out long totalBytes,
            out long totalFreeBytes,
            IDokanFileInfo info)
        {
            long currentSet = GC.GetTotalMemory(true);
            freeBytesAvailable = 1024 * 100 - currentSet;// 1024 * 10;// 512;
            totalBytes = 1024 * 100;
            totalFreeBytes = 1024 * 100 - currentSet;
            return DokanResult.Success;
        }

        public NtStatus WriteFile(
            string fileName,
            byte[] buffer,
            out int writtenBytes,
            long offset,
            IDokanFileInfo info)
        {
            writtenBytes = 0;

            var append = offset == -1;

            string fileName2 = fileName.TrimStart('\\');
            var fileInfo = new FileInformation(); // dummy fileInfo
            LinkedRecord item = RecursiveSearchItem(root, fileName2, ref fileInfo, ref info);
            if (item == null)
                return DokanResult.FileNotFound;

            if (info.Context == null)
            {
                if (!append)
                {
                    item.Contents.Clear();
                    item.Contents.AddRange(buffer);
                    writtenBytes = buffer.Length;
                }
            }
            else
            {
                return DokanResult.Error;
            }
            return DokanResult.Success;
        }

        public NtStatus GetVolumeInformation(out string volumeLabel, out FileSystemFeatures features,
            out string fileSystemName, out uint maximumComponentLength, IDokanFileInfo info)
        {
            volumeLabel = "LinkedFS";
            features = FileSystemFeatures.None;
            fileSystemName = "MASTER_DB";
            maximumComponentLength = 256;
            return DokanResult.Success;
        }

        public NtStatus GetFileSecurity(string fileName, out FileSystemSecurity security, AccessControlSections sections,
            IDokanFileInfo info)
        {
            security = null;
            return DokanResult.Error;
        }

        public NtStatus SetFileSecurity(string fileName, FileSystemSecurity security, AccessControlSections sections,
            IDokanFileInfo info)
        {
            return DokanResult.Error;
        }

        public NtStatus EnumerateNamedStreams(string fileName, IntPtr enumContext, out string streamName,
            out long streamSize, IDokanFileInfo info)
        {
            streamName = string.Empty;
            streamSize = 0;
            return DokanResult.NotImplemented;
        }

        public NtStatus FlushFileBuffers(
            string filename,
            IDokanFileInfo info)
        {
            return DokanResult.Success;
            //return DokanResult.Error;
        }

        public NtStatus FindStreams(string fileName, out IList<FileInformation> streams, IDokanFileInfo info)
        {
            streams = new FileInformation[0];
            return DokanResult.NotImplemented;
        }

        public NtStatus FindFilesWithPattern(string fileName, string searchPattern, out IList<FileInformation> files,
            IDokanFileInfo info)
        {
            files = new FileInformation[0];
            return DokanResult.NotImplemented;
        }
        #endregion

        private NtStatus RecursiveSearch(LinkedRecord root, string fileName, ref IDokanFileInfo info)
        {
            string[] data = fileName.Split('\\');
            for (Int32 i = 0; i < root.LinkedItems.Count; i++)
            {
                if (root.LinkedItems[i].Name == data[0])
                {
                    if (root.LinkedItems[i].IsDirectory == true)
                    {
                        var id = fileName.IndexOf('\\');
                        if (id < 0)
                        {
                            return DokanResult.Success;
                        }
                        else
                        {
                            string fileName_ = fileName.Substring(id).TrimStart('\\');
                            if (fileName_ == String.Empty)
                            {
                                return DokanResult.Success;
                            }
                            else
                            {
                                return RecursiveSearch(root.LinkedItems[i], fileName_, ref info);
                            }
                        }
                    }
                    else // ファイルである
                    {
                        if (data.Length > 1) // 要求がディレクトリである
                        {
                            return DokanResult.NotADirectory;
                        }
                        else
                        {
                            return DokanResult.Success;
                        }
                    }
                }
            }
            return DokanResult.FileNotFound;
        }

        private LinkedRecord RecursiveSearchItem(LinkedRecord root, string fileName, ref FileInformation fileInfo, ref IDokanFileInfo info)
        {
            string[] data = fileName.Split('\\');
            for (Int32 i = 0; i < root.LinkedItems.Count; i++)
            {
                if (root.LinkedItems[i].Name == data[0])
                {
                    if (root.LinkedItems[i].IsDirectory == true)
                    {
                        var id = fileName.IndexOf('\\');
                        if (id < 0)
                        {
                            return root.LinkedItems[i];
                        }
                        else
                        {
                            string fileName_ = fileName.Substring(id).TrimStart('\\');
                            if (fileName_ == String.Empty)
                            {
                                return root.LinkedItems[i];
                            }
                            else
                            {
                                return RecursiveSearchItem(root.LinkedItems[i], fileName_, ref fileInfo, ref info);
                            }
                        }
                    }
                    else // ファイルである
                    {
                        if (data.Length > 1) // 要求がディレクトリである
                        {
                            return null;
                        }
                        else
                        {
                            fileInfo.Attributes = FileAttributes.Normal;
                            fileInfo.CreationTime = System.DateTime.Now;
                            fileInfo.LastAccessTime = System.DateTime.Now;
                            fileInfo.LastWriteTime = System.DateTime.Now;
                            fileInfo.FileName = root.LinkedItems[i].Name;

                            // 長さ
                            //byte[] raw = root.LinkedItems[i].Contents.ToArray();
                            fileInfo.Length = root.LinkedItems[i].Contents.Count;// raw.Length;

                            return root.LinkedItems[i];
                        }
                    }
                }
            }
            return null;
        }


        
    }
}
// LinkedRecord.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace Linked
{
    public class LinkedRecord
    {
        private String name = "\\";
        public String Name {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public Boolean IsDirectory { get; set; } = true;

        public List<LinkedRecord> LinkedItems { get; set; } = new List<LinkedRecord>();

        public List<Byte> Contents { get; set; } = null;//new List<byte>();
    }
}
 
//Program.cs
using DokanNet;
using DokanNet.Logging;
using Linked;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LinkedB
{
    class Program
    {
        static void Main(string[] args)
        {
            LinkedRecord root = new LinkedRecord();
            LinkedFS linkedFS = new LinkedFS(ref root);

            var dir = new LinkedRecord();
            dir.Name = "明治";
            dir.IsDirectory = true;
            root.LinkedItems.Add(dir);

            var file = new LinkedRecord();
            file.Name = "大正.txt";
            file.IsDirectory = false;
            file.Contents = Encoding.UTF8.GetBytes("昭和平成").ToList<Byte>();
            dir.LinkedItems.Add(file);

            linkedFS.Mount("X" + ":\\", /*DokanOptions.DebugMode | */DokanOptions.RemovableDrive, new NullLogger());
        }
    }
}

≫ Read More

2021/04/11 コンピュータ   TakeMe

.NET Frameworkとネイティブdllが混ざったときの不具合追及

.NETでDllImportを使っていると例外が発生しないで突然「××は動作を停止しました。」と出てしまうことがある。最近この場面に遭遇した。

「××は動作を停止しました。」と出たときにWindowsコントロールの「信頼性の履歴の表示」に,APPCRASHというイベント名で記録が残る。
これは便利なのだが,例外の種類が0xc0000374になっていて発生頻度が割と低い場合には非常にデバッグが難しい。
これはヒープ破損らしい。
そもそも,.NET Framework 4.0からAccessViolationExceptionを検知できなくなっているらしい。そのため,例外をキャッチしてクラッシュログを残そうなどということができない。app.configに<legacyCorruptedStateExceptionsPolicy enabled="true"/>を追加することでこれは回避できる。
しかし,それだけでは見逃す。0xc0000374はヒープ破壊を起こした時に起きるというより,壊れているのを検知したときに起きる(実害が発生しそうなタイミングで起きる)ので問題のあるコードはもっと前に実行されていることがある。

そこで,Windows SDKに含まれるDebugging Tools for WindowsgflagsでPage Heapを有効にして置きexe.configの<runtime>要素内に<legacyCorruptedStateExceptionsPolicy enabled="true"/>を追加して,ヒープ破壊っぽいことが起きたらAccessViolationExceptionが上がるようにして例外をキャッチしてクラッシュログを残す。(といいらしい)

Page Heapは非常に重いが,例外発生の確実性が大幅に増す。

≫ Read More

2021/04/09 コンピュータ   TakeMe