なんかする

なんかいろいろやる

aapt.exe finished with non-zero exit value 1

What went wrong:

Execution failed for task ':app:processDebugResources'.
> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'C:\Users\Tsugumi\AppData\Local\Android\Sdk\build-tools\24.0.2\aapt.exe'' finished with non-zero exit value 1

Try:

Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

android初心者がこのエラーで10時間以上悩んだ話
ググってもググっても解決方法がよくわからない

最終的に解決に至ったのはこの記事のおかげ
qiita.com

Terminalに
gradlew assembleDebug --info
を入力したら、ビルドは失敗するんだけど詳細なエラー内容がちょっと上のほうにかいてあるからそれを見てサクサク解決できた

assetsフォルダに日本語名のファイルやフォルダを置いていたのが敗因だった
日本語ダメなんだね……

エラー: Setter HogeHoge is not associated to any field

Setter HogeHoge is not associated to any field

AndroidStudioでRealmを用いたアプリの開発を始めたところタイトルのようなエラーがでてビルドに成功しない

Error:(52, 17) エラー: Setter HogeHoge is not associated to any field
注意:Creating DefaultRealmModule
警告: 最後に作成されたタイプ'io.realm.DefaultRealmModule'のファイルは注釈処理に渡されません。
警告: 最後に作成されたタイプ'io.realm.DefaultRealmModuleMediator'のファイルは注釈処理に渡されません。
エラー1個

結論

boolean型のフィールドの名前に、接頭辞としてisを用いていたのが原因だったらしい
isを取り払うことでエラーが解消した
boolean型のフィールドのgetter/setterの自動生成をするとメソッド名にisって勝手に付くから、そのへんでエラーが起きてたんじゃないかと予想
java - Getter is not associated to any field - Realm - Stack Overflow

疑問点

ブログに書くためにサンプルコードを用意したところ再現性がなかった
なにかもっと色々と間違ってる気がしてきた

public class TestClass extends RealmObject {
    //接頭辞にis書いたのにビルド成功したよ!!
    private boolean isHogeHoge;

    public boolean isHogeHoge() {
        return isHogeHoge;
    }
    public void setHogeHoge(boolean hogeHoge) {
        isHogeHoge = hogeHoge;
    }
}

初回ビルドのみのエラーとかありえるのかな……?
とりあえずこのエラーに悩んでる人がいればちょっと試してみてはどうでしょうか

CLR20r3と格闘

CLR20r3があらわれた!

先日配布を始めたアプリに寄せられたバグ報告として、起動すらせずに落ちるというものがあった
自分の環境ではもちろん動くし、バグ報告をしてきた一人以外は問題なく動いてるっぽい
エラー表示は何かでないのかと聞いたところ以下のようなものらしい

問題のイベント名:CLR20r3
問題の署名01:○○○.exe
~中略~
問題の署名07:f
問題の署名08:182
問題の署名09:N3CTRYE2KN3C34SGL4ZQYRBFTE4M13NB

CLR20r3なんて初めてみたがな……

結論

結論から言うと、コンストラクタ内でNull Reference Exceptionが発生していたのが原因だった
解決方法は

  1. 例外をなんとかして補足してe.Exception.GetType();する
  2. 例外の種類に合わせてスタックトレースやらなんやら情報を吐き出させる
  3. がんばる

以下6時間の格闘の軌跡
自分の手元で再現できない(と思っていた)せいもあって時間が超かかった

.NETのバージョン?編

.NET 3.5のアプリを.NET 2.0と.NET 4.0環境で動かすと落ちる – majishini
Tech TIPS:.NET Frameworkのバージョンを整理する (1/2) - @IT
CLR20r3でググると.NETのバージョンや修正パッチの未適用に起因する場合があるということでインストールし直してみてもらう
→効果なし

ildasmを使う編

引き続きググると、ildasmを用いて逆アセンブルして原因を突き止めましょうというような記述が多いのでやってみる
VisualStudio→ツール→外部ツール→追加からメニューにildasmを追加してメニューから起動できるようにする
タイトル:今回はildasm
コマンド:ildasmのパスを入力 自分の環境ではC:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\ildasm.exeを入力した
引数:$(TargetPath)と入力
@IT:.NET TIPS Visual Studio .NETからILDASMを起動するには? - C# VS.NET
メニューに追加されたのでildasmを使って問題の署名を読み解いていく
DD開発ROOM: .NETプログラム例外落ちイベント(CLR20r3)を読み取る
ツール→ildasm起動→表示→メタ情報→表示!を選択

問題の署名07

問題の署名07が欠陥アセンブリの型とメソッド(16進数)を表しているとのこと
自分はfと表示がでていたのでこの場合は0600000fで検索をかけるもよう

Method #8 (0600000f) 
-------------------------------------------------------
	MethodName: .ctor (0600000F)
	Flags     : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor]  (00001886)
	RVA       : 0x000022a8
	ImplFlags : [IL] [Managed]  (00000000)
	CallCnvntn: [DEFAULT]
	hasThis 
	ReturnType: Void
	No arguments.

なるほどわからん
.ctorで調べてみるとコンストラクタを表すとのこと。
当時の自分はスルーしちゃったけど今思えばここにちょっとヒントがあったらしい

問題の署名08

問題の署名08が欠陥メソッドのIL命令(16進数)とやらを表しているらしいのでみてみる。
参考サイトの例に倣って「IL_0182」で検索するがヒットしない……どころか「IL_」すら一件もヒットなし

問題の署名09

問題の署名09がスローされた例外の種類(32文字制限付き)を表すらしい ただし長過ぎるとハッシュ化
一応N3CTRYE2KN3C34SGL4ZQYRBFTE4M13NBをググってみるも有力な情報を得られず

エラーの詳細に迫る編

イベントハンドラを利用したい

vb.netアプリケーションで発生した例外エラー - Visual Basic | 【OKWAVE】のベストアンサーの参考URLにある
捕捉されなかった例外がスローされたことを知る: .NET Tips: C#, VB.NETを参考に、AppDomain.UnhandledExceptionイベントハンドラを利用することにしてみる

static void Main(string[] args)
{
    //UnhandledExceptionイベントハンドラを追加する
    System.AppDomain.CurrentDomain.UnhandledException +=
        new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

    //例外をスローする
    throw new Exception("エラーのテストです。");
}

なるほどMainメソッドの中でイベントハンドラを追加するんだな
WPFのメインメソッドってあったっけ

WPFのMainメソッド

WPF と Main メソッド - しばやん雑記
つまりApp.xamlのビルドアクションをApplicationDefinitionからPageに変えたうえでApp.xaml.csにMainメソッドを書いてやればいいらしい

public partial class App : System.Windows.Application
{
    [STAThread]
    public static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

        App app = new App();
        app.InitializeComponent();
        app.Run();
    }

    private static void CurrentDomain_UnhandledException(object sender,UnhandledExceptionEventArgs e)
    {
    	/* 中略 エラーの種類をMessageBoxで表示させる */
    }
}

動かしてもらったところエラーは補足されなかったとのこと
以前と全く同じエラーの出方だしメッセージボックスもでないらしい

DispatcherUnhandledExceptionイベント

他に有効そうなものがないかグーグル先生にお尋ねする
WPFサンプル:未処理例外に対応する:Gushwell's C# Dev Notes
App.xamlにDispatcherUnhandledExceptionイベントを追加してApp.xaml.csに以下を記述

public partial class App : System.Windows.Application
{
    private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
    {
        string message = string.Format("{0} {1}", e.Exception.GetType(), e.Exception.Message);
        MessageBox.Show(message);
  }
}

実行してもらったところメッセージボックスが出た!

System.Reflection.TargetInvocationException 呼び出しのターゲットが例外をスローしました。

TargetInvocationException

とりあえずググる
InnerExceptionは誰が設定するのか(例外が再スローされるときにいつでも設定される訳ではない) - tekkの日記 C#,VB.NET
つまりTargetInvocationExceptionのInnerExceptionには本来の例外が設定されてる
じゃあそれを踏まえて表示させていけば解決の手がかりになりそう
TargetInvocationExceptionのプロパティで役に立ちそうなものは全部出力してみる

private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
    var ex = (TargetInvocationException)e.Exception;
    MessageBox.Show("Data:" + ex.Data.ToString() +
                    "\nInnerException:" + ex.InnerException.ToString() +
                    "\nMessage:" + ex.Message +
                    "\nSource:" + ex.Source +
                    "\nStackTrace:" + ex.StackTrace +
                    "\nTargetSite:" + ex.TargetSite.ToString() +
                    "\nException:" + ex.ToString());
}

String.Formatメソッドで書いたら何かそこで例外が発生してめんどくさかったから思考停止文字列連結
結果は

~中略~
InnerException:System.NullReferenceException: オブジェクト参照がオブジェクト インスタンスに設定されていません。
場所 (アセンブリ名).MainWindow..ctor() 場所 D:\Users\(中略 ファイルのフルパス)\MainWindow.xaml.cs:行 114
~中略~

ぬるりだと……
114行目を見てみるとそこにはレジストリキーを使ってあるソフトのインストールパスを取得して、それを変数に代入してる処理
これで解決できたからいいけど今思えばInnerExceptionのほうの色々を出力したほうが良かったかも

余談だけど、いままでの試行錯誤は全てバグが出ると言ってる人にexeを渡して実行してもらって結果を受け取ってる
なのにファイルのフルパスに表示されてたものが完全に筆者の環境のものですごく焦った(ユーザ名も含まれている)
なんなんだろうこれ心臓に悪すぎた

レジストリキー取得の罠

nullなら入れてあげれば解決じゃんということで原因調査
本アプリのユーザなら間違いなくインストールしているはずのソフトのレジストリキーが取得できてないという謎の事態
レジストリを参照できないからインストールフォルダを特定できずにぬるりが発生している
とりあえずインストールしてるPCで動かしているかどうか聞いてみるけどしているとの返答

一旦フォルダ選択ダイアログでユーザ自身でインストールフォルダを指定するようにしたものを渡して動かしてもらう→動いたとのこと

調べるとどうやら32bitと64bitでレジストリキーの場所が違うらしい
x64 Windows でのレジストリの扱い - Life like a clown
確認したところ、確かにバグの出るユーザのみ32bitだったから原因はこれで間違いなさそう
上記のページを踏まえてWow6432Nodeありで取得してnullだったらWow6432Nodeなしで取得を試みるといいんだなと思い実践
Before

var regkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\SoftwareName", false);
this.folderPath = (string)regkey.GetValue("InstallLocation");

After

var regkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\SoftwareName", false);
if (regkey == null)
{
    regkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SoftwareName", false);
}
this.folderPath = (string)regkey.GetValue("InstallLocation");

……取得できない!
そんなに単純なことじゃなかったのかなと思いつつ更に調べる
[C#, dotNet4.0] レジストリのリダイレクトを回避 | 学習B5デスノート
どうやらWow6432Nodeにリダイレクトされているらしい
ということで[.NET, C#]レジストリの値が取得できない原因を参考にしながらコードを変更する

var regkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\SoftwareName", false);
if (regkey == null)
{
  var reg32 = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, Microsoft.Win32.RegistryView.Registry64);
    regkey = reg32.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SoftwareName", false);
}
this.folderPath = (string)regkey.GetValue("InstallLocation");

取得できた!
なんでRegistryView.Registry64と書くことで32bitのレジストリが参照できるのかはわからないけど取得できた!
長い戦いだった

WPFで音楽再生しようと試行錯誤

SmallBasicのSound.Play(filePath)でつまづく

C# 音を鳴らす とかでググると、一番簡単なサウンド(mp3含む)の再生方法としてSmallBasicのSound.Play(filePath)がよく紹介されている
C#で音楽を再生する一番簡単な方法 - Null and void
しかし開発中のアプリで使ってみたけど音がならない……
参照の追加はやったしこんな簡単なメソッドで間違える要素あるのか?なんておもいながら格闘してたけど
なんのことはないフルパスでの指定が必要なようだった

WPFで実行フォルダのパスの取得

じゃあフルパス指定しよねとなったけどWPFには実行フォルダを取得するApplication.StartupPathがないらしい
そのあたりをググってると

string exePath = Environment.GetCommandLineArgs()[0];
string exeFullPath = System.IO.Path.GetFullPath(exePath);
string startupPath = System.IO.Path.GetDirectoryName(exeFullPath);

これで大体は同じような結果が得られるようだった
[WPF]アプリケーションのStartupPathをちゃんと取得する | OITA: Oika's Information Technological Activities

WPFでループ再生

でもやっぱり音楽のループ再生をしたいなということになり、Sound.Playではできないようなので別の方法を探す
WindowsMediaPlayerを利用したものが楽そう
COMコンポーネントを追加する方法も紹介されていたけど、今回はMediaPlayerのフォームへの配置は必要なかったし
そもそも互換性的にアウトと警告がでたので参照の追加に切り替え
"C\Windows\System32\wmp.dll"を参照に追加すると、WMPLibが参照に追加される

var player = new WMPLib.WindowsMediaPlayer();
// ループ再生を指定
player.settings.setMode("loop", true);
// 通常は自動再生にファイルを指定すればループ再生がはじまる
player.URL = @"C:\test.mp3";

// その他の基本操作
player.settings.setMode("loop", false); // ループ再生解除
player.controls.play(); // 再生
player.controls.stop(); // 停止(再生中停止すればplay()で頭から再生)
player.controls.pause();// ポーズ(play()で再開)
player.settings.volume = 10; // 0から100

参考
C#で、BGMとしてmp3の音楽をループで再生するにはどうしたらいいですか... - Yahoo!知恵袋

いい感じにループ再生してくれて満足
書き方も簡単だし基本的な機能は揃ってるし今度からこれを使うことにしよう
音はここからもらいました
クレジットの明記とかは必要ないっぽいけど良さげだったのでメモ
www.kurage-kosho.info

Androidアプリ開発はじめました

Androidアプリの開発をはじめた
デスクトップアプリのJavaとぜんぜん違って戸惑う
R.Idとかその他いろいろ定型文みたいなのが超めんどくさげ
慣れの問題もあるんだろうけど……

既存のAccessをRealmに移行ってできるのかが気になるところ
Accessからエクスポート→プログラムから読み込んでrealmに書き込み じゃなくて
Access→(プログラム)→Realmってできるといいんだけど 

正規表現 Groupクラス

にちゃんねるのスレッド一覧はsubject.txtというファイルに記述がある
こんなかんじのものが数百行
f:id:thisisname:20161009161358p:plain

このファイルを一行づつ読んで

を一気に抽出できたら便利だなと思った

完成品

public class ThreadInfomation
{
    public long threadKey { get; set; }
    public string threadTitle { get; set; }
    public int resCount { get; set; }

    public ThreadInfomation(string line)
    {
        Regex re = new Regex(@"(?<threadKey>\d{10})\.dat\<\>(?<title>.*)\t.*\((?<resCount>\d{1,4})\)$");
        Match match = re.Match(line);
        this.threadKey = long.Parse(match.Groups["threadKey"].Value);
        this.threadTitle = match.Groups["title"].Value;
        this.resCount = int.Parse(match.Groups["resCount"].Value);
    }
}

正規表現 Groupクラス

()で囲んだ部分がグループ化される
マッチした部分文字列はMatch.Groupsプロパティでアクセスできる
スレキー抽出部分を例につかうとこう

Regex re = new Regex(@"(\d{10})\.dat");
Match match = re.Match(line);
this.threadKey = long.Parse(match.Groups[1].Value);

インデックスの0は正規表現全体にマッチした部分が格納されている

名前付きグループ

(?<groupName>)とするとグループに名前をつけられる
スレキー抽出部分を例につかうとこう

Regex re = new Regex(@"(?<threadKey>\d{10})\.dat");
Match match = re.Match(line);
this.threadKey = long.Parse(match.Groups["threadKey"].Value);

同じ要領でスレタイとレス数についても書いていって完成
なんとなく読みやすい気がする