Variant
すべての変数。
仕事とプライベートでプログラムを書いていますが、いろいろ調べたり試したりしたことをメモしていきます。
数年前に作ったメール送信機能のあるプログラムを作り変えようとしたら、「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)
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の方がコード的にもすっきりしていますが、使用する場面では使い道があるかもしれせん。
VB InternetExplorer.getElementById 「オブジェクトが必要です」エラー対策
VBの InternetExplorer.getElementById は該当する要素がない場合に「オブジェクトが必要です」が発生する。これの扱いに困ったが、次の要領で対処できた。
Dim elm As IHTMLElement
Set elm = ie.document.getElementById("someID")
そこで、次のようなコードにしてみる。
Dim elm As Variant
Set elm = ie.document.getElementById("someID")
こんどは「型が一致しません」エラーが発生する。
これはSetではNullをObjectとして扱おうとするため発生する。
それでは、これではどうだろう。
Dim elm As Variant, divs as IHTMLElementCollection
elm = ie.document.getElementById("someID")
if IsNull(elm) Then Exit Sub
Set divs = ul.getElementsByTagName("div")
Nullのときの処理はうまくいくが、今度はelmがObjectとして設定されていないため、後続のコードで「オブジェクトが必要です」エラーが発生する。
そこで次のように変更し、エラー発生時に処理を継続してみた。
On Error Resume Next
Set elm = ie.document.getElementById("someID")
If IsNull(elm) Then Exit Sub
Set divs = someID.getElementsByTagName("div")
On Error Resume Nextとした結果 elm がEmpty値になるため、elmをObjectで宣言している。
次の式でも判別できる。
If IsEmpty(elm) Then Exit Sub
If IsNullOrEmpty(elm) Then Exit Sub
なお、
On Error Resume Next
でエラーを無視するようにしたので、前後の処理の関係ではこのあとで再度On Errorを設定しなおす必要があるでしょう。
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の交換ってできませんか?
メインのマシンをWindows 11にしたので、いささか古いサブマシンにWindows 10をインストールした。いちおう完了したのでバックアップを作ろうとしたら「ディスク領域が不足しているため、バックアップに失敗しました」が発生した。
十分ディスク容量はあるのにおかしいなあ、と思いながらもパティション構成を変えたりして何度かためしたが、相変わらず同じエラーで失敗する。
などといったメーセージが出るが、何が悪いのかよくわからない。ググってみても未解決のQ&Aか、ディスクの空き容量を増やして成功したというものばかり。
「重要なボリュームの1つ」ということでバックアップ対象を眺めてみると、システム予約のパティション管理領域もボリュームの1つに含まれている。256GのSSDのためか、この領域が50mbと小さく、残り容量がわずかだった。そこで、これをAOMEI Partition Assistant Standard(無料版)で150mbほどに増やして再度試みたところ、成功した。
解決したあとで改めてググってみると
というQ&Aで「原因:100MBのシステム領域の空き容量不足」と回答されていた。
というわけで、私の場合は「重要なボリュームの1つ」というのがシステム予約のパティション管理領域でした。
ついでなので、AOMEI AOMEI Partition Assistant Standardを使って対処する場合につまづいた点について補足しておく。
今度は「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の文字列を取得している箇所を削除することで検疫をパスするようになった。
------------
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
いささか行き詰まり状態になり、復元ポイントやバックアップで前の状態に戻すことを考え始めたが、Window 11では個人データだけでなくアプリも残したのまま上書きインストール可能というのを発見し、これを試みることにする。Windows 10でも上書きインストールできたんですね。
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コマンドでもエラーが発生。
しばらく前から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日本語入力を使い続けることになりそうだ。
Panasonic DIGA DMR-BRT220 が故障した。
最初はハードディスクが認識されなくなったと思われたのだが、どうやら電源が数十秒で落ちるようになっていたためらしく、最終的には冷却ファンを掃除することで直ってしまった。とはいえ、そこまでたどり着くのに結構手間暇かかったので、ちょっとまとめておく。
まずは録画済みの番組をなんとか復活できあいものかと試みたが、これは残念ながら失敗してしまった。これについては後ほど補足する。
もともとのハードディスクと同じモデルのものを購入、換装することで動作するように見えたが、30秒程で電源が落ちてしまう。
最初に見つけたのはこのページ
電気屋さんのお仕事 パナソニックディーガ XW300電源が入らない
ICプロテクタ(フューズのようなものらしい)が切れると同じ現象になるらしい。
このページを参照して、同様の故障を修理した記事もある。
DIGAとICプロテクタに関しては、他にもいろいろ見つかる。
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が曖昧です
IPRT, ITRK, TRCKの3つが使われる場合があるようです。
TagLibはIPRTで読み書きしていますが、WindowsのエクスプローラはITRKの値を表示します。
TagLibでITRKに値のRead/Writeは次のようにします。
ByteVector idTrack = new ByteVector(Encoding.ASCII.GetBytes("ITRK"));-------
ついでながら、TagLibは様々なフォーマットに対応しています。
wma、m4a(aac)を試したところ、これらはutf8で問題ないようで、基本的にmp3と同じ要領で読み書きできます。
TagFile.GetTag(TagLib.TagTypes)の引数に使う定数は次のようになります。
wma: TagLib.TagTypes.Asf
m4a: TagLib.TagTypes.Apple