2024年5月26日日曜日

Mac インストールUSBドライブ作成エラー ...app does not appear to be a valid OS installer application.

Webページ https://support.apple.com/ja-jp/101578 を参照し、
Macbool Air 2011 A3169 用に High Sierra のインストールUSBドライブを作成しようとしたらエラーが発生。

Webページ「ターミナルを使って起動可能なインストーラを作成する」に記載されているHigh Sierra用のコマンドをそのままコピペ(MyVolumeは適宜変更)でターミナルで実行するとエラーが出る。

コマンド
sudo /Applications/Install\ macOS\ High\ Sierra.app/Contents/Resources/createinstallmedia --volume /Volumes/MyVolume

エラー
/Applicaiont/macOS\ High\ Sierra.app does not appear to be a valid OS installer application.

これは日本語環境でAppStoreからインストーラをダウンロードするとインストーラの名前が

Install macOS High Sierra.app

ではなく

Install macOS High Sierraインスール.app

となり、異なっているため。どうも"Install macOS High Sierra.app”という名前をチェックしているようなので、ファイル名を英語表記に合わせて変更しないとダメなようだ。

MyVolumeは実際のターゲットのマウントポイント名に変更するが、実際のマウントポイントが表示名と異なることがある。この場合は見た目は一致していてもマウントポイントが見つからないというエラーが出る。

ディスクユーティリティーでUSBドライブのプロパティーを見て、コマンドのMyVolumeをマウントポイントに一致させる。

あるいはディスクユーティリティーでUSBドライブを消去し、名前を”MyVolume”でフォーマットすればコマンドはそのままでよい。

MyVolumeは createinstallmedia 実行後に既定の名前に変更される。










2024年4月7日日曜日

C# FormアプリでMicrosoftのライブラリだけでheic画像表示

WPFではサポートされてもFormアプリでは使えない機能があります。

Imageのheic対応もそのひとつで、次のようなコードでheicファイルを表示しようとするとメモリ不足のエラーが発生します。

pictureBox1.Image = Image.FromFile(mediaItem.FilePath);

Webでheic表示関連を検索するとMagick.NETを使う例が見つかりますが、Microsoftのライブラリだけで行う方法はまだ見つかりませんでした。Magick.NETは様々なフォーマットを扱える優れたものだと思いますが、dllをアプリに含めると、小さいなアプリでは本体よりMagick.NETの方が大きくなってしまいます。heicサポートの追加だけが目的な場合はあまり好ましくないので、Microsoftのライブラリだけで行う方法をいくつか試してみました。

なお、前提としてWindows 10, 11にHEIF画像拡張をインストールしてあるものとします。HEIF画像拡張がインストールされていない場合はMicrosoft Store から ダウンロードできます。

方法1
BitmapImage
BitmapEncoderを使用する。
今のところ、これが一番良好な結果になっています。
次のようなステップで行います。

System.Windows.Media.Imaging.BitmapImageをファイルから作る。BmpBitmapEncoderでMemoryStreamに書き出す。
MemoryStreamからSystem.Drawing.Imageを作る。
pictureBox1.Imageにセットする。

コードは次のようになります。

            BitmapImage bImage = new BitmapImage();
            bImage.BeginInit();
            bImage.CacheOption = BitmapCacheOption.None;
            bImage.DecodePixelHeight = height;
            bImage.DecodePixelWidth = width;
            bImage.UriSource = new Uri(FilePath, UriKind.Absolute);
            bImage.EndInit();
            using (MemoryStream ms = new MemoryStream())
            {
                BitmapEncoder enc = new BmpBitmapEncoder();
                enc.Frames.Add(BitmapFrame.Create(bImage));
                enc.Save(ms);
                pictureBox1.Image = Image.FromStream(ms);
            }

ひとつの静止画を扱うなら、これで十分でしょう。イメージのサイズを設定できるので、メモリ消費も必要最低限に抑えられ、スピードもこれ以上早くするのは難しいでしょう。

また、書き出し先をファイルにすればフォーマット変換にも使えます。

System.Windows.Media.Imagingのための参照追加 は次のとおりです。

プロジェクトの参照右クリック⇒参照の追加⇒アッセンブリ
PresentationCoreをチェック⇒OK

方法2
BitmapDecoder
BitmapEncoderを使用する。

BitmapDecoderの使い方はよくわかっていませんが、複数のFrameを持ち、Animationなども扱えます。複雑な画像表示を行えるようですが、ひとつの静止画だけを扱うなら方法1で十分そうです。もしかしたら有用かもしれないので紹介しておきます。

ステップは方法1のBitmapImageがSystem.Windows.Media.Imaging.BitmapDecoderに置き換わっただけです。

コードは次のようになります。

            BitmapDecoder uriBitmap = BitmapDecoder.Create(
                new Uri(FilePath, UriKind.Absolute),
      BitmapCreateOptions.None,
                BitmapCacheOption.Default);
            using (MemoryStream ms = new MemoryStream())
           {
               BitmapEncoder enc = new BmpBitmapEncoder();
               enc.Frames.Add(uriBitmap.Frames[0]);
               enc.Save(ms);
               pictureBox1.Image = Image.FromStream(ms);
           }

調べた限りでは、この流れでは画像サイズを指定できません。そのため、高解像度画像ではメモリ消費が多くなり、スピードも少し遅くなります。

方法3
 AxWMPLib.AxWindowsMediaPlayerを使用する。

MediaPlayerは静止画も表示でき、HEIF 画像拡張インストールされていればheicフォーマットも表示できます。ちなみにHEVCビデオも再生できます。静止画を扱う場合は uiMode="none"が良いでしょう。

メリットとしては、まずは静止画も動画も同じインタフェイスで扱えることです。また、対応していないフォーマットや壊れたファイルのエラー対応もMediaPlayerまかせにできます。

これまでのFormアプリと同じ要領で使え、次の要領でコントロールにファイルのパスを設定するだけなので、お手軽といえます。

(AxWMPLib.AxWindowsMediaPlayer)player.URL = filePath;

VisualStudioでMediaPlayerを使う方法は次のリンクを参照してください。
Microsoft Visual Studio で Windows メディア プレーヤー コントロールを使用する
https://draft.blogger.com/blog/post/edit/6538117324271148932/209217070553378409#

デメリットとしては、方法1,2に比べると遅いこと、メモリ消費量も特に高解像度のheicの場合は多くなります。

HEIFコーディックの有無チェック 
Magick.NETと違い、HEIFコーディックがないとエラーになります。
以下の要領でHEIFコーディックの有無がチェックできます。 

bool HeifDllExists = Registry.GetValue(@"HKEY_CLASSES_ROOT\CLSID\{E9A4A80A-44FE-4DE4-8971-7150B10A5199}\InprocServer32", null, null) != null;

参照:HEIF Format Overview
https://learn.microsoft.com/ja-jp/previous-versions/windows/desktop/legacy/mt846532(v=vs.85)




2024年2月25日日曜日

Excel VBA 備忘録 基礎知識編

ときどきExcelのVBAを書きますが、普段使わない言語だとなにかとつまづきます。
自分自身のための備忘録ですが、きっと初心者ならだれもつまづくところだと思います。

VariantとObject
    Variant
     すべての変数。
     Integerなどの値のみを持つプリミティブなデータ型とオブジェクト型のどちらも代入可能。
       配列もプリミティブなデータ型なのでVariantで宣言した変数にしか代入できない。    
    Object
     データとプロパティー、メソッドがカプセル化されたもの。
       オブジェクト型のみ代入可能。
       これにプリミティブなデータを代入すると「オブジェクトが必要です」エラーとなる。

Null, Nothing, Empty, vbNullString の違い
 Null             Variant型変数内に有効な値が入っていない状態
 Nothing       Objectとして空の状態
 Empty         Variant型として空の状態
   vbNullString 値が0のString 

代入時にSetが必要な場合、不要な場合
   Setが必要な場合
        Objectに代入する場合で、値だけでなくObjectのプロパティーの設定が行われる。
 Setが不要な場合
     Integerなどのデータ型に値のみ代入する場合。

Sub呼び出しでCallが必要な場合
 引数を()でくくる場合。

コンパイルエラー:プロパティーの使い方が不正です
    Setを付けずにObjectへ代入しようとした場合。

コンパイルエラー:型が一致しません
 Objectで宣言した変数ににIntegerなどの値をSetしようとした場合。
  Dim o as Object: Set o = 1

コンパイルエラー:オブジェクトが必要です
   Variantで宣言した変数に値をSetしようした場合。
  Dim v as Variant: Set v = 1
 Stringで宣言した変数にSetで文字列を代入しようとした場合。
  Dim s as String: Set s = "str"

コンパイルエラー:ユーザ定義型は定義されていません
    Dimや引数の型宣言で規定のデータ型にもユーザ定義型にもない名前が使われている場合。
    だいたいは書き間違え

実行時エラーオブジェクトが必要です
    値がNothingのオブジェクトのプロパティーにアクセスした場合。
    Objectで宣言した変数にプリミティブなデータを代入しようとした場合。
    Objectで宣言した変数にプリミティブなデータを代入しようとした場合。

実行時エラー:オブジェクト変数またはWithブロック変数が設定されていません。
    Objectで宣言した変数にSetをつけずにオブジェクト、または値を代入しようとした場合。
     Dim o as Object: o = "a" 
     Dim o as Object: o = 1 

実行時エラー 400










2023年9月7日木曜日

C# メール送信プログラムの「SSPIへの呼び出しに失敗しました。内部例外を参照してください。」エラー対応

 数年前に作ったメール送信機能のあるプログラムを作り変えようとしたら、「SSPIへの呼び出しに失敗しました。内部例外を参照してください。」エラーが発生した。

以前作ったとき、.NETのSmtpClientがexplisit SSLのみの対応の、私が使っているjcomのサーバのimplicit SSLで発生が発生したため、そのとき見つけたAegisImplicitMailを利用していた。

今回のエラーはSmtpSocketConnection.Openメソッドの中の

    sslStream.AuthenticateAsClient(host)

で発生した。

検索してみたところ、参考になる記述を発見。

 

PowershellでSSL証明書情報取得時にTLSエラーにハマッた件    https://qiita.com/pizza_slice/items/00d00fd900bb3f0fd697

Windows Server 2016

Window 10 21H2 だったら正常にコマンドが通る。
恐らくこれもOS依存のTLSのバージョンの問題。

# 下記を参考にしてオーバーロード
# AuthenticateAsClient(String, X509CertificateCollection, SslProtocols, Boolean)
# Tls12:3072 TLS1.2セキュリティ プロトコルを指定します$stream.AuthenticateAsClient($commonName, $null, "3072", $false)


同じ理由かもしれないと、AuthenticateAsClientをこれに対応するメソッドに置き換えてみる。

    sslStream.AuthenticateAsClient(host, null, (SslProtocols)3072, false);

するとエラーが発生しなくなった。
(SslProtocols)3072 としたのは、プロジェクトの.NETのバージョンのせいかと思うが、SslProtocols 列挙型にTls12が含まれていなかったため、強引にそのInt値をcastしたからです。
列挙型の名前とint値の対照は次のページを参照してください。

SslProtocols 列挙型

追記

エラーが発生したのは.NET4.0でした。4.5(以上)に変更するとSslProtocols .Tls12が定義されており、上記のintからのcastは不要になります。

なお、このプロジェクトで使用しているAegisImplicitMailの.NETバージョンの関係で、4.5より新しいバージョンでは試していません。SslProtocols .Defaultの値にTls12が含まれていれば、AuthenticateAsClient(host)でもエラーが発生しなくなるでしょう。




2023年9月4日月曜日

VB Excelのシートを名前で探す方法 高速版

VBAでExcelのシートを名前で探す方法を調べると、たいていループで名前が一致するものを探す方法が紹介されています。

たとえばこんなコードです。

    Sub Sample1(name As String)
      For i = 1 to Worksheets.Count
            if Worksheets(i).name = name Then
                Debug.Print Worksheets.Index
                Exit For
            End If
        Next
    End Sub

一回だけ実行する場合は十分高速で問題ないでしょう。しかしループの中で使用すると繰り返し実行されるため、場合によってはかなり時間がかかります。 かつ結果"シート名"と一致するものがない場合はシート数Xループ回数実行され、無駄に時間を浪費します。

上記の例ではWorksheets(i)と引数にindexを使用しています。またiの範囲がCountの範囲内なので、Worksheet(i)がエラーを起こすことはありません。

では、Worksheetsは引数にシート名を使用することもできるので、次のようなコードを試してみましょう。

    Dim sheet As Worksheet
    Set sheet = Worksheets("シート名" )

"シート名"のWorksheetが存在する場合は問題ないのですが、存在しない場合は「インデックスが有効範囲にありません。」というエラーが発生します。Worksheet(i)でもiが有効範囲外であれば同じエラーが発生しますので、内部では同様のループ処理が行われていることが想像できます。

今度は、つぎのような方法を試してみます。

    Sub Sample2(name As String)
        On Error GoTo NotFound
        Debug.Print Worksheets(name).Index
    NotFound:
    End Sub

これでエラーが回避でき、コードもすっきりしているかと思います。面白いことに、計測してみると単純ループの55~60%程度の処理速度となります。75~80%のスピードアッ!!とも言えます。内部的にはループと同等のことを行っていると思いますが、コンパイル済みかどうかの差が大きいのでしょう。

ループによる実行回数が多い場合はバカにできない差になります。

このひとつ前のブログ『InternetExplorer.getElementById 「オブジェクトが必要です」エラー対策』で使用した「On Error Resume Next」も試してみました。

    Sub Sample3name As String)
        Dim sheet As Worksheet
        On Error Resume Next
        Set sheet = Worksheets(name)
        If Not sheet Is Nothing Then
            Debug.Print Worksheets(name).Index
        End If
    End Sub

これは処理速度的にはSample2と同等でした。この例の場合ではSample2の方がコード的にもすっきりしていますが、使用する場面では使い道があるかもしれせん。

---------------------

おそらくほとんどの場合は上記の方法で十分高速になると思いますが、シート数が多く、何十回も検索をするような場合にはDictionaryを使うとさらに高速になるでしょう。シート数が少ないとかえって逆効果になり得ます。

Rem シート名がnameのWorksheetを返す。
Function GetSheetByName(name As Variant)    
    static SheetNameDict As Object

    ’SheetNameDict初期化。
    'SheetNameDict未設定の場合、全ワークシートのKey=name, Value = sheetをセット。
    If SheetNameDict Is Nothing Then
        Set SheetNameDict = CreateObject("Scripting.Dictionary")
         For Each sheet In Worksheets
            Call SheetNameDict.Add(sheet.name, sheet)
         Next sheet
    End If

    On Error Resume Next
    Set GetSheetByName = SheetNameDict(name)
End Function

使用例
Dim sheet as Worksheet
Set sheet = GetSheetByName("someSheetName")
if Not sheet is Nothing then
    ....
End If


Sheetが追加される場合は次のようなコードが必要でしょう。

    If SheetNameDict.Exists(name) Then
        Set GetSheetByName = SheetNameDict(name)
    Else
        On Error GoTo NotFound
        Dim sheet As Worksheet
        Set sheet = Sheets(name)
        Set GetSheetByName = sheet
        Call SheetNameDict.Add(name, sheet)
        Exit Function
NotFound:
        Set GetSheetByName = Nothing

なお、このサンプルの場合はSheetが削除されるとその後存在しないSheetを返すことになり、オートメーションエラーを起こします。必要であればWorkbook_SheetBeforeDeleteイベントでSheetNameDictから削除、あるいはOnErrorで対処します。









2023年8月18日金曜日

VB InternetExplorer.getElementById 「オブジェクトが必要です」エラー対策

VB InternetExplorer.getElementById 「オブジェクトが必要です」エラー対策

VBの InternetExplorer.getElementById は該当する要素がない場合に「オブジェクトが必要です」が発生する。これの扱いに困ったが、次の要領で対処できた。

まずは、次のようなコードでエラーが発生する。

    Dim elm As IHTMLElement
    Set elm = ie.document.getElementById("someID")

要素がない場合にgetElementById はNullを返す。
Nullを代入できるのはVariant型だけなので、IHTMLElementとかObjectで宣言した変数に代入しようとすると「オブジェクトが必要です」が発生する。

そこで、次のようなコードにしてみる。

    Dim elm As Variant
    Set elm = ie.document.getElementById("someID")

こんどは「型が一致しません」エラーが発生する。
これはSetはObjectを対象とするが、NullがVariantであるため発生する。

それでは、これではどうだろう。

    Dim elm As Variant
    elm = ie.document.getElementById("someID")
    if IsNull(elm) Then Exit Sub

    Dim divs as IHTMLElementCollection
    Set divs = elm .getElementsByTagName("div")

Nullのときの処理はうまくいくが、今度はelmがObjectとして設定されていないため、後続のコードで「オブジェクトが必要です」エラーが発生する。

そこで次のように変更し、エラー発生時に処理を継続してみた。

    On Error Resume Next
    Set elm = ie.document.getElementById("someID")
    If IsNull(elm) Then Exit Sub

    Set divs = elm.getElementsByTagName("div")

一応処理できるようになったが、ちょっとおかしい。
IsNull(elm)がTrueにならない。この場合、elmがEmpty値になっている。
Setでelmに代入するので、elmはObjectとなり、Nullにはならないということですね。
elmがEmptyだと後続処理でエラーになる。

そこで、IFの判別を次のようにすると有効になる。

    On Error Resume Next
    Dim elm As Object, divs as IHTMLElementCollection
    Set elm = ie.document.getElementById("someID")
    If elm Is Empty Then Exit Sub 

On Error Resume Nextとした結果 elm がEmpty値になるため、elmをObjectで宣言している。

次の式でも判別できる。

    If IsEmpty(elm) Then Exit Sub

NothingはObjectとして空の状態、EmptyはVariant型として空の状態ということですね。Set式のあとなので実際にはObjectとして扱ってよいと思いますが、Dimで宣言した型に合わせるのがコード的には一貫性があると言えるでしょう。

実際にはNullになることはないが、次の方がより安全かもしれない。

    If IsNullOrEmpty(elm) Then Exit Sub

なお、

    On Error Resume Next

でエラーを無視するようにしたので、前後の処理の関係ではこのあとで再度On Errorを設定しなおす必要があるでしょう。

2022年10月4日火曜日

DELL Inspiron 14 3452 復活奮闘記

DELL Inspiron 5458を使用していますが、しばらく前にキーボードの一部が反応しなくなってしまいました。交換部品を探したがお手軽なものがなく、ヤフオクやメルカリで5458の出物をしばらく待っていましたが、安いものはキーボードのテストをしていないとか、メモリもHDDも取り外されて廃墟状態になったようなものばかり。

そこで、同じ型のキーボードと思しきInspiron 3452をヤフオクで落札(これもジャンク品)。送料込で3,500円。結果的には目的のキーボードに加え、ACアダプタと8Gバイトメモリも入手できたので十分元は取れたと思ってます。

残りは部品取りに使えればよいと思って思っていました、分解、組み立てを行っているうちに具合が悪かったキーボードが何かの拍子に復活し、3452も一応使用可能な状態に組み上がりました。そうなるともったいなくなり、なんとか使える状態にしておこうと奮闘が始まりました。

奮闘というのは、3452というモデルが32G eMMCが換装不可で、HDDスペースは空いているもののコネクタがないといった、いささか特異な仕様が故です。入手したときには容量不足でWindows Updateもできない状態に陥っていました。

この貧弱な仕様のおかげでWeb閲覧やメール程度の使用しかしていなかったのか、ジャンク品とはいえキーボードもACアダプタも良好でした。部品取り目的なら3452は狙い目かもしれません。

私が入手したマシンはCPUがCeleron N3050 2コア、Windows 10 64bitで、サクサク感はイマイチながら光学ドライブ、HDD無しなため5458より軽量になってます。あれこれ試してみた結果、ちょっと持ち歩いて軽い使い方をするには悪くないと思い始めてます。

さて、インタネットで見ると皆さん工夫したり、苦労したり、諦めたりしているようです。

クセが強いジャンクの Dell Inspiron 14-3452 へ Linux をインストールしてみた。

Dell Inspiron 14-3452 eMMC 32GB Win10 ⇒ 1709 アップデート作法

Inspiron 14 3452のメモリ増設とSDDの交換ってできませんか?

amazon カスタマーレビュー


これらを参考にいろいろ試したので、今後のために書き留めておきます。
なお、手間がかかるので追試検証など不十分です。

--------------------

番外編
DELL Inspiron 5458のキーボード故障への応急対策
一部のキーが無反応になったため、まずはWindows付属のスクリーンキーボードで対応。
キー入力の途中でキーボードからマウスに手を移すのが面倒なため、KeySwap for XP を導入。普段使わないFuntion keyへ割り当てる。

5458とキーボード互換のモデル
Ali-Expressのキーボードの広告があった。これ自体はBR(ブラジル?)キーボードのものだが、キーボード表裏の写真から互換性がありそうと判断し、3452の購入を決めた。3452と互換性があるモデル、と言い換えもできる。保証はないが互換性がある可能性大。

Dell Inspiron 14-3000 3441 3442 3443 3451 3452 3458 3459 5447 5442 5445 5448 5451 5455 5458 7447 5452 5457 5459用brキーボード

準備
バックアップ
お約束として、大幅なシステム変更を行う前はバックアップを取りましょう。システムイメージを作っておいたほうよいでしょう。

--------------------

レベル1
特別なことはせず、そのまま使う場合のTIPS

ディスククリーンアップ
Cドライブの空き容量を少しでも増やす。
エクスプローラでCドライブを選択⇒プロパティー「ディスクのクリーンアップ(D)」
⇒「OK」実行
⇒プロパティー左下の「システム ファイルのクリーンアップ」も実行

dism.exeというコマンドでクリーンアップする方法もあります。これで更新ファイルを全部削除するようです。
>dism /Online /Cleanup-Image /RestoreHealth 

Windows Updateのクリーンアップ
設定⇒システム⇒(左ペイン)記憶域⇒「記憶域の管理」
またはWindows Update⇒「関連するリンク」グループの「ストレージを確認する」
⇒「ストレージセンサーを構成するか、今すぐ実行する」
⇒「今すぐ空き領域を増やす」の「今すぐクリーンアップ」

OneDriveの設定確認
タスクバーのOneDriveアイコンクリック
⇒設定(歯車アイコン)⇒設定(メニュー)
⇒「ファイルオンデマンデド」がチェックされていることを確認

Windowsオフラインアップデート
メモリ容量不足の場合はインストールメディアを作り、オフラインアップデートを試す。

Microsoftの「Windows 10 のダウンロード」ページ
⇒「ツールを今すぐダウンロード」をダウンロードでUSBインストールメディアを作成
最新のメジャーリリースなので、既存バージョンより新しいとは限らない。

USBインストールメディア装着
⇒再起動時にF12キー連打
⇒起動オプション表示
⇒USB起動(バッテリー不良でBIOSの警告画面が出るようなら、バッテリーを外して起動)
⇒言語/キーボード選択⇒OK
⇒「今すぐインストール」
⇒「適用される通知とライセンス条項」
⇒(同意するなら)「同意します」にチェック⇒「次へ」
⇒「アップグレード:Windowsをインストールし、ファイル設定、アプリを引き継ぐ」


Windows再インストール
実は、私はWindows 10を再インストールしています。中古で入手し、容量不足でWindows Updateもできない状態ですから、そこから復旧する意味はありません。Windowsオフラインアップデートと同じ要領でインスールメディア作り、内蔵eMMCを初期化してインストールしました。

インストール後、いくつかドライバが正常動作しません。Dellのサイトからダウンロード、インストールします。

ドライバアップデート後にWiFiのパスワード再設定が必要。

Windows再インストール直後にフォルダ構成変更を行う場合は、Windows Updateを一旦停止するのがよい。作業中にもバックグランドでダウンロードやインストールが行われるため、作業に支障が出たり、フォルダ移動により不具合が出る可能性がある。

Windowsアップデートの一時休止
フォルダ移動の作業中にもWindowsアップデートがバックグランドで動作し、Cドライブの容量が不足する可能性がある。作業が一段落つくまでWindowsアップデートを休止する。

休止モードの無効化
休止モードが有効だとC:\hiberfil.sysに復帰のためのデータが保存されるため、Gバイト超えに肥大する。

メインメモリに余裕がないためか、設定の電源オプションには休止モードの有効/無効に関する項目が表示されないが、hiberfil.sysが肥大化していた。そのため、コマンドで無効にする。管理者モードのCMD.EXEで実行する。

>powercfg.exe /hibernate off

hiberfil.sysは「保護されたオペレーティングシステムファイル」に属するため、デフォルトではエクスプローラで表示されない。エクスプローラのオプション⇒表示で「保護されたオペレーティングシステムファイルを表示しない(推奨)」のチェックを外す。
CMD.EXEのDIRで表示するには"/AS"のオプションを付ける。

バッテリー切れになるとシステムダウンとなるので、長時間のスリープには注意が必要になる。


ライブラリの移動
「ドキュメント」「ピクチャ」「ミュージック」などのライブラリをUSBメモリに移動。
「ドキュメント」などのフォルダを右クリック
⇒プロパティ⇒場所
⇒フォルダパスを変更(ドライブ名をUSBメモリのドライブ名に変更)⇒移動
⇒「フォルダー"...."は存在しません。新たに作成しますか?」の場合は「はい(Y)」
⇒移動の確認ダイアログ「はい(Y)」


新しいコンテンツの保存先変更
設定⇒システム⇒ストレージ⇒新しいコンテンツの保存先を変更する
CドライブからDドライブへ変更

OneDriveを移動
サブマシンとして使用する場合、OneDriveでメインマシンとデータ共有しておくと用途が広がる。

OneDriveのフォルダに指定するドライブはNTFSでフォーマットされている必要がある。
USBがFATでフォーマットされている場合は次のコマンドで変換可能。
>convert D: /fs:ntfs ("D:"の部分は実際のドラブ文字で置き換え)

タスクバーのOneDriveアイコンクリック
⇒設定(歯車アイコン)⇒設定(メニュー)
⇒ アカウントタブ⇒「このPCのリンク解除」
⇒サインアウト後、OneDrive設定のダイアログ表示
⇒「OneDriveを設定」ダイアログ⇒アカウント入力しサインイン
⇒「OneDreveフォルダー」ダイアログ⇒左下の「場所の変更」でUSBへ移動

USBへ移動後はファイル オンデマンドをOFFにしておいた方が良いでしょう。
上の「OneDriveの設定確認」参照


Tempの移動
Windows使用中にTempにデータが溜まり、容量不足の原因になります。シャットダウン後も残るデータもあります。USB3.0ならTempも移動した方がよいでしょう。USB2.0だと速度の問題が出そうです。
  • Temp移動は起動直後に行う。
  • Cドライブのプロパティーで一時ファイルを削除する。
     「ディスクのクリーンアップ」で「一時ファイル」を削除
     「システム ファイルのクリーンアップ」で「一時ファイル」を削除
  • Tempフォルダにファイルが残っている場合は、移動先のTempフォルダにコピーする。
  • robocopyコマンドでコピーした方がファイル属性が保存されて安全だと思います。
    実行例(管理者権限でcmd.exeを起動)
    >robocopy C:\Windows\Temp D:\Windows\Temp /MIR
    >robocopy C:\Users\usrename\AppData\Local\Temp D:\...\Temp /MIR
  • 環境変数のTEMP、TMPをUSBメモリのパスに書き換える
    参考:【Windows10】一時フォルダを別ドライブに移動して高速化する
  • 変更直後に再起動
再起動後、不要になったTempフォルダが空でない場合は削除する。
私は、念のため一旦フォルダをリネームし、再起動後にフォルダ内のデータを削除し、空のフォルダを残した。(環境変数に関わらずC:\Windows\Tempを使用するプロセスがあるようだ。)
Tempフォルダのリネーム、削除がアクセス権なしで失敗する場合は、cmd.exeを管理者権限で起動し、ren、rmdirなどのコマンドで操作する。

--------------------
レベル2
USBメモリまたはSDカード常時装着&固定ドライブ化
速度的にはUSB3.0(以上)がお勧めです。本体左側がUSB3.0、右側がUSB2.0です。
USB3.0なら内蔵eMMCと同等の速度が期待できます。
SDカードスロットはUSB2.0なので、高速なSDカードを使ってもそれなりの速度しかでません。
以下、SDカードも含めUSBメモリとします。また、ドライブ文字をDとします。

どのような方法にせよUSBメモリにデータを保存すれば紛失時のデータ流出の危険があります。そのような心配のない用途に限るか、心配なら(私は試していませんが)BitLockerによる保護などが必要です。

eMMCやUSBメモリのデータ圧縮はまだ試していません。Cドライブ全体を圧縮すると起動が遅くなると決めつけているからですが、Cドライブの残り容量があまりに少ないので検討してみる予定です。

USBメモリを固定ドライブ化
アプリケーションをUSBメモリにインストールをしようとすると「インストール ディレクトリはローカル ハード ドライブにあることが必要です。」と警告される。
そこで、いくつかの方法を試した。
ファイル/フォルダ削除でゴミ箱移動が可能になる方法もある。

USBメモリにアプリをインストールした後、アンインストール時に,msiフォルダが作成できないというダイアログが表示され、アンインストールに失敗することがある。検証不十分だが、パーティションをactiveにするとエラーが発生しなくなっている。

方法1
USBメモリ上のファイルをVHD(Virtual Hard Disk)に割り当てる。
HDDとして扱ってくれることで、アプリケーションのインストールやライブラリ類の移動、ファイル履歴の保存先に選択などが行える。

また、リムーバルディスクの場合にファイル/フォルダ削除はゴミ箱への移動ではなく完全削除となり、かつ削除のたびに「このファイルを完全に削除しますか?」のダイアログが表示されるが、この方法だとファイル/フォルダ削除がゴミ箱移動となる

VHDは再起動時にマウントする必要がある。タスクスケジューラで自動マウントを設定する。


タスク設定時にユーザのパスワードを要求される。PINではダメなので、ローカルユーザに変更し、パスワードを設定しておく。

欠点として、(設定の問題があるかもしれないが)サインイン前にマウントが完了していないことが多く、その場合にサインイン時の環境が不完全な状態となる。手動でマウントするか、エクスプローラの再起動や再サインインが必要が必要になる。これが許容できるなら、良い方法と思う。

サインイン直後にマウントが完了していないとディスクトップ、タスクバーの再現ができないため、デスクトップはDドライブに移動した。この場合、デスクトップからのファイル削除は完全削除となる。

VHDは起動時にはまだマウントされていないため、起動時に他のUSBメモリが装着されているような場合、若いドライブレターが先に適用される。そのためVHDのドライブレターはD、Eなど他のメモリに使われる可能性があるものは避ける。私はVを割り当てることにした。

方法2
ディスク管理を使用し、USBメモリのパーティションをCドライブ上の空のフォルダにマウントする。

アプリケーション インストール先をこのフォルダ下に設定すると、インストールは完了し起動するが、アンインストール、修復で失敗した。何か設定に問題があったのかもしれない。

もし試す場合は、バックアップからの復旧か再インストールの準備をしておくこと。

もしアプリ削除で問題が発生したら、USBメモリに移動したライブラリ類を標準に戻して、アプリ削除/再インストールを試みる。それでもダメだとバックアップへの復旧か再インストールとなる。

方法3
USBメモリに(例えば)”C:\Program Files (USB)”フォルダを作り、次のコマンドでCドライブにリンクを作る。
>mklink /J "C:\Program Files (USB)" "D:\Program Files (USB)"

アプリケーション インストール先をこのフォルダ下に設定する。インストールは完了し起動し、アンインストール、バージョンアップも成功した。

VHDマウントのタイムラグの問題がないため、この方が良い場合がありそう。

方法4
USBメモリ上のフォルダを共用にし、ネットワークドライブに割り当てる。
このドライブはファイル履歴の保存先として選択可能になる。
あまり好ましく思えなかったので、よく試していない。

--------------------
レベル3
肥大化するフォルダ、サイズの大きいフォルダをUSBメモリへ移動する
多くのフォルダアクセスで管理者権限が必要となる。以下の手順でアクセス拒否が発生した場合は、管理者権限でCMD.EXEを起動し、コマンドで操作する。
また、USBメモリへのデータコピーは属性保存のため、原則としてrobocopyを使用した。
アプリケーション起動後に残る常駐プロセスのためにフォルダはリネームができない場合がある。その場合は再起動直後に行う。
バックグランドの常駐サービスが使用しているためにリネームができない場合がある。その場合は回復の再起動でコマンドプロンプトを表示し、リネームした。

バックアップを作っておく
不具合が発生した場合、最悪Windows再インストールの可能性もある。
これ以降に進む前に、バックアップを作っておいた方がよい。

仮想ハードディスクもバックアップ対象のドライブとして選択可能になるが、USBメモリはWindowsバックアップに含めず、別にrobocopyでコピーする。試していないが、再起動不可の場合はVHDドライブのマウントもできていないので、復元に失敗すると思う。

また、USBメモリにライブラリなどのフォルダを移動するためか、システムイメージは作成できるが、ファイルのバックアップには失敗する。

ページングファイルの縮小
メインメモリが不足するとPagefile.sysに書き込まれ、肥大化する。
固定HDDがある場合などは別ドライブにPagefile.sysを移すことができるが、USBメモリの場合は単純にはできない。
VHDをPagefile.sysにすることはできるが、Cドライブは使用せずVHDだけにPagefile.sysを作る設定だと、おそらく起動時にはまだマウントが完了していないためCドライブにPagefile.sysが作られた。
そこで、CドライブにPagefile.sysに200Mを固定で割り当て、VHDにPagefile.sysシステム管理で設定した。今のところこれで動作している。


AppDataのOneDrive移動
C:\Users\username\AppData\Local\Microsoft\OneDrive
  • OneDriveのリンクを解除する、終了
    タスクトレイのOneDriveアイコン⇒設定アイコン⇒「設定」クリック
    ⇒「このPCのリンク解除」クリック
    ⇒「OneDrive終了」クリック
  • OneDriveのデータコピー
    >cd C:\Users\username\AppData\Local\Microsoft
    >robocopy OneDrive D:\...\OneDrive /MIR
  • OneDriveリネーム
    >cd C:\Users\username\AppData\Local\Microsoft
    >ren OneDrive OneDrive~
  • リンク作成
    >mklink /J OneDrive D:\...\OneDrive
  • OneDrive再設定
     [スタート] ⇒[検索] ⇒[OneDrive] と入力⇒検索結果で [OneDrive] を選択
    参考:OneDrive の同期に関する問題を解決する
  • 再起動、動作確認後、リネーム後のOneDrive~を削除
AppDataのEdge移動
C:\Users\username\AppData\Local\Microsoft\Edge
「AppDataのOneDrive移動」と同様の手順でEdgeをコピー、元フォルダをリネーム、リンク作を行い、Edgeの動作に問題なければ元データを削除する。
Edgeが起動していなければ成功したが、再起動直後に行った方が確実。

AppDataのGoogle移動
Chromeをインストールした場合に実行。

--------------------
レベル4
危険度の高いフォルダ移動。
インストールされたアプリを移動する場合、USBメモリに異常が発生するとアプリのアンインストールができなくなる恐れがある。移動後、速やかにUSBメモリのバックアップを取る。

SoftwareDistributionの移動
WindowUpdateでフォルダが肥大化するため、”C:\Windows\SoftwareDistribution”をUSBメモリへ移動する。

DataStore.edbをコマンドで削除する方法ことで小さくすることができるそうだ。大きくなったらその都度コマンド実行するのも面倒なので、私はSoftwareDistributionを移動した。
  • Windowsアップデートで最新状態にする
  • Windowsアップデートを停止する
    >net stop wuauserv
  • 管理者権限のcmd.exeでコピー、リネーム、リンク作成
    >cd C:\Windows
    >robocopy SoftwareDistribution D:\...\SoftwareDistribution /MIR
    >ren SoftwareDistribution SoftwareDistribution~
    >mklink /J SoftwareDistribution D:\Windows\SoftwareDistribution
  • Windows Update再開
    >net start wuaserv
  • 問題なければリネームしたC:\Windows\SoftwareDistribution~を削除
SoftwareDistribution移動後にWindows Updateでエラーが発生したら、SoftwareDistributionを削除する。

Program Files (x86)のEdge移動
「AppDataのEdge移動」と同様の手順で以下のフォルダを移動。
  • C:\Program Files (x86)\Microsoft\Edge
  • C:\Program Files (x86)\Microsoft\EdgeCore
  • C:\Program Files (x86)\Microsoft\EdgeUpdate
  • C:\Program Files (x86)\Microsoft\WebView
  • C:\Program Files (x86)\Microsoft\Temp
Program FilesのGoogle移動
Chromeをインストールした場合に実行。

Program FilesのDell移動
SupportAssistをインストールした場合に実行。
「AppDataのEdge移動」と同様の手順で以下のフォルダを移動。
  • C:\Program Files\Dell
(リネームに失敗したら回復⇒再起動⇒詳細⇒コマンドプロンプ⇒コマンド実行)

--------------------
レベル5
HDD用空きスペースの利用
How to install SSD for Inspiron 14 3452?で紹介されている。

外付けHDD/SSDを使用するのがアプリインストール、バックアップなども含めて用途的にはUSBメモリより望ましい。SSDをUSB3.0に接続すると、CrystalDiskMarkの測定では内蔵eMMCの3倍近い速度がでた。

しかし、常に外付けを繋いでおくのはうっとうしく、軽量ノートパソコンの意味がなくなる。まだ試していないが、HDD用のスペースにSATA-USB変換アダプタを付けた内蔵HDD/SSDを入れ、USBコネクタと結線する。

USB2.0の口となら実現できそうだが、それだとHDD/SSDの速度は引き出せない。
SB3.0は、線の引き回しが難しそうで、ノイズが乗って期待どおりの速度が出ないとか、エラーが発生する可能性がありそう。

SATAコネクタ追加
マザーボードにはコネクタを取り付けるはずの端子が付いている。果たして実際に動作するかわからないが、腕に自信があり部品が調達できるなら試してみる価値はあるかも。

--------------------

現状のシステム構成
USB3.0にSunDiskの小型USBメモリを常時装着。(Dドライブ)
VHD.vhdフォルダを作成し、V:ドライブにマウント。
D、VにUser、Windowsなどの移動対象のフォルダを原則同じパスになるように設定。
ライブラリ類は移動時に新規作成してくれる。

Vドライブにはvhdマウントのタイムラグを許容できるもので、ファイル削除時にゴミ箱移動にしたいもの(ドキュメントなど)を移動。
Cドライブを圧迫しないよう、新規アプリケーションはVドライブにインストールする。

Dドライブにはvhdマウントのタイムラグの影響が大きいもの(デスクトップなど)を移動。TEMPなどWindowsが直接操作するものもDドライブへ移動。

OneDriveはvhdマウントの影響がでることがあるが、ドキュメントを置く都合でVドライブに移動した。

------------------------------

テスト中に利用したコマンド類
試みにUSBメモリをこの方法で初期化してみた。この目的では特に効果なし。

共有フォルダをネットワークドライブに割当たが、それを解除したときにドライブに☓印のついたアイコンが残り、消さなくなった。次のコマンドで解消。
>net use /PERSISTENT:NO
直後に再起動

ディスク使用状況チェック
DiskInfoを使用し、データ量の多いフォルダを探す。

------------------------------

ストレージRead/Writeベンチマーク
CristalDiskMarkによる測定結果

内蔵eMMC
USB3.0メモリ(SanDisk 64GB Ultra Fit USB 3.1)
シーケンシャルRWはeMMCに匹敵するが、ランダムアクセスで劣る。
頻繁にデフラグしておくのが望ましい。

SDカード(SanDisk Extream Pro 64G)
USB2.0らしい結果。

SSD-USB3.0接続
さすがに速いが外付けなので採用せず。

SSD-USB2.0接続

SSDでもUSB2.0では価値がない。


2022年9月3日土曜日

Windows 10 ディスク領域が不足でのバックアップ失敗

 メインのマシンをWindows 11にしたので、いささか古いサブマシンにWindows 10をインストールした。いちおう完了したのでバックアップを作ろうとしたら「ディスク領域が不足しているため、バックアップに失敗しました」が発生した。

十分ディスク容量はあるのにおかしいなあ、と思いながらもパティション構成を変えたりして何度かためしたが、相変わらず同じエラーで失敗する。

  • ディスク領域が不足しているため、保存場所にボリュームのシャドウコピーを作成できません…
  • 重要なボリュームの1つに十分な空き領域がないため、システムイメージのバックアップをスキップしました。

などといったメーセージが出るが、何が悪いのかよくわからない。ググってみても未解決のQ&Aか、ディスクの空き容量を増やして成功したというものばかり。

「重要なボリュームの1つ」ということでバックアップ対象を眺めてみると、システム予約のパティション管理領域もボリュームの1つに含まれている。256GのSSDのためか、この領域が50mbと小さく、残り容量がわずかだった。そこで、これをAOMEI Partition Assistant Standard(無料版)で150mbほどに増やして再度試みたところ、成功した。

解決したあとで改めてググってみると

PCバックアップエラー
https://okwave.jp/qa/q8675779.html

というQ&Aで「原因:100MBのシステム領域の空き容量不足」と回答されていた。

というわけで、私の場合は「重要なボリュームの1つ」というのがシステム予約のパティション管理領域でした。

ついでなので、AOMEI AOMEI Partition Assistant Standardを使って対処する場合につまづいた点について補足しておく。

  • Partition Assistant Standardでディスク先頭の管理領域の右隣のパティションを右クリック
  • 「パティションサイズをリサイズ/移動」をクリック
  • パティションの先頭に丸アイコンがない場合は「このパティションを移動したい」をチェック(これがつまづきポイント)
  • 先頭位置の丸アイコンをドラッグして先頭(左側)に空き領域を作る
    (サイズの上下アイコンクリックでサイズ微調整が可能)
  • ディスク先頭の管理領域を右クリック
  • 右端の丸アイコンのドラッグ(またはサイズの上下アイコンクリック)で領域拡張
  • 「適用」クリックでリサイズ実行
パティションリサイズ後にCHKDSKで異常がないか確認するとよいでしょう。

もちろん、管理領域以外にもバックアップ対象のボリュームで空き容量が少ないものがあったら、同様の方法でパティションサイズを調整するか、不要なファイル削除などを行う必要があります。

2022年8月22日月曜日

WiX ToolSet v3.xでの.NET Frameworkの必須バージョンの追加

WiX ToolSet v3.xは.NET Fraomeworkのバージョンチェックのための定義を4.6.2までは含んでいるが、それ以降のバージョンについては定義の追加が必要になる。

WiX ToolSetで定義済みの.NETバージョンの場合は、
このページに簡潔な説明がある。

また、次のQ&AでDOT.NET 4.8の定義を追加して、同様のバージョンチェックを行う方法が紹介されている。
NetFxExtension should support .net 4.8 
  <?define NetFx480MinRelease = 528040 ?>でNetFx480MinReleaseの名前で値を定義
  
WiX ToolSetで定義済みの.NETバージョン定義を PropertyRef で取得できる。
例:<PropertyRef Id='WIXNETFX4RELEASEINSTALLED'/>

この値はレジストリに登録されている.NET release keyとなる。

この値と.NET のバージョンの最小値(NetFx480MinRelease=528040)を比較することで、インストールされている.NET Frameworkが要件を満たしているか判別できる。

設定例
次の例ではWIXNETFX4RELEASEINSTALLED とバージョンの最小値を直接比較する。
<PropertyRef Id='WIXNETFX4RELEASEINSTALLED'/>
<Condition Message='This setup requires the .NET Framework 4.7.2 (or greater) to be installed.'>
<![CDATA[Installed OR (WIXNETFX4RELEASEINSTALLED >= "#461808")]]>
</Condition> 次の例ではNetFx480MinReleaseという名前でバージョン番号を定義し、これと比較してWIX_IS_NETFRAMEWORK_480_OR_LATER_INSTALLEDの値を設定する。
<?define NetFx480MinRelease = 528040 ?>
<PropertyRef Id="WIXNETFX4RELEASEINSTALLED" /> <Property Id="WIX_IS_NETFRAMEWORK_480_OR_LATER_INSTALLED" Secure="yes" /> <SetProperty Id="WIX_IS_NETFRAMEWORK_480_OR_LATER_INSTALLED" Value="1" After="AppSearch"> WIXNETFX4RELEASEINSTALLED >= "#$(var.NetFx480MinRelease)" </SetProperty>

2022年8月19日金曜日

自作アプリがウイルス(トロイの木馬)扱いになった ⇒ 解決?

今度は「Behavior:Win32/Hive.ZY」が見つかった警告された。

やれやれまたか...で、こんどは自作アプリを疑う前にDefenderに関する書き込みをチェック。すると「窓の杜」からニュースが流れていた。

マルウェア「win32/hive.zy」が検出されるトラブル ~「Microsoft Defender」ウイルス対策の誤検知か

日本時間(2022年)9月4日より、内部で「Chromium」を利用するアプリ(「Google Chrome」や「Microsoft Edge」、「Spotify」など)を起動するたびに「Microsoft Defender」ウイルス対策が「win32/hive.zy」というマルウェアを検知する現象が発生しているようだ。編集部でも確認をした

このページのお勧めに従って[ウイルスと脅威の防止の更新]を行った。さっそくアップデートがあったようだ。

------------

解決と書いたが、しばらくして再発。

AxWindowsMediaPlayerを使用しており、コントロールを追加するとAxInterop.WMPLib.dll、Interop.WMPLib.dllが追加される。このあたりを疑って試しているうちに、また安定し始めた。

以前にウイルス扱いとなったバージョンも今は問題ない。

どうやらDefenderの定義ファイルが修正されたようだ。

------------

先日「自作アプリがウイルス扱いになったのがWIndows 11再インストールで解決した」と書き込みをしたが、その後システムドライブバックアップ中に検疫にひっかかり、再度トロイの木馬扱いとなった。

ソースコードで関連ありそうなメソッド呼び出し順次止めて試したところ、Assemblyからcopyrightの文字列を取得している箇所を削除することで検疫をパスするようになった。

Attribute copyright = Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(System.Reflection.AssemblyCopyrightAttribute));

ただし、このメソッド呼び出しは別アプリでも使用しており、なにかしら別要因との組み合わせがあると思われる。

------------

2022年8月初旬に自作アプリ(RenameMe)がトロイの木馬(Trojan:Script/Wacatac.H!ml)扱いになって、Defederで検疫されてしまった。

表示されたダイアログをコピーしておかなかったが、次のようなメッセージが表示され、挙句Defenderから脅威が見つかったと警告された。

・This applicaion could not be started.
・このアプリを実行すると、PC に問題が起こる可能性があります。

アプリ本体はDOT.NETのライブラリとメディアプレーヤのWMPLibで、いずれもMicrosoft製のものしか使用していない。

インストーラはオープンソースのWixTookSetを利用しているが、これはMicrosoft製のツールを引き継いだもので、Windows Installer (MSI) パッケージを作成するが実際のインストールはWindowsの機能で行うものだ。また、インストールしたアプリだけでなくReleaseビルドしたexeを起動しても「このアプリを実行すると、PC に問題が起こる可能性があります。」となるのでインスーラは関係なさそう。

今まで動いていたものを突如トロイの木馬扱いにするのだから、これは誤検知に相違ないのだが、といってアプリが動かないのは困る。

まずはアプリをビルドしなおしてみる。やはりダメなので次のことを順次試して、アプリケーションの削除/インストールを繰り返してみる。

・DOT.NETのバージョンを4.7.2から4.8に上げる
・WixTookSetを3.11.2に上げる

DOT.NET Framework4.8の再インストールを試みるが、これは既にインストール済みと拒否される。

これでも同じ現象が続くので、Windows Updateで更新状態をチェックすると、KB5016629のインストール待ち状態だった。これのインストールを試みるが失敗。

次のようなコマンドを実行するがエラーで成功しない。

dism /Online /Cleanup-Image /ScanHealth
dism /online /cleanup-image /startcomponentcleanup
sfc /scannow

エラーメッセージは次のようなもの。
・指定したバッファーに誤った形式のデータが含まれています。
・Windows リソース保護は要求された操作を実行できませんでした。

当然ながらWindows Updateは再度失敗。
オフライン更新も試みるが同じエラーで失敗。

いささか行き詰まり状態になり、復元ポイントやバックアップで前の状態に戻すことを考え始めたが、Window 11では個人データだけでなくアプリも残したのまま上書きインストール可能というのを発見し、これを試みることにする。Windows 10でも上書きインストールできたんですね。

参照: Windows11 上書きインストールで現状を全て保持して修復

isoをダウンロードしインストールを開始すると、なんとTPM2.0が有効になっていないという。UEFI(BIOS)画面でチェックすると、確かに無効になっていた。しばらく前にBIOSをアップデートしたので、そのときに初期状態に戻っていたようだ。Windows 11はTPM2.0必須ではあるが、インストール後これが無効でも起動するのですね。

ついでに高速スタートアップが無効になっていることを確認。

TPM2.0を有効にし、再度上書きインストールを試み、成功。

再起動後にWindows Updateを見るとKB5016629の前のKB5015732、KB4023057も待ち状態になっていた。

KB4023057は「.NET Framework 3.5 および 4.8 の累積的な信頼性の向上が含まれています」となっており、どうもこれが関係しているように思われる。

その後、アプリを再ビルド、再インストールし、今のところ安定して動作している。
⇒その後システムドライブ バックアップ時に再発

振り返ってみると、つぎのような順序で問題が発生したのではないかと思われる。

・しばらく前にWindowsの動作が重くなり、BIOSのアップデートを行った。(実際はBIOSの問題ではなくIMEをGoogle日本語入力に置き換えることで解決した。)

・BIOSアップデート時に設定が初期化され、TPM2.0がオフになった。

・Windows 11は動作しつづけたが、もしかしたらTPM2.0オフが一因でWindows Updateで更新失敗が発生。もしかしたらDOT.NET 4.8が関係してウイルス検知を誘発。

・KB5015732、KB4023057いずれかの更新失敗のため、KB5016629も更新失敗。

・このときのゴミでdism、sfcコマンドでもエラーが発生。

2022年7月27日水曜日

IMEが原因でWindows 11 がえらく重くなった

しばらく前からWindows 11 の動作がえらく重くなった。

特にIMEの動作が遅くなった。キー入力後に漢字変換の候補が出るまでに、時には数秒待たされることもあった。タスクバーのIMEアイコンが☓になり、「IMEが無効です」となったりした。

IMEが全く反応しないこともあったが、これはIMEの設定で以前のバージョンに戻すことで改善はしたものの、もたつきは相変わらずだった。また、IMEアイコンは出ても右クリックに反応せず、コンテキストメニューが表示されないことがあった。

ctfmon.exeを手動で実行しても変化がない。タスクマネージャで動作をチェックすると、一瞬現れるがすぐに消えたりする。

ビデオ編集にVideoStudioを使っているが、マウスドラッグがスムーズに行えなくなったりもした。このせいで、IMEというよりもWindowsそのものや、ハードになにか問題がありそうに思えた。

もともとメモリにちょっと問題があったこともあり、BIOSやドライバをアップデートしたり、またWindowsを少し前の復元ポイントに戻したりもしたが、改善されない。

ウイルスも疑い、defenderでフルスキャンをしたが、特に問題は見つからない。

IMEの設定で予測変換をOFFにすると若干よくはなったようだが、やはり我慢できないほどもたつく。

などなど、さんざんに試した挙げ句、Googleの日本語入力に置き換えてみると、入力はさくさくできるようになり、VideoStudioの動作も元にもどった。

"Windows 11 ime 不具合"といったキーワードで検索すると結構出てくるので、IMEの不具合自体は珍しいことではないようだ。それでも、そのせいで他のアプリのマウス操作にまで影響がでるのは驚きだ。

しばらくはGoogle日本語入力を使い続けることになりそうだ。

2022年3月28日月曜日

Windows関連メモ

Windiws 11 で Windows セキュリティーが開かない
アップグレード後にフルスキャンをしておこうと思ったらWindows セキュリティーが開かない。
次のリンクの方法で、とりあえず起動するようにできた。コマンドの意味が書いてなく、まだ調べていないので他のアプリへの影響などはわからない。
手順
  • すべてのアプリを終了した状態(アイドル状態)
  • Windows PowerShell を管理者権限で起動
  • コマンド実行
    Get-AppxPackage Microsoft.SecHealthUI -AllUsers | Reset-AppxPackage
  • Windows セキュリティを起動、動作確認
Windows 11 コマンドでシステム>電源を開く
ディスプレイの電源OFF、スリープまでの時間設定のダイアログにたどり着くに多くの手数が必要。次の要領でシステム>電源へのショートカットを作れる。
  • 画面の空白部分を右クリック > 新規作成 > ショートカット
  • [項目の場所を入力してください]に"ms-settings:powersleep"を設定
  • [次へ]クリック
  • [このショートカットの名前を入力してください]に(例えば)"電源スリープ"を設定
  • [完了]クリック
デスクトップにショートカットアイコンが作成される。

Windows 11 エクスプローラの行間を狭くする(以前と同じにする)
ファイルエクスプローラーの行間が広くなって表示項目数が減ってしまった。
エクスプローラ > 表示 > コンパクトビュー で行間を以前と同じように狭くできる。

アクティブなウインドウのディスプレイ切替
マルチディスプレイ環境でディスプレイの接続が切れると、そこにあったウインドウが表示されなくなり困ることがある。そんなときに便利。

スタートメニューの場所

  • ユーザ別
    C:\Users\UserName\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
  • 全ユーザ共通
    C:\ProgramData\Microsoft\Windows\Start Menu\Programs

Windows MediaPlayer再インストール



2022年2月3日木曜日

DIGA DMR-BRT220 電源が落ちる 修理記録

 Panasonic DIGA DMR-BRT220 が故障した。

最初はハードディスクが認識されなくなったと思われたのだが、どうやら電源が数十秒で落ちるようになっていたためらしく、最終的には冷却ファンを掃除することで直ってしまった。とはいえ、そこまでたどり着くのに結構手間暇かかったので、ちょっとまとめておく。

まずは録画済みの番組をなんとか復活できあいものかと試みたが、これは残念ながら失敗してしまった。これについては後ほど補足する。

もともとのハードディスクと同じモデルのものを購入、換装することで動作するように見えたが、30秒程で電源が落ちてしまう。

最初に見つけたのはこのページ

電気屋さんのお仕事 パナソニックディーガ XW300電源が入らない

ICプロテクタ(フューズのようなものらしい)が切れると同じ現象になるらしい。
このページを参照して、同様の故障を修理した記事もある。

検索したらただ乗りだった件

DIGAとICプロテクタに関しては、他にもいろいろ見つかる。


そこで、分解し基盤を取り出して調べてみる。DMR-BRT220のICプロテクタについてズバリ書いてある記事は見つからなかったので、それらしいものを探してみる。
基盤に印刷してある部品名がIPで始まり、四角の中にKとマークしてものが表に二つ、裏にひとつあった。テスタで調べてみるとどれも導通している。う~~む、これのせいではなさそうだ。

電源回路のコンデンサが壊れている場合も、同様の現象になるらしい。


この場合には膨れているコンデンサがあるようだが、見ためにはそれらしきものは見当たらない。

半ばあきらめつつ他の手がかりを探してみると、冷却ファンの故障が原因というのが見つかった。"ファン"をキーワードに入れるといくつも出てくる。


電源を入れてみると、ファンが一瞬回るがすぐ止まる。これが正常な動作なのか分からない。埃を取って、指で回したりを何度か繰り返しているうちに回るようになった。すると、電源も入ったままになり、なんと直ってしまった。

ということで、電源がすぐに落ちてしまう現象が発生したら、まずはファンが回転しているかチェック。回転していない、あるいは不安定ならファンの掃除を念入りに行う。精密機械用潤滑油をさすのもよさそうです。

参考リンク


それでもだめなら、ICプロテクタや電源回路のコンデンサを疑いましょう。

2021年6月14日月曜日

C# TagLib#でwavファイルのタグを設定する方法

TagLibでmp3のタグ編集アプリを作りつつ、ついでにwavのタグ設定を試してみました。

mp3で文字化けに悩まされたましたが、wavでもやはり発生しました。

wavの場合の問題は、WindowsはwavのタグをShift-JISで読み書きするのに対し、TagLibはUTF8で読み書きすることです。

TagLibはファイルから読み込んだバイト列をByteVectorオブジェクトに格納し、これをAPIを介してStringにしています。

なので、そのAPIを介さずに、TagLibが保持しているバイト列をShift-JIS EncodingでString化し、Saveする前にバイト列を直接セットすれば文字化けを回避できます。

TagFileを作るときはファイルの種類を意識する必要はありません。

    TagLib.File TagFile = TagLib.File.Create(FilePath);

TagFileからTagを取得するときは、TagLib.TagTypes.RiffInfoを使います。

    TagLib.Riff.InfoTag tag = TagFile.GetTag(TagLib.Riff.InfoTag, true);

次の要領でShift-JIS Endodingでバイト列をString化します。

    Encoding SjisEnc = System.Text.Encoding.GetEncoding("shift-jis");
    String GetStringFromSjisPropery(TagLib.Riff.InfoTag tag, TagLib.ByteVector id)
    {
        TagLib.ByteVectorCollection vals = tag.GetValues(id);
        foreach (TagLib.ByteVector v in vals)
        {
            byte[] b = v.Data;
            if (b.Length > 0)
            {
            return SjisEnc.GetString(b);
            }
        }
        return null;
    }

Artist, Album, Titleのidは次のようになります。

    ByteVector idArtist = new ByteVector(Encoding.ASCII.GetBytes("IART"));
    ByteVector idAlbum = new ByteVector(Encoding.ASCII.GetBytes("IPRD"));
    ByteVector idTitle = new ByteVector(Encoding.ASCII.GetBytes("INAM"));

出力時は、save直前にShift-JISでバイト列化したByteVectorを当該項目にセットします。

    ByteVector ToByteVector(ByteVector id, String str)
    {
        return new ByteVector(SjisEnc.GetBytes(str+ " "));
    }

なぜか文字化けるので、strにスペースを一文字足してからバイト列化しています。
(スペースではなくnull('\0')が正しいのかも?)

StringをByteVectorにしたものを、次の要領でtagにセットします。

    ((TagLib.Riff.InfoTag)tag).SetValue(id, ToByteVector(id, str));

uintのTrackなど、String以外の項目はTagLibのAPIで直接操作できます。

Windows内だけ利用する場合は、これでOKでしょう。他のOS環境や、アプリによっては文字化けるかもしれません。

[追記]

Wavのヘッダー仕様はRIFFで定義されていますが、これのTRACKのIDが曖昧です
IPRTITRK, TRCKの3つが使われる場合があるようです。
TagLibは
IPRTで読み書きしていますが、WindowsのエクスプローラはITRKの値を表示します。

TagLibでITRKに値のRead/Writeは次のようにします。 

  ByteVector idTrack = new ByteVector(Encoding.ASCII.GetBytes("ITRK"));
  //Read
  uint trackNum = ((TagLib.Riff.InfoTag)tag).GetValueAsUInt(idTrack);
  //Write
  ((TagLib.Riff.InfoTag)tag).SetValue(idTracktrackNum);

-------

ついでながら、TagLibは様々なフォーマットに対応しています。
wma、m4a(aac)を試したところ、これらはutf8で問題ないようで、基本的にmp3と同じ要領で読み書きできます。

TagFile.GetTag(TagLib.TagTypes)の引数に使う定数は次のようになります。

    wma: TagLib.TagTypes.Asf
    m4a: TagLib.TagTypes.Apple

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 開発 / メッセージ管理 / メッセージフック