2019年12月26日木曜日

Windows 10 システムを「バックアップと復元」を使って別ディスク(SSD)へ移行する方法

システムディスクをSSDへ移行するにあたり、引っ越しソフトがあればそれを使うのが簡単ですが、ディスクが壊れた時の復旧時にも必要なことなので「バックアップと復元」を使って行なってみました。これが意外と単純ではなかったので、メモしておきます。

「バックアップと復元」でシステム移行を行う場合、移行先ディスクのデータ容量は元ディスクと同じか、大きくなければいけません。

もし小さいディスクへ移行させたい場合はAOMEI Partition Assistantなどを利用し、元システムのパティション構成を変更してからバックアップを作れば対応可能です。

移行作業

①「バックアップと復元」で「システム修復ディスク」を作成する。
②「バックアップと復元」で「システムイメージ」を作成する。私はシステムディスク(C:)だけのイメージを作成。
③移行先のディスクをdiskpartで完全消去する。
 もしかしたら、購入したままのまっさらなディスクならこの作業は不要かもしれない。
 これを行なわないと復元実行時に次のエラーが発生する。
  データ ディスクが現在 BIOS 上でアクティブに設定されています。その他の
  ディスクをアクティブに設定するか、または DiskPart ユーティリティを使用
  してデータ ディスクから不要なデータを消去した後で、復元操作を再試行し
  てください。

 コマンド実行要領
 >diskpart
   DISKPART> list disk   
   (表示される一覧からターゲットディスクの番号を探す)
   DISKPART>select disk n
   DISKPART>clean

 詳しくはこちらを参照
 <windows|トラブル|解決済>OSバックアップを復元出来ない

④現在のシステムディスクを取り外し、移行先のSSDを取り付ける。現在のシステムディスを取り付けたままだとそのディスクが回復対象になり、別ディスクには移行できませんでした。

⑤「システム修復ディスク」で起動
 詳しくはこちらを参照
 Windows 10 システムイメージからの復元(戻す)方法を徹底解説

⑥「イメージでシステムを回復」を実行
 詳しくは⑤のリンクを参照

追記
システム修復時に再起動後ブートできず”inaccessible boot device”エラーでブルースクリーンになった。UEFI(BIOS)のBOOT設定でACHIがIDEに変わっていたことが原因で、ACHIに変更後、無事再起動できた。

移行先ディスクの回復パティション移動

これで別ディスクにシステムが移行ができたのですが、元ディスクに「回復パティション」がある場合、これも含めてまったく同じ場所に復元されてしまいます。より大きなディスクを用意しても、これが邪魔でシステムのパティションを大きくすることができません。
フリーバージョンのAOMEI Partition Assistantで、パティションをディスクの末尾に移動させることができます。

詳しくはこちらを参照

試していませんが、diskpartを使って回復パティションの移動(削除・再設定)を行う方法が紹介されています。
詳しくはこちらを参照

回復パティション移動/削除後は、「ディスクの管理」を使ってパティションの拡張が可能です。

回復パティションの削除
引っ越し完了後にもとのディスクを再利用しようするにあたり「回復パティション」を削除したいが、「ディスクの管理」では表示のみで削除不可なので、diskpartコマンドで削除しました。
「AOMEI Partition Assistant」では、できそうでできませんでした。有償版なら可能かもしれません。

・コマンドプロンプトを管理者権限で起動
・次の要領でコマンド実行
 >diskpart
 DISKPART> list disk
   ディスク      状態           サイズ   空き   ダイナ GPT
   ###                                          ミック
   ------------  -------------  -------  -------  ---  ---
   ....
   ディスク 3    オンライン           119 GB   118 GB

 DISKPART> select disk=3
 ディスク 3 が選択されました。

 DISKPART> list partition
   Partition ###  Type                Size     Offset
   -------------  ------------------  -------  -------
   Partition 1    回復                 510 MB   118 GB

 DISKPART> select partition=1
 パーティション 1 が選択されました。

 DISKPART> delete partition override
 DiskPart は選択されたパーティションを正常に削除しました。

詳しくはこちらを参照
Windows10 - 回復パーティションの削除(DiskPart)

2019年6月11日火曜日

C# TagLib#のMP3タグ文字化け対策

C#でMP3タグの編集を行うアプリを作るのにTagLib-sharpを使ってみました。
まずは、こんな簡単にタグを設定できます、といった例が見つかります。

    TagLib.File tagFile = TagLib.File.Create(filePath);
    tagFile.Tag.Title = "My Favorite Things";
    tagFile.Tag.Album = "BEST";
    tagFile.Tag.Performers = new string[] { "My Favorite Singer" };
    tagFile.Save();

確かにこれでできるのですが、日本語だとWindows 10のファイルエクスプローラで文字化けすることがあります。そんなときもGrooveミュージックやiTunesでは表示されるので、これはTagLibの問題ではなく、タグを解釈するアプリ側の問題です。

手っ取り早い解決策は、次の例のようにタグのバージョンをID3v2.4にすることです。

    tag = (TagLib.Id3v2.Tag)TagFile.GetTag(TagLib.TagTypes.Id3v2, true);
    tag.Version = 4;

File作成時はMP3ファイルのタグバージョンが適用されるので、読み込み後に変更します。
GetTag()の第二引数にtrueをつけると、Tagがない場合は新規作成します。

いささか解せないのは、タグ未設定のMP3にエクスプローラのプロパティーで属性を設定すると、ID3V2.3のタグが設定されることです。ID3V2.3がデフォルトバージョンならちゃんと対応してほしいものですが...

もうひとつの方法はいったんID3V2.3を全て削除し、必要なタグだけ設定する方法です。これは後述しますが、ID3V2タグを再作成すると非同期化がOFFになるからです。

    TagFile = TagLib.File.Create(filePath);
    TagFile.RemoveTags(TagLib.TagTypes.Id3v2);
    TagLib.Id3v2.Tag tag =
        (TagLib.Id3v2.Tag)TagFile.GetTag(TagLib.TagTypes.Id3v2, true);

余分なタグを消してファイルサイズを小さくできるので、場合によっては有用でしょう。

これで一件落着なのですが、なぜ文字化けが発生するか調べたので書いておきます。

冒頭のコードで発生するエクスプローラの文字化けには、次の要因が絡んでいます。
①ID3v2タグの非同期化フラグがONになっている。
②項目により文字化ける場合と無効(非表示)となる場合がある。
③ID3V2タグで未設定だがID3V1に対応する項目があると、ID3V1の項目が適用される。
④ID3V2タグが無効だがID3V1に対応する項目があると、変更は無視され、ID3V1の項目が表示される。

①の非同期化とは、MP3v2タグに対応していなアプリがタグを音声データと誤認しないようにするための処理で、この結果UTF16文字列の先頭BOMが0xFFEE"だと”0xFF00EE"に変換されます。この場合にWindows 10のエクスプローラで文字化けが発生します。試しにバイナリエディタで”0xFFEE00"に変更してみると、文字化けが解消します。

冒頭のコードのように tagFile.TagのプロパティーにStringをセットした場合は、ID3V2.3ではUTF16ですが、ID3V2.4ではUTF8で出力されます。そのため、ID3V2.4では”0xFF00EE"は発生しません。

非同期化フラグはID3V2.4にも存在し、ONの場合に類似の処理が行われます。以下のコードでUTF16で出力すると”0xFF00EE"というバイトシーケンスが発生します。ですが、エクスプローラはID3V2.4は適切に処理してくれるようで、文字化けしません。

frameの文字コードを設定し、frameのTextにStringをセットします。

    TextInformationFrame fTIT2 =
        TextInformationFrame.Get(tag, FrameType.TIT2, StringType.UTF16, true);
    fTIT2.TextEncoding = StringType.UTF16;
    fTIT2.Text = new String[] { "タイトル” };

非同期化をOFFにすれば文字化けしなくなるのですが、MP3ファイルの非同期化フラグはTagLib.Tagクラスのプライベート変数にセットされ、それを操作するパブリックメソッドはありません。

ただし冒頭に書いたように、いったんID3V2タグを削除し再作成すれば、非同期化がOFFの状態になります。

TagLibのソースコードを変更すればプログラムでの対応も可能で、例えば次のようにTagLib.Id3v2.Tag.cs にメソッドを追加し、Save()を呼ぶ前にこのメソッドを呼ぶと非同期化をOFFにできます。

    public void ClearUnsynchronisationFlag ()
    {
        header.Flags &= ~HeaderFlags.Unsynchronisation;
    }

非同期化はID3V2.3を認識しない再生アプリのための処理なので、そんな古いアプリを考慮する必要がなければ問題ないでしょう。Windows 10のエクスプローラでも、MP3ファイルのタグ情報をプロパティダイアログの詳細で変更、保存すると非同期化はOFFになります。これを利用すればID3V2.3のままでの文字化け対策にもなります。

項目によって現象が異なり、TitleとAlbumは次の要領でUTF16BEを適用することで文字化けを解消できます。UTF16BEにはBOMがなく”0xFF00EE"が発生しないことが影響しているのでしょう。

    TextInformationFrame fTIT2 = TextInformationFrame.Get(tag2, FrameType.TIT2, StringType.UTF16BE, true);
    TextInformationFrame fTALB = TextInformationFrame.Get(tag2, FrameType.TALB, StringType.UTF16BE, true);
    fTIT2.TextEncoding = StringType.UTF16BE;
    fTALB.TextEncoding = StringType.UTF16BE;
    fTIT2.Text = new String[] { TitleString };
    fTALB.Text = new String[] { AlbumString };

既存のFrameがある場合、Getが返すFrameのStringTypeは既存の設定のままなので、StringTypeの再設定を行っています。

残念ながらPerformers(参加アーティスト)はこれでも文字化けます。あまり現実的ではありませんが、Performersは半角英数字(Laten1)に限れば次のコードで文字化け回避できます。

    TextInformationFrame fTPE1 = TextInformationFrame.Get(tag2, FrameType.TPE1, StringType.Latin1, true);
    fTPE1.TextEncoding = StringType.Latin1;
    fTPE1.Text = new String[] { PerformersString };

③の現象は、MP3ファイルにまだID3V2タグがない、あるいは対象項目がセットされていない場合に発生します。
TextInformationFrame.Get(tag2, FrameType.TIT2, StringType.UTF16, true);を使用することでID3V2タグがまだない場合は新規作成してくれます。
 TextInformationFrame.Get(tag2, FrameType.TIT2, StringType.Latin1, true);を使用することで、対象項目がセットされていない場合は追加してくれます。

それでも非同期化がONだと④の現象が発生し、ID3V2への変更は無視され、ID3V1の情報が表示されます。

ついでながら、TagLibではUTF16LEも定義されています。TagLibとしては処理しているのですが、UTF16LEではエクスプローラに文字が表示されません。GrooveミュージックでもiTunesでもダメです。調べた範囲では、ID3V2の仕様ではUTF16LEは定義されていないようです。

----------

【追記】
既に設定済みのMP3タグをTagLibで読み込むと文字化けしてしまうことがあります。
次のページに対応策が紹介されています。

2019年1月7日月曜日

Windowsキーを無効にする方法

幼児用のお遊びアプリを作ってみたのですが、なにせメチャクチャにキーをたたくので、Windowsキーなどシステムが優先的にイベントを扱うキーが押されるとスタートメニューが表示されたり、Print Scrで画面がキャプチャーされたりしてしまいます。

そこで、こうしたキーを無効にする方法を調べてみました。

まずはMicorosoftのページ。
Disabling Shortcut Keys in Games

ゲームなどで誤って Windows key を押してしまっても、好ましくない動作をしないようにする方法を紹介しています。しかし、C++なので C# のプロジェクトにはそのままでは導入できません。

こちらからはC#のサンプルがダウンロードできます。
A Simple C# Global Low Level Keyboard Hook (StomySpike / Code Project)

そのままソースを取り込んでも動作可能ですが、このソースのままだと次のようなエラーが発生します。

マネージ デバッグ アシスタント 'CallbackOnCollectedDelegate'  が発生しました
…コールバックが、型 '...+keyboardHookProc::Invoke' のガベージ コレクションされたデリゲートで行われました。…デリゲートをアンマネージ コードに渡すとき、デリゲートは 2 度と呼び出されないことが確実になるまでマネージ アプリケーションによって維持されなければなりません。'

これの解決策が次の Stack Overflow のQ&Aにありました。私は、これに従って若干変更して使っています。

C++のアンマネージドの環境ではdelegateがガベージ コレクションされなかったが、C#のマネージドの環境ではガベージ コレクションされてしまい、その結果上記のエラーが発生します。

同様の内容のページがあります。
CallbackOnCollectedDelegate が発生しました。(Cafe's Room)

callbackOnCollectedDelegateについてのMicrosoftの解説です。
callbackOnCollectedDelegate MDA

このソースで使われている関数については、次のページの説明が参考になります。
EternalWindows / Windows 開発 / メッセージ管理 / メッセージフック