月別アーカイブ: 2023年12月

無効な前方参照か、コンパイルされていない種類への参照です。

2023年12月30日、Excelファイル(.xlsm)の読み書きで、見たことないエラーメッセージに遭遇。
それがコレ!

朝から晩まで、右往左往、ほぼ1日ハマりました T_T

これは、その解決方法です。

このエラーに遭遇した際、改良していたのはこちらのプログラムから読み取ったマークをExcelへ出力するコードです。もし、よかったらあわせてご参照ください。

自作のマークシートリーダーです。プログラムは上記リンク先から無料でダウンロードできます。単体でも動作しますが、別途組み込みPython環境を追加するとさらに高速動作します。
自作のマークシートリーダーの読み取り速度をPython4Delphiで高速化。組み込み用Pythonのプログラム一式は上記リンク先から無料でダウンロードできます。

【もくじ】

1.まったく同じコードが動かない!
2.朝からのできごと
3.Web上の情報は?
4.壊れてたのはプログラムじゃなくて・・・
5.まとめ
6.お願いとお断り

1.まったく同じコードが動かない!

Delphiで作成した Excel Book 読み書き練習プログラムでは『何の問題もなく動いたコード』。これを、そのまま本番用プログラムにコピペしたら、なぜか動かない!・・・という、これまでに経験したことのない問題に遭遇しました。

解決するための唯一の手掛かりは、冒頭のエラーメッセージ。

(でも・・・このエラーは、これまでに 一度も見たことない・・・ T_T )

目を皿のようにしてコードを見ても、練習用プログラムと本番用プログラムで、一字一句違ってない。てか、コピペしたんだから、違ってるわけがない・・・。
なのに、練習プログラムは期待通りに動作し、本番用プログラムは絶対に動かない。
必ず、このエラーで停止する・・・

これまで、いろいろなエラーに遭遇したけれど、このエラーは未経験。
もちろん、周囲に頼れる人はいない。自分で何とかするしか、ない。

なんでこんなコトに・・・

2.朝からのできごと

朝、目覚めてからずっと、むかし書いたExcelのマクロ有効Bookを読み書きするDelphiのプログラムを改良してたら、なんかこのエラーが出るようになって・・・。それから、データの書き込みが出来ない!

今、思えば、この時点で気がつくべきことがあったんだけれど、その時の僕は自分で書いたコードの何処かに間違いがあるはずだと思い込んでいるので、その「間違い」をさがすことに夢中になっていて、その他のことはまるで眼中にない。

ってか、Excel関連のプログラムはそこがやっかいなんだけど、大ゴケすると起動したExcelのプロセスが残ってしまい、Ctrl+Alt+Delで残ってるExcelのプロセス探して終了させてって、その繰り返しになってしまう。

この日はエラーの原因がわからなくて、これを際限なく繰り返してしまい、システムも何度か再起動。

で、さんざん頑張って彷徨った挙句、考えたことは・・・

問題の切り分けのため、シンプルなプログラムで実験してみよう!

・・・ということ。

Delphiから接続するExcel Bookはマクロ有効テンプレートにしてあるから、このおおもとのテンプレートをダブルクリックするたびに新しいマクロ有効Excel Book(拡張子はxlsm)が生成される。

そうやって新しいマクロ有効Excel Bookを作成。これを入れる所定のフォルダを作り、保存。準備万端にして、新しい Windows VCLアプリケーションも作成。で、これまで勉強した中で、いちばん動作が確実と思えるコードで「ワークシート間で式をコピーする」手続きを作成、コンパイル、そして「実行」。期待通りに、エラーなく、データのコピー(読み出しと書き込み)終了。胸がすっきり。Bookを開いて結果を確認。データはちゃんと書き込まれ、ワークシートが初期化されてる。もちろん、Excelもきれいに終了。タスクマネージャーで確認してもプロセスは残ってない。

改良中の元のアプリに戻って、新しくボタンを一つ作成。ダブルクリックして新しい手続きを一つ、作り、ここへ、ついさっき動くことを確認したばかりのコードをコピペする。で、実行すると・・・

なんでだよ!!

みたいな・・・

3.Web上の情報は?

もう、こうなったら Googleせんせー に聞きまくるしか、ない。

いくつかのそれらしい情報がヒット。例えば・・・


『sheet名はマクロの実行順に並んでいる必要がある』

(ほんとにー?)

さっそくSheetの順番をそれっぽく入れ替えて実行。結果、変化なし。エラーは普通に出る。


『解決策はモジュール単位の再コンパイル』

(そうなのか?)

ビルドし直しても、変化なし。これも違った。少なくても僕のエラーには効かない。


『あんなことや、そんなことや、こんなことも』全部ダメ。

(マジ、泣きそう T_T )

(このプログラムが動かないと、約束が・・・)


徹底抗戦の覚悟も新たに、腰を据えて、検索結果を、上から順番に、全部開けて熟読。
そしたら次のリンク先ページに、今日一日考えもしなかった情報を発見!

Excel VBA ― マクロ実行時に謎のオートメーションエラー“-2147319767”が発生する場合の対処法

https://ippeintel.com/archives/4644 より引用

解決方法は二つあり、そのひとつは・・・

ブックを新しく作り直す!

これを読んだ僕は、おおきな声で
「あ っ」といいました。

もうひとつは・・・

「開いて修復する」でファイルを開く

とのこと。

4.壊れてたのはプログラムじゃなくて・・・

ファイルだったんですね。

それからExcelの名誉のために言うけど、
壊れたんじゃなくて、僕が壊しちゃったんですね・・・

エラーが出るようになった あの時から。

あわててテンプレートから新しくファイルを生成。今までのと入れ替えて書き込んでみると・・・

これはExcelからのメッセージ?

これは僕が用意した書き込み完了のメッセージ。

今日の苦労は、いったい なんだったんだ・・・

IPPEIさん、ほんとに、ほんとに、ありがとー!!

5.まとめ

(1)ソフトウェアはプログラムとデータからできています。
(2)コードの誤りでエラーになることは僕の場合「日常茶飯事」です。
(3)データが壊れても(壊せばもっと確実に)エラーになります。

・総合的に、僕の場合、エラーがでないことのほうが異常でした!!

そぉか、今日はいつも通りの
普通の日だったんだ☆

みなさん、よいお年を!!

6.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

マークシートリーダーをP4Dで高速化

マークシートリーダー第2弾!
今回は Python環境を組み込んで、マークの読み取り速度を高速化 します。
出来る限り丁寧に組み込み方法を説明しますので、どうか最後までお付き合いください。

前回の記事はこちらからどうぞ

追記(20240929)

当Blogで紹介してきた自作のデジタル採点プログラムを一つにまとめました。次のリンク先にその紹介とダウンロードリンクがあります。

【追記_P4D環境で読み取り実行時、エラーが発生するときは?】

Python環境を組み込んで、これを利用してマークシートの読み取りを実行する場合、次のエラーが発生することがあります。エラーの内容からは推測すると、エラーはテンプレートマッチングの際に利用するテンプレート画像のサイズに起因して起きているように見えますが、ほんとうの原因は違います。

このマークシートリーダーは、Python環境を利用して動作する際は、マークの有無を読み取るJpeg画像の名称(及びフォルダの階層)が次の規則に従っていることを前提としています。

ProcData\XXX\Sample-01.jpg
ProcData\XXX\Sample-02.jpg
ProcData\XXX\Sample-03.jpg
・・・
ProcData\XXX\Sample-40.jpg
ProcData\XXX\Sample-41.jpg

この命名規則にJpeg画像のファイル名(及びフォルダの階層)が従っていない場合、読み取りエラーが発生します。例えば、次のような場合です。

ProcData\XXX\Sample-01a.jpg
ProcData\XXX\Sample-01b.jpg
ProcData\XXX\Sample-02a.jpg
ProcData\XXX\Sample-02b.jpg
・・・
ProcData\XXX\Sample-40a.jpg
ProcData\XXX\Sample-40b.jpg
ProcData\XXX\Sample-41a.jpg
ProcData\XXX\Sample-41b.jpg

特に数学(や情報)用途で2枚1セットのJpeg画像を処理する際は、注意してください。このエラーを防止するには、ファイルメニューの「1 画像変換」⇨「専用画像を作成」を利用してファイル名が必ず連番になるように読み取り専用Jpeg画像を生成して、この画像に対して、マークの読み取りを実行してください。

以下、発生するエラーメッセージの一覧です。

このメッセージは2回表示されます


なお、Python環境を利用しないモード(P4Dを使用のチェックボックスをOFF:下図右上)であれば、読み取り対象Jpeg画像ファイルの名称は動作に関係しないので(読み取り速度は低下しますが)、読み取り可能です。

画面右上の □ P4Dを使用のチェックを外して、Delphi用のOpenCVで読み取りを実行。
読み取り速度は低下しますが、マークを正しく読み取っています。


【読み取り実行前に、選択肢の始まり番号も指定してください】

選択肢の番号は、デフォルト1始まりに設定してあります。教科「情報」用途で読み取りを実行する場合は、読み取り実行前に、選択肢が「1」始まりであるのか、「0」始まりであるのか、その指定を画面上の設定欄で必ず指定してください。

【もくじ】

1.Python環境を準備する組み込み用Pythonのダウンロードリンクがあります
2.Python環境のドッキング
3.高速化の確認
4.システムにC++ランタイムライブラリがない場合は?
5.Python Engine の初期化の問題?他
6.まとめ
7.お願いとお断り

1.Python環境を準備する

Qiita の記事で「 Embeddable Python 」なるものの存在を知り、ほぼ同時に Delphi に Python のスクリプトを埋め込んで、VCL で GUI を作成、内部的に Python のスクリプトを実行する方法を学びました。

この辺の詳しい経緯は、かなり前に記事として書いた通りです。

2022/01/01
2022/01/02


こうして出来上がった、マークシート読み取りに必要なライブラリだけをインストールした、組み込み用のPython環境の内容は、こんな感じです(組み込み用途に作成した Embeddable Python があるフォルダをコマンドプロンプトで開き、「 Python -m pip list 」コマンドを実行した結果です)

ライブラリの主役は Numpy と OpenCV-Python。
Pillow は、日本語を含む Path を読むためにインストール。


最初に用意した Embeddable Python が14MBくらいで(おー!ちいさい☆)と喜んだけど、上記のライブラリを三つ入れたら 158MB に・・・。

ライブラリを構成しているファイルの依存関係がわかれば、必要ないファイルを消しまくって、もっと小さく出来ると思うのですが・・・、その具体的な方法がわかりません!!

仕方がないので、そのまま組み込み用の「Python39-32」フォルダを作成。

フォルダ名の Python は、「Python関連のフォルダだよ!」ってコトが一目でわかるように工夫(?)しました。その次の 39 はVersion番号、ハイフンで繋いだ 32 は 32bit 用って意味です。

これを前回紹介したマークシートリーダーにドッキングさせます。

展開に少々時間がかかりますが、もし、よかったら使ってください。
MS_Reader 組み込み用 Embeddable Python です。

2.Python環境のドッキング

ダウンロードした「Python39-32.zip」を MS_Reader.exe のあるフォルダにコピー・貼り付け、展開してください。※ 動作確認が完了したら「Python39-32.zip」は削除しても OK です!

【展開前】

MS_Reader.exe とダウンロードした Python39-32.zip を同じ階層に置き、
zipファイルを展開(右クリックして「すべて展開」を選択)してください。
展開にはしばらく(1~2分)時間がかかります。

展開時のPC環境?によっては「ものすごく(20~30分)」時間がかかることが実際にありました!!(原因はわかりませんが、時間がかかるだけで、展開そのものは正しく行われました)

【展開後】

重要 MS_Reader.exe と Python39-32 フォルダは同じ階層に置いてください。

MS_Reader.exe と Python39-32 フォルダは必ず同じ階層に置いてください。


ここで念のため「Python39-32」フォルダの構造を必ず確認してください。

〇:Pathに注目してください。これならOK!

MS_Reader\Python39-32\Lib であり、また、
MS_Reader\Python39-32\Scripts であります。

これはダメです。Pathが二重になってます。

MS_Reader\Python39-32\Python39-32\Lib
MS_Reader\Python39-32\Python39-32\Scripts

上の「ダメな例のようにならない」ようにPython39-32.zipを作成しましたから、大丈夫だと思いますが・・・念のため、必ずご確認いただけますようお願いいたします。

以上が『 ドッキング作業 』です!!

MS_Reader.exe と同じフォルダに、Python39-32.zip をコピペして、展開すれば Python環境のドッキングは完了です。

これを夢見て、ンか月。マジ、挫けそうな時もあった・・・ けど。

MS_Reader.exe をダブルクリックして、マークシートリーダーを起動してみてください。

僕のマークシートリーダーは、自動的に、高速動作モードで、起動します。

3.高速化の確認

Python環境がないと(MS_Reader.exe がある場所に Python39-32 フォルダがない場合)・・・

MS_Reader 起動時、マークシートの読み取りを高速化するP4D(PythonForDelphi)モードは利用できませんが、

Python環境があれば(MS_Reader.exe がある場所に Python39-32 フォルダがある場合)・・・

マークシートの読み取りを高速化するP4D(PythonForDelphi)モードを利用する状態で、MS_Reader は起動します。

当たり前ですが、ダミー(中が空っぽ)の「Python39-32」フォルダを作成し、設定を偽ってMS_Readerを起動しても、メリットは何一つありません!

エラーが2つ出るだけです。

実際に、空の「 Python39-32 」フォルダを作成して実験してみました!


もう一つ。


こんなコトする方は皆無と思いますが。あくまでも、プログラムの動作検証として、ご参考まで。

【動作確認】

前回、設定したテンプレートを利用して動作確認します。

いったん、「P4Dを使用」のチェックを外して読み取りを実行します。前回試行した3列25行8選択肢の1枚あたり600マークあるシート3枚の読み取りにかかる時間は・・・


1枚0.805秒で読んでます(PC環境により、数値は当然異なります)が・・・

「P4Dを使用」のチェックを ON にして再び読み取りを実行します。私の PC での結果は・・・


1枚0.245秒強で読みました。

これが速度的に「はやい」か・どうか、このソフトウェアをお使いいただく方により、その判断基準は異なりますから、その思い(感じ方)は違って当然ですが、Python環境を利用しない場合に比較して、Python環境を組み込み、これを利用した場合は(PC環境により、その数値は悉く異なると思われますが)マークの読み取り速度は間違いなく高速化されるはずです(僕の環境では、「それがない」場合に比較して、「それがある」場合は3.3倍速で動作しました)。

ただ、Python環境を組み込んだ場合、プログラム全体の大きさは、12倍以上に巨大化します・・・

プログラムサイズを選ぶか、動作速度を優先するか、
ご使用目的、お使いのPC環境に合わせて選択していただけたら幸いです。

僕は・・・

今日の空みたいな・・・

プログラムを書きたかった・・・だけです *(^_^)*♪

僕が、この世から消えたあとも、動く。

いつか、夢みたとおりの・・・ プログラムを。

だいすきな・・・

大好きな Delphi と・・・

僕の Object Pascal で。

4.システムに Visual C++ランタイムライブラリがない場合は?

お使いのシステムに Visual C++ランタイムライブラリがインストールされていない場合は、MS_Reader 起動時に次のエラーが発生します。

『アプリケーションを正しく初期化できませんでした(0xc0150002)。「OK」をクリックしてアプリケーションを終了してください。』

英文の場合もあるようです。

PCの解像度の関係だと思うのですが、画像がボケていてごめんなさい!


このエラーが発生する原因を調べてみたところ、組み込みPython環境内にある「Python39.dll」が Visual C++ランタイムライブラリを必要とするようで、これがシステムにない場合は、プログラム起動時にバックグラウンドで行っているPython Engine の初期化に失敗して、上記のエラーメッセージが表示されることがわかりました。

お使いのPCで、Visual C++ ランタイム ライブラリのインストール状況を確認するには、[スタート] ボタンを右クリックし、「ファイル名を指定して実行」をクリックして、appwiz.cpl と入力して[Enter]を押します。Python環境を組み込んだ MS_Reader が動作する環境であれば、システムにインストールされている Microsoft Visual C++ ランタイム ライブラリが以下のように表示されるはずです。

現在、私のシステム(Windows 11 Pro 23H2)にインストールされているC++ランタイムライブラリの一覧。
もちろん、このシステムでPython環境を組み込んだマークシートリーダーが正常に動作しています。


システム内で起きていた別のエラーを解決するために、2023年12月上旬に工場出荷状態に戻すリカバリ作業を行いました。同時にOSを最新のバージョンに更新しました。それ以前のシステムの状態は次の通りです(OS のバージョンは 22H2)。※ 私のPCでの話です。

現在の状況とは異なっています。
この状態でもPython環境を組み込んだマークシートリーダーは正常に動作していました。


エラーを解決するには、Visual C++ランタイムライブラリをインストールすればいいわけですが、上の例のように Visual C++ ランタイムはたくさんあるので、手動でひとつひとつダウンロードしてインストールするより、Visual C++ ランタイムインストーラーを使って全ての Visual C++ ランタイムを一括インストールする方が簡単です。

システムをリカバリする前は、次のようにして Visual C++ ランタイムをインストールしていました。

【ご注意願います!】
ここで紹介する方法で Visual C++ ランタイムをインストールする場合、他のプログラムの実行環境との整合性は、一切保証できません。また、最悪の場合、Windowsが起動しなくなるトラブルが発生することも十分に考えられます。インストール作業の全てが自己責任であることを十分ご理解の上、重大な問題が発生した場合は元の環境に戻せるよう、システムのバックアップを取る・現在の設定をメモに記録する等、不具合の発生に備え、必要かつ十分な準備を整えた上で、Visual C++ ランタイムのインストールを行ってください。

以下のサイトから「Visual C++ v56.exe」をダウンロードしてインストール(私の環境にインストールする分には、なんの問題も起きませんでした。もちろん、マークシートリーダーも問題なく起動し、安定動作しました)。

Visual C++ Runtime Installer (All-In-One) v56

https://www.majorgeeks.com/files/details/visual_c_runtime_installer.html

こちらのWebサイトでも(次のリンク先Webページの下の方で)、このインストーラが紹介されています。

Microsoft Visual C ++ 再頒布可能ファイルを削除して再インストールする方法

https://www.autodesk.co.jp/support/technical/article/caas/sfdcarticles/sfdcarticles/JPN/How-to-remove-and-reinstall-Microsoft-Visual-C-Runtime-Libraries.html

インストーラーを立ち上げると、本当にインストールするかどうかを「YES」か「No」かで尋ねられるので、インストールする場合は「Y」をタイプします。その後はPCの画面に表示される英文の指示にしたがって操作してください。

ここから先は、上記のインストーラーを用いて Visual C++ ランタイムをインストールした際、私が実際に経験したトラブル?です(最終的にインストールは成功しました)。

お決まりのUAC起動後(PCの設定によっては)管理者ID 及びパスワードの入力が求められますが、これを入力すると、そのままPCがフリーズしたような状態になり、数分待機しても進展が見られないので、いったん作業を Ctrl+Alt+Delete でキャンセルし、再度、「Visual C++ v56.exe」を起動して Visual C++ ランタイムのインストール作業を実行、今度はトラブルなくインストールに成功する事例です。これは「ある特定のAD環境下にあるPCのすべてに共通して見られた」現象です。現在もその原因はわかりませんが、ご参考まで。

また、システムの状態によっては(現在システムにあるランタイムをアンインストールしているのか?)複数回(と言っても最高2回ですが)、再起動を求められることも(何度も)経験しました。

C++ランタイムライブラリのインストールについて、経験を加味して私がわかるのはここまでです(実は、何もわかってないのとイコールなのですが)。これ以外のエラーメッセージが表示されてインストーラーが起動しない場合も、もしかしたらあり得るかもしれません。大変恐縮ですが、そのような場合は原因の究明を含めて、自己責任でご対応ください。

5.Python Engine の初期化の問題?他

MS_Reader では、マーク読み取り時の体感速度を上げるため、FormCreate時にバックグラウンドで Python Engine の初期化を行っています。MS_Reader.exe のあるフォルダに小さなマークシートの画像とマーカー画像があるのにお気づきになった方がいらっしゃるかもしれません。これは Python Engine 初期化用に用意した画像です。

Python Engine 初期化用の画像をリソースに埋め込み、もし、それがない場合は再生して、
プログラム起動時に Python Engine の初期化が必ず行われるようにしています。


この初期化を「するか・しないか」で、MS_Reader 起動後、初めてマークを「読む」ボタンをクリックした際のプログラムの挙動がまるで違ったものになります。初期化を行った場合は、ごくスムーズにマーク読み取りが始まるのに対し、行わなかった場合は PC が一瞬フリーズしたような状態になり、その後、息を吹き返すかのようにマークの読み取りが始まります。

Python Engine の初期化コードです。

  AppDataDir:=ExtractFilePath(Application.ExeName)+'Python39-32';

  if DirectoryExists(AppDataDir) then
  begin
    //フォルダが存在したときの処理
    CheckPython.Enabled:=True;
    CheckPython.Checked:=True;
    PythonEngine1.AutoLoad:=True;
    PythonEngine1.IO:=PythonGUIInputOutput1;
    PythonEngine1.DllPath:=AppDataDir;
    PythonEngine1.SetPythonHome(PythonEngine1.DllPath);
    PythonEngine1.LoadDll;
    PythonDelphiVar1.Engine:=PythonEngine1;
    PythonDelphiVar1.VarName:=AnsiString('var1');
    PythonEngine1.Py_Initialize;
    //イニシャライズされたことを記憶
    P4D_ini:=True;
  end else begin
    CheckPython.Checked:=False;
    CheckPython.Enabled:=False;
    PythonEngine1.AutoLoad:=False;
    P4D_ini:=False;
  end;

(どこに問題があるのでしょうか?)

PC によっては、この Python Engine の初期化に非常に長い時間を要することがあるようです(エラーメッセージは出ません。この沈黙の時間が終わった後、プログラムは問題なく動作します)。偶然、ある PC でこの現象に巡り合い、あわてて時間を計ってみたところ、その PC では初期化に4分必要でした! なぜ、このような現象が発生するのか、その理由がわからないのですが、「そのようなことがある」ことだけは経験的に明らかですので、ここに書いておくことにしました。

また、マーク読み取り開始時に、マーカー画像の位置をテンプレートマッチングで確認して、それが「本当に見えている」ことをユーザーに明示的に知らせていますが、ここでもその処理に少し時間が必要なことがあります。私のPCでも、この現象は「起きたり・起きなかったり」するような気が・・・。エラーが出るわけでもなく、ただ・・・「ん?」みたいな時間があるだけなのですが・・・。こちらもその原因がよくわかりません。

以上が、現象としてはわかっているのですが、原因が解明できていないPython環境を使う上での問題点です。

それから、私の想定外の操作が行われた場合、メモリーリークが起きる可能性があります。Python環境をドッキングさせた当初は、このメモリーリークにかなり悩まされました。どう頑張っても小さなメモリーリークが発生するのを取り切れず、( Python環境はそういうもの? )と割り切ってしまおうかと思ったこともあったくらいです。

そのたびに思い直し、メモリーリークが発生する原因を突き止めて対応することを繰り返しました。なので、私が想定した操作範囲内でのメモリーリークは全て取り切れたと思います。が、もし、それが発生した場合は、その発生を知らせるメッセージがプログラム終了直後に表示されます( FormCreate時に実行されるコードの中にメモリーリークがあれば検出するコードを残してあります)。

  //メモリーリークがあれば検出
  ReportMemoryLeaksOnShutdown:=True;
メモリーリークが起きたことを伝えるメッセージ
(上の例のメモリーリークは故意に発生させたものです)

6.まとめ

(1)Python環境を利用するとマークシートリーダーは高速化できる。
(2)高速化できるかわりに、プログラム全体のサイズは大きくなる。
(3)原因不明のフリーズのような現象が発生することがある。

7.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容及びダウンロードしたプログラムを利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

マークシートリーダー

自分的に必要と思った機能は全部搭載しました・・・が、
プロが作った有償販売できるレベルのソフトウェアではありません。
見た目も、使い勝手も、よくないと思います。
もちろん、無料でお使いただけますが、サポートも、動作保証もありません。
ダウンロードから設定まで、ALL自己責任でお願いします。

快適と感じる速度で動作させるには、かなり高性能なCPU搭載のマシンが必要です。
私のプログラミング技術が足りない部分を、CPUパワーでカバーしてもらってます。
マシンによっては、読み取り結果のチェック等がかなりトロいかもですが・・・

それでも、もし、よかったら使ってください。
Delphiで作ったマークシートリーダーです。

【ご案内】追記(20240929)

当Blogで紹介してきた自作のデジタル採点プログラムを一つにまとめました。次のリンク先にその紹介とダウンロードリンクがあります。

当Blogで紹介したデジタル採点プログラムのすべてをまとめました!

【使い方のご案内】

1.デスクトップにMS_Reader.zipを展開(解凍)
2.高解像度ディスプレイへの対応
3.マークシート画像の読み取り準備
4.テンプレートを作成
5.マークの読み取りを実行
6.読み取り結果のチェック
7.CSVファイルへの書き出し
8.Excel Book の準備作業
9.Excel Book への書き出し
10.マークシート印刷用紙について
11.まとめ
12.お願いとお断り

どんな環境でも、100%動作する保証はできません・・・が、
私と同じ環境・条件を揃えていただければ、きっと動くと思います。

使用したPC及びOS、開発環境は、次の通りです。

・プロセッサ 11th Gen Intel(R) Core(TM) i7-1185G7 3.00 GHz
・実装 RAM 32.0 GB

・Windows 11 Pro 64ビット版
・バージョン 23H2
・OS ビルド 22631.2861

・Embarcadero® Delphi 12 バージョン 29.0.50491.5718

・設計時の画面解像度は「1366 × 768」です。これ以上の解像度でお使いください。

使い方をなるべく丁寧に説明しますので(マニュアルも同梱してありますが)、まず、ここに書かれている順番で、一通り操作してみていただけたら幸いです。

1.デスクトップにMS_Reader.zipを展開(解凍)

ダウンロードした MS_Reader.zip をお使いのPCのデスクトップにコピペして右クリックするとサブメニューが表示されます。この中の「すべて展開」をクリックしてください。

デスクトップに MS_Reader.zip を展開します。


無事、展開に成功したら、MS_Readerフォルダをダブルクリックして開きます。

フォルダ内に展開されたファイルの中に MS_Reader.exe があります。これをダブルクリックしてマークシートリーダーを起動します。


次のメッセージが表示された場合は、「詳細情報」(画像中、赤い枠で囲んで示した部分)をクリックします(プログラムの発行元が不明である場合に、Windows のDefender機能である SmartScreen がこの表示を出すそうです。自分の責任で実行すれば、次回からこのメッセージは表示されなくなります)。

「詳細情報」をクリックします。


すると、次の画面が表示されます。「実行」(画像中、赤い枠で囲んで示した部分)をクリックしてMS_Readerを起動してください。

「実行」をクリックします。

2.高解像度ディスプレイへの対応

高解像度ディスプレイをお使いの場合の対応方法です。高解像度ディスプレイをお使いの場合、設定から「システム」⇨「ディスプレイ」と順にクリックすると、次のように表示されると思います。

拡大縮小に「150~200」という値が設定されていれば、高解像度ディスプレイです。


この場合、起動したマークシートリーダーの画面が小さくて見えにくいと感じることがあるかもしれません。その場合は、次のように操作してください。

MS_Reader.exe を右クリックして、表示されるサブメニューのプロパティをクリックします。

MS_Reader.exe のプロパティを表示します。


「互換性」タブをクリックします。


高DPI設定の変更をクリックします。


「高いDPIスケールの動作を上書きします。」にチェックを入れて、「拡大縮小の実行元:」は「システム」をComboBoxの選択肢から選択して指定。OKボタンをクリックします。


続けて「適用」⇨「OK」とクリックして設定は終了です。これで画面が見やすい大きさで表示されるようになります。

3.マークシート画像の読み取り準備

デスクトップに展開した MS_Reader フォルダ内に「ScanData」フォルダがあります。この中に練習用のサンプル画像が2種類(解像度150dpiと200dpi)入っています。この画像を用いて説明します。

重要 マークシートは、解像度150~200dpiでスキャンしてください。

重要 1回の操作で読み取り可能な枚数は最大99枚です。

MS_Reader.exe をダブルクリックしてマークシートリーダーを起動したら、画面左上の「画像変換」をクリックし、表示されるメニューの「専用画像を作成」をクリックします。


画像変換用のWindowが表示されたら、画面右上の「選択」ボタンをクリックします。


「フォルダの選択」ダイアログが表示されます。ここでは「Scanner_A」フォルダを選択します。フォルダ名をクリックして、下のFolder欄に「Scanner_A」と表示されたことを確認し、「OK」をクリックします。

スキャンしたマークシート画像は「ScanData」内に適切な名前を付けたフォルダを作成し、必ずその中に保存してください!

重要 フォルダ名にハイフン(-)を使わないでください。

参考 フォルダ名には、文字の他、アンダースコア(_)が使用できます。

注意してください。選択するのは「フォルダ」で、「ファイル」ではありません。
(Scanner_Aをダブルクリックして開いても何も表示されません)


画面は、次のようになります。赤い枠で囲んだ部分にマークシート画像のサムネイルが表示されます。回転の必要性の有無と回転方向を確認してください。


この場合は、回転の必要性「有り」で、回転方向は左90°です。これを「画像の回転」のオプションボタンをクリックして指定します。

左90°のオプションボタンをクリックします。


必要であれば、次に画像のリサイズ指定を行います。リサイズを指定「する・しない」の判定基準は、スキャナーでマークシート画像をスキャンした際の解像度の数値で判断してください。

「Scanner_A」フォルダ内のマークシート画像は、ScanSnap iX1500 のノーマルモードでスキャンした画像で、その解像度は 150dpi です。この場合は、ちょうどよい大きさでマークシート画像が表示されますので、画像をリサイズする必要はありません。

重要 解像度150dpi ・A4横置きの場合、リサイズは必要ありません!

重要 解像度200dpi ・A4横置き・解答マーク欄4列の場合、80%の大きさにリサイズしてください。読み取り後のチェックまで含めて、作業しやすくなります。

マークシート画像の読み取り解像度が 200dpi でも、マークシートがA4横置き、解答マーク欄の列数が3列の場合、リサイズは必要ありません

また、A4以外の大きさのマークシートは使ったことがありません!
(用紙の左上にマーカー画像■■■を入れ、その他はここでダウンロードできるサンプルと同様に作成していただければ、用紙サイズに関係なく動作すると思いますが、試行したことがありませんので確かなことは言えません。ただ、画像のサイズが大きくなればなるほど、動作速度は間違いなく低下します。また、複合機のスキャナーを用いて、マークシートを画像化する際も、B4やA3の大きさだと私が使用している機材ではメモリがいっぱいになるのでしょうか? 30枚程度読み込んだあたりで一旦動作が停止します。数百枚単位での読み取りにはそれなりに時間がかかります。そのような理由から、マークシートに使う紙の大きさはA4サイズ以下が適切だと思います。)

参考:プログラムを書いた本人が言うのもナンですが、自動でのリサイズはおまけ程度にお考えください。
ScanDataフォルダのScanner_Bフォルダに保存されたサンプル画像の大きさは、2338 × 1653
これを自動リサイズオプションボタンを指定して、変換してみます。
ProcDataフォルダのScanner_Bフォルダに保存されたサンプル画像の大きさは、1760 × 1248
いちおう、これでマークシート画像が横方向のはみ出し「なし」で表示されました。

重要 画像のリサイズの有無を必ずメモ(記録)してください!

⇨ 複数クラスのマークシート読み取り時に、同じ設定を適用する必要があります。

重要 大きな解像度の画像を扱う場合、動作速度が大幅に低下します!

回転の有無と方向、リサイズの有無を指定したら、画面中央右にある「参照」ボタンをクリックして、保存先のフォルダを選択します。


「フォルダの選択」ダイアログが開きます。Pathを見ると「ProcData」フォルダが指定されていることがわかると思います。なお、Procは「Processed(加工済み)」という意味です。

プログラムは「ScanData」フォルダで指定したフォルダと同名のフォルダを「ProcData」フォルダに自動作成します。この自動作成されたフォルダをクリックして選択します(しつこいようですが、選択するのは「フォルダ」で、「ファイル」ではありません)。下のFolder欄に「Scanner_A」と表示されたことを確認し、「OK」をクリックします。

読み取り用のマークシート画像は、必ず「ProcData」内の自動作成されたフォルダに保存してください!

重要 ProcData以外のフォルダには画像を保存しないでください。

読み取り用画像を保存するフォルダは自動で作成されます!
(自動作成されたフォルダをクリックして選択してください)


「変換実行」をクリックします。

回転とリサイズの有無を指定して「変換実行」をクリック!


次に表示される案内メッセージには「いいえ」を選択してください。


このマークシートリーダーとは別に、手書き答案の採点プログラムを作成しました(準備が整い次第、公開する予定です)。このマークシートリーダーは、そちらと連動しての動作も可能な設計にしてあるため、このメッセージが表示されます。

画像の変換が完了すると、メッセージが表示されますので、OKをクリックします。


変換された読み取り専用画像のサムネイルが表示されます。作成された読み取り用の画像ファイルには連番の名前が自動的に付きます(自動生成されたファイル名は変更しないでください)。

重要 Python環境を利用する場合はファイル名は必ず連番にしてください。

画像処理のアルゴリズムは、GDI+を利用しています。画像の回転とリサイズが伴う場合は、変換に時間がかかります。処理が完了するまでお待ちください。

(後日、別途ご案内する予定の)手書き答案の採点プログラムと併用する場合は、採点やり直しのために必要な画像もここで作成します(Loopが二重にまわり、時間も2倍かかります)。

クラス別に処理する場合は、「画面の初期化」ボタンをクリックします。
変換元フォルダの選択から、画像の変換処理を再実行できます。

画像の変換処理が完了したら、「終了」ボタンをクリックして、この画面を閉じます。

参考:画像を変換する理由は以下の3つです!
(1)Jpeg画像のサイズを最適化するため(全体が画面内に収まるようリサイズしてください)。
(2)画像の名前が連番になるよう、自動的にリネームするため。
(3)証拠画像としてのオリジナルを残したまま、読み取りに最適な大きさの画像を生成するため。

4.テンプレートを作成

次に、マークシートの情報を記録した読み取り用のテンプレートを作成します。これを作成することにより、同じ採点を複数クラスに対して実行したり、設定(縮小処理の有無を含む)が同じマークシートを異なる考査での使いまわしが可能となる・・・

・・・ように設計したのですが、実際には使いまわしがなんとなく不安なので、考査毎にテンプレートを再生成して運用しています。ですので、同じ設定(大きさ)のマークシート画像の情報を記録したテンプレートの使いまわしが可能か・どうか、これについては未確認です。

「確実なマークシート読み取りを実行する」ためには、お手数をおかけしますが、試験ごとに使用したマークシートのテンプレートを作成していただくのが最良の方法であると思います。

メニューの「2 テンプレート」をクリックして表示されるサブメニューの「テンプレートの新規登録」をクリックしてください。別のWindowが開きます。


画面右上の「取得」ボタンをクリックします。


今度は「ファイルを選択」するダイアログが表示されます。任意のマークシート画像を選んでください(1番のファイルを選ぶ方が多いのではないでしょうか?)。ファイルをクリックしてファイル名を取得し、「開く」をクリックします。

マークシート画像のサムネイルをクリックするとファイル名が取得できます。


画面は次のようになります。

このプログラムでは、マーカー(特徴点)画像を利用してマークシートのマーク位置を計算しています。ですので、このプログラムで処理するマークシートには必ずマーカー(特徴点)画像が必要です。

重要 マークシート左上にマーカー画像(■■■)を必ず用意します。

重要 マーカー画像は、マークシート1枚に1つだけ用意します。

画面右の操作パネル上段にある「マーカー」オプションボタンをクリックして選択状態にします。

「選択対象」の「マーカー」オプションボタンをクリックしてください。


マークシートの画像が拡大表示され、マウスのカーソルが大きな「+」になります。

マーカー画像の「左上」をクリックし、ボタンを押したまま「右下」へドラッグしてください。画像上には点線のラバーバンドが表示されます。

マーカー画像の左上を左クリックして、マウスの左ボタンを押したまま、マーカー画像の右下へドラッグ。
点線のラバーバンドでマーカー画像が囲まれます。


ドラッグ中の画像です(わかりやすさのため、マーカー画像より大きめにドラッグしています)。

黒点線がラバーバンドを示します。


マーカー画像の座標を正しく取得できる例です。

マーカー画像とラバーバンドがぴったり重なるようドラッグしてください。


マウスの左ボタンから指を離すと、取得できたマーカー画像が画面右側に表示されます。

数値は、画像左上からの距離です。

マークの読み取り時、プログラムは、コンピュータの眼である「OpenCV」のテンプレートマッチングの機能を利用して、まず、最初にマークシート画像中にあるこのマーカー(特徴点)画像を探し出します(これはマークシート画像1枚1枚について必ず行います)。

次に、マーカー(特徴点)画像左上隅を原点(0,0)として、テンプレートに記録されたマーク欄の座標からマーク一つ一つの位置を割り出して、これを切り抜いて画像化(正確に言うと、マークの切り抜き処理前に、ボカシ・二値化・白黒反転の各処理を行い、マークの切り抜き後に白面積計算処理を行って)、マークの有無を判定しています。

この方式の利点は、印刷そのものが左右にズレでも、マーカー画像と解答欄の相対的な位置関係は一定で変わりませんから、印刷がズレすぎて解答欄が印刷されなかった場合以外は、必ずマークの位置を探し出せる(=マークの有無を判定できる)ことです。

事実、輪転機で印刷(非推奨ですが!)して、チェックから漏れた(チェックしなかった?)、正しい位置から印刷が5cmくらいズレたマークシートも、このプログラムでなんの問題もなく読み取れました・・・。印刷のズレを申告せず、そのまま解答して提出する受験者も受験者ですが・・・。A4横・4列のシートで、解答には3列めまでしか使わなかったから「4列めはなくてもOK! 大丈夫」と思ったのでしょうか? それともただ単にめんどくさかったのでしょうか? たぶん、後者だと思いますが・・・

次は、そのテンプレートマッチングの機能をテストします。画面右にある「マーカー画像の読み取りテスト」ボタンをクリックしてください。テンプレートマッチングが正しく実行されると、マーカー(特徴点)画像が太い赤枠で囲まれます。


表示されるメッセージをお読みいただき、「OK」をクリックしてメッセージを閉じてください。


結果が良好であれば「選択対象」グループの「解答欄」をクリックします。


次に、マークシートのマーク(解答)欄の「行数」と「列数」及び「選択肢の数」を指定します。


マークシートの列数・行数・選択肢数の数え方は次の通りです(Scanner_Aフォルダにあるマークシート画像は、A4横置き・3列・25行・8選択肢の形式です)。


ですので、これを次のように設定します。


ComboBox に正しく設定を入力したら、その下の「採点方法の設定」の座標「1列」のオプションボタンをクリックして選択状態にします。マウスのカーソルが大きな「+」になります。


第1列目のマーク(解答)欄の座標を取得します。マーカー(特徴点)画像の時と同様、第1列の枠のうち、設問番号欄の矩形を除いた選択肢のマークが印刷されている欄の矩形の左上隅を(左)クリックして、そのままボタンを離さずに、枠の右下隅へドラッグします。この作業は正確に、慎重に行ってください。この作業の良し悪しでマークの読み取りの可否が決まります。

極めて重要 設問番号欄を含めて指定してはいけません!

極めて重要 指定するのはマーク欄のみ!

プログラムは、ここで取得した座標値(矩形の高さ)を行数で割り算して列を設問毎1行ずつに切り出し、さらに切り出した1行を選択肢数で割って1つ1つのマークを切り出し、その塗りつぶし面積を計算して、マークの有無を判定しています。

マーク(解答)欄の枠線と、表示されるラバーバンドがぴったり重なるようにドラッグしてください。

※ 下図は2つともドラッグ直後の結果を示しています(〇はドラッグ開始点と終了点です)。

マーク欄第1列めの左上隅を(左)クリックしてそのまま指を離さずに右下隅へドラッグ


ドラッグ中は、黒点線のラバーバンドが表示されます。これを目安に位置決めを行ってください、

画像中の〇印の位置までドラッグします。


指を離すと、ドラッグした範囲が赤い矩形で囲まれます。画面右側に取得できた座標が表示されます。


「再範囲選択」ボタンをクリックして、座標の取得をやり直すこともできます。


1列目が済んだら、同様にして2列目の座標を取得します。この作業を「マークシートの列数」分だけ繰り返します。

すべての列の座標を取得できたら、「保存」ボタンをクリックして取得した座標を保存します。


「保存」処理が完了すると、次のメッセージが表示されます。

参考:テンプレートの名前について
例 N_R25C03S08
N:ノーマル(通常の大きさ:解像度150~200dpi)
R:Row(行数)は25行
C:Col(列数)は3列
S:Selection(選択)は8個


「二値化テストの実行」ボタンをクリックすると、第1列めを「平滑化(ぼかし)処理&白黒反転して二値化」した画像の状態が確認できます。「マークあり」の部分が白く表示されていればOKです!
(プログラムは、この白部分の面積を計算して、マークの有無を判定しています)

「終了」ボタンをクリックして画面を閉じ、マーク(解答)欄座標の取得作業を終了します。

二値化テストを実行した場合は、終了ボタンをクリックする前に、保存ボタンをクリックすることを忘れないでください!

二値化の閾値と平滑化(ぼかし処理)のパラメータは、まずデフォルト設定でお試しください。

5.マークの読み取りを実行

これでマークを読む準備ができました。メニューの「テンプレート」をクリックし、表示されるサブメニューの「テンプレートの選択」をクリックします。


次のように、テンプレートを選択するWindowが表示されます。マークシートの形式に合ったテンプレートをクリックして選択し、決定をクリックします。

シングル/ダブルとあるのは、数学や教科「情報」のテストで、マークシート2枚1セットの採点を行うための設定です。選択肢数が16のマークシートを選ぶと、この設定も選択できるようになります(選択肢数が16未満のマークシートでは、この設定は利用できません)。

数学及び教科「情報」用の設定は、後日別記事として掲載する予定です。


次のメッセージが表示されます。「はい」をクリックしてください。


マークの読み取りを実行したいマークシート画像のあるフォルダを選択し、「Ok」ボタンをクリックしてください。


保存してあるマーカー(特徴点)画像をもとに、自動的にテンプレートマッチングが行われ、見つかったマーカー(特徴点)画像から、マークシートのマーク(解答)欄第1列第1行目の座標が計算され、それぞれが赤い矩形で囲まれて表示されることを確認してください。

Python環境を利用する場合(ここでワンクッション置くような感じで)テンプレートマッチングにしばらく時間がかかることがあります。同じプログラムを走らせているのですが、PCにより、このフリーズしたような時間の長さが極端に違うようです・・・、その辺の理由が私にはさっぱりわかりませんが・・・。

Python環境利用時に、この画面が表示されるまで、フリーズしたようになることがあります!


ここまでの設定操作が順調に進行していれば(抜け・落ち・欠けがなければ)、間違いなくテンプレートマッチングが成功し、マーカーと1列1行目が赤い矩形で囲まれるはずです。次のメッセージが表示されますので、お読みになったら「OK」ボタンをクリックしてください。


「読む」ボタンをクリックすると、マークシートの読み取りがスタートします。


画面下部の StringGrid に読み取り結果がリアルタイムで表示されます。また、読み取り完了後、処理にかかった時間が画面左下に表示されます。


8選択肢・25行・3列だから、合計600マーク ×3枚=1800マークの読み取りで、早ければ2013ミリ秒、遅くて2467ミリ秒で読んでます(PCの性能により、この値は変わります)。


遅かった方で1マークあたりの読み取り時間を計算すると、

2.467秒 ÷3≒ 0.82秒/枚
0.82 ÷ 600 ≒ 1.4ミリ秒/1マーク

そう書くと、すごく早いような気がしますが・・・

600マーク3枚で2.5秒だから、30枚ならその10倍で25秒かかります。平均的な高校の1学年分の生徒数を1学年8クラス320名とすると、さらに10倍で280秒程度、約5分処理時間が必要です。

300名分、5分だと慣れてくるとちょっと遅く感じてしまうかな? みたいな気が・・・

このプログラムには、内部的にPython環境を組み込んで高速動作させるモードがあります。数学用途の16選択肢・25行・3列で1200マーク/枚のマークシートで処理速度を計算・比較してみます。
(組み込みPythonの利用方法は後日ご案内します)

まず、Python環境を利用しない場合、1200マーク×40枚=1クラス分の48000マークを読むのにかかった時間が・・・


約78秒です。2枚1セットのダブルモードならその倍になります。
1枚(1200マーク)読むのに1.95秒かかってます。

次に、Python環境を利用した場合です。同じ読み取り条件で実験すると・・・


約11.5秒。8クラスあっても2分かかりません。ダブルモードでも4分未満。
1枚0.3秒未満で読み取ってます。

何やってもダメな自分にしては、よく頑張ったって正直、思います・・・。
よほど、びみょーなマークでない限り、期待した通り、ほぼ正しく読み取ってるし・・・。
かあさん、オレ、がんばったよ☆☆☆

まぁ このプログラム作成そのものに50万枚くらい採点できる時間をかけてますから・・・

それと合算すれば、
たぶん、プラマイ0ですー!!

6.読み取り結果のチェック

マークシートリーダーで最も重要な部分は、マーク読み取りの正確さであることは言うまでもありませんが、読み取り結果のチェック機能も非常に重要であると考えます。

人によってマークの濃さや大きさは少しずつ異なり、また、マークを訂正した箇所に残る消し跡も判定に少なからぬ影響を及ぼします。常に100%正しい読み取り結果が保証されないのが現実ですから、如何に効率よく、読み取り結果をチェックできるかで、プログラムの使用感はずいぶん変わってくると思います(CPUパワーにかなり依存したプログラムを書いておいて、そう言うのもナンですが・・・)。

自分自身の書いたものがベストだなんて、到底、思えませんが、このプログラムを書くにあたり、マークの読み取り部分と同等か、それ以上に頑張って書いたのが、この読み取り結果のチェック部分です。

機械との協働。機械との融和。これをテーマに、ヒトと機械とが一体化しての「快適なチェック作業」の実現を目指しました。

・・・が、プログラミング技術の未熟。自分自身の勉強不足。見い出した妥協点。等々の理由により、視覚による機械と協働してのチェックも、聴覚(音声出力)による機械と協働してのチェックも、いずれも全面的にマシンのCPUパワーに依存した、もっさりした感のある処理となってしまいました・・・。

処理性能の高いマシンなら、それなりに快適に作業できると思うのですが。以下、チェック機能の使い方です。

マーク読み取り結果のチェックを実行。


上の図の左のGUIから説明します。

白紙にチェックすると、マークがひとつもないシートのチェックは行わない(飛ばす)設定で動作します。この機能はデフォルトでON(チェックあり)です。

マーク(解答)がなかった場合の読み取り結果の表示が「999」です(デフォルトOFFです。このプログラムでは、「空欄」のフラグを「999」としています。マークの番号にも、得点にも「999」は通常ないことがその理由です。ちなみに複数マークは「99」と表示しています。色は「999」が「青」、「99」が「赤」です。少しでも視覚に訴えた方がチェックしやすいと考えました)。

ごく薄い色でマークされた答案が混じっていないことが大前提ですが、答案全体(1クラス分!)のマークの濃さが十分「濃い」と保証されていれば、チェック開始時のみ「999」のチェックを外してチェック(機械がきちんと空欄を識別していることをヒトが目視して確認)、で、確実に空欄を見分けていることが確認できたら、「999」にチェックして続行。こうすれば大変スムーズな確認作業を実現することができます。あくまでもごく薄くマークされたシートがないことが大前提ですが・・・

いずれにしても「Check!」ボタンをクリックすると、プログラムは次の「空欄(999)」もしくは「複数マーク(99)」を探し、それが見つかった場合は該当箇所を赤い矩形で囲んで表示します。処理性能の高い(CPUパワーのある)マシンであれば、それなりに快適に動作しますが、そうでない場合は、かなり「もっさり」した動作になりますので、イライラするかも知れません。ごめんなさい。

【空欄と判定した場合】

マークがない場合の表示例(設問番号25が空欄であった場合)

【複数マークありと判定した場合】

複数マークと判定した場合の表示例(設問番号43が複数マークであると判定)


複数マークの判定はパラメータ設定を厳し目にしてあります(上の図はそれがわかるよう、大きめに表示しました)。ごく小さなシミは「平滑化(ぼかし)」処理である程度消えますが、ある程度の面積があるシミや汚れは上のように複数マークと判定されます。

いずれの場合も、ヒトの眼で確認して、訂正の必要がなければ「Check!」ボタンをクリックしてチェックを続行。読み取り結果の訂正が必要な場合は、正しい値を直接入力します(上の場合であれば「2」と入力してください)。


【処理をスキップして次のシートへ】

「Skip」ボタンをクリックすると、現在チェックしているシートの残りの部分のチェックを省略し、次のシートのチェックへ移動します。チェック対象シートの残りの行が全部空欄であった場合などに利用してください。


【チェックの再実行】

「ReDo」をチェックすると、初めからチェックを再実行できます。

ReDoにチェックすると表示されるメッセージ①
ReDoにチェックすると表示されるメッセージ②


【音声読み上げ】

読み取り結果が表示されているStringGridの任意の行をクリックして、「▶」ボタンを押すとWindowsに標準搭載されている日本語の音声合成エンジン(Microsoft Haruka Desktop)の音声で読み取り結果をアナウンスしてくれます。

マークの読み取りが正しく行われているか・どうか、少しでもラクに確認できないかと考え、この機能を搭載しました。処理性能の高いマシンでないと快適な動作は期待できませんが、CPUパワーのあるマシンであればそれなりに使えると思います。

「▶」ボタンの下にある「×」ボタンをクリックすると、音声読み上げを途中で中止することができます。


【列を指定して、任意の行からその列の最後の行までのチェックをスキップ】

数学用のマークシート等で、第1問の解答をシート第1列にマーク、第2問の解答をシート第2列にマーク、第3問の解答を・・・というような設定にしたい場合、「指定列の任意の行から最後の行までをチェックの対象から外す」ことができます。以下、その方法です。

任意の行を指定して、その行以降のチェックをスキップできます。


図のいちばん左にある「Skip」にチェックすると、この機能が有効になり、続けて「Check!」ボタンをクリックすると、ここでの設定に基づいたチェックを実行できます。

上の例であれば、1列目25設問あるうちの20設問目以降25設問目までのチェックをスキップ(チェックは19設問まで実行)、2列目は設問番号26から始まるので34設問目以降50設問目までを、3列目は設問番号51から始まるので70設問目以降75設問目までのチェックをそれぞれスキップします。スキップの設定はComboBoxへ入力した指定値「以降」であることにご注意ください。

また、シートの型式により、列の指定の可否をプログラムが自動的に判断し、ComboBoxのEnabled プロパティが設定されます(上の例では4列目は指定不可)。

「覚」ボタンをクリックすると、現在の設定を ini ファイルに書き込んで記憶します。「消」ボタンをクリックすると「設定なし」の状態に初期化できます。

数学用途等で2枚1セットの処理を実行する場合は、1枚目と2枚目を分けてスキップ処理の設定を行うことができます(数学用途の処理方法は後日掲載します)。

7.CSVファイルへの書き出し

マークの有無の読み取り結果は、CSVファイルとExcel Book への書き出しが可能です。

【CSVファイルへの書き出し】


「ファイルへの出力」にある「CSV」をクリックして選択し、「書き出し」ボタンをクリックしてください。


上記の場所にCSV形式で、読み取り結果が出力されます。

フィールド名として1行目に「設問番号」、レコード名としてA列に「マークシート番号」が書き込まれます。

8.Excel Book の準備作業

【Excel Bookへの書き出し準備】

Excel Book への読み取り結果の書き出しは、自分用に(あれば便利かなー☆)と思って作成したものです。ですので、式の入ったセルを保護する等、第三者が使うことへの配慮は何一つ行っていません。セルに入力された式やVBAの内容をご自身でメンテナンスできる方なら、お使いいだけるかな? という程度のシロモノです。

添付した Excel Book はこれまでに何度も「実際に使用して動作に誤りがないことを確認済み」ですが、誤って式を削除したりした場合は(当然ですが)意図した通りに動作しません。ですので、こちらも動作保証は一切ありません。ご使用はあくまでも自己責任でお願いします。この Excel Book に対しても、このプログラムの使用要件にあります免責事項がそのまま適用されますことを申し添えます。

以下、試験実施前に行っておくとよい採点準備作業です。

eFile フォルダに「一般用マークと手書き併用採点シート.xltm」というマクロ有効テンプレートがあります。これをダブルクリックすると「一般用マークと手書き併用採点シート1.xlsx」という名前で新しい Excel Book が作られます。拡張子に注意してください。「.xlsx」です。このままでは期待通りに動作しませんので、適切な名前を付け、拡張子を「.xlsm」(マクロが有効な Excel Book )に変更して eFile フォルダ(必ずこのフォルダに保存してください!)に保存します。

ここでは test.xlsm という名前で保存したことにして説明を続けます。

「コンテンツの有効化」をクリックしてマクロが実行できるようにしてください。


【インターネットからダウンロードしたマクロ有効 Excel Book の取り扱い】

いつからこうなったのか、わかりませんが、インターネットからダウンロードした拡張子 xlsm の Excel Book をダブルクリックして開くと、次のメッセージが表示されるようになりました。

「編集を有効にする」をクリックすると・・・
マクロを動かすことができません!


こうなった時は、いったん Book を閉じて、その Excel ファイルを右クリックして表示されるサブメニューのプロパティをクリックして、全般タブのいちばん下にある「セキュリティ:」の「許可する」にチェックします(チェックする=マクロの実行をご自身の責任で行うことになります。どうか、ご注意ください)。

全般タブの下の方にあるセキュリティの設定。
マクロの実行をご自身の責任で行う場合は、「許可する」にチェックしてください。


「許可する」にチェックした状態で、「適用」をクリックすると「セキュリティ」の表示そのものが消えます。「あなたの責任でマクロの実行が可能になりました」ということなのでしょう。「OK」をクリックしてプロパティの設定画面を閉じます。


これでマクロが実行できるようになります。


【欠席者がいた場合】

Excel Book を利用して採点する場合、大変重要な注意事項があります。それは欠席者がいた場合の処理です。該当試験に欠席者がいる場合は、その欠席者の出席番号位置に未使用のマークシートを挿入し、シートが確実に出席番号順に並んでいることを確認してから、スキャナーでスキャンしてください。
※ 可能であれば、この用途専用に未使用のマークシートを複数枚、最初から手元に準備しておくとよいと思います。

重要 未使用のマークシートを欠席者の出席番号位置に挿入しておく!

これを忘れると、あとから「すーぱーめんどくさい」コトになります(もし、忘れたらマークシートのスキャンからもう一度、採点をやり直した方が効率がいいかもしれません)。


【受験者の氏名データを準備する】

test.xlsm をダブルクリックして開き、「コンテンツの有効化」を行ったら、いちばん最初に「名票への貼付元名票」シートをクリックして開き、ここに「採点対象者全員分の氏名」を準備してください。

もっとわかりやすく言うと、採点したいテストを受験した生徒全員の「クラス・出席番号・氏名・ふりがな・性別」データを「クラスごと」に「出席番号順」で、「名票への貼付元名票」シートに用意します。なんで「ふりがな」まで必要なのか? 疑問に思う方もいらっしゃるかもしれませんが、最近の若い方々のお名前は難読である場合が多く、採点結果を個票でお知らせする際に、個票の氏名欄のところに「ふりがな」も印刷しておくとスムーズに答案返却が行えます。そのための「ふりがな」準備です。

また、テストの受験者全員分の氏名データを1シートに準備する理由は、次のような使い方を想定しているからです。

(1)同じテストを受験 ⇨ クラス毎に採点用 Excel Book を用意するのは非効率的。
(2)採点用 Excel Book は1個だけ作成し、これをコピーして全クラス分を作る。

具体的には、eFile フォルダの Excel Book(test.xlsm)をコピーして、クラス別(AHR.xlsm)に名前を変えて MS_Reader.exe がある場所に保存。採点結果もコピーした Excel Book(AHR.xlsm) に書き込みます。さらに、この作業はすべてプログラムから自動実行します。

採点者は、採点結果が書き込まれた Excel Book(AHR.xlsm)を開いて、「名票への貼付元名票」シートに用意した氏名データから「A組の受験者の氏名データ(クラス・出席番号・氏名・ふりがな・性別)を範囲選択してコピーし、「名票」シートに値のみ貼り付けます。

こうすることで同じ内容のファイルを複数個準備することなく、言わば「採点原本」として利用する Excel Book を1つ作成するだけで、試験を実施した全クラス分の採点が可能となります。

ここでは「クラス」と表現しましたが、用意する氏名データを適宜変更すれば「講座」等の採点もまったく同じように行えます。※ プログラムの仕様としては、1回の採点作業で採点する人数を100名以下と想定していますが、実際の採点作業は1採点40名程度で行っています。ですので、40名程度を1つのまとまりとして採点していただく方向でお考えください。


【正解を入力】

氏名データの準備が完了したら、「正解」シートをクリックして表示し、設問毎に「正解」の選択肢の番号を入力します。設問がない場合(無解答でよい設問番号の欄)は空欄のままにしておきます。入力したら、入力内容に間違いがないか、よく確認し、上書き保存してください。

正解の入力を間違えるとたいへんなコトになります!
慎重に入力し、最低2回は間違いがないことを確認してください。


【配点を入力】

次に、「マークシート配点」シートをクリックして「配点」を入力します。入力と同時に合計が自動的に計算されます。入力が完了したら上書き保存してください。なお、この配点表の下には観点別評価の表もありますが、この表には一切入力しないでください(観点別評価の表は入力禁止です)。

配点を入力すると「合計」が自動計算されます。
確認作業にお役立てください。


【観点別評価の区分を入力】

次に、「マーク&手書き観点別評価」シートをクリックして「観点別評価の区分」を入力します。
「知識・技能 ⇨ 1」、「思考・判断・表現 ⇨ 2」として設問毎に、半角数字で入力してください。デフォルト設定では、すべての設問に「1」が入っています。解答を要しない設問は「空欄」にしてください。入力したら上書き保存します。


以上で、試験実施前の準備は終了です!

9.Excel Book への書き出し

重要 すべての Excel Book を閉じてから実行してください!

危険 Excelが起動した状態で実行すると重大なエラーが発生します!

Excel へデータを書き込む際は、上記注意事項を必ずお守りください。この注意を忘れて Excel が起動したまま、Excel Book への書き込みを実行すると最悪の場合、Excel のプロセスが幽霊のように残り、これを終了することが出来なくなって、復旧するには、システムの再起動しかない状態になります。未保存の重要なデータがあるような場合、当然そのデータは失われます。Excel Book へのデータ書き込み時は、Excel が起動していないことを(タスクバーに眠っている Excel Book がないことも含めて)十分確認した上で、書き込み作業を行ってください。


【書き出し処理】

マークシートを読み取り後、読み取り結果のチェックまで完了したら、Excel Book への読み取り結果の書き出しが可能となります。次のようにマークシートリーダーを操作してください。

最初に、ファイルへ出力の Excel のオプションボタンをクリックして選択します。すると、その右側にある「選択」ボタンがクリックできるようになりますから、このボタンをクリックしてください。


ファイル選択のダイアログが表示されますので、読み取り結果を書き込む Excel Book をクリックして選択し、その後、下にある「開く」ボタンをクリックします。Pathの指定は、デフォルトで eFile フォルダになっています。準備作業で作成した test.xlsm を eFile フォルダに保存したのは、この読み取り結果を書き込む Excel Book を選択する作業を円滑に実行するためです。


次のメッセージが表示されます。

重要 ここで Excel が起動していないことを必ず確認してください!

選択した Excel Book が書き込み先として表示されていることを確認し、「書き出し」ボタンをクリックします。


書き込みには、しばらく時間がかかります。次のメッセージが表示されるまでお待ちください。


すぐに書き込み結果を確認する場合は、「はい」をクリックします(ここでは「はい」をクリックしたものとして説明を続けます)。

「はい」をクリックした場合は、エクスプローラーが自動的に開きます。先ほど選択した「test.xlsm」のコピーが「Scanner_A.xlsm」として、eFile フォルダではなく、MS_Reader.exe のあるフォルダに生成されています。


ファイル名がなぜ「Scanner_A.xlsm」になったかというと、マークシートの読み取り元フォルダとして選択したのが、ProcData\Scanner_A であったためです。プログラムは、マークシートの読み取り元フォルダの名称をそのまま、原本「test.xlsm」をコピーして生成する読み取り結果書き込み先 Excel Book の名称として利用します。

マークシートの読み取り元フォルダの名称が、Excel Book の名称になります!


マークシートの読み取り元フォルダの名称が「R05_情報Ⅰ_1A」であれば、MS_Reader.exe のあるフォルダに「R05_情報Ⅰ_1A.xlsm」が生成されます。

ここは、この仕様に慣れるまで混乱が生じやすいところと思われます。しかし、この仕様(仕組み)を十分に理解して、マークシートリーダーを使いこなしている職場の同僚からは「よく考えられた採点システムだと思います」と言ってもらえました。うれしかったなー!!


【成績一覧表の印刷】

生成された Excel Book をダブルクリックして起動します。起動したら「名票への貼付元名票」タブをクリックして開き、採点対象クラス(等)の氏名データを範囲選択してコピーし、「名票」タブをクリックして B3 セルに値のみ貼り付けます。次に「採点」タブをクリックしてください。次のような画面が表示されます。「氏名がある場合のみチェックする」ボタンをクリックしてください。画面上方に表示されている平均点が正しく再計算されます。なお、欠席者の得点は「0」と表示されていますので、この場合は手動で「受験確認」のチェックを外し、平均点の計算対象から除外してください。

「氏名がある場合のみチェックする」をクリックしてください。


このシートは通常の印刷操作で印刷できます。ただし、デフォルト設定で100名分を2枚に分けて印刷する仕様となっているため、成績一覧表が1枚でよい場合は、次のように指定して1ページ分のみ印刷を実行してください。

成績一覧表を1枚だけ印刷したい場合の設定


【観点別評価を行う場合】

観点別評価を行う場合は、「正答率」タブをクリックして、上と同様の操作を行ってください。欠席者がいた場合の処理も同じです(このシートは印刷しません)。


【個票の印刷】

最後に、試験の採点結果を受験者に知らせる成績個票を印刷します。よー書いた。さすがに私も疲れました。あと、もぉちょっとです!

「個人表」タブをクリックします。次のような画面が表示されます(表示倍率は異なります)。まず、考査名と科目名を入力してください(忘れやすい部分です! ご注意願います)。印刷はVBAでマクロを組んであります。設問数に合った「印刷(QXX)」ボタンをクリックしてください。

重要 セルを保護していません。誤って式を消さないでください!


次の印刷フォームが表示されます。開始番号と終了番号を入力し、「印刷実行」をクリックします。

重要 印刷は途中で中止できません!

VBAではプログラム書いてない!のに、Engterキー押し下げでフォーカスが移動します・・・

この印刷は Excel の仕様上、印刷データをためてからイッキにプリンタへ送信という方法が取れません。1枚ずつ送信しますので、ちょっとギクシャクした感じで印刷が実行されます。プリンタが壊れているわけではありません。


【個票を個別に確認したい場合は?】

受験者個々の個票を確認したい場合は、A2 セルに「採点」シートの通番を入力します。いろいろなクラスの生徒が混在した講座の処理に対応するため、入力値は「出席番号とは異なる」ことにご注意願います。

採点シートの通番を入力します!


個票を確認したい受験者の通番は「採点」シートを表示して確認してください。

受験者の通番を確認します。


【壊しちゃったときは?】

個人表シートを壊してしまった時は、次のようにすれば直せます。「個人票_Back」タブをクリックします(このシートは絶対に非表示にしないでください)。A 列の左、1行めの上(図の〇印を付けた部分)を右クリックしてシート全体を選択し、表示されるサブメニューのコピーをクリックします。

A 列の左・1行めの上を右クリック!
シート全部をコピーします。


個人表シートに戻って、先ほどと同じ A 列の左・1行目の上を右クリックして表示されるサブメニューの「数式fx」をクリックします(罫線データ等を壊してしまった場合は、すべてを貼り付けます)。

数式が壊れた場合は数式を貼り付けます。
面倒な場合は、いちばん左の全部「貼り付け」でもOK!

10.マークシート印刷用紙について

紙の「白さ」の度合いを「白色度」というそうです。このマークシートリーダーで読み取りに使用したマークシートはすべて「再生紙 or 再生コピー用紙」と呼ばれる紙に印刷したものです。

ですから、ここで紹介したマークシートの読み取り結果は、すべて「白色度70%」前後の「再生紙」に印刷してのもので、ホームセンターで一般的に販売されているような「白色度」が「再生紙」よりはるかに高い「真っ白に見える」用紙を用いての読み取り結果ではないことに、十分ご注意願います。

マークシートの印刷に使用する紙の「白色度」によっては、読み取りパラメータ設定の見直しが必要になるかもしれません(私自身は、実験・試行していませんので正確なことはわかりませんが)。入手可能なすべての紙について、実験することは現実的に無理でありますので、マークシートを印刷する用紙については、本ソフトウェア使用者の責任で十分な試行を行い、確実に動作するパラメータ設定を行った上で、このプログラムをお使いいただけますよう、お願いいたします。

印刷はインクジェットプリンタで行うことを推奨しましたが、長期にわたって使用していない(メンテナンスもしていない)インクジェットプリンタ(複合機)では、インクの吸い込みに問題が生じ、「いくら調整しても・何度クリーニングを行っても」期待した濃度での印刷ができないということも経験しました。サービスマンの方に伺ったところ、「こういう状態になると通常のクリーニングではなかなか復旧しない」と教えていただき、あらためて日常的に使用してインクを動かすことと、不具合が見えたらすぐにメンテナンスをお願いすることの大切さに気づいたこともあります。

そのサービスマンの方からは、マークシートに付着していた消しゴムの「屑」がスキャナーのローラー等可動部の動きを悪くして、マークシートがやや斜めにスキャンされたりする原因となり得ることも教えていただきました。実際に大量のマークシートを読み取ってきた複合機のスキャナー部分からは、かなりの量の消しゴム屑が・・・。受験者には消しゴム屑をよーく落としてから答案(マークシート)を提出するよう注意しておく必要があります。まさに塵も積もればなんとやら・・・です。

また、ご使用のスキャナーの読み取り設定によっては(デフォルトの読み取り設定が)0~255段階のグレースケールでなく、カラーであったり、ある閾値で白黒二値化しての読み取りであったりという、私の想定外の設定であることも、当然のようにあり得ると思います。それがカラー画像であった場合の影響はほとんどないと思われますが、ある閾値での白黒二値化画像であった場合は、判定に重大な影響を及ぼす可能性があります。ですので、マークシートの読み取りに、使用されるスキャナーの読み取り設定に関して、予め、使用者様の責任で十分ご確認いただけますよう、併せてお願い申し上げます。

11.まとめ

このマークシートリーダーで出来ること、出来ないことをまとめました。

【出来ること】

・マークシートのJpeg画像を回転&適切なサイズに縮小
・マークシート画像のマーク読み取り(1設問当たり最大16選択肢まで対応)
・読み取り結果の確認(GUI & 音声出力)
・読み取り結果のCSVファイル出力
・読み取り結果を採点結果通知用Excel Bookへ出力(新教育課程に対応)
・共通テスト形式の数学試験に対応(選択肢:-、±、0-9、記号:a~d)※ 後日掲載します。
・共通テスト形式の情報Ⅰ試験に対応(選択肢:0始まりの設定も可能)※ 後日掲載します。
・使用環境に合わせて各種パラメータ設定を変更可能
 ⇨ ScanSnap iX1500のノーマルモード(解像度150dpi相当?)、もしくはEPSON PX-M7110F(解像度200dpi)でスキャンしたJpeg画像のマーク読み取りに最適化した値をデフォルト値に設定済み。

【出来ないこと】

・1設問について、複数の解答が設定された採点
・前問の解答内容に応じて、次の問いの解答が変わる採点
・その他、答案1枚のみの採点等、このプログラムで想定外の採点全て
・1回の読み取り操作で処理できるJpeg画像は99枚までで、100枚を超える枚数は処理できません。

【その他の使用方法】

MS_Reader.exe の「ヘルプ」にある「PDFを表示」をクリックすると利用方法の手引きがお使いのPDFリーダーで表示されます。マークシートの作り方等、このブログの記事にないことも書いてありますので、必要に応じてこちらも併せてご参照いただけますよう、お願いいたします。

12.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容及びダウンロードしたプログラムを利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

ini ファイルの Section の有無を確認

レジストリを汚したくない気持ちと、ini ファイルの手軽さから、僕はパラメータの設定を、いつも ini ファイルに記録してしまう。

今回、あるプログラムを見直していて、これまで ini ファイル内のセクション自体の存在を「確認」するようなプログラムを書いた経験がないことに気がついた。

ini ファイル内に「XXX」という名前のセクションが、あるか・ないか?
これは、それを確認する方法の覚え書き。

【もくじ】

1.もし、セクションがなかったら?
2.セクションの存在を確認
3.まとめ
4.お願いとお断り

1.もし、セクションがなかったら?

この秋から、マークシートリーダーのプログラムを見直す作業を、ヒマを見つけては行ってきた。
なぜ、そんな作業をすることにしたかというと、読み取り速度に目をつぶってもいいから、ファイル容量的に軽い(=小さな)マークシートリーダーも用意したくなったというのがその理由。

そもそも、自作のマークシートリーダーを作ろうと思い立ったのが、2020年の2月。最初はDelphi用のOpenCVライブラリを利用。読み取り速度より何より、「読めるかどうか」それすらわからない中でのチャレンジ。読み取り性能に重きを置いたのは言うまでもない。結果、読み取り性能的には十分満足できる、夢見た通りのプログラムを完成させることができた。その動作速度は(PCの性能にも左右されるが、My環境では)1枚あたり1200マークあるシートを、1枚あたり約1.2秒で処理。これだと40名分のマークシート読み取りに48秒、200名分ならば6分。

MyPCのスペック・・・プロセッサ:11th Gen Intel(R) Core(TM) i7-1185G7 3.00GHz / 実装RAM 32GB

その後、がむしゃらに高速化を目指した時期があって、その学びの中で、僕は embeddable Python の存在を知った。これにPython用のOpenCVその他のライブラリをインストールして、Pythonの力を借りることで上記のシートを0.34秒/枚、40名分を14秒以下、200名分を1分強で読み取れるマークシートリーダーへ、プログラムは進化。きちんとマークしてあれば、読み取りミスはもちろん「0」。マークの濃さや塗りつぶし面積をさまざまに変えて、読み取りパラメータの設定を調整した結果、相当薄いが明らかにマークしてあるものや、逆に、消し方が不十分でうっすらとマークの痕跡が残ったものにもそれなりに対応、さらに、読み取り結果をヒトと協働して確認できるチェック機能も搭載(非力なPCではややもっさりした動作になるが・・・)。ついでに音声読み上げ機能も欲しくなり、Windowsに標準搭載されている日本語の音声合成エンジン(Microsoft Haruka Desktop)のHarukaさんにも登場してもらって(Windowsの機能の利用だから著作権的には問題ないと思うんだけど・・・)ユーザーが選択した任意のシートのマーク読み取り結果を、簡単にチェックできるプログラムとした。

性能的な部分では、十分、満足した・・・けど、そのかわり、プログラム全体のサイズは超巨大化。exe それ自体は3.81MB程度なのだが、Python用のOpenCVをインストールしたフォルダの容量が、なんと158MB!(フォルダのプロパティで確認したところ、ファイル数: 2,820、フォルダー数: 283 とのこと)

これら、プログラムの動作に必要な一式をZipファイル一つにまとめて、他のPCにコピー&展開すると、PCによってはその展開(解凍)処理だけで20分くらいかかってしまう・・・。ライブラリには相当数、このプログラムには必要のないファイルも含まれているはず、そう思えても、どれが不要なファイルなのかが僕にはわからない。

(多少、動作速度は遅くてもよいから、サクっとコピーして、すぐに動かせるマークシートリーダーも欲しい・・・ )

ってか、exeのある場所にPython環境があれば、Python環境を利用して高速動作、Python環境がなければ(自動的に)Delphi用のOpenCVを利用して動作するような、Python環境を「後付けで追加することも可能」(ユーザーが動作環境を選べる)ようなカタチにしたいって、いつの間にか思い始めて・・・。

そのような経緯から僕は、プログラムの起動設定の見直し作業に着手した。それが冒頭に書いたようにこの秋の始まりの頃だった。

まず、行ったことはPython環境の完全な切り離し。せっかく、くっつけたものを切り離すのは大変だったけど、あっちこっちをいじくりまわして、なんとか切り離しに成功☆ Delphi用のOpenCVだけで動作する状態に戻せた。

内部的にはPython環境を利用する際に必要なコードはすべて残してあるので、次に exe と同じ場所にPython39-32フォルダがあった場合には、Python環境を利用して動作するようにプログラムを修正。これも何とか実現できた。

次に、ミニマム構成でプログラムを動かすために最低限必要なDLL等を確認。実際にミニマム(と思える)構成を作ってみて、動作テストを繰り返し行った。その中で、起動時に設定する読み取りパラメータに関して解決しておいた方がいい問題があることを発見。

Delphi用とPython用のOpenCVでは、起動時に設定するパラメータの一部が異なっている。Python環境の有無で(具体的にはPython39-32フォルダの有無で判断)、当然デフォルト・パラメータ設定を変えて起動させなければならない。その部分のプログラムを見直していて ini ファイルに「もし、読みだすべきセクション(名)そのものがなかったら?」という場合も想定しておいた方がいいことに、僕は初めて気がついた。

ちなみに、これまで書いてきたのは次のコード。これでも第2引数に指定したキーがなかった場合に加え、第1引数に指定したセクションそのものがなかった場合にも、第3引数に指定したデフォルト値が変数にセットされるから、エラーにはならないのだけれど・・・、ユーザーには「セクションそのものがない」ということが伝わらない。ユーザーがデフォルト値として設定されたパラメータを調整・保存して初めて ini ファイルに「Section1」が生まれる・・・。

uses
  System.IniFiles;

procedure TFrmMain.FormCreate(Sender: TObject);
var
  Ini: TIniFile;
  str01, str02: String;
begin
  //iniファイルからデータを読込み
  Ini := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
  try
    str01:=Ini.ReadString('Section1', '文字列型_XXX', 'ABC');
    str02:=Ini.ReadString('Section1', '文字列型_YYY', 'DEF');
  finally
    Ini.Free;
  end;
  AAA.Text:=str01;
  BBB.Text:=str02;
end;

つまり、これまでの僕のプログラムは、各パラメータ値の設定とデフォルト・パラメータの設定を記録したセクションが「必ず ini ファイル内にある」という大前提で動いていたわけだ。Ini.ReadString の第3引数で「セクション」や「キー」がなかった場合のデフォルト値を指定してあるから、ini ファイル内にそれらがなくてもエラーは発生しないのだけれど、ほんとうにそれでいいのか? って、そう考えるとそれは「よくない」気がして・・・ならないし。

いちばん気になる、各パラメータ値の設定を記録した「セクションそのものがない」場合にはどうするか?、その対処方法を、知ってるか? って問われたら、その答えを僕はもちろん、知らない。そのことに、ここでようやく気付いた。この場合、知らないでは済ませるのは「すーぱー嫌」なので、良い機会だと思い、それを調べてみることにした。

2.セクションの存在を確認

調べてみると、やはりini ファイルにセクションが存在するか・どうかを調べるメソッドがちゃんと用意されていた。TIniFile の仕様的にエラーが発生しないから、このメソッドの必要性をこれまで僕は感じなかったんだろう・・・。

System.IniFiles.TCustomIniFile.SectionExists

https://docwiki.embarcadero.com/Libraries/Sydney/ja/System.IniFiles.TCustomIniFile.SectionExists
SectionExists メソッドを使用すると,FileName で指定した INI ファイル内にセクションが存在するかどうかがわかります。Section は,SectionExists が存在することを決定する,INI ファイルのセクションです。SectionExists は Boolean 値を返して,対象のセクションが存在するかどうかを示します。 (上記リンク先より引用)


で、書いてみたのがこちら。

  //iniファイルから設定データを読込み
  Ini := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
  try
    //Settingsセクションの有無を調査
    if Ini.SectionExists('Settings') then
    begin
      str1:=Ini.ReadString('Settings', '文字列型_閾値', '180');
      str2:=Ini.ReadString('Settings', '文字列型_平滑化強度', '41');
      str3:=Ini.ReadString('Settings', '文字列型_判定係数', '3');
      //Python39-32フォルダの有無を調査
      if System.SysUtils.DirectoryExists('Python39-32') then
      begin
        str4:=Ini.ReadString('Settings', '文字列型_手法', '正規化相互相関');
      end else begin
        str4:=Ini.ReadString('Settings', '文字列型_手法', '差分相関');
      end;
      str5:=Ini.ReadString('Settings', '文字列型_選択肢の始まり', '1');
      //str5:=Ini.ReadString('Settings', '文字列型_左補正', '');
      //str6:=Ini.ReadString('Settings', '文字列型_右補正', '');
      //str7:=Ini.ReadString('Settings', '文字列型_上下補正', '');
      str8:=Ini.ReadString('Settings', '文字列型_判定領域', '70');
      str9:=Ini.ReadString('Settings', '文字列型_読上速度', '3');
      //追加
      CheckZahyo.Checked:=
        Ini.ReadBool('Settings', '論理値型_座標チェック', True);
      CheckVolMixer.Checked:=
        Ini.ReadBool('Settings', '論理値型_音量ミキサー表示', False);
      boolSpeed:=Ini.ReadBool('Settings2', '文字列型_速度', False);
    end else begin
      //デフォルトのパラメータを表示
      str1:='180';
      str2:='41';
      str3:='3';
      //Python39-32フォルダの有無を調査
      if System.SysUtils.DirectoryExists('Python39-32') then
      begin
        str4:='正規化相互相関';
      end else begin
        str4:='差分相関';
      end;
      str5:='1';
      str8:='70';
      str9:='3';
      //追加
      CheckZahyo.Checked:=True;
      CheckVolMixer.Checked:=False;
      boolSpeed:=False;
      //設定を保存するようユーザーに案内するためのフラグを設定
      P4D_Exist:=False;
    end;
  finally
    Ini.Free;
  end;

プログラムが冗長化しただけ・・・のような気もするし、「セクションがない」場合には勝手にそれを作成して保存してしまう手もあるんだけど・・・。デフォルト・パラメータはあくまでも僕の環境(マークシートの紙質、画像化する際に利用するスキャナー、読み取り解像度、その他いろいろ)でのベストな値であって、環境が異なれば当然調整しなければならない場合もあり得る。そう考えると、もし、ini ファイル内にセクションそのものが存在しなかった場合は、それをユーザーに案内してきちんとセクションとして保存してもらえる仕様にするのがいちばんいいと思えてきた。

そこで、上記のように、ユーザーへの案内(通知)の要/不要を判断するフラグを用意。さらに、ユーザーへの案内(通知)は、Formが表示されてからの方が、なんとなく安心感があるかな? って思ったので、もし、セクションがなかった場合は、Form が表示された直後に案内(通知)するようにプログラミング。

private
    { Private 宣言 }
    //Python4Delphiの有無を知るフラグ
    P4D_Exist:Boolean;

procedure TFormMSReader.CMShowingChanged(var Msg: TMessage);
var
  strMsg: string;
begin
  inherited; {通常の CMShowingChagenedをまず実行}
  if Visible then
  begin
    Update; {完全に描画}
    if not P4D_Exist then
    begin
      strMsg:='読み取りパラメータの設定が、デフォルト値となっています。'+#13#10+
      '必要に応じて読み取りパラメータの調整を行い、'+
      '「設定を保存」ボタンがアクティブな状態で保存してください。';
      Application.MessageBox(PChar(strMsg), PChar('情報'), MB_ICONINFORMATION);
    end;
  end;
end;

セクションそのものがない場合には、i マークのアイコン付きでメッセージボックスを表示する。

3.まとめ

今回、初めて知った SectionExists メソッド。使わなくてもエラーは発生しないのだけれど、このメソッドについて調べたことで、あらためて「プログラムの仕様」について考える大変良い機会を得ることができました。いつか先輩に言われた言葉が胸によみがえります。

「きちんと動くプログラムが、いいプログラムなんだ。」

きちんと動く・・・って、その意味は、僕が思っていた以上に、深いようです。

4.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

クリーン・インストール

【20240204追記】

この記事で述べた方法では、問題は解決できません!(再発します)
この記事で述べている問題の正しい解決方法は、下記リンク先の記事をご参照ください。


Delphi のF9(実行)押下時に、原因がわからないエラーが発生(デバッグ出力)することに気づいた。
アプリケーションは何の問題もなく起動し、期待通りに動作、表面上はエラーも一切出ないのだけれど、水面下で「何か良くないこと」が起こってる感じ。

エラーを抱えたままの開発環境が作成した exe はさすがにヒトには配れない。なので、なんとかするべくいろいろ調べたが、なんと!情報の『欠片』すら見つからない・・・。困りに困って、頼るべき最終手段、サポートセンターに相談すると、親切丁寧なアドバイスを複数いただき、その中でDelphi自身の再インストールも状況改善のための選択肢の一つと知る。

「なるほど!」、早速実行。

再インストールは順調に終了。(再インストール時の)エラーの発生は皆無。
で、VCLアプリケーションを新規に作成し、実行(F9)すると

デバッグ出力:
clientcore\windows\advcore\ctf\shellhandwriting\client\handwritingclient.cpp(287)\Msctf.dll!77A3FC28: (caller: 77A345FD) LogHr(3) tid(988) 8007007E 指定されたモジュールが見つかりません。
    Msg:[onecore\internal\sdk\inc\wil\opensource/wil/winrt.h(1686)\Msctf.dll!77A37442: (caller: 77A3F94D) Exception(1) tid(988) 8007007E 指定されたモジュールが見つかりません。
]

プロセス Project1.exe (6408)

状況は変わらず・・・。まったく同じエラーが出現・・・。
そうか・・・。原因はDelphiじゃない。

Windows なんだ・・・。

もくじ

1.handwritingclient.cppって何だ?
2.インストールメディアを作成
3.クリーン・インストール
4.登録回数の上限に達しました
5.エラーが消えた!
6.まとめ
7.お願いとお断り

1.handwritingclient.cppって何だ?

去年の今頃、僕は「手書きカタカナ文字認識機能」を自作の手書き答案採点補助プログラムに搭載できないかと考え、毎日夢中で認識率100%を実現できる学習モデル作りに取り組んでいた。

Pythonのスクリプトもたくさん書いたけど、それ以外に、いろんなアプリも試した。
もしかして、それがエラーの原因に?

embeddable Python を内部的に動かして手書きカタカナ文字をPCに認識させるため、Lobeで学習モデルを作成し、GUIはDelphiで作って自分的には(OK!)と思えるレベルを達成(できたんだけど認識率100%じゃなかったから公開はしなかった)、あの時、相当トライ&エラーを繰り返してPCをフリーズさせたり、いろんなライブラリを思い出せないくらいインストールしたりしたからなー。でも、基本的にSDカードに仕込んだWinPythonで実験したから、Cドライブには影響ないと思うんだけど・・・。

(後に読み書き速度を向上させるため、使わなくなったノートPCから取り外したSSDをMyPCに外付けして、そちらにWinPythonを入れて実験を継続。もちろん、動作速度はめちゃめちゃ速くなった!)

でも、LobeはCドライブにインストールして学習モデル作ったしなー。
もしかして、原因はコレかなー?

いずれにしても、Windowsのどこかに不具合があることは間違いない。handwritingclient.cpp がどこからやってきたC++のソースファイルなのか? さえわかれば、手の打ちようもあると思うんだけど、それも解明できない。

僕の力では、この不具合は直せない。・・・とすれば、残された方法は一つ。
リカバリーメディアを作成して、PCを工場出荷時の状態に戻す。

そう、クリーン・インストールだ。

2.インストールメディアを作成

幸いにして日付時刻は週末の早朝。急いでしなければならない仕事はない。今日の用事を強いてあげれば、バイクのエンジンを動かすこと、それから、イケナイ水を買いに行くことくらいだ。それは夕方、いっぺんにできる。

( そう言えば、このPCのリカバリー用インストールメディア、作ってなかった・・・ )

僕はずっとPanasonic製の Let’s Note を使っている。現在の使用機種は CF-QV だ。高価なマシンだけど、頑丈で壊れないし、すごく入力しやすいし、重さも動作もすこぶる軽快。あの世に持っていけるものを一つだけ選べと言われたら、僕は間違いなく、Delphi を入れた Let’s Note を選ぶ。

スタートボタン ⇨ Panasonic PCリカバリーディスク 作成ユーティリティを起動。

初めて拝んだリカバリーディスク作成ユーティリティの起動画面(引用)。

昔はPC購入直後に必ずリカバリーディスク作って大切に保管してたんだけど・・・

いつのまにか・・・


手近にDVD-R DL(2層)メディアが数枚あった。これを外付けドライブに挿入して、リカバリーディスクの作成を開始。

( 80分かぁ ちょっと長いなー )

1枚目のディスクのチェックの進み具合を示すプログレスバーが半分くらいまで進んだとき、予期しないエラーメッセージが・・・

『・・・メディアの作成に失敗しました・・・』

ちゃんと made in Japan って書いてあるディスク使ったのにー T_T

仕方ないから、別のディスクに入れ替えて、再チャレンジ。

ところが、またチェックの進み具合を示すプログレスバーが半分くらいまで進んだところで、

『・・・メディアの作成に失敗しました・・・』

なんでー!

さすがに三度目の正直を目指そうとは思いませんでした。

PCを乗せた机が振動した可能性(はないと思ったけど、いちおう)も考え、PCの置き場所を変更。

さらに、外付けドライブも、むかし使ってた別のちょっと古い機種に変更。

さらに、使用するメディアも DVD-R( Made in Vietnam )に変更。

今度は順調に進行。1時間半ほどかけて無事DVD-R4枚のリカバリー用ディスクが用意できた。あとは、重要なデータをバックアップして、リカバリーするだけだ。

これまでに数限りなく、データのバックアップでイタいめにあってきて、今の僕は深い階層には一切データを置かないようになった。もうトシだし、ほんとにバカだから、どこに、なにを置いたのか、すぐに忘れる・・・、メモしてあっても、そのメモの存在を忘れてしまう。だから、リンク先確認等で起動に多少時間がかかろうと、そんなのは一切無視。重要なデータは全部デスクトップにおいている。

・・・とは言っても、現在、僕にとって重要なデータは2種類しかない。一つは、Delphiで書いたプログラムのソースを保存してあるフォルダとその中のファイルたち。もう一つは、その時々で遭遇したプログラミングする上で解決しなければならなかった問題と、その解決方法を記録したNaNaTreeのファイルたちだ。これがPictureやDBなど、分野別に分類して21ファイルある。更新し続けてもう20年・・・。Delphiを続ける限り、このファイルが完成することはない。僕が死んだその日に、更新が永遠に止まるだけだ・・・。

僕は、必要に応じて、この21個のファイルを、とっかえひっかえ表示しながら、ほとんどコピペを繰り返すようなスタイルでDelphiのプログラムを書いて(?)いる。だから、この21個のファイルには、僕とDelphiのこれまでのすべてが詰まっている。そう、僕のいちばんの『たからもの』だ。

これをバックアップすればPC本体はいつでもリカバリーできる。そこで別の外付けSSDを接続して現在デスクトップにある重要なファイルとフォルダのバックアップを新規に作成。リカバリーに成功したら、SSDのバックアップからデータをPCのデスクトップに書き戻す。

以前はPythonで書いたスクリプトも保存してたけど、Pythonの方はDelphi以上にコピペの集合体だし、バックアップを探すより、Webにある情報を検索した方が便利なことに気づいたことと、何よりPythonはスクリプト本体より、そこから呼び出しているライブラリが膨大なファイルを含んでいるため、バックアップに時間と容量が必要だったり、かつ、ライブラリのフォルダ階層が深すぎてコピー時にエラーが起きたり、とにかくバックアップそれ自体が何かと面倒で、いつの間にか、僕はWinPythonを仕込んだSDカードのバックアップをとるのをやめてしまった。

3.クリーン・インストール

外付けDVDドライブにリカバリー用のディスクを入れて、PCを再起動。なんか起こるかと思ったら、普通にWindows11が起動してしまった。そうだ、BIOSの起動設定を変更するのを忘れてた!

再起動してF2を連打。BIOS画面が表示される。こちらもMy PCで拝むのは、もしかして初めてかも。外付けDVDドライブの起動を最上位に持ってきて設定を保存。再々起動。

今度は見たことのない画面が表示され、リカバリーが始まった。ひたすら1枚目のDVDディスクを読んでいるようだ。じっと見ていても仕方ないので、いずれ「2枚目のディスクを入れてください」みたいな表示が出ると見込んで、別のPCをいじって過ごす。リカバリー完了までのインターネット接続はメインで使用しているPCの前に使っていたB5サイズの Let’s Note があるので、こちらを久々に起動して必要な情報を取得する。ただ、このPC。もう長いこと、OSのアップデートを行っていない。それどころか、前回、いつ、電源をONにしたのか・・・それすら記憶にない。

( このPCもメンテナンスしなきゃ )

気がつくと、リカバリーしているPCの画面に2枚目のディスクを求めるメッセージが表示されている。2枚目を入れると、また延々とデータの読み込みが始まった。これを3枚目、4枚目と繰り返す。

4枚目のディスクを挿入後、なんだか安心して、しばらくまどろんでしまった・・・。目が覚めたら、リカバリーは無事完了。PCは工場出荷状態に戻った。昔は、ここからの設定がほんとにたいへんだったけど、今はWeb上にほとんどすべてのMy設定が記録されているので、本当にセットアップが簡単になった。何回か、IDとパスワードなどを入力してWindows11が正常に起動する状態まで戻せた。

リカバリーする前のWindows11のバージョンは 22H2 だった。前に使っていたPCでWindowsの更新情報を検索して、最新のWindows11は23H2であることを知る(2023年12月上旬現在)。どうせならWindowsも最新の状態にアップデートしたい。そう思ってWindowsの更新をいくつか入れてるうちにWindows Update に 23H2 へのアップデートのリンクが表示されるようになった。ラッキー☆

迷うことなく、23H2へのアップデートを実行。PCは無事、復活。それも最新のOSを身にまとって。

4.登録回数の上限に達しました

今回のリカバリーの最大の目的は、Delphiのデバッグ出力で表示されるエラーを解消すること。OSは無事リカバリーできたから、エラーも解消されるはず。次は Delphi 12.0 のインストールだ。

my.embarcadero.comへ行き、Web Install のリンクをクリック!


インストーラーを取得して、起動。必要事項を入力して先へ進もうとすると・・・

登録回数の上限に達しました

でたー T_T また、コレかぁ・・・ まだ 12.0 インストールした回数は新規で1回、PythonEngineの登録で実験的にインストールしなおして1回、今回のリカバリー後の再インストールで1回、合計3回だと思うんだけど・・・。

しかも日付は土曜日。サポートセンターは間違いなくお休みの日。もちろん、明日も。

( 制限を解除してもらえるのは月曜日かぁ・・・ )

とりあえず、サポートフォームを開いて、「製品登録(使用許諾)の上限更新依頼」だけしておこう。

カチャカチャ・・・

・・・というわけで、Delphiのインストールは翌週に持ち越し(持ち越され)て、その他の必要なソフトウェアを導入して週末の1日を過ごすことに。

( 今、どれくらい空き容量が残ってるんだろう? )

ふと気になって、Cドライブの空き容量を確認。なんと!驚いたことに全460GBあるうち、100GB程度しか使ってない。あと350GBも、空きが残ってる。

( 確か・・・ リカバリーする前は、残りが100GBくらいだったぞ。いったいナニが入ってたんだ? )

容量を食いつぶすデータといえば、その筆頭はもちろん動画。しかし、僕はバイク関連のごくわずかな動画しかPCに保存してない。アプリはそれなりに入れてたけど・・・250GB分もそれがあったとは到底思えない・・・。

そんなことを考えていたら、突然、デスクトップに見たことのあるフォルダが次々に『全自動で生成』されて行く現象が発生・・・。あっけにとられた感じで、しばし呆然とこれを見つめる・・・

( そうか、OneDriveのデスクトップとデフォルトで同期がONなんだ・・・ )

( 今、クラウドにあるOneDriveのデータは、いずれ、そのうちMyPCに・・・ )

( これまではOneDriveになんでも投げ込んでたから・・・ ちりも積もればナントカで・・・ )

うわー☆ 同期の設定OFFにしないと
またすぐ空き容量がなくなるー!!

慌てて画面右下の雲のアイコンを右クリックして設定を開き、同期とバックアップの「バックアップを管理」ボタンをクリックして、「このPCのフォルダーをバックアップする」の設定をすべてOFFにする。僕は、僕自身が選んだデータだけ、OneDriveに置いておきたいんだ。バックアップは自己責任で行うよ。Windowsに全部、面倒を見てもらいたくない。PCのストレージ容量が「無制限」で、OneDriveも保存容量に「制限がない」なら、全自動バックアップ大歓迎だけど・・・。


これまで気がつかなかったけど、空き容量が減ってたのは、たぶん、この設定も原因の一つだ・・・。PCをリカバリーして、今までぼんやりとしか見えてなかったものが初めて『はっきり・くっきり見えてきた』ような・・・気がする。

あとは月曜日を待って、Delphiをインストール。
F9押下時のエラーが消えれば(絶対消えるはずだけど)・・・

もっと『すっきり』するんだけどなー☆

5.エラーが消えた!

月曜日、「製品登録(使用許諾)の上限更新依頼」に対する返信があり、登録回数の上限が更新されたことを知る。Delphi 12.0 を再インストール。

Python4Delphiがない状態で確認。

新しくVCLプロジェクトを作成。保存して、F9(実行)押下。

デバッグ出力にエラーは出ない。

( やった。多分、もう、大丈夫だ )

いったん、Delphiを終了。

Delphiを再起動して、GetItパッケージマネージャからPython4Delphiをインストール。

既存のプロジェクト(マークシートリーダーのプロジェクト)を呼び出す。

F9(実行)押下。

エラーメッセージは表示されない!(P4DのインストールもOK!)

Delphiが直ったー☆☆☆

6.まとめ

エラーの根本的な原因がわからないまま、リカバリーという最も強引で、安全、確実な方法で問題を解決することになったが、目的外に実にいろんなことを経験できたリカバリー作業だった。

なぜ、DVD-R DLの書き込みに2回連続失敗したのか?

気になったので調べてみると、あちこちで同じような経験をした人がかなりいるようで、「片面2層」の DVD-9 でディスクの枚数を減らすより、ディスクの枚数は増えても、片面1層(約4.7GB)の DVD-5 を使用した方が書き込みエラーが発生する頻度は低いようだ。

メディアと機械の相性とか、いろんな問題がありそうだけれど、画質にこだわりつつ、ディスク1枚に動画を記録するのとは明らかに目的が異なる(今回のリカバリーディスク作成みたいに)単にデータが記録できればOKって場合は、片面1層のメディアを選んだ方がいい。DVD-R DLへの書き込みに失敗した本当の原因は、こちらもわからないままなんだけど、経験としてリカバリー用途のメディアには何を使ったら良いのかがとてもよくわかった。

それから、今まで「わかっているようで、実はわかっていなかった」OneDriveの使い方を本気で考えるイイきっかけにもなった。

今回のリカバリー前後で比較したCドライブの空き容量の違いに気づいたことで、自分にとっていちばん便利なOneDriveの使い方(=付き合い方)が見いだせたのはとてもラッキーだった。なんでもかんでもOneDriveに保存することは止めて、いつでも・どこでも参照したい必要最小限度のデータのみを保存し、その他の重要なデータは複数の外付けのSSDにバックアップを取る。そうすれば、僕にとって必要なSSDの容量は、150GB程度であることまで明らかにできた。今後、新しいPCの購入を考える際にも、この数字は役に立ちそう。

あと、今後、役立つことはまぁないと思うけど、Delpjiの登録回数の上限もなんとなくわかってしまった・・・。これを知るためだけにインストールとアンインストールを繰り返す人はまずいないだろうから、ある意味、これは貴重な情報と言えば言えるかもしれない。

僕はDelphiという開発環境と、Object Pascal というプログラミング言語が好きだし、その文化の存続を願うから、例えそれを高額と感じても正規の利用料を支払って、Delphiのブラッシュアップを続けてくれている Embarcadero Technologies さんの発展を支えたい。

何より、最大の目的だったDelphiのF9(実行)押下時のエラーを解消できた。これで安心してプログラムが書ける。それがいちばんうれしい。プログラムを書いていると、自分自身がよい方向に歩いているような気がしてくる。それが何よりもうれしくて、僕はプログラムを書くのかもしれない。

僕がこの世から消えた後も、それが動くことを願って。

7.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

RAD Studio 12.0にPython4Delphiをインストールする!(その2)

前回、Delphi 12.0 に Python4Delphi をインストールする記事を書いた。

次は、その過去記事へのリンク。


実は、もっと、イイ方法が・・・☆☆☆

今まで知らなかったのですが、ウェルカムページの右上にある「GetItパッケージマネージャ」からインストールすれば、さらにカンタンでした!!

【もくじ】

1.エラーが消えない!
2.GetItパッケージマネージャからP4Dをインストール
3.まとめ
4.お願いとお断り

1.エラーが消えない!

いつからそうなったのか、わからないのだけれど、Delphi 11.2 & 12.0 で F9(実行)するとデバッグ出力にエラーがいくつか表示されるようになった・・・。

デバッグ出力:
clientcore\windows\advcore\ctf\uim\tim.cpp(800)\Msctf.dll!7577215A: (caller: 7576D910) LogHr(1) tid(1ff8) 8007029C アサーション エラーが発生しました。

プロセス XXX.exe (5568)

デバッグ出力:
clientcore\windows\advcore\ctf\uim\tim.cpp(800)\Msctf.dll!7577215A: (caller: 7576D910) LogHr(2) tid(1ff8) 8007029C アサーション エラーが発生しました。

プロセス XXX.exe (5568)

デバッグ出力:
onecore\internal\sdk\inc\wil\opensource/wil/winrt.h(1686)\Msctf.dll!757A7442: (caller: 757AF94D) Exception(1) tid(1ff8) 8007007E 指定されたモジュールが見つかりません。

プロセス XXX.exe (5568)

デバッグ出力:
clientcore\windows\advcore\ctf\shellhandwriting\client\handwritingclient.cpp(287)\Msctf.dll!757AFC28: (caller: 757A45FD) LogHr(3) tid(1ff8) 8007007E 指定されたモジュールが見つかりません。
    Msg:[onecore\internal\sdk\inc\wil\opensource/wil/winrt.h(1686)\Msctf.dll!757A7442: (caller: 757AF94D) Exception(1) tid(1ff8) 8007007E 指定されたモジュールが見つかりません。
] 

プロセス XXX.exe (5568)

「handwritingclient.cpp」ってなんだろ? ・・・と、Google先生にお伺いをたてても、関連する情報は何一つ得られず、「Msctf.dll」の方は検索結果にいろんな情報が出てくることは出てきても、なんかみんな的外れな感じで、走召!困った。

どうにもならないので、サポートセンターに援けを乞う。

サポートセンターの担当者の方と何度かメールでのやりとりを行った結果、Delphi 11.2 & 12.0 をどちらもアンインストールして、12.0 のみを再インストールすることに。

アンインストール & 再インストール作業は何のエラーもなく進行。

で、結果から言うと、F9(実行)で「エラーはそのまま表示され、消えません」でした。

T_T

エラーは消えなかったのですが・・・

ふと、思いついて update の有無を確認しに行った GetItパッケージマネージャで「愛しのP4D」を発見!

( こんなところにあったんだ。何で今まで気づかなかったんだろう? )

そう思いながら「インストール」ボタンを「ポチ」っとクリック。

結果・・・

今までの大苦労はなんだったんだ・・・
そう思うくらい、あっけなく
P4Dがインストールされました!

2.GetItパッケージマネージャからP4Dをインストール

転んでもタダでは起きない・・・とはまさにこのこと? 以下、GetItパッケージマネージャからP4Dをインストールする方法です。

Delphi 12.0 を起動すると表示される(デフォルト設定)、ウェルカムページ右上の

 ↑
これをクリック。

すると、次の画面が表示されるので、オプションボタンを図のようにクリック。

カテゴリ「Python」のオプションボタンをクリックすると、そこにP4Dがある。


ちなみに、著者の方のお名前の部分をクリックすると、GitHub の Python4Delphi のページに飛びます。

GitHub の Python4Delphi のページ

https://github.com/pyscripter/python4delphi


続けて、「インストール」をクリック。

「すべて同意する」をクリック。


インストールはすぐ完了。

カンタンなこと、この上なし!
七匹のヘビを無事発見!

3.まとめ

Delphi に Python4Delphi をインストールするなら、GetItパッケージマネージャからインストールするのが「走召」カンタンで便利!

さて、Delphi の「消えないエラー」をどうしよう・・・


追記(20241005)

2024年2月4日に、この問題の解決策を見つけていました!

僕のPCは、上記リンク先の方法で、正常な状態に復旧できました。
(ここに追記するのを忘れてました。ごめんなさい!!)

4.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

VCLのMessageBox

Delphi 12.0を使い始めて、最初に気づいたことは、メッセージダイアログの異変だ。

( なんだか、シンプルになったなー )

Delphi 12.0 のメッセージダイアログ


そう。MessageDlgからアイコンが消えちゃった!

【もくじ】

1.Delphi 12.0 のメッセージダイアログ
2.MessageBoxがあった!
3.MessageBoxの使い方を学ぶ
4.MessageBoxの使用例
5.ダイアログからの戻り値
6.まとめ
7.お願いとお断り

1.Delphi 12.0 のメッセージダイアログ

今まで( Delphi 11.2 )なら・・・

i マークのアイコンがあるメッセージダイアログを表示するコード


実行すると・・・

表示されたメッセージダイアログ


System.UITypes を uses してないと・・・

「System.UITypesをusesしなさい」とアドバイスされる(これは 12.0 も同じ)。

Delphi 12.0 では・・・、System.UITypes は、もちろん uses して、

Delphi 12.0 でメッセージダイアログを表示


11.2 と同じコードを実行すると・・・

青地に白の i マークのアイコンが「ない」


それに、なんかエラーも。

僕には理解不能


( どうする? )

取り敢えず、Help でVCLのライブラリ・リファレンスを調べてみた。

キーワードを指定してEnterキーを押すと・・・


表示されたリファレンスの下の方に、次の一文を発見!

mtInformation じゃなくて、mtConfirmation だけど「Microsoftは、・・・削除してしまいました。」とある・・・


( これかー T_T )

mtInformation ではなく、mtConfirmation で試してみると・・・

引数を mtConfirmation に変更


実行すると・・・

アイコンは表示されないが、タイトルは「確認」に変わる


タイトルが「情報」から「確認」に変わるから、mtConfirmation は確かに効いてる。

「このアイコンがメッセージの具体的な種類を明確に表さない」って、んじゃもっと良いアイコンに変更するとか・・・、元々、アイコンはキャプションの補佐的役割を果たしているだけだから、それが「ない」よりは「あった」方が親切だと思うんだけれど・・・。

リファレンスによれば、第2引数に指定可能な値とその意味は次の通り。

Delphi 12.0 の Help の VCL のライブラリ・リファレンスを引用


リファレンスには「以前のダイアログ ボックスの概観を使用するには、Vcl.Dialogs ユニットの UseLatestCommonDialogs 変数を False に設定しなければなりません。」と但し書きがある。

以前の・・・って、XPの時代のことかな?・・・なんて思いながら、次のように指定して、

UseLatestCommonDialogs 変数を False に設定


実行すると・・・

さらにシンプルに・・・T_T


僕的には「シンプルじゃないのがよかった」ので、
とても「Simple is Best 」的な気持ちにはなれない!

あぁアイコンが欲しい!

もう、二度と・・・


きみに会えないかと思うと。

魂が折れそうなくらいの、さみしさがこみあげて・・・くる。

なんでだよー!

そう思いながら、ついでに mtError も試してみる。すると・・・

エラーのアイコンは表示される


「 × 」マークはMicrosoftさん的には、全世界共通「ダメです」って通じるってこと?


mtWarning もやってみた!

警告もアイコンは表示された


黄色の三角形に「i」が表示されたら、世界中の人が「警告」だって思うのかなー☆
Microsoftさん的には、「思う」ってことなんだろうなー。


ともあれ、これで確認できた。「情報」と「確認」がダメなんだ・・・。

でも、ない袖は振れない。

自分専用のダイアログを作るという、奥の手がないわけじゃないけど、それは最終手段。

MessageDlg のかわりになるものは・・・、ShowMessage 以外になんか、なかったっけ?

2.MessageBoxがあった!

ものごころついた時から・・・ってわけじゃないけど、僕はずっと MessageDlg関数を使ってきた。
ダイアログで使用する Font の大きさや色を変えたいなど、余程の理由がない限り、ユーザーに何かを伝えるいちばんの方法は、僕にとっては MessageDlg関数 で表示するメッセージだった。

それが ShowMessage ではない最大の理由は、テキストの他にアイコンも表示できるから。

だから、階層化テキストエディターの NaNaTree に書き溜めてきた、さまざまなメッセージの表示方法に関する覚書も、そのほとんどが MessageDlg関数 についてのもので、これにかわる代替手段など、これまで僕は考えたこともなかった。

( バージョンアップとかすると、毎回、いろんな落とし穴があるけど・・・ )

( 今回のも、これまで普通に使ってきたダイアログの仕様変更だもんなー )

( すーぱー困るけど、元に戻してくれるわけないし・・・ )

( なんか、代替手段、なかったっけ? )

そう思いながら、NaNaTree に保存した「メッセージ」に関する記録を下のほうへスクロールしてみる。

すると・・・

MessageBoxなる文字が・・・


あんまり使ったことのない、未整理の項目が下の方にあって、その中にMessageBoxという文字を発見。

( ほぼ使った記憶がないけれど、そんな関数もそう言えばあった・・・ )

さっそく、Helpを読んで、書き方を確認し、次のコードを実行してみた。

MessageBox関数を試す


はたして、アイコンは表示されるか?

祈るような気持ちで実行すると・・・

アイコン付きのダイアログが表示された!

何にも(追加で) uses しなくてイイし、いきなり書いて、すぐ動く!

コレだ! コレ!!

今度からコレで行こう☆

自分でもあきれるくらいの変わり身の早さ・・・

MessageDlg が泣いてるぞ。

愛してたんじゃなかったのかい・・・?

3.MessageBoxの使い方を学ぶ

VCL ライブラリの Help を読んでわかったことは、「MessageBox は、Windows API MessageBox 関数をカプセル化したものである」ということ。

このカプセル化で「具体的に何がどうなったか」と言うと、Windows.MessageBox とした場合には必要であったウィンドウ ハンドル パラメータが欠けていても、自動的に補完される、つまり、所有者ウィンドウへのハンドルは引数内で指定しなくてもよいということ。

ありがたき しあわせ。

ちなみに、Win32APIのリファレンス(C++)では、次のようになっているが、

int MessageBox(
  [in, optional] HWND    hWnd,
  [in, optional] LPCTSTR lpText,
  [in, optional] LPCTSTR lpCaption,
  [in]           UINT    uType
);

Delphi の Application.MessageBox のリファレンスでは、次のように

function MessageBox(const Text, Caption: PChar; Flags: Longint = MB_OK): Integer;

所有者ウィンドウへのハンドルが確かに省略されている。

実際のコードで、Windows.MessageBox とした場合には・・・

procedure TForm1.Button4Click(Sender: TObject);
begin
  Winapi.Windows.MessageBox(Handle, PChar('Do you know Delphi?'), PChar('情報'), MB_OK or MB_ICONINFORMATION);
end;

だったのが、第一引数のHandle は必要なくなり、( OK ボタンのみの表示でよければ)MB_OK も省略できるようなので、次のように

procedure TForm1.Button3Click(Sender: TObject);
begin
  Application.MessageBox(PChar('Do you know Delphi?'), PChar('情報'), MB_ICONINFORMATION);
end;

・・・とずい分、短くなる。それどころか、PChar型への型変換も省略可能なようで・・・

procedure TForm1.Button5Click(Sender: TObject);
begin
  Application.MessageBox('Do you know Delphi?', '情報', MB_ICONINFORMATION);
end;

型変換も内部で自動的に処理してくれるようだ(このまま実行可能。エラーも、警告も、ヒントも表示されない)。

Good! Gooder! Goodest!

これからは MessageBox で行こう!

4.MessageBoxの使用例

「 i 」マークのアイコンで、OK ボタンのみ表示する場合は、

Application.MessageBox(PChar('メッセージ'), PChar('情報'), MB_ICONINFORMATION);


警告マーク(黄)

Application.MessageBox(PChar('メッセージ'), PChar('警告'), MB_ICONWARNING);


禁止・エラー・停止マーク(赤地に白)

× の意味は、第3引数にSTOPとあるから「停止」が正しいのかな?

Application.MessageBox(PChar('メッセージ'), PChar('禁止'), MB_ICONSTOP);
Application.MessageBox(PChar('メッセージ'), PChar('エラー'), MB_ICONSTOP);


?マーク(青地に白)

Application.MessageBox(PChar('メッセージ'), PChar('質問'), MB_ICONQUESTION);

文字列型の変数を用意して、

var
  strMsg:string;
begin
  strMsg:='メッセージ';
  Application.MessageBox(PChar(strMsg), PChar('情報'), MB_ICONINFORMATION);
end;


別の文字列型変数をさらに代入したり、また、改行を含む表示も、

procedure TForm1.Button2Click(Sender: TObject);
var
  strMsg, strPath:string;
begin
  strPath:='C:\abc\def';
  strMsg:='出力先は次の場所です。' + #13#10 + #13#10 + strPath;
  Application.MessageBox(PChar(strMsg), PChar('情報'), MB_ICONINFORMATION);
end;

複数のボタンを表示。例えば、「はい」・「いいえ」の二択なら、

procedure TForm1.Button3Click(Sender: TObject);
begin
  //Information
  if Application.MessageBox(PChar('Do you know Delphi?'), PChar('情報'), MB_YESNO or MB_ICONINFORMATION) = mrYes then
  begin
    //[はい]が選ばれた時
    Application.MessageBox(PChar('Gooooooooooooood!'), PChar('情報'), MB_ICONINFORMATION);
  end else begin
    //[いいえ]が選ばれた時
    Application.MessageBox(PChar('No!'), PChar('情報'), MB_ICONINFORMATION);
  end;
end;
二択だから「キャンセルはない」
(閉じるボタンは自動的に無効になる)

ユーザーに「キャンセル」も許可するなら、

procedure TForm1.Button4Click(Sender: TObject);
var
  StrMsg: String;
  intRet: Integer;
begin
  StrMsg := 'Do you know Delphi?';
  intRet := Application.MessageBox(PChar(StrMsg), PChar('情報'),
                         MB_YESNOCANCEL or MB_ICONQUESTION);
  if intRet = mrYes then begin
    //[はい]を選択した時の処理

  end else
  if intRet = mrNo then begin
    //[いいえ]を選択した時の処理

  end else
  if intRet = mrCancel then begin
    //[キャンセル]を選択した時の処理
    Application.MessageBox(PChar('ユーザーによる処理のキャンセル'), PChar('情報'), MB_ICONINFORMATION);
  end;
end;
閉じるボタンは自動的に有効化されている


ちなみに「キャンセル」ボタンではなく、ダイアログ右上の「閉じる」ボタンをクリックすると・・・

閉じるボタンをクリックした場合は、キャンセル扱いになるようだ。

デフォルトでフォーカスを与えるボタンの指定方法は、第3引数の中で MB_DEFBUTTON3 のように、MB_DEFBUTTON の後ろにフォーカスを与えるボタンの番号を付けて指定する。番号はダイアログに表示するボタンの左から順番に1、2、3、4となるようだ。

第3引数に MB_YESNOCANCEL or MB_DEFBUTTON3 or MB_ICONQUESTION を指定すると、

左から三つめの「キャンセル」ボタンにフォーカスされた状態でダイアログが表示される


数字を付けない MB_DEFBUTTON や、数字を付けても MB_DEFBUTTON0(ゼロ)や MB_DEFBUTTON5 は未定義の識別子エラーになることから、指定可能なボタンの数は1~4の範囲内と決まっているようだ。

MB_DEFBUTTON5 は未定義の識別子エラーになる


「はい・いいえ・キャンセル」の三つボタンを表示する設定で MB_DEFBUTTON4 を指定しても未定義の識別子エラーにはならないし、実行時コンパイラはヒントも警告も表示しないが、フォーカスは最も左のボタンに当たるようだ。

intRet := Application.MessageBox(PChar(StrMsg), PChar('情報'),
                         MB_YESNOCANCEL or MB_DEFBUTTON4 or MB_ICONQUESTION);
MB_DEFBUTTON4 を指定してもエラーにはならない。
MB_DEFBUTTON4 を指定したにもかかわらず、
4つめのボタンがない場合には、最も左のボタンにフォーカスされる。

ってか、四つ目のボタンでナニ?

想像したけど、ちょっと思いつかない。表示できるボタンの種類を調べてみた。

MessageBox に表示できるボタンの種類は全部で七つあるようで、Flags パラメータで指定可能な値は、次の通り(Delphi 12.0 の VCL リファレンスより引用)

意味
MB_ABORTRETRYIGNORE メッセージ ボックスには、次の 3 つのボタンが配置されます: 中止、再試行、無視。
MB_OK メッセージ ボックスには、次のボタンが配置されます: OK。 これがデフォルトの設定です。
MB_OKCANCEL メッセージ ボックスには、次の 2 つのボタンが配置されます: OK、キャンセル。
MB_RETRYCANCEL メッセージ ボックスには、次の 2 つのボタンが配置されます: 再試行、キャンセル。
MB_YESNO メッセージ ボックスには、次の 2 つのボタンが配置されます: はい、いいえ。
MB_YESNOCANCEL メッセージ ボックスには、次の 3 つのボタンが配置されます: はい、いいえ、キャンセル。
Flags パラメータで指定可能な値

リファレンスには、さらに「これらの値は、希望する効果を得るため、組み合わせて使うこともできます。」とある。組み合わせるって、どういうこと?

ちなみに、MB_OK と MB_RETRYCANCEL を組み合わせてみると・・・

procedure TForm1.Button5Click(Sender: TObject);
begin
  Application.MessageBox('Do you know Delphi?', '情報', MB_OK or MB_RETRYCANCEL or MB_ICONINFORMATION);
end;

OK と 再試行、キャンセル 三つのボタンが表示されるのかなー? って思ったけど・・・

OK ボタンは表示されませんでした!


これは MB_OK と MB_ABORTRETRYIGNORE を組み合わせても同じで、MB_OK はやはり表示されない。MB_DEFBUTTON4 の役割は何なんだろう???

Web上の資料を調べてみた!

MessageBox 関数 (winuser.h)

https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-messagebox

上記リンク先の説明によれば MB_HELP もあるらしい。

Application.MessageBox('Do you know Delphi?', '情報', MB_OK or MB_HELP or MB_ICONINFORMATION);

実行してみると・・・

OK とヘルプは共存できるようだ

ただ、ヘルプをクリックしても、なんにも起きなかったが・・・
(処理を書いてないんだから、当然と言えば、当然)

しかし、戻り値の中に「ヘルプ」に相当する値がない・・・ような気が。上記の資料には「ユーザーが [ヘルプ ] ボタンをクリックするか F1 キーを押すと、 WM_HELPメッセージが 所有者に送信されます。」とあったけど、これ以上、追いかけると深みにはまりそうな気がするので、ヘルプに関する勉強は後日それが必要となった時にあらためてすることにして、ここではお茶を濁す。

結局、MB_DEFBUTTON4 の果たす役割はわからずじまい。残念!

それから、MessageBox関数には「すべてはい・すべていいえ」のボタンがないようだ。これもまぁ、それが必要になった時、考えることにする。僕が書くプログラムでそれが必要になることは、まず、ないだろう・・・。

あとわかったことは、「Text パラメータの値はメッセージで、必要なら 255 文字以上になっても構いません。 長文メッセージは、メッセージ ボックスでは自動的に改行されます。」ふむふむ、なるほど。

さらに「Caption パラメータの値はキャプションで、ダイアログボックスのタイトル バー上に表示されます。 Captions は 225 文字より長く指定できますが、改行されません。長文キャプションの場合、メッセージ ボックスの幅が広げられます。」とのこと。

5.ダイアログからの戻り値

ダイアログからの戻り値は、次の通り(Delphi 12.0 の VCL リファレンスより引用)

定数意味
mrNone0 なしユーザーが終了する前にデフォルト値として使用される
mrOkidOK ユーザーが[OK]ボタンで終了した
mrCancelidCancelユーザーが[キャンセル]ボタンで終了した
mrAbortidAbortユーザーが[中止]ボタンで終了した
mrRetryidRetryユーザーが[再試行]ボタンで終了した
mrIgnoreidIgnoreユーザーが[無視]ボタンで終了した
mrYesidYesユーザーが[はい]ボタンで終了した
mrNoidNoユーザーが[いいえ]ボタンで終了した
ダイアログからの戻り値

6.まとめ

プログラムからユーザーに対してメッセージを表示する方法は、他にもいろいろあるみたいだけれど、MessageBox関数最大の強みは「すべての OS で利用可能」なこと。

OK と、はい・いいえ、キャンセル の各ボタンが利用できれば十分というのであれば、アイコンが表示されなくなった MessageDlg関数のかわりに MessageBox関数が使える。もちろん、ダイアログにアイコンも表示される。

ただ、これまでに書いてきたプログラムを Delphi 12.0 で修正・更新する際にはMessageDlg関数を、MessageBox関数に変更するという、地道な作業が待ってることだけがちょっと気になるが。

まっ ヒマだから、いいか!

7.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。