- 締切済み
VC++ Exprss:ファイルアクセス拒否
C言語でのプログラミングを行っています。環境はVC++ Express 2010、XPのSP3です。 バイナリファイルへの書き込みで、errno13、Permission deniedが発生します。 循環リストを読み込み/書き込みをするプログラムです。 以下、エラー発生までの流れです。 1. fopen_s "rb"でファイルをオープン。freadで読み込み。 -存在しなければエラーメッセージを表示して入力処理へ。 -存在する場合は、バイナリデータを構造体ポインタへ読み込む。 2. 構造体への入力処理。 3. "wb"でファイルをオープン。リストの先頭から終端までをfwriteで書き込み。 -存在しない場合は、新規作成。 -存在する場合は、ファイル自体を上書き。(上書きを行う時にのみ、errno13のエラーが発生します。) ファイルはデスクトップに作成しています。 情報に不足があるかもしれませんが、これ以上は思いつかなかったので必要な情報があればご指摘をください。コードを載せたかったのですが、行数が多いので流れを書かせていただきました。 ご教授お願いいたします。
- みんなの回答 (6)
- 専門家の回答
みんなの回答
- Wr5
- ベストアンサー率53% (2173/4061)
>確かにそうなんです。でも循環リストを作る時に始めと最後の構造体をHEADとENDとした場合、 >END->NEXT_PTRにはHEADが入っているので無限ループに陥ってしまいます… 循環にする必要があるのかどうかを検討した方がいいんじゃないですかねぇ…。 リスト構造で作ったリングバッファみたいなもんなんでしょうか? 私ならHEADのアドレスを記憶しておいて回しますね。 int COUNTER(list_t *HEAD) { int COUNT; list_t *TEMP; for(COUNT = 0, TEMP = HEAD;TEMP->NEXT_PTR != HEAD;TEMP = TEMP->NEXT_PTR, COUNT++); return COUNT; } こんな感じですかね。 リスト構造作るときは面倒ですが…双方向リストにしておけばHEADの前がENDになるんじゃないですか? # 関係ありませんが…変数名や関数名を「全て大文字」にするのは個人的にちょっと……。 >どうにか解決しようと調べたりしてるんですが、解決策は特に思いつかず… どういう構造体なのか謎のままなので細かいところは自分で考えて貰うことになりますが…。 構造体の中身だけ書き出し、読み込む時はmalloc()なりでメモリを確保しながら読み込んでリスト構造に追加していく…ってところでしょう。 構造体定義をまるっと書き出すとポインタの問題以外に無駄なデータの書き出しも発生するでしょうしね。 パディングとか、文字列記憶のために固定長の配列があれば'\0'以降のゴミとか。 # うっかりその固定長の領域にパスワードなり記録していたらそれもゴミとして書き出されますよ。 私は固定長のバッファでも文字列長(WORDサイズで書き出しなど)して、その後にそのままベタで書き出し…とかやってますね。 1つのリスト構造の書き出しサイズは一定にならないんでファイル内でシークして…という手は使えなくなりますけど。 # 検索するならどうせメモリにリスト構造で読み込んでから…なのでシークの必要がない。 # どうしてもファイル内でシークが必要ならサイズ情報を拾いながら都度都度シークしていく。 # まぁその場合はリスト構造1つ分のサイズを予め算出して書き出しておきますけどね。
- Wr5
- ベストアンサー率53% (2173/4061)
>少し長くなりますが、こちらが正確です・・・ 掲示されている「だけ」の範囲であれば、とりあえず動くこんじゃないですかね。 リスト構造体をまるっとファイルに書き出し&読み込みしているっぽいところは、かな~り怪しいですが…。 # ポインタ変数の内容をファイルに書き出して、次に読み込んだときにそのポインタが有効かはかなりギモン。 あと、リスト構造なら終端はHEAD->NEXT_PTRがNULLになっている。というのが一般的かと思われます。 # まぁどう作ろうと自由ですが。 なお… file(1,~,○○)とfile(2,~,○○)はセットにしないと正常動作しませんな。 file(1,~,○○)のあとにfile(2,~,××)してfile(1,~,○○)だと多重オープンですし。 # まぁ、第3引数で私ファイルポインタが別でも対象になるファイル名は固定みたいなんでどっちみち多重オープンの余地は残されていますな。 printf()デバッグでもしてfile()のコールされている状態確認してみたらどうですか? 私ならファイルのオープンとクローズは一つの関数内に纏めますね。 もちろん読み書きも…ですが。 ファイルをオープンしたまま関数から抜ける。という事態はなるべく避けます。 # 読み書きの実体を関数として切り出すことはありますけどね。長くなったりするときとか。 多重オープンではなくNTFSのファイル権限が変…という可能性もありますかね。 新規作成は可能でファイルの更新は禁止…なんていうアクセス権が設定できたかはちと調べ切れていませんが。 # Linuxとかだと、上位ディレクトリのパーミッションによっては妙な事になることもあったような。 # http://wiki.bit-hive.com/north/pg/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8%A5%EA%A4%CE%A5%D1%A1%BC%A5%DF%A5%C3%A5%B7%A5%E7%A5%F3 # -wで読み書きは可能だけど新規作成や削除は不可…ですな。 Windowsでそれが可能かは不明。
- wormhole
- ベストアンサー率28% (1626/5665)
#3の補足を読む限りいろいろ問題ありそうです。 ポインタ値をそのままファイルに書き込み、読み込み時にもそのまま使用してるようですし(シリアライズ、デシリアライズがされてない)。 読み込み処理がされた場合の読み込まれたポインタ値はあてになりませんので、「さまざまな処理」とコメントされてる部分もまともに動作してない可能性が高いです(というよりほぼ100%してないと思う)。 あと余計な突っ込みになりますがfile()関数で読み込みオープン、書き込みオープン、クローズをすべて行わせることで可読性が落ちてると思います。マジックナンバーも使われてますし。
補足
>ポインタ値をそのままファイルに書き込み、読み込み時にもそのまま使用してるようですし >(シリアライズ、デシリアライズがされてない)。 >読み込み処理がされた場合の読み込まれたポインタ値はあてになりませんので、 >「さまざまな処理」とコメントされてる部分もまともに動作してない可能性が高いです >(というよりほぼ100%してないと思う)。 おっしゃる通りです。 シリアライズ、デシリアライズの概念?と実装方法について勉強しようと思い、サイトをいろいろ見て回りましたが、C++向けの解説サイトが多くC向けのものがありません。 以下のサイトはC++ですが、Cでの実装方法を学ぶのに問題ありませんでしょうか? http://omt.sourceforge.jp/serializer.html C++の知識、オブジェクト指向概念の知識は皆無に等しいです。
- Wr5
- ベストアンサー率53% (2173/4061)
既に指摘されてますが… if文中でfopen_s()でオープン試みて成功した場合に二重にオープンしようとしてますね。 共有モードが排他であれば「既にオープン済み」ってコトで2回目のfopen_s()はエラーになるでしょう。 共有モードを正しく設定する必要があります。 『どうしても多重オープンする必要がある』のであれば…ですが。 そして、先にオープンしたハンドルはめでたくリソースリークです。 # プロセスが終了したときにOSが回収してくれるとは思われますが…。お行儀いいとは言えないですかねぇ…。 ところで…… >//エラーメッセージ >printf("OPEN ERR. - %d\n ERR^Msg: %s\n",error,strerror(error)); >fclose(DATA); と >printf("MAKE FILE, Please. Err- %d\nERR^Msg: %s\n",error,strerror(error)); >Sleep(0); >fclose(DATA); は正しく通過できるんですか? 手元のVC++2010EEでは前者(のfclose())がNULLポインタで怒られましたけど。
補足
少し長くなりますが、こちらが正確です・・・ ///BOOLEAN #define FALSE 1 #define FALSE_ALLOC 2 #define TRUE 0 ///File Name #define __FILENAME__ "G:\\Documents and Settings\\アカウント名\\デスクトップ\\DATA.u" int file(const int mode_OpCl, char const *mode_WR, FILE **DATA){ errno_t error; if (mode_OpCl == 1){ if(strcmp(mode_WR,"_READ") == 0) { if((error=fopen_s(DATA,__FILENAME__,"rb")) != 0) { printf("\t***** Cannot Open FILE ****\n\t* File OPEN ERR. - %d *\n\t* ERR^Msg: %s *\n\t*****************************\n",error,strerror(error)); Sleep(0); //Make Sound return FALSE; } } else if(strcmp(mode_WR, "_WRITE") == 0) { if((error=fopen_s(DATA,__FILENAME__,"wb")) != 0) { printf("MAKE FILE, Please.Err- %d\nERR^Msg: %s\n",error,strerror(error)); Sleep(0); //Make Sound return FALSE; } } } else if (mode_OpCl == 2) { fclose(*DATA); } else{puts("ERROR argument.");} return TRUE; } extern int WRITE_DATA(list_t *POS_HEAD, int W_COUNTER, FILE *fp){ if(fwrite(POS_HEAD, sizeof(list_t), W_COUNTER, fp) ==W_COUNTER) { puts("Cannot Write."); return FALSE; } return TRUE; } extern int READ_DATA(list_t *LIST, int R_COUNTER, FILE *fp){ if(fread(LIST, sizeof(list_t), R_COUNTER, fp) ==R_COUNTER) { puts("Cannot Read."); return FALSE; } return TRUE; } extern int COUNTER(list_t *HEAD){ int COUNT = 0; while(HEAD->STATUS != NULL) ///構造体の数を数えます { ++COUNT; HEAD = HEAD->NEXT_PTR; } return COUNT; } int main(void){ puts("\nReading......\n"); if((file(1, "_READ", &DATA)) == FALSE) { printf("There is NO DATA FILE. But if you said...\n\t1. I have made DATA FILE yet, move the file in this directory.\n\t"); printf("2.I have not made DATA FILE, Please Make the file .\n"); } READ_DATA(R_LIST, amount, DATA); file(2, NULL,&DATA); /* さまざまな処理。 */ //書き込み file(1, "_WRITE", &DATA); //ここでエラーが起こり、停止します。「MAKE FILE, Please.Err- 13 ERR^Msg: Permission denied」 count = COUNTER(HEAD); WRITE_DATA(HEAD, count+1, DATA); //先頭から書き込む(読み込んだデータも処理済) file(2, NULL,&DATA); }
- wormhole
- ベストアンサー率28% (1626/5665)
>/*書き込み*/ >//ファイルが存在しなかったしなかった場合 >if((error=fopen_s(&DATA,上述のディレクトリ,"wb")) != 0) >{ fopen_sが成功した場合、それをfcloseせずにfopen_sしてるようですが。 そもそもfopen_sしなおす必要ありますか? 読み込み時も同様。 また >//既存ファイルから読み込んだ構造体の数をCOUNTERに代入 >fread(読み込み用の構造体ポインタ, sizeof(構造体サイズ), COUNTER, DATA); はCOUNTERに代入などされませんよ。
- wormhole
- ベストアンサー率28% (1626/5665)
1 の読み込み処理後にクローズせずに、3で上書きしようとしてるとかでは? こういう場合は、流れを書かれるのではなく処理を簡略化して同様のエラーが出るコードを書かれた方がいいです。流れを書いたつもりでも実際のコードはそうなってないという事もありますから。
お礼
間違いがありましたので、こちらを・・・すみません。 ------------------------------------ FILE *DATA; errno_t error; /*読み込み*/ //ファイルが存在しなかった場合 if((error=fopen_s(&DATA,"G:\\Documents and Settings\\アカウント名\\デスクトップ¥\DATA.unm","rb")) != 0) { //エラーメッセージ printf("OPEN ERR. - %d\n ERR^Msg: %s\n",error,strerror(error)); fclose(DATA); } //ファイルが存在する場合 else { fopen_s(&DATA,"上述のディレクトリ","rb") //既存ファイルから読み込んだ構造体の数をCOUNTERに代入 fread(読み込み用の構造体ポインタ, sizeof(構造体サイズ), COUNTER, DATA); fclose(DATA); } /* 以降構造体の作成、検索、削除などの処理。 */ /*書き込み*/ //ファイルが存在しなかったしなかった場合 if((error=fopen_s(&DATA,上述のディレクトリ,"wb")) != 0) { printf("MAKE FILE, Please. Err- %d\nERR^Msg: %s\n",error,strerror(error)); Sleep(0); fclose(DATA); } else { fopen_s(&DATA,上述のディレクトリ,"wb"); //ここでエラーが起こる。 //ここまでに作成した構造体リストの数をカウントしてint型変数countに代入 fwrite(HEAD, sizeof(構造体のサイズ), count, DATA); fclose(DATA); } -----------------------------
補足
コードが2000行近くあって、関数もたくさんあるので簡略化の仕方がわからずこうなってしまいました・・・ ファイル処理の流れはこの二つです。 ------------------------------------ FILE *DATA; errno_t error; /*読み込み*/ //ファイルが存在しなかった場合 if((error=fopen_s(&DATA,"G:\\Documents and Settings\\アカウント名\\デスクトップ¥\DATA.unm","rb")) != 0) { //エラーメッセージ printf("OPEN ERR. - %d\n ERR^Msg: %s\n",error,strerror(error)); fclose(DATA); } //ファイルが存在する場合 else { //既存ファイルから読み込んだ構造体の数をCOUNTERに代入 fread(読み込み用の構造体ポインタ, sizeof(構造体サイズ), COUNTER, DATA); fclose(DATA); } /* 以降構造体の作成、検索、削除などの処理。 */ /*書き込み*/ //ファイルが存在しなかったしなかった場合 if((error=fopen_s(&DATA,上述のディレクトリ,"wb")) != 0) { printf("MAKE FILE, Please. Err- %d\nERR^Msg: %s\n",error,strerror(error)); Sleep(0); fclose(DATA); } //ここでエラーがでて停止する else { //ここまでに作成した構造体リストの数をカウントしてint型変数countに代入 fwrite(HEAD, sizeof(構造体のサイズ), count, DATA); fclose(DATA); } ----------------------------- これでなんとかお願いできますでしょうか・・・
補足
wormholeさんもおっしゃっているとおり、リスト構造自体をバイナリデータに書き出している部分は、正常動作しません。どうにか解決しようと調べたりしてるんですが、解決策は特に思いつかず… >あと、リスト構造なら終端はHEAD->NEXT_PTRがNULLになっている。というのが一般的かと思われます。 確かにそうなんです。でも循環リストを作る時に始めと最後の構造体をHEADとENDとした場合、 END->NEXT_PTRにはHEADが入っているので無限ループに陥ってしまいます… breakしようかとも思ったのですが、ループ中のHEADポインタがENDを指すタイミングを確実に判断できず… とりあえずはchar型のポインタを利用して、END構造体の場合のみNULLを代入することでそれを終了条件としています。 >printf()デバッグでもしてfile()のコールされている状態確認してみたらどうですか? やってみます。 >私ならファイルのオープンとクローズは一つの関数内に纏めますね。 >もちろん読み書きも…ですが。 >ファイルをオープンしたまま関数から抜ける。という事態はなるべく避けます。 オープンとクローズをまとめることにします。 ファイル権限は大丈夫でした。 ありがとうございました。