そろそろ電卓から離れないと、怒られてしまいそうなので、ちょっとサンプルの方向を変えよう…(独り言である),簡単なので、前Sectionで使用したダイヤログを使って、[パスワード入力]→[名前とメールアドレスを入力]→<ファイルに保存>という流れの構成を考えてみた。
- C言語そのままのファイル処理
Windowsアプリだからと言って、何も身構える必要は無いのだ。ファイルの入出力は、FILE構造体を使って、fopen()し、fgets(),fgetc()で読み出し/fprintf()等で書き込みする、馴染みのC言語関数が全然問題無く使えるのだ。しかも、32bitだから、ロングファイルネームも使えるのだ。処理スピードから考えたら、この方法がBESTという説も有るので、積極的に使うのが良いだろう。因みに、当然の事だが、STDIO.H若しくはIOSTREAM.Hをインクルードしなければ使えない。
テストサンプル007
因みに、パスワード入力と言っても、パスワードのチェックはしていないので、何も考えずに試してもらいたい。実行すると、入力された内容が、test007longfile.txtというファイルに保存されている筈である。
- 小技:EDITTEXTでリターンキーで入力完了を認める,
これは、以前から謎だった事なのだが、他人が作成したソフトでは、パスワードの入力画面で、リターンを押すと、入力完了と判断されて次に進むのだが、自分の造ったソフトでは、これが出来なかったのだ。EDITTEXTの制御方法に、何か有るのだろうと調べたのだが、答えには至らなかったのである。最近,遅いPCでメールソフトを使ったところ、パスワード入力画面でリターンキーを押すと、ボタンが押された様な気がして、実は、ボタンの制御方法に秘密が有るのでは?と疑って調べたところ、通常のPUSHBUTTONでは無く、DEFPUSHBUTTON(デフォルト属性のPUSHBUTTON)を使えば、実現出来る事が分かったのだ。つまり、同一のダイヤログ上に、DEFPUSHBUTTONが有れば、例えフォーカスがEDITTEXTにあたっていても、リターンキーを押された時,このボタンを押された事になるのだ。このボタンのイベントについては、ボタンと同様にハンドル出来るから、これが押されて、テキスト入力がされていれば、次の処理に進む…といった処理が出来るのである。
- ファイルダイヤログ
C言語時代のファイル処理が使えるからと言って、ファイルの選択や、ファイル名入力に、ファイルダイヤログを使わない手は無い。その昔、自分がファイルダイヤログの使い方を知らなかった頃,ファイル名を、_dos_findfirst()〜_dos_findnext()の組み合わせで探して、リストボックスに放り込み,選択してファイル名の指定にする…と言った原始的な手法を使った事が有るが、絶対にカレントディレクトリしか使わせないとか、特別な理由が無い限り,今の時代にそんな面倒な事をする奴は居ないだろう。簡単に呼び出せるファイルダイヤログが存在するのだから、これを利用しない手は無いのだ。具体的には、読み出す(LOADする)ファイル名を指定する場合は、GetOpenFilename()、逆に、保存(SAVE)するファイル名を指定するには、GetSaveFilename()の関数を使用する。(この両関数,表示や動作等が若干違うのだが、中身は同じものの様に思える…どうなのだろうか?) 何はともあれ、OPENFILENAME構造体に必要なパラメータを設定して、呼び出せば、ファイルダイヤログを表示してくれる。戻り値が、TRUEの場合は、ファイル名を取得出来ているので、そのファイル名を、そのまま使用出来る。また、FALSEの場合は、キャンセル若しくはファイルダイヤログの起動失敗と考えて、処理を行えば良い。一応、サンプルに、簡単に設定出来る関数を定義しておいた。
尚、このファイルダイヤログ,親WindowのハンドルはHWND_DESKTOPでも良いのだが、プログラムファイルの先頭に、
#define _WIN32_WINNT 0x400
#define WINVER 0x400
の記述を付け加えなければ、全く動作しなかった。(何故かは不明)
ファイルダイヤログ設定→起動関数
動作例は、
テストサンプル008
で確認してほしい。あっと、忘れていた!ソースを読めば分かると思うが、パスワードは"test008"になっている。また、ソースは、テストサンプル007とい比べると、結構色々な機能を付け足して居るが、このサンプルの意図しているのは、ファイルダイヤログの使い方だけなので、内容については説明を省略する。
- ファイル入出力
ファイルの入出力について、ここまでは、C言語?(コンソールプログラムと言った方が良いか?)の遺産を使用してきたが、他の方法も幾つか有るので、ここで紹介したい…と思ったが、勉強不足の為後日。
〜未稿〜
- ドラッグ&ドロップの処理
Windowフレームにドロップされたファイルを読み込む仕様にするのは、格好良いのである。以前から、やりたいなとは思っていたのだが、なかなか使う場面というのが無いのである。実際、ファイルをドロップすると、Windowプログラム側では、そのファイル名だけを受け取るだけなので、あまり、効果的に使う場面が無いという事実もあるが、ドロップされたファイルのファイル名を取得する方法(サンプル)だけ、簡単に書いておく。
- ドラッグ・ドロップの許可/禁止
許可は、
DragAcceptFiles((HWND)hwnd,TRUE) ;
禁止は、
DragAcceptFiles((HWND)hwnd,FALSE) ;
とすれば良い。hwndはもちろん、親となるウインドウのハンドルである。最後に、禁止を行うのを忘れない様に!
- メッセージID
ドラッグ・ドロップを許可している場合、hwndで指定したWindowに、
WM_DROPFILES
のメッセージがイベントとして上がるので、これを、
case WM_DROPFILES:〜ドロップ時の処理〜
の様に、ハンドルすれば良い。
- ドロップファイルのファイル名抽出
説明はややこしいので、サンプルを記述する(何処かからパクって来たもの)。以下の例では、複数のドロップが有った場合、自分を複数起動して、全てを受け取る仕様となっている。尚、引数のwParamは、Windowプロシージャのパラメータをそのまま使えば良い。この場合、親Windowのハンドルは、HWND_DESKTOPでは無理だと思う。
void GetDragFilename(HWND hwnd,UINT wParam,char *fname)
{
char execfilename[256] ;
STARTUPINFO si ;
PROCESS_INFORMATION pi ;
int i,dC ;
// -- ShellExecuteの為、自分アプリの実行ファイル名を
// コマンドラインから抜き出し
strcpy(execfilename, GetCommandLine()) ;
PathRemoveArgs(execfilename) ;
// -- ドロップされたファイル数dCを取得
dC=DragQueryFile((HDROP)wParam,-1,NULL,0) ;
// -- ドロップ数だけ、ファイル名を取得し、処理を行う。
for(i=0 ; i<dC ; i++){
DragQueryFile((HDROP)wParam,0,fname,256) ;
if(i==0) loadTargetFile(fname) ;
else
ShellExecute(hwnd,NULL,execfilename,fname,NULL,SW_SHOW) ;
}
DragFinish((HDROP)wParam) ;
}
|
この辺の使用については、将来的には、class CoolFname,class CoolFile : public CoolFnameと言った形でクラスにまとめたいと思っている。
- コマンドライン
コンソールプログラムで、コマンドラインのコマンドをハンドルした事が有るだろうか?
test.exe -t -v test003.dat
等というあれである。
コンソールプログラムでは、main()関数の引数に、main(int argc,char **argv)[第3引数としてenvというのが有ったが、殆ど使わなかったので割愛]という形で、argcにアーギュメント数,**argvにコマンドラインのトークン毎の切り出しが入っていたので、簡単に使えたのである。上の例では、
argc=4 … アーギュメント数(実行ファイル含めて)
*argv[0]="(path)\test.exe" … 実行ファイル名
*argv[1]="-t" … 第1トークン
*argv[2]="-v" … 第2トークン
*argv[3]="test003.dat" … 第3トークン
となるのである。自分としては、結構これが好きだったので、これで、デバッグは殆ど事足りてしまう程、利用しまくったものである。さて、WinMainでは、どの様に考えれば良いのだろうか?
WinMainの引数に、コマンドラインの文字列が有るが、これが、実行ファイル名を含まないコマンドライン全てだと思えば良い。ここでは、コンソールプログラムの様に、スペースでトークンを区切ってはくれない様なので、単純に、『MS-DOSプロンプトに記述した文字列から、実行ファイル名と、区切りのスペース1個を取り去ったもの』と考えれば良い。例えば、コマンドラインにて、ファイル名を指定する場合は、
if((lpszCmdLine) && (*lpszCmdLine!='\0')){
strcpy(filename,lpszCmdLine) ;
}
|
等として、簡単使用する事が出来る。
逆に、トークン毎に、切り出して使いたいという場合は、切り出しの操作を自分で記述しなければならない様である。(未調査)
- プロファイルの制御
プロファイルというのは、Windowsのフォルダに、いつの間にかザクザク溜まっていく、WIN.INI,SYSTEM.INI等の、いわゆる、初期化ファイル(INIファイル)だ。プロファイルにも、プロファイルとプライベート・プロファイルという種類が有って、普通、自分の造ったアプリの設定を保存するのは、プライベート・プロファイルと呼ばれる。プロファイルと呼ばれるものは、WIN.INIにあたるので、不用意にプロファイル(WIN.INI)に書き込みを行ったら、既存の設定を壊してしまう事も有り得なく無い。プロファイルの方を扱う場合は、充分に注意しなければならない。使い道としては、Windowsの設定をいじる場合と、Windowsの設定を読み出して、自分のアプリの制御パラメータとして使う…等というのが一般的だろう。素人が、あまり触らない方が身のためなのである。
ツール等のプログラミングで使用するのは、もっぱら、プライベート・プロファイルの方になる。例えば、test009.exeというアプリのプロファイルは、test009.ini(別のファイル名も指定できるが、この形で使うのが望ましい)という感じだ。書式については、WIN.INIも、test009.iniも変わりなく、テキストファイルであるので、テキストエディタで開けば読む事が出来る。
(例)test009.ini
[STATUS]
LastComLine=unlock
[CONTROL]
Locked=0
|
上記例は、今回のサンプルtest009.exeのプライベート・プロファイル(test009.ini)である。ここで、鍵括弧([])で囲まれた文字から次の鍵括弧まで(若しくはファイルの終わりまで)をセクション(Section)と呼ぶ。例えば、[STATUS]の行から[CONTROL]までの間がSTATUSセクション,[CONTROL]の行からファイル末尾までがCONTROLセクションという訳だ。このセクション部分に、設定などのデータを記述するのだが、ここに関しても書式が決まっていて、
キーワード文字列=データ(設定値) … 以降,設定文字列と呼ぶ
の様な形式を採らなければならない。(正確に言うと、別にどんな書式でも良いのだが、標準の関数を使って読み書きしようとするならば、この様な書式にした方が扱いやすい)プライベート・プロファイルを扱う関数は、他にも幾つか存在するが、とりあえず、以下の関数を知っておけば問題無い。
- WritePrivateProfileString(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
LPCTSTR lpString,
LPCTSTR lpFileName
);
Windowsフォルダ内のプライベート・プロファイルlpFileNameの、セクションlpAppName中にキーワード文字列lpKeyNameの行を追加,若しくは書き換えて、
lpKeyName=lpString
の設定文字列とする。
セクション名lpAppNameが、ファイル中に存在しなければ、追加を行う。
ファイルlpFileName自体が存在しなければ、作成して上記を行う。
- GetPrivateProfileInt(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
INT nDefault,
LPCTSTR lpFileName
);
プライベート・プロファイルlpFileName中の、セクションlpAppName中にある、キーワード文字列lpKeyNameの行のイコール(=)以降の数値を得る。取得に失敗した場合には、nDefaultの値を返す。
TestData=102
等の様に、数値がターゲットの場合に有効である。
- GetPrivateProfileString(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
LPCTSTR lpDefault,
LPTSTR lpReturnedString,
DWORD nSize,
LPCTSTR lpFileName
);
プライベート・プロファイルlpFileName中の、セクションlpAppName中にある、キーワード文字列lpKeyNameの行のイコール(=)以降の文字列をlpReturnedStringに取得する。取得に失敗した場合には、lpDefaultを返す。尚、nSizeは取得するMAX文字数を指定する。
TestData=ABCDEFG
等の様に、文字列がターゲットの場合に有効である。
以上を利用すれば、プライベートプロファイルに関しては、一通りの制御が出来るのである。この他にも、WritePrivateProfileSection(),WritePrivateProfileStruct(),GetPrivateProfileSection(),WritePrivateProfileStruct()等の関数が用意されているので、適宜、使い分ければ良いだろう。特に、WritePrivateProfileSection()(プロファイル内の指定セクション全てを書き換え/追加)という関数については、使う可能性も有るので、注意点をひとつ,この関数,セクション中に記述するストリングの最後が必ずNULLになっていないと、おかしな結果になる。"TestData=102"の場合は、"TestData=102\0"と、¥0を付け加えておく必要が有るので注意を!(バグかな?)
サンプルとして、プライベートプロファイルを使う例を一つ,テストサンプル008のアプリに、パスワード3回間違いで、ロックする機能を付けてみた。(このサンプルのパスワードはtest009である。)一度でも起動すれば、Windowsのフォルダに、test009.iniというファイルが作成される筈である。ここに、ロック状態を保存する様にしてみた。因みに、ロックした場合、解除する方法は、MS-DOSプロンプトから、
test009 unlock
の様に打ち込めば良い。
テストサンプル009
|