ブログ

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

WPF

.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は非常に重いが,例外発生の確実性が大幅に増す。

≫ 続きを読む

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

WPF 文字列の入ったResourceDictionary.xamlファイルをテキストファイルのまま同梱する方法

WPFのResourceDictionaryに文字列を追加することも可能らしい.
ResourceDictionaryを記述するxamlファイルは,普通はビルドの時にアッセンブリファイルにくっつける。
このとき,WPFのリソースを提供する場合にはxamlファイル(テキストファイル)のまま提供することもできるみたい。

ResourceDictionaryを記述するxamlファイルは,テキストファイルのまま同梱できるらしいのだ。

Xaml_file_as_a_textfile_buildaction.png

xamlファイルのビルドアクションをコンテンツに直して出力ディレクトリにコピーする。

そして,例えばリソースに以下のように追加するだけ。
(この場合には出力ディレクトリをルート(/)として/Resoures/Resources.xamlにコピーされるファイルをResourceDictionaryとして読み込み)

<Window.Resources>
        <ResourceDictionary Source="/Resources/Resources.xaml" />
    </Window.Resources>

ウインドウのリソースに書いているとウインドウが生成されると時に読まれる。
このようにしておくとビルド環境がない場合にも文字列だけはその場でなおせる(かな)。
※文字列だけでなく,通常のResourceDictionary.xamlの記述内容なら変更可能なので言語によって文字列を変えるだけでなく言語を変えたところ同じ意味を表す文字列の長さが変わった場合にそれを収めるBorderのサイズを変えるとか文字サイズを変えるとか絵やアイコンをかえるとかも一応可能

画像ファイルについては単独でアッセンブリファイルに同梱できる。 以下の例では/Resouces/sample1.pngにファイルを入れている。

            <TextBlock Text="{DynamicResource test_string1}">
                <TextBlock.Background>
                    <ImageBrush ImageSource="/Resources/sample1.png"/>
                </TextBlock.Background>
            </TextBlock>

≫ 続きを読む

2021/03/13 コンピュータ   TakeMe
Tag:WPF

Windows Embedded でのMessageBoxの扱いの注意

Windows Embedded Standard向けのアプリを開発していたら,.NETで64bitでも32bitでもどちらでもアプリが動かしるようにしていたところ,32bitでの動作では普通にMessageBoxが開くのに...

組み込み用のWindowsではWindows標準のMessageBoxの動作を変更することができる.すべてOKを応答させてダイアログを表示させないようにするのだ.

.NET Frameworkで開発したアプリで特に64bitで動かしたときにできるらしい.
この機能は開発時にMessageBoxを出させておいて,展開時にはMessageBoxを抑制するというという使い方になる.
知らなかったのでダイアログが出ない理由を一週間かけて探した.

≫ 続きを読む

2019/10/19 コンピュータ   TakeMe

Live ChartsでScatter Plotに線が付かない仕様 Vol. 2

前回の記事ではX, Yのデータを含むモデルを新たに起こしているが,x, yだけなら標準ではObservablePointも使用できるので一部訂正

つまり,ObservablePointを使うと,前回のXYPointModelはわざわざつけなくて良い.
ChartValuesはListでもよいらしいが確認していない.
実はこの例では以下のようにデータグリッドを追加するとデータグリッド上での変更がチャートに反映されるということになっている.
<DataGrid ItemsSource="{Binding listXYPoint}" FontSize="20"/>
まあ使うかどうかはべつですが

using MahApps.Metro.Controls;
using LiveCharts;
using LiveCharts.Wpf;
using LiveCharts.Configurations;
using LiveCharts.Defaults;

namespace LiveChartsTEST
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : MetroWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            ChartData cd = new ChartData();

            var sc = new SeriesCollection()
            {
                new LineSeries
                {
                    LineSmoothness = 0,
                    Values = cd.listXYPoint
                }
            };

            cd.seriesCollection = sc;
            this.DataContext = cd;
        }

        public class ChartData
        {
            public SeriesCollection seriesCollection { get; set; }
            public ChartValues<ObservablePoint> listXYPoint { get; set; } = new ChartValues<ObservablePoint>();
            public ChartData()
            {
                for (int i = 0; i < 10; i++)
                {
                    listXYPoint.Add(new ObservablePoint(i, i * i));
                }
            }
        }
    }
}

≫ 続きを読む

2019/09/09 コンピュータ   TakeMe
Tag:WPF

Live ChartsでScatter Plotに線が付かない仕様

Live Charts WPFを使い始めた.
Line ChartとScatter Plotはそれぞれあるが,どうしてだろう 2つを組み合わせて使用する方法が書いていない.

Live Chartsに関してGitHubで質問が上がっていた.
https://github.com/Live-Charts/Live-Charts/issues/737
結局のところ2019年初の段階で 質問にまともに答えていないのだ.

ところが,現在Tutorial and Examples (https://lvcharts.net/App/examples/v1/wpf/Date%20Time)がようやく追い付いてきたようだ.サンプルではDateTimeを横軸に設定しているが,doubleに置き換えることは当然可能である.例えば以下のように行う(MahAppsも一緒に使っているが必須ではないのはご存じの通りです).

using MahApps.Metro.Controls;
using LiveCharts;
using LiveCharts.Wpf;
using LiveCharts.Configurations;

namespace LiveChartsTEST
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : MetroWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            ChartData cd = new ChartData();

            var xypointConfig = Mappers.Xy<XYPointModel>()
                .X(model => model.ValueX)
                .Y(model => model.ValueY);

            var sc = new SeriesCollection(xypointConfig)
            {
                new LineSeries
                {
                    Values = new ChartValues<XYPointModel>
                    {
                        new XYPointModel
                        {
                            ValueX = 0,
                            ValueY = 5
                        },
                        new XYPointModel
                        {
                            ValueX = 10,
                            ValueY = 9
                        }
                    }
                }
            };

            cd.seriesCollection = sc;
            this.DataContext = cd;
        }

        public class ChartData
        {
            public SeriesCollection seriesCollection { get; set; }
        }

        public class XYPointModel
        {
            public double ValueX { get; set; }
            public double ValueY { get; set; }
        }
    }
}

XAMLについて以下のようにしておくなど 必要かも

<lvc:CartesianChart Series="{Binding seriesCollection}" LegendLocation="Bottom" />

≫ 続きを読む

2019/09/08 コンピュータ   TakeMe
Tag:WPF

WPFのメモリリーク対策

C#ではメモリリークはほとんど心配ないと思っていたが,WPFアプリケーションを作っていると,メモリリークが頻繁に起きることが分かって恐ろしくなった.

まず,参考のページ「俺が遭遇したWPFイメージコントロールのメモリーリークと回避法(?)の1つ」を参考にすると,Imageコントロールはよくリークを起こす.参考のページのサンプルコードの最後の方にあるように,明示的にSourceにnullを入れてやらないと割と残るようだ.

リーク以外に,キャッシュという仕組みもありわかりにくい.xamlにイメージのファイル名を直接書いていると一度使ったイメージはキャッシュされ次の使用が速くなる(らしい).

このほか,UserControlもうまく設計していない場合,リークを起こす.Imageを使う場合やTimerを使う場合にはIDisposalインタフェースを実装して明示的にDisposeを呼び出すことを強く推奨する.TimerなんかはいったんStartしたままコントロールを破棄してしまうと(Windowを閉じるなど),一見コントロールが使えなくなくなっても残ってしまう.しかも,消えるタイミングが不定で恐ろしいバグになる(たいていはtickも残る).

Visual Studio 2017 ProfessionalやCommunityなら診断ツールを用いてヒープを表示させると,参照しているオブジェクト種類の一覧と参照されているオブジェクトの一覧が取れるのでデバッグの助けになる.
ポイントはImageが残っていないか?Start()を呼んだままのTimerが残っていないか?かな

≫ 続きを読む

2019/02/13 日記   TakeMe
Tag:WPF

WPFでTextBox内で上下キーでフォーカスを遷移

WPFでTextBoxにフォーカスが当たっているときに上下キーを押すとフォーカスを遷移できる仕組みを実装していた。

xamlにはTextBoxが複数並べた。
そのTextBoxにはKeyUpのイベントハンドラを設定している。

<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120" KeyUp="TextBox_KeyUpEvent"/>

コードビハインドでは以下のように設定を追加してみた。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void TextBox_KeyUpEvent(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Down)
            {
                var direction = FocusNavigationDirection.Next;
                (FocusManager.GetFocusedElement(this) as FrameworkElement)?.MoveFocus(new TraversalRequest(direction));
            } else if (e.Key == Key.Up)
            {
                var direction = FocusNavigationDirection.Previous;
                (FocusManager.GetFocusedElement(this) as FrameworkElement)?.MoveFocus(new TraversalRequest(direction));
            }
        }
    }
}

FocusNavigationDirection.Nextとか,FocusNavigationDirection.Previousとかはよいが,?を使った書法は参考ページにあったものだが,珍しい書き方だったので引用してしまった。
もし私がコーディングしたら以下のようになる(かな)

private void TextBox_KeyUpEvent(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Down)
            {
                var direction = FocusNavigationDirection.Next;
                FrameworkElement element = (FrameworkElement)FocusManager.GetFocusedElement(this);
                if (element != null)
                    element.MoveFocus(new TraversalRequest(direction));
            } else if (e.Key == Key.Up)
            {
                var direction = FocusNavigationDirection.Previous;
                FrameworkElement element = (FrameworkElement)FocusManager.GetFocusedElement(this);
                if (element != null)
                    element.MoveFocus(new TraversalRequest(direction));
            }
        }

≫ 続きを読む

2018/10/13 コンピュータ   TakeMe
Tag:WPF