dポイントプレゼントキャンペーン実施中!

Visual StudioでWindowsプログラミングをしています。

RESULT CALLBACK WndProc(HWND hwnd, UINT imsg, WPARAM wp, LPARAM lp){

 switch(imsg){
  case WM_COMMAND:
   switch(LOWORD(wp)) {
    case ID_FILEOPEN:
     FileSet(hwnd);
     break;
   case ID_WRITE:
     if((fp_test=fopen("test_data.dat","w"))==NULL)
      exit(0);
     fputs("******",fp_test);
     fclose(fp_test);
     break;
  …
  }
  return 0;
}

のようにメニューからファイルの読み書きを行わせたいと思っています。

プログラムを実行してすぐにWRITE部分を行うと、
ファイルは正常に作られ、書き込みもできています。

ところが、先にFILEOPENを行うと、その後WRITEが正しく動かず、
ファイル自体が作成されません。
ファイルオープンに失敗すれば、終了するはずですが、それもしません。

FILEOPENで呼び出しているFileSet関数は以下です。

void FileSet(HWND hwnd){//ファイルの選択
 char filePath[MAX_PATH];
 char fileFolder[MAX_PATH];
 char *pt,*ptEnd,*ptEndNext;

 if(OpenFiles(hwnd)==1){
  pt = FileName;
  ptEnd = strchr(FileName,'\0');
  ptEndNext = ptEnd + 1;

   if(ptEnd){
    int nLength;
    if(*(ptEndNext) == '\0'){//ファイルが1つしか選択されていない
     strcpy( filePath, FileName );
     FileOperation(filePath); //ファイルオープンとデータの平滑化
   }else{ //複数選択
     strcpy( fileFolder, FileName );//フォルダ名の取得
     nLength = strlen(fileFolder);
     if(fileFolder[nLength]!='\\')
      strcat(fileFolder, "\\");
     while(*(ptEndNext) != '\0'){
      strcpy(filePath, fileFolder); //パス名を作成
      strcat(filePath, ptEndNext ); //ファイル名を連結
      FileOperation(filePath);
      ptEnd = strchr( ptEndNext, '\0' ); //次のファイルを探す
      ptEndNext = ptEnd + 1;
     }
   }
  }
 }
}

FileSet関数内で呼んでいるFileOperationは以下です。
テキストデータを数値に変換し、indataという構造体配列に代入しています。
indata,numはグローバル変数です。

void FileOperation(char *filePath, int b){//ファイルオープンとデータスケーリング
 int i,j;
 double datawork;
 char work[10];

 if((fp=fopen(filePath,"r"))==NULL)//ファイルオープン
  exit(0);
 for(i=0,j=0;(i<DATA_NUM) && (fscanf(fp,"%s",work)!=EOF);++i){//データ読み込み
  datawork = atof(work);
  indata[num].data[j] = (float)datawork/NORM;
  j++;
 }
 fclose(fp);
 datawork = 0;

 indata[num].number = num;
 strcpy(indata[num].fname,filePath);
 num++;
}

おそらくこのあたりに原因があるとは思うのですが、
自分ではわかりませんでした。
お力を貸していただければと思います。

WinXP SP3, VisualStudio 2005で作成しています。

説明不足な部分がありましたらすみません。
よろしくお願いいたします。

A 回答 (2件)

多分関係ありませんが,


if(fileFolder[nLength]!='\\')
 strcat(fileFolder, "\\");
この処理はおかしいような気がします. 「\ で終わっていれば何もせず, \ で終わっていない場合には \ を付ける」という処理をしたいのだと思いますが, \ で終わっている場合にも \ を付けるのではないでしょうか.
例えば fileFolder が "a" だと nLength は 1 になりますが, fileFolder[nLength] は '\0' ですよね.
    • good
    • 0
この回答へのお礼

回答ありがとうございました。

ファイルが作れないことに関しては自己解決いたしまして、
FILEOPENした時にカレントディレクトリが変更されていたようで、、
オープンしたファイルのあるディレクトリに、
ファイルはきちんと作成されていました。

書き込みファイル名を絶対パスで指定していなかったことと、
私の早とちりが原因でした。お騒がせいたしました。

ご指摘の部分ですが、確かに変な処理をしていますね…。
実行時に、予期せぬ動作などしなかったので、見過ごしていました。
修正したいと思います。ありがとうございました。

お礼日時:2008/12/12 13:45

Windowsのコールバック関数は、リエントラントである事が要求されます。



しかし、Cの標準ライブラリ関数のfopenは、リエントラント関数である保証はありません。

もし、fopenが非リエントラントで実装されている場合、リエントラントである事が要求される関数内で使用すると、動作は未定義です。

つまり「RESULT CALLBACKと定義されたWndProc関数内では、fopen関数を使用すると、動作は未定義」と言う事です。

リエントラントでない実装のfopen関数をコールバック関数で使用した為、fopenは「予期せぬ動作」をします。それが「どんな動作」かは、誰にも判りません。

今回は「予期せぬ動作」として「ファイルも作らず、何もせず、エラーにもせず、何だか正体の判らないFILEへのポインタを返す」と言う動作をしました。

fopenがリエントラントでない場合については、以下の質問の、当方の回答を参照して下さい。
http://oshiete1.goo.ne.jp/qa4545663.html

リエントラントである事が要求されるコールバック関数では、fopenの使用を避けCreateFile(API)を使用するか、fopenに再入しないようミューテックスを設けるなど、工夫が必要です。

ともかく、現状のままでは「fopenは使えない」と思って下さい。
    • good
    • 0
この回答へのお礼

回答ありがとうございました。

ファイルが作れないことに関しては自己解決いたしまして、
FILEOPENした時にカレントディレクトリが変更されていたようで、、
オープンしたファイルのあるディレクトリに、
ファイルはきちんと作成されていました。

書き込みファイル名を絶対パスで指定していなかったことと、
私の早とちりが原因でした。お騒がせいたしました。

リエントラント関数については、全く知識がありませんでした。
これから勉強したいと思います。
ありがとうございました。

お礼日時:2008/12/12 13:43

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!