[[BORLAND C++]]
INDEX
Section.8 File
そろそろ電卓から離れないと、怒られてしまいそうなので、ちょっとサンプルの方向を変えよう…(独り言である),簡単なので、前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
2002/04/23
HomeSweetHome2
Ozzy's Software