とりあえず覚書き
GUI として作りあげたアプリをバッチなんかで処理させたい場合。ウィンドウ作るのを止めさせるだけでも良いが、余計な処理はしないに越した事は無い。それ何処に書くのが良いか?
winmain.cpp - AfxWinMain を覗くと
if (pApp != NULL && !pApp->InitApplication()) goto InitFailure; if (!pThread->InitInstance()) {
となっておるから、InitApplication()をオーバーライドし、そこに必要な処理を書いて FALSE を返すのが良さそう。InitInstance() でも悪くないが、そこで FALSE を返しても ExitInstance()が呼ばれてしまうので宜しくない。
何だかんだ言っても Windows 環境だとまだまだ Get/SetPrivateProfile*** を使う。
で、たまに困るのがキーが特定文字列でない場合。例えれば連想配列のような定義が欲しい時がある。で、GetPrivateProfileSection 関数を使ってみたのがコレ PrivateProfileSection.hpp
色々と癖がある。
重複するキーを扱うにも、こっちを使って対処するのが手っ取り早いだろう。
Windows で環境変数設定はやり難い。せめて見易くならないかとツールを自作した。つか、set コマンド酷過ぎ。
1. awk でセミコロン毎に改行するスクリプトを用意する。xxx.awk
BEGIN { FS=";" } { for (i = 1; i <= NF; i++) print "\t" $i }
2. コマンドラインで環境変数を読み出し、1. のスクリプトへ渡す。
@echo off echo. echo %include% > yyy.tmp echo include = awk -f xxx.awk yyy.tmp echo. echo %lib% > yyy.tmp echo lib = awk -f xxx.awk yyy.tmp echo. echo %Path% > yyy.tmp echo Path = awk -f xxx.awk yyy.tmp
3. 実行結果
include = C:\Program Files\vs98\VC98\include C:\Program Files\vs98\VC98\atl\include C:\Program Files\vs98\VC98\mfc\include lib = C:\Program Files\vs98\VC98\lib C:\Program Files\vs98\VC98\mfc\lib Path = C:\WINNT\system32 C:\WINNT C:\WINNT\System32\Wbem C:\jdk\bin C:\Program Files\vs98\Common\Tools\WinNT C:\Program Files\vs98\Common\MSDev98\Bin C:\Program Files\vs98\Common\Tools C:\Program Files\vs98\VC98\bin C:\Program Files\Support Tools C:\Program Files\Resource Pro Kit C:\Program Files\Microsoft SQL Server\90\Tools\binn\
以前はよく IPMSG を使っていたのだが、最近起動すらしなくなっていた処、使ってる人は使ってるようで「ちょっとメッセージ送るけど IPMSG 起動してる?」と尋ねられた。
そこでふと思い出したのが Messenger サービスが起動していれば net コマンドでちょっとしたメッセージを送れる事。
送信者は次のようにコンソールから送信する。
> net SEND 192.168.x.x hoge メッセージは 192.168.x.x に正常に送信されました。
すると受信者の画面に次のメッセージがポップアップする。
Ctrl+C を押せばクリップボードにテキストが入る。
--------------------------- メッセンジャ サービス --------------------------- xxxxx から 192.168.x.x へのメッセージ (2010/03/13 18:16:11) hoge --------------------------- OK ---------------------------
最前面に出てウザイ場合もあるので、
C# で使えた時に「気の利いた拡張」と思っていたら...
ところで Win32 API FormatMessage は、FORMAT_MESSAGE_ALLOCATE_BUFFER を指定せずに自前のバッファ(とバッファサイズ)を渡しても構わないが、指定したサイズが小さ過ぎるとバッファに何も書き込まないようだ。
@err = 122 「システム コールに渡されるデータ領域が小さすぎます」となる。
int FormatMsg(LPTSTR lpBuffer, DWORD nSize, LPCTSTR lpszFormat, ...) { va_list argList; va_start(argList, lpszFormat); int res = 0; if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING, lpszFormat, 0, 0, lpBuffer, nSize, &argList) == 0) { res = 1; //AfxThrowMemoryException(); } va_end(argList); return res; }
格納できる分だけ書き込む事に何を躊躇うのか気が知れないが、そういう仕様なら大事なログには使えない。メッセージが何も出ない時がある、なんてのも勘弁して欲しい。
格納できる分だけ書き込めばいいじゃん、と思うのだが、そうもいかないのだろうか…
こういう仕様だと、いざと言う時に何もメッセージが出なかったり、何もログ残らなかったりする可能性がある訳で、アプリケーション開発側として非常に気持ちが悪い。
従って FORMAT_MESSAGE_ALLOCATE_BUFFER でシステムに格納させた後、ユーザー領域に必要なだけコピーし直す事になる(CString::FormatMessage がそうしている)。
効率悪いっすね、無駄なバッファコピーを減らそうと直接ユーザー領域に書き込んで欲しいが、バッファサイズが不足する場合は一切書き込まれない為、バッファコピーせざるを得ない。
この辺の説明はマニュアルにも無く、MSのAPI開発者の素人っぽさと言うか手抜きが滲み出ている。
本題に戻って、精度/幅を表す為に * を含める場合の「%n+1 という挿入シーケンスを使います」と言う件は printf と同じく1個ずれるよ、の意。しかし、シーケンスとか表現が仰々しいですな。
// 高々3文字で打ち切る。 FormatMsg(str, sizeof str, "%1!.*s!", 3, "123456789"); // 5文字分の幅に高々3文字。 FormatMsg(str, sizeof str, "%1!*.*s!", 5, 3, "123456789");
また、Visual Studio 2005 から新しいランタイム ライブラリにもある模様。
昔々、雑誌の質問コーナーで某社が回答していたもの。
// INT_MAX ... 2147483647 // 9876543210 // ここで返されるポインタの示す内容は,呼び出されるたびに破壊されます。 // そのため,printf("%s, %s", camma(x), camma(y)); のような // 使い方はできません。 LPCSTR comma( int iVal ) { static char buf[20]; // 10進数値の桁数 nDigit と、位(10のn乗) nBase を計算。 int nBase = 1; for (int nDigit = 0; nDigit < 9 && iVal / nBase >= 10; nDigit++) nBase *= 10; int nNum; // 各桁での数字 [0~9] int i = 0; do { nNum = iVal / nBase; buf[i++] = '0' + nNum; iVal -= nNum * nBase; if (nDigit % 3 == 0) buf[i++] = ','; nBase /= 10; } while (nDigit--); buf[i-1] = '\0'; return buf; } main() { printf("%s\n", camma(12345L)); }
もう少し実践で使えるよう、負の整数やスレッドセーフに対応する。
char * comma(char * buffer, int val) { char * p = buffer; if (val < 0) { if (val == INT_MIN) // これは近道ではなく... { strcpy(buffer, "-2147483648"); return buffer; } *p++ = '-'; val = 0 - val; // 符号無しとして渡す。 } // 下の unsigned int 版を呼ぶ。 comma(p, (unsigned int )val); return buffer; } char * comma(char * buffer, unsigned int val) { char * p = buffer; // 10進数値の桁数 dig と、位(10のn乗) wei を計算。 int wei = 1; int dig = 1; for (; dig < 10 && val/wei >= 10; ++dig) wei *= 10; int n; for (;;) { n = val / wei; // 各桁での数字 [0~9] val -= n * wei; *p++ = '0' + n; // '0'..'9' if (--dig <= 0) break; if (dig % 3 == 0) *p++ = ','; wei /= 10; } *p = '\0'; return buffer; }
既に文字列化されている数字をカンマ表記にしたい場合もあるかも
その場合、数字の前後に余計な文字列がくっ付いている事がありがちなので対応しておく。
#include <string.h> #include <ctype.h> /* * 指定された文字列の中で最初に見つかった10進整数の並びを3桁毎にカンマを挟む。 * バッファは十分に採っておく事。 */ char * sprint_comma(char * buffer, const char * number) { char * p1 = buffer; const char * p2 = number; // 数字に出会うまで単純コピー while (*p2 != '\0' && !isdigit(*p2)) *p1++ = *p2++; // 数字部の測長 size_t len = 0; for (number = p2; isdigit(*number++); ++len) continue; for (; len; --len) { *p1++ = *p2++; if (len >= 4 && len % 3 == 1) *p1++ = ','; } // 残りを単純コピー while (*p2 != '\0') *p1++ = *p2++; *p1 = '\0'; return buffer; } int main(int argc, char* argv[]) { char s[32]; char* x = "-1230円"; printf("[%s]\n", x); printf("[%s]\n", sprint_comma(s, x)); // "-1,230円" return 0; }
アルファベットはCourier New、日本語部分はMS ゴシックで表示、なんて事をしたい人に。どこかで見かけた。
Windows2000 の場合、
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink
に新規文字列 Courier New を作成。値のデータに
MSGOTHIC.TTC,MS Gothic
と記入して再起動。
お気に入り以外にも大抵の定義がある。
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders] 'Favorites'='任意のパス'
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer
この下に View Source Editor\Editor Name を作成し、(標準)のデータにエディタのフルパスを書く。
REGEDIT のキー作成時、及びレジストリキー参照時共、末尾の空白を削除したりせず、そのまま探すので注意!