計算機関係の雑多なこと
計算工学は専門外というか全くもって門外漢なのですが、時には必要に迫られて勉強せざるを得なくなることが生じます。そうこうしているうちに、此方方面の興味もときどき持つようになりました。見知ったことの内、いくばくかでも役に立ちそうなネタをメモ書きしておくために、このコーナーを再開することにしました。
MS-Windows上での疎行列計算
SuperLU
Eigen
Lis
MUMPS
Reference BLAS
Windows コマンドプロンプト・バッチファイル
条件付きカスケード実行
&& を使う。errorlevel が 0 なら続くものが実行され、非 0 ならそこで終了する。なお、& の場合は常に続くものが実行される。
無限ループ
for /L %i in (0,0,0) do waveflt2 foobar.wav waveout
応用 Wi-Fi の電波強度表示(コマンドプロンプト)(注:ここの find は MS-DOS/Windows のそれである。unix 系ユーティリティーのそれではない。)
for /L %1 in (0,0,0) do (netsh wlan show interface | findstr "シグナル Mbps" & timeout 10)
バッチファイル内の記述では %i → %%i
https://jj-blues.com/cms/wantto-useloop/
http://d.hatena.ne.jp/simply-k/20100821/1282490379
ファイルを比較するバッチファイルの便利なもの
fc ないし diff を起動するとき、ファイルの数が足りなければ、尋ねるようにする。尋ねられた状態では、ファイル名をキーボードで打つか、エクスプローラーからファイルをドラッグ&ドロップすると反映される。
echo off
if "%1" == "" goto QueryAB
if "%2" == "" goto QueryB
diff -u -w %1 %2
goto end
:QueryAB
set /P STR_FILEA="比較対象のファイル名を指定してください:"
set /P STR_FILEB="もう1個のファイル名を指定してください:"
diff -u -w %STR_FILEA% %STR_FILEB%
goto end
:QueryB
set /P STR_FILEB="もう1個のファイル名を指定してください:"
diff -u -w %1 %STR_FILEB%
:end
pause
ダウンロード fdiff.bat.zip (diff, diff -u -w, fc, fc /b のセットで、上記より少し改良してある)
IF でかっこを使う(ブロック形式)と、なぜか動かないので(なんで?)、goto で済ませた。
その他どうでもいいことのうち、自分でやったこと
MinGW GCC-3.4.5を今風にインストールする [2015/09/23]
mingwrt-3.21.1をMinGW GCC-3.4.5でビルドする(お試し) [2015/09/22]
GCC-3.4.5 付属の libstdc++-v3 にはいろいろとバグがあるようです。GCC-3.4.6(バイナリは配布されていない)のソースでもこの辺りは変わっていないらしい。
起動元がコマンドプロンプトかGUIかを判定する [2016/01/10]
gfortranのバグ-IF文中の論理演算子の処理系依存挙動 [2016/02/11]
(ページなし)Visual Basic 6.0並びにVBAにはC言語の &&
や ||
に相当する論理演算子(ショートサーキット演算子)が無く、全部が常に評価(実行)されてしまう。IF文のネストで代用するしかない。一方、VB.NETなら AndAlso
と OrElse
がある。
f2c の tips - CYCLE文・EXIT文に対応させるプリプロセッサと len_trim 関数の代替品 [2018/04/15]
f2c において len_trim を組み込み関数扱いにするための改造 [2022/07/02]
libf2c のバグ [2016/09/10]
関連:ANSI実装において isspace(int c)
など ctype.h にある「is系関数」に8bit文字(日本語文字列のほか、ISO-8859-1なども該当する)を食わせる可能性があるときは isspace((unsigned char)*wp)
としないと、暗黙の型変換でchar型をint型に変換するときの符号の扱いのために潜在バグになる。
これはリリースビルトでは発見が非常に困難で訳の分からない挙動につながるが、デバッグビルドをVS IDE上で実行すると ASSERT が発生してわかる場合がある。VS IDE上で実行しにくい外部利用DLL等を作成する場合は細心の注意を払う必要がある。
f2c.h はマクロ abs
, max
, min
を定義しているので C++ STL や std::valarray などとの衝突に注意。これは Windows.h における NOMINMAX
と類似の問題。
ANSI実装における _splitpath()
/ _splitpath_s()
の罠 [2017/01/21]
文字列リテラルへの _T 付加置換 (ANSI/Unicode)
Windows ini file と ANSI/UNICODE ポータビリティー [2021/03/07]
C++ STL map の要素を erase するときは範囲 for 文を使ってはいけない(イテレータが寿命切れになる場合がある)。また、map に対しては remove_if() は使えない。こうする(cpprefjp) 。
MS-VC++ は fopen()
について warning C4996 セキュリティ云々という文句を言ってくるが、 fopen()
に限って言えばその実態は、ファイルをロックしないということと、書き込みモードではファイルが存在する場合には強制上書きをせず中断するという選択肢がないということだけである。strcpy()
等のバッファオーバーラン・特権昇格などの関数とは問題の性質が違う。もちろん、読み書き中のファイルに別の内容を差し込んだり内容を消滅させるという状況を作ってファイルポインタを破損させシステムを破壊工作することはできるが、そもそもそういう入力の正当性チェックは別途でやるべきでしょう、という気がする。それに C++ の iostream/fstream はそんなファイルロック機構など持ってないぞ。お節介というか、片手落ちもいいところ。 そうであれば、その C4996 エラー(警告)メッセージで推奨すべきなのは C99 と微妙に非互換な実装になっている fopen_s()
ではなく、第3引数以外 fopen()
と同一仕様である _fsopen()
なんじゃないか?
なお、自分のソースコード記述の腕を過信しつつ fopen()
を継続利用するなら次の方法がある。
fopen()
の直前の行に #pragma warning(suppress:4996)
を書く。
fopen()
の前後を #pragma warning(push-disable:4996)
と #pragma warning(pop)
で囲む。
その場所に限って明示的に警告を外すというスタンスである。
一方、ソースコード中への #define _CRT_SECURE_NO_WARNINGS
記述、あるいはコマンドラインコンパイラオプション /wd4996
追加によるグローバルな指定は、本当にまずい関数も通してしまう乱暴なやりかたであり、使用にはきっちり吟味のうえ細心の注意が必要な気がする。
もちろん、どちらにしても、そういうことをしていることは、ソースコードや Makefile だけでなく、どこか別のドキュメントにも記録をしておくべきであろう。自分だけがプログラミングしている間はよくても、他の初心者にソースコードを渡すときに、こういう「使い倒し」は案外と伝わりにくかったりする。
size_t
と int
の変換における潜在バグの防止用関数
…例えば int i2=0; int i1=1; putchar(lpszString[i2-i1]);
すなわち (size_t)(-1)
のように、単なる暗黙静的キャストではとんでもない値に変換されてしまうケースへの備え。(std::string::npos
の定義のように意図的な場合は別ですよ)
CとFORTRANとの混合言語プログラミング(FORTRAN には size_t はおろか unsigned の概念自体がない)や、Windows での 64bit ビルド (LLP64) の warning C4267 の正当な解決に役立つ。
まずは純粋Cで、黙って変換可能範囲内に丸め込むバージョン。よほどのことがない限りこれで十分でしょう。
static size_t int_to_size_t(const int n){
return (size_t)(n > 0 ? n : 0);
}
static int size_t_to_int(const size_t n){
return (int)(n < INT_MAX-1 ? n : INT_MAX-1);
}
C++ならば、変換しきれない場合に例外を投げてもよいかもしれない。純粋Cならば代わりに abort()
でしょうか。
static size_t int_to_size_t(const int n){
if(n < 0){
throw range_error("conversion failure negative int to size_t");
}
return (size_t)n;
}
static int size_t_to_int(const size_t n){
if(n > INT_MAX-1){
throw range_error("conversion failure too large size_t to int");
}
return (int)n;
}
Visual Studio 2013 C++コマンドラインコンパイラ(cl.exe) リンカ オプションの与え方:
cl /MD /LD ExcelDLLTest.cpp user32.lib /link /DEF:ExcelDLLTest.def
C++11の正規表現検索 regex_search() の微妙な挙動(regex_match() も同じ):
検索対象文字列には関数を抜けた後も寿命が保たれる文字列が必要であり、一時オブジェクト文字列を与えてはいけない。
これはOK
smatch result;
string str = " 10: 04:01.2345 ,teststring"
regex rex = regex("^\\s*(\\d+)\\s*:\\s*(\\d+)\\s*:\\s*(\\d+\\.?\\d*)\\s*,\\s*([^\\s]+.*)");
if(! regex_search(str, result, rex)){
cout << "error [" << str << "]" << endl;
return 1;
}
for (auto x : result) {
cout << "[<" << x << ">]" << ',';
}
これはダメ。MS-VC++2013ではコンパイラ警告は一切出ないが、落ちたり、落ちないまでも結果がおかしくなったりする。C++14ではコンパイルエラーになるとのこと。
smatch result;
string str = " 10: 04:01.2345 ,teststring"
regex rex = regex("^\\s*(\\d+)\\s*:\\s*(\\d+)\\s*:\\s*(\\d+\\.?\\d*)\\s*,\\s*([^\\s]+.*)");
if(! regex_search(string(str) , result, rex)){
cout << "error [" << str << "]" << endl;
return 1;
}
for (auto x : result) {
cout << "[<" << x << ">]" << ',';
}
これもダメ。
smatch result;
string str = " 10: 04:01.2345 ,teststring"
regex rex = regex("^\\s*(\\d+)\\s*:\\s*(\\d+)\\s*:\\s*(\\d+\\.?\\d*)\\s*,\\s*([^\\s]+.*)");
if(! regex_search((str + "abcde") , result, rex)){
cout << "error [" << str << "]" << endl;
return 1;
}
for (auto x : result) {
cout << "[<" << x << ">]" << ',';
}
なんでだろ??
→smatch という typedef はマッチ要素を指す一種のイテレータである のだそうだ。 成程、result はメモリ領域が確保された実体文字列ではなく、各要素を指すポインタの配列と言い換えられる。regex_search() に一時オブジェクトを渡すと、regex_search() が終わった時点でその一時オブジェクトの寿命は尽きるから、result の各要素が何を指すかは未定義になってしまう。これでは何が起きても文句は言えない。納得。
→事柄はcpprefjpにちゃんと載っていました。ただしMS-VC++では対応バージョンが不明 とある。この実験から、cpprefjpでいうところの (4) はVC2013では =deleted されていないということがわかった。C++11の範囲内だと引数の型が暗黙型変換で合致するものがあるので、コンパイラ警告は一切出ないのだ。なるほどなあ。
Excel 2010 64bitにおいて、Excelブックと同じフォルダに置いているはずの自作DLLの呼び出しが「[実行時エラー'53'] ファイルが見つかりません: 自作DLLファイル名.dll」となって困った。
VBAにおけるカレントディレクトリ変更は既に実施してある状態にて。(ChDrive ActiveWorkbook.Path
, ChDir ActiveWorkbook.Path)
ネットを見ても、Declare Function ~ においてDLL名を絶対パス指定する以外に手頃な解決法がないらしいが、環境変数でPATHに「.」(ピリオド1個だけ)を追加したら、あっさり動くようになってしまった。ただし、環境変数でPATHに「.」を追加するということは、結局、セキュリティー対策としての SetDllDirectory("") の呼び出しをほとんど無力化することに他ならないという問題がある。
当該DLLをPATHを通してあるフォルダにコピーしてもよい。
その場合であって、その当該DLLからさらに別の下位DLLを呼び出している場合、その別の下位DLLをコピーする必要があるか否かは、その当該DLLにおいてセキュリティー対策として SetDllDirectory("") を呼び出してあるか否かに依存し、やっていない場合はその別の下位DLLをExcel VBAにおけるカレントディレクトリに置いてあってもよいようだ。
カレントディレクトリにあるDLLを読み込み許可するとVBAからWinAPIを呼び出すようなケースでWindowsシステム標準DLLと同名の悪意を持ったDLLがカレントディレクトリに仕込まれた場合に対して脆弱性になるのでサーチパスから外すという対処の影響、なのかなあ。https://www.jpcert.or.jp/sc-magazine/codezine02-8.html なんだか本末転倒だが、これはこれでしょうがない。
_spawnvpe() とか CreateProcess とかを使ってExcelのこういう用途に対してだけPATHを上書きした環境で起動するランチャーを作るとか?
こんなのがあった。https://qiita.com/mmYYmmdd/items/fc1d3cce6a39771c0f36 すげー! この例において p = AddDllDirectory(StrConv(ActiveWorkbook.Path, vbUnicode))
とすることで解決。
[2018/02/03]
ファイルのクローズの扱いにおける C++ iostream の挙動
既に閉じられたストリームについてメンバ関数 close() を実行すると、エラービットが立ってしまう。具体的には、fail() が真を返す。また、既にエラービットが立っている状態で exceptions() で例外の設定を行うとその瞬間に例外が投げられる(エッジトリガーではなくレベルトリガーである)。
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main(int argc, char *argv[])
{
fstream ofs;
ofs.close();
cout << "ofs.good()" << ofs.good() << endl;
cout << "ofs.eof()" << ofs.eof() << endl;
cout << "ofs.fail()" << ofs.fail() << endl;
cout << "ofs.bad()" << ofs.bad() << endl;
cout << "ofs.is_open()" << ofs.is_open() << endl;
ofs.open("abcde.in",std::ios_base::out);
cout << "ofs.good()" << ofs.good() << endl;
cout << "ofs.eof()" << ofs.eof() << endl;
cout << "ofs.fail()" << ofs.fail() << endl;
cout << "ofs.bad()" << ofs.bad() << endl;
cout << "ofs.is_open()" << ofs.is_open() << endl;
ofs.close();
cout << "ofs.good()" << ofs.good() << endl;
cout << "ofs.eof()" << ofs.eof() << endl;
cout << "ofs.fail()" << ofs.fail() << endl;
cout << "ofs.bad()" << ofs.bad() << endl;
cout << "ofs.is_open()" << ofs.is_open() << endl;
ofs.close();
cout << "ofs.good()" << ofs.good() << endl;
cout << "ofs.eof()" << ofs.eof() << endl;
cout << "ofs.fail()" << ofs.fail() << endl;
cout << "ofs.bad()" << ofs.bad() << endl;
cout << "ofs.is_open()" << ofs.is_open() << endl;
try{
ofs.exceptions(std::ios_base::failbit | std::ios_base::badbit);
ofs.close();
cout << "ofs.good()" << ofs.good() << endl;
cout << "ofs.eof()" << ofs.eof() << endl;
cout << "ofs.fail()" << ofs.fail() << endl;
cout << "ofs.bad()" << ofs.bad() << endl;
cout << "ofs.is_open()" << ofs.is_open() << endl;
ofs << "a" << endl;
}
catch(exception& e){
cout << "err" << e.what() << endl;
}
catch(...){
cout << "err" << endl;
}
return 0;
}
VC2013 付属のヘッダのバグ。/Zc:strictStrings があると、デバッグモードでは次のソースファイル(C++標準ヘッダを取り込んだだけ)が error C2665 となりコンパイルできない。2015以降では直っている。ここ に書いてあるのだけど、ふつうこんな Note を見つけられるかいな。
#include <iostream>
#include <limits>
#include <string>
using namespace std;
Win32API GetOpenFileName と GetSaveFileName の初期ディレクトリの設定が反映されない件(とある用途で強制的に初期フォルダを指定したいが、プログラムを再起動しても最後に開いたフォルダを覚えてしまっていてそれが初期フォルダになってしまう)。よくよく調べたら ここ(Microsoft Docs / Windows / Dialog Boxes / Commdlg.h / OPENFILENAMEA structure) の lpstrInitialDir の項目に Windows 7 および以降では挙動が違うと書いてあるのをやっと見つけた。この記載を見つけるのは至難? それとも常識? いずれにせよ、ネット上のいくつかの(今となっては古いが検索で上位に出てくる)例題に騙されてしまっていた。
MS-Word の相互参照機能が実際にどんな文字列を表しているかの一覧をメッセージボックスに出す VBA マクロ [2021/12/29]
文章中に相互参照で章番号などを引用しているものがいつのまにか「0」になってしまうお困りごとがあったので。なお 同じ事象の報告例は他にもある 。自分の場合は、ある章・節の見出しに関する相互参照を設定した後、ページ内容記述の都合で章・節の見出しを改ページで次のページに送る場合に「0」になってしまう現象が発生している。修正方法は現状、相互参照の設定をやり直すしかないようです。明らかに Word のバグだと思います。
実装には Stack Overflow のこの記事 が大変参考になりました。ほとんどそのまま流用しています。
Sub ExamineWordCrossReferenceFields()
' based on https://stackoverflow.com/questions/24951526/get-all-cross-references-in-word-with-vba
Dim objDoc As Document
Dim objFld As Field
Dim i As Long
Dim sresult As String
Set objDoc = ActiveDocument
' Loop through fields in the ActiveDocument
For Each objFld In objDoc.Fields
' If the field is a cross-ref, do something to it.
If objFld.Type = wdFieldRef Then
sresult = sresult + objFld.Code + ",[" + objFld.Result + "]" + vbCrLf
objFld.DoClick
MsgBox "check it!"
End If
Next objFld
MsgBox sresult
End Sub
使い勝手をもう少し便利にするとこんな感じ
Sub ExamineWordCrossReferenceFields()
' based on https://stackoverflow.com/questions/24951526/get-all-cross-references-in-word-with-vba
Dim objDoc As Document
Dim objFld As Field
Dim i As Long
Dim sresult As String
Set objDoc = ActiveDocument
' Loop through fields in the ActiveDocument
For Each objFld In objDoc.Fields
' If the field is a cross-ref, do something to it.
If objFld.Type = wdFieldRef Then
sresult = sresult + objFld.Code + "=【" + objFld.Result + "】" + vbCrLf
End If
Next objFld
If "" = sresult Then
MsgBox "相互参照はありません。", vbOKOnly Or vbInformation, ActiveDocument.FullName
Exit Sub
End If
sresult = "以下の一覧を個別確認しますか?" + vbCrLf + sresult
If vbCancel = MsgBox(sresult, vbOKCancel Or vbQuestion, ActiveDocument.FullName) Then
Exit Sub
End If
' Loop through fields in the ActiveDocument
For Each objFld In objDoc.Fields
' If the field is a cross-ref, do something to it.
If objFld.Type = wdFieldRef Then
objFld.DoClick
Application.ScreenRefresh ' 表示倍率によりレンダリングが遅れるようなので
objFld.DoClick ' もう一度そこへ飛ぶ
If vbCancel = MsgBox( _
objFld.Code & "=【" & objFld.Result & "】", _
vbOKCancel, _
ActiveDocument.FullName _
) Then
Exit For
End If
End If
Next objFld
End Sub
matplotlib の日本語フォント対応(MS-Windows の場合) [2022/01/23]
https://apc.hatenablog.jp/entry/2019/04/21/131855 (APC的雑記 - はてなブログ) にある方法が使えるが、游明朝の例しか上がってないので、いくつか試してみました。
要はフォントの MS-Windows 内部名がわかれば、それを名指しすることはおおむね可能のようです。
ちなみに、本家:https://matplotlib.org/stable/gallery/text_labels_and_annotations/font_family_rc_sgskip.html
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = 'Arial' # 欧文専用(日本語は化ける)
plt.rcParams['font.sans-serif'] = 'Yu Mincho' # 游明朝
plt.rcParams['font.sans-serif'] = 'MS Gothic' # MSゴシック:これだけで使用可能です。ローカルコピーやリネームする必要は無いようです。ただし等幅なので、論文を書くときとかの見栄えは良くありません。
# plt.rcParams['font.sans-serif'] = 'MS PGothic' # だめです、matplotlib の ttc 対応が完全ではないため。matplotlib デフォルトの DejaVu Sans に fallback してしまいます。
# plt.rcParams['font.sans-serif'] = ['Arial', 'MS Gothic'] # だめです。Arial は Windows に存在するが、その中だけを探して日本語文字グリフが見つからなければ、そこで諦めてしまうようです。
plt.rcParams['font.sans-serif'] = 'Arial Unicode MS' # とりあえず使用可能だが、汚い。
plt.rcParams['font.sans-serif'] = 'Source Han Sans JP' # 源ノ角ゴシック(Source Han Sans):窓の杜にあるリンクを頼りにフォント自体を自分で取得・インストールする必要がある。現時点での妥協点か。
matplotlib (というか、そのベースになっている tkinter)の i18n は正直いまいち。日欧フォント組み合わせが pTeX とはいわなくても MS-Excel 並み程度になってくれるといいのだが…。
https://qiita.com/yniji/items/3fac25c2ffa316990d0c
https://qiita.com/yniji/items/2f0fbe0a52e3e067c23c
https://gammasoft.jp/blog/matplotlib-japanese-fonts/
matplotlib の軸設定
https://www.yutaka-note.com/entry/matplotlib_axis (YutaKaのPython教室)
※メモが分散するのも困るので、ここに一緒に引用してしまいます。
数値計算誤差
以下のコードは、数学的には同じものを求めようとしているが、2進コンピューターでは同じ答えが出てこない、という単純な例。
#include <iostream>
using namespace std;
int main(void)
{
const double a = 1.0;
const double b = 0.9;
const double c = 0.1;
cout << a << endl;
cout << b << endl;
cout << c << endl;
cout << a-b << endl;
cout << (a-b) -c << endl;
cout << a- (b+c) << endl;
return 0;
}
CMake-GUI (windows) において Visual Studio 2017 Express の存在を認識させる [2022/04/16]
(この方法では Audacity 自体のコンパイルは結局うまくいかなかったのだが)Audacity の ASIO 対応コンパイルで Visual Studio Community をダウンロードするのが面倒で、下記引用元サイトの情報を参考にしつつ、既にある Visual Studio 2017 Express を使おうとしたが、Express には 64bit の Native コンパイラが入っていないため、そのままの手順だと ConanException: VS non-existing installation: Visual Studio 15
というエラーが出てしまい、libmp3lame で止まってしまう。「VS 2017 用 x86_x64 Cross Tools コマンド プロンプト」を起動し、そこに CMake-GUI のショートカットを放り込んで起動すれば、環境変数を取り込んで認識してくれた。
しかし、この方法では結局 Audacity 自体のコンパイルはうまくいかなかった。なんでも wxWidgets のソース内に GNU GPL が wxT マクロでべたテキストで取り込まれているところが 64 KB を超えていて 2017 Express では扱えないのだとか。
Visual Studio Community 2022 インストールのトラブル(IDE から SDK の C++ ヘッダーファイルが見つからない) [2022/04/30]
上記の通り 2017 では Audacity のコンパイルが完了できないため、観念して 2022 Community を導入。インストーラー(Visual Studio Installer)の手順を普通にやったにもかかわらず、なぜか CMake から 2022 Community のコンパイラが正しく認識されない上、2022 の IDE 上からコンソールアプリの新規プロジェクトを選んで Hello World をテストビルドしようとすると「エラー E1696 ソースファイルが開けません」と言われてビルドできない事象に見舞われた(ctype.h とか stdio.h とかの C/C++ 標準ヘッダーが多数)。
CMake のログでは ucrtd.lib が見つからないということだが、そのログに書かれたコンパイル・リンクのコマンドラインを手動で動かすと問題ないという、訳の分からない挙動。
where
コマンドでヘッダーやライブラリを探してみたら、なんと、2022 Community と同時導入したはずの最新 SDK 一式が本来 Program Files
配下に配置されるべきところ Program Files (x86)
配下へ入ってしまっていた。これはおそらく、2013 Express や 2017 Express (いずれも 32bit なので x86 配下に入る)が先に存在している状態で 2022 をインストールしたことの弊害なのかもしれない。
100%「おま環」な気がするが、しかたがないのでシンボリックリンクを貼って 64bit の 2022 Community からも x86 配下にあるインクルードファイルやライブラリが見えるようにしたら、あっさり解決した。なんでこんなことせなあかんねん💢。
cd "C:\Program Files\Windows Kits\10"
mklink /D Include "C:\Program Files (x86)\Windows Kits\10\Include"
mklink /D bin "C:\Program Files (x86)\Windows Kits\10\bin"
mklink /D Lib "C:\Program Files (x86)\Windows Kits\10\Lib"
これで CMake が無事通過。CMake の進捗に Configurating done と出て設定項目が赤くなったところで audacity_has_asio_support を on にして Configure をもう一度実行。再度 Configurating done と出て(設定項目が全部白)Generate をクリックし、sln を開いてのビルドへ移行。これで Audacity の ASIO 対応コンパイルが完了し、動作可能な実行ファイルが得られた。
Unicode における Win32API GetWindowTextLength() の戻り値 [2022/05/29]
この例 でハマった。例えば 300 という文字列のあるウインドウ(エディットボックスとか)に対して GetWindowTextLength() を実行すると、その戻り値は ANSI でも Unicode でも 3 になる。この「文字数」 の文字列を格納する先を malloc で確保するときの注意。
これはダメ…バッファオーバーランする。 GetWindowTextLength() の戻り値も GetWindowText() の第3引数も「文字数」 であるのに対して、malloc の引数はあくまで「バイト数」 なのです。
LPTSTR strText = (LPTSTR)malloc(GetWindowTextLength( hWnd_foobar) + 2 );
GetWindowText(hWnd_foobar , strText , GetWindowTextLength(hWnd_foobar) );
…何かの処理…
free(strText);
これなら OK。
LPTSTR strText = (LPTSTR)malloc(sizeof(TCHAR) * (GetWindowTextLength( hWnd_foobar) + 2) );
GetWindowText(hWnd_foobar , strText , GetWindowTextLength(hWnd_foobar) + 2);
…何かの処理…
free(strText);
むしろ、「要素数と1要素当たりサイズを明示的に指定」する calloc のほうが、ゼロクリアの時間はかかるにしても、ソースコードの可読性が良いかもしれない。
LPTSTR strText = (LPTSTR)calloc(GetWindowTextLength( hWnd_foobar) + 2, sizeof(TCHAR) ) ;
GetWindowText(hWnd_foobar , strText , GetWindowTextLength(hWnd_foobar) + 2);
…何かの処理…
free(strText);
ところで、上記のダメな例において Visual Studio 2022 Community のデバッグモードでは free() のところでエラーになって、最初なんで free() がエラーになるの?と思ってしまったが、要はデバッグモードにおける free() の機能として「確保長を超えるメモリー領域に何かを書き込んだ痕跡( 0xCD
ではない値になってる)があったかどうかを調べたら見つかった」、ということなんだろう。実行中(実際に書き込んでいる間)にはそこまでの監視をするのは流石に無理ということなのかな。
Visual Studio 2022 / .NET 6 WinForms C# での高 DPI 対応における表示崩れの修正
Visual Studio 2022 / .NET 7 WinForms C# での高DPI/PerMonitorV2 対応(仮)
Visual Studio 2022 / .NET 8 WinForms C# での高DPI/PerMonitorV2 対応(速報)
音に関係する雑多なプログラミング(別ページ)
参考リンク集
行列計算(一般)
行列計算パッケージ
OpenBLAS :かつてx86用高速BLASライブラリとして名を馳せた GotoBLAS (特殊ライセンス)並びに GotoBLAS2(3条項BSD) の系譜を継いだx86用高速BLASライブラリ(3条項BSD)
SurviveGotoBLAS2 というものもある(GNU AGPL3)
TAUCS 2016/09/23 このリストに追加。今後調査。長らく更新停止だが現在もこれを使っているプロジェクトがあるらしい。コンパイルについて上記f2c関連が役に立つ可能性あり。
MS Visual Studio
Visual Studio 2013 C++ で C/C++ の標準ヘッダが見つからない場合、Windows SDK とエミュレーターのアーカイブ から Windows 8.1 SDK や Windows SDK for Windows 7 および .NET Framework 4 をインストールすればよい。
http://www.slis.tsukuba.ac.jp/~fujisawa.makoto.fu/cgi-bin/wiki/index.php?Visual%20Studio%20%A5%E1%A5%E2
Visual C++ Win32 コンソールアプリケーションへのメモリリーク検出の埋め込み
自作DLLのメモリリーク検出も、冒頭部にこれの追記で可(/LDd /MDd /EHsc)
#if defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#define new ::new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
...(以下、DLLの中身)...
DLL呼び出し側で _CrtSetDbgFlag 等を設定してあれば、それがDLL内にも引き継がれる。
一方、呼び出し側を gcc でコンパイルする場合は _CrtSetDbgFlag 等設定ができないが、DLLMain の DLL_PROCESS_ATTACH で _CrtSetDbgFlag を設定すれば、表示はしてくれるようだ。
BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
{
switch (dwReason) {
case DLL_PROCESS_ATTACH: // DLLがプロセスのアドレス空間にマッピングされた。
// 終了時にメモリリーク情報をダンプさせる。
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
// レポートをファイルに書き出させる。
// _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE にするとIDEデバッグ窓と併用になるようだ
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
// レポートを書き出させるファイルを標準エラー出力にする。
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
break;
case DLL_THREAD_ATTACH: // スレッドが作成されようとしている。
break;
case DLL_THREAD_DETACH: // スレッドが破棄されようとしている。
break;
case DLL_PROCESS_DETACH: // DLLのマッピングが解除されようとしている。
break;
}
return TRUE;
}
VC++2013 迄と異なり、VC++2015 および以降は scanf("%lf", ...) は文字列 NAN を認識する。https://msdn.microsoft.com/ja-jp/library/bb531344.aspx (または、https://docs.microsoft.com/en-us/cpp/porting/visual-cpp-change-history-2003-2015?redirectedfrom=MSDN&view=msvc-170 )
VC++2015 および以降は /source-charset:utf-8 および /execution-charset:utf-8 というのがある(ソースが cp932 なら /source-charset:shift_jis とする)。https://docs.microsoft.com/en-us/cpp/build/reference/source-charset-set-source-character-set 残念ながら VC++2013 迄では使えない。またエンコードが違うソースファイルを include している場合もうまくいかないようだ。
MinGWを用いたWindows DLL作成のtips
http://otsukemono.blogspot.jp/2014/11/mingw-w64-gcc-dll-msvc-import-library.html
MinGW GCCが生成するdefファイル内の序数を消すには sed
を使って
sed -i "s/ @[^ ]*//" dllname.def
とすればよい。sed
はMSYSに入っている。
静的リンクを意図してインポートライブラリを作る場合、その前に序数を必ず消しておくこと。 これを忘れると関数名一切無視で序数だけで関数探索が行われてしまうため、あとでDLLに関数を追加して序数が変化したときに、更新されたインポートライブラリと共にアプリケーションを再コンパイルするのを忘れると、本来呼び出すべきものとは違う関数に、本来の関数用の引数を積んだスタックを渡してしまい、悲惨なことになる! (やりました!クラッシュしないままデータ大破壊!)
MinGW 上の sed による置換で permission denied を回避するには
sed のオプション -i
に関する対処法だが、たぶん、アクセス権の再設定が必要なんて、仕事しているうちに忘れてしまう。
また、フォルダによって、これでうまくいかないケースがあるようだ。どうやらC:\直下に作ったフォルダ以下で再帰的にこの制約が生じるらしいが、よくわからない。
結局、エレガントでないがオプション -i
を排除すべく、Makefile中のルールのコマンド行を
sed "s/ @[^ ]*//" dllname.def > dllname.def.new
rm -f dllname.def
mv dllname.def.new dllname.def
あるいはそもそもコンパイラが出すdefファイルの名前から変えてしまって(抽象的かつ、FORTRANとCとC++の混合言語な例ですみません、$(FC)はg77またはgfortran)
$(FC) -shared $(CFLAGS) $< $(dllobjs_c) $(dllobjs_cpp) $(dllobjs_fort) -o $@ -lstdc++ -Wl,--output-def,$(defname).tmp
sed "s/ @[^ ]*//" $(defname).tmp > $(defname)
rm -f $(defname).tmp
などとするのが吉。
--kill-at
という便利なオプションがある。__stdcall
の関数の @ 以下を自動で外してくれる。sed もなにもいらず、これで一発で Excel 等から呼べる DLL ができる。MSVC にはこれと同等のオプションはなく、dumpbin /EXPORTS
で見て DEF を書くしかない。
g++ --shared test.cpp -o test.dll -Wl,--kill-at
他方、 @ 以下をそのまま残しておいて、簡易的ながらも仕様合致検査を兼ねておくという考え方もある。__stdcall
やその異名の WINAPI
では原則(固定引数の場合)呼ばれた側の関数がスタックを清掃するので、スタックに積んだもののバイト数が呼び出し側の想定と合っていないと大変なことになるからだ。この場合は gcc でコンパイルするときに def が必要になるが、これは上記の defname.tmp を何かで加工すれば容易に作れる。
その他お役立ち情報へのリンク
DLL から「正しい」LIB ファイルを作るには
このLibファイルの作り方自体は呼び出し規約 __stdcall
に関してであり、通常暗黙の __cdecl
には通用しません。ただし、最後の「序数という罠の山」にはすべての呼び出し規約の一般論として、絶対に序数を書いてはいけない理由が端的にまとめられています。その視点を持って読むと、前半も大いに参考になる。
追記:MSVC の pragma での対応方法は、もとの修飾名を付した関数名が省略できない。たとえば #pragma comment(linker, "/EXPORT:testfunc=_testfunc@8")
。ただし、最後の「@8」がどうであるかは一旦コンパイルして dumpbin に掛けないとわからないようであるし、この方法で作った dll には元々ある「_testfunc@8」と追加した「testfunc」の二つのエントリが併存してしまう(--add-stdcall-alias
相当)。実害はないが、どうせなら自分で DEF を書いて食わせるほうが良いだろう。
IPA セキュア・プログラミング講座 Windowsパス名の落とし穴
古いドキュメントだが、目を通す価値あり。
Visual C/C++用getoptでPOSIX的コマンドラインオプション解析
C++標準機能だけでできているようで、プラットホーム不問で汎用的に使えそうだ。MIT license。ただし、これに対応するヘッダファイルがないので、自分で作るか、MinGW からとってくるしかないようだ。
C言語の__LINE__マクロをプリプロセッサの段階で文字列に変換する
__LINE__
に限らず、_MSC_FULL_VER
などの数値リテラルとして定義されたマクロ一般に適用可能。
レジストリの値をコマンド プロンプトで取得する
http://www.atmarkit.co.jp/ait/articles/0402/21/news005.html
サクラエディタの指定桁縦線をテキストより後ろ側に表示させる
google「サクラエディタ 指定桁縦線」ですぐ探し出せるのだが、しょっちゅう忘れるのでメモ。
「指定桁縦線」の「文字色」を「テキスト」の「背景色」に指定してさらに「指定桁縦線」の「背景色」を線の色で使いたい色で指定する。このようにすると内部的に XOR 処理で色を作って描画するのだそうです…とても便利というか、T、Yといった全角英数や漢字を扱うときに必須な気がするのだが、ヘルプには書いてない。
gcc のプリプロセッサの定義済みマクロ(Predefined Macros)の確認方法
Cygwin の方法は Mingw でも使える。
Windows 10 RS4に追加された「curl」と「tar」コマンド
Win10 1909 で一般向けにも降臨したようだ。これのせいで、w32tex などのユーザー定義パスに入れてあった tar が優先して使われなくなり、.tar.gz や .tar.bz2 などが展開できない(うんともすんともいわない)。System32 フォルダに bzip2.exe などをぶちこむのも野暮な話であり、ユーザー定義パスの tar をフルパスで指名する必要があるが、手前環境だけかもしれない。システムのパスを書き換えれば済むのだけど。
tnefparse ( https://pypi.org/project/tnefparse/ )
winmail.dat
が来たら、これで中身を抽出可能。
コマンドプロンプトでファイルの更新日時をミリ秒単位まで取得する
PowerShell "Get-ChildItem -Recurse | Select Name, Length, Attributes, @{Name=\"LastWriteTime\"; Expression={$_.LastWriteTime.ToString(\"yyyy/MM/dd HH:mm:ss.ffffff\")}}"
PowerShell "gci -Recurse | Select Mode, Name, @{Name=\"LastWriteTime\"; Expression={$_.LastWriteTime.ToString(\"yyyy/MM/dd HH:mm:ss.ffffff\")}}, Length"
-Recurse
を消したらカレントディレクトリだけになる。抽出したければ後ろに | find [ファイル名の部分文字列]
を加えればよい。ただしここで使うのは MS-DOS/Windows の find であり、MinGW の find ではない。
[Word] Word 2016で数式に式番号を付ける方法
Good Job!! とても分かりやすく丁寧に記述されています。おすすめ。
検索式まとめ:Windows10ファイルエクスプローラ
こちらも Good Job!! とても丁寧に記述されています。おすすめ。なお、例えば「名前:~A* AND 名前:B」みたいな気の利いたことはできない模様。
サクラエディタ:全角文字の検索方法(怒られ・半角カナ回避版)
いつも戸惑うのでメモ。
【エクセルマクロ】等高線グラフを虹色にする: IT - Infinite Trouble
Good Job!!
Envelope Control Points , Text Envelope (Audacity Wiki)
Audacity のエンベロープを数値入力する。二種類あったが、中身が違う。前者のほうが新しいが、Audacity 2.4 以降とのこと。3.x でも使える。でも願わくば Audacity 2.x 時代に可能だったような xml 直接編集みたいなことができるといいのだが、それはまだないらしい。さすがに Audacity のソースコードの ProjectSerializer.cpp とか ProjectFileIO.cpp(BufferedProjectBlobStream クラス)とかをいじるほどの気力はない。
なお、Nyquist プラグインをインストールしたり、それを ツール メニューに表示させるには、編集→環境設定のエフェクトにおいて Nyquist エフェクト にチェックを入れる必要がある。
今後調べたいもののリスト
to be continued...
トップページ /
計算機関係の雑多なこと
このサイトについて・著者連絡先
←2000年以来ずっとお世話になっているテキストエディタ。技術面では何も貢献できないので、バナーを貼って応援!