none
GetPrivateProfileInt関数がネットワークドライブ上のiniファイルの情報を取得できない場合がある RRS feed

  • 質問

  • ネットワークドライブ上のiniファイルに

    [AAAA]
    bbbbbb=3

    の記述があるのですが、GetPrivateProfileInt関数が 0を返すことがあります。

        UINT uVal = GetPrivateProfileInt("AAAA", "bbbbbb", 3, ”R:\AppFolder\data\AppSetting.ini”);

        (Rドライブは、プログラム起動時に WNetAddConnection2関数で接続しています。)



    毎回ではないのですが、プログラムに重要な条件に必要な値であるため、困っています。

    ユーザーの環境でしか発生しておらず、頻度も低いため、再現テストもままならず、自身のプログラムのバグを疑い調査しているのですが、原因がつかめずじまいです。

    iniファイルの格納先がネットワークドライブ上であることや、iniファイルのパスがUNCでない等で、不安定になる要因があるのでしょうか?

    宜しくお願いします。

    2011年2月23日 6:17

回答

  • 今回、GetPrivateProfileInt がなぜか想定しない値を返すこと、それにもかかわらずに正常終了となることを問題提起されていたはずです。
    正直、ネットワークの物理的なトラブルによって、読み込みが正常終了しているのにもかかわらず、あり得ない値が取得されるという事態は、検知しようがないです。「ネットワークが不安定だから」という理由で片付けると、ほかのファイルアクセスの信頼性も担保できないのでネットワークを使うなという結論になりかねないように思います。

    確かに、切断されること、経路に問題が生じることは考慮すべきことです。
    これらは API などがエラーを上げること、例外をスローすることで検知し、適切なエラー処理を実装することでカバーする話だと思います。
    今回はエラーも上げない、例外も投げないのに結果が不正になるということで、ネットワーク環境の信頼性の問題よりも、OS / API の信頼性の話が先に出てくるのではないかと思います。
    もちろん、ネットワークに何らかの問題があって、OS / API のバグでそれを検知できないという可能性もありますが、まずは OS / API の層ですよね。

    さて、それを踏まえて何かできるかと言われると、難しいです。
    GetPrivateProfileInt を使わなかった場合にエラーを検知できる、あるいは正常に読み込めるというところであれば、GetPrivateProfileInt に不具合があると推定して、使わないと言うことで回避するのも一手だと考えます。
    やりたいことは原因究明ではなく、問題の解消、回避であろうからです。
    原因究明はコストばっかりかかって、「不具合ですが、直しません」とか言われたりすることもあり得ますので。

    # 仕事柄、社内のファイルサーバーを利用する身からすると、ネットワークトラブルでファイル I/O がエラーも上げず、
    # 不適切な動作をされたらファイルサーバー不信になりかねません、ははは…。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 周パパ 2011年3月11日 2:44
    2011年3月8日 14:35
    モデレータ

すべての返信

  • GetPrivateProfileInt関数が 0を返すことがあります。

    これは確実なのでしょうか?
    どういった方法で「GetPrivateProfileInt 関数が 0 を返す」という事実を突き止めたのでしょうか?
    (この一行だけでは、uVal が 0 になる、ほかの要因の有無がわからないためにお聞きしました)

    iniファイルの格納先がネットワークドライブ上であることや、iniファイルのパスがUNCでない等で、不安定になる要因があるのでしょうか?

    可能性はありますが、正直何とも言えません。
    API の動作としては読み込めなかったときなどはデフォルト値を返すなので、0 を返すことがあるとしたら何らかの不具合の可能性があります。
    本当に 0 を返しているかの裏付けをきちんと取ること、再現環境をなんとか構築することを満たせないと、Microsoft のサポートに頼るのも難しいのではないかと思っています。

    ところで、その GetPrivateProfileInt を実行する段階で、R ドライブがきちんとアクセス可能になっていることは前提として良いのでしょうか?


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年2月23日 13:27
    モデレータ
  • Azuleanさん、ご返信ありがとうございます。

    期待どおりだと 3 が得られて処理が進むのですが、そうならない。

    GetPrivateProfileInt 関数の呼出し直後に、0 ならメッセージボックスを表示するようにしてみたところ、毎回ではないですがメッセージボックスが表示されましたので、0 が返ってきていると判断しました。

    R ドライブはプログラム起動時に接続し、直前の処理でR ドライブ上のファイルに(CFileクラスで)書き込みは成功し、ファイルの内容も確認しましたが問題ないようでした。

    再現性はあるのですが、いつ発生する分かりません。

    同じ操作を行っても、この現象が発生する場合としない場合があります。

    プログラム内のバグの可能性も否定できないと、データの状態を慎重に考慮しながらソースを追っかけて見てはいるのですが、バグらしきロジックは見つけ出せず。

    他のユーザーでは発生しないため、手掛かりが少なくお手上げ状態です。



    環境で一つ気になるところはあるのです。

    同じユーザーの同じネットーワーク上で、ネットワークドライブ上にあるmdbファイルを使用するプログラムで原因不明の不具合が発生していたのですが、DBをoracleに変更したところ問題が解決したようなのです。

    簡単にDBの変更ができたようなので、ODBCデータソースを使用してDBにアクセスするプログラム(?)だったのでしょう。

    私のプログラムでも、共有ファイルの書き込みで考えられない現象が発生したため、共有設定のオフラインのキャッシュをオフラインで使用しない設定に変更したところ、問題が発生しなくなりました。

    どうも、ユーザーのネットワーク環境ではファイル共有に問題がありそうなのですが…


    2011年2月24日 1:08
  • 0 が返ってきたときに GetLastError したら何らかのエラーコードが返ってくるのでしょうか。

    ところで、

    UINT uVal = GetPrivateProfileInt("AAAA", "bbbbbb", 3, ”R:\AppFolder\data\AppSetting.ini”);


    実際のコードでは、パスの区切りはちゃんと "\" を2個つなげているのですよねぇ。
    2011年2月24日 1:49
  • totojoさん、、ご返信ありがとうございます。

    GetPrivateProfileInt関数のヘルプには、GetLastErrorで拡張エラーが取得できるような記述がなかったので、GetLastErrorの値は調べていません。

    やってみる価値はあるのかもしれませんね。

    実際のファイルパスは大丈夫です。失礼しました。


    2011年2月24日 3:00
  • 1.GetPrivateProfileStringで文字列として取得すると、どのような文字列が取得されますか?
    2.Applicationから0をiniファイルに書きこむ機会がありますか?あるのなら、その機会との整合性は取れていますか?また、書き込んだ直後にGetPrivateProfileIntを呼ぶと、期待した値が取得できますか?
    3.0が取得されたタイミングで、直後に何度も繰り返しGetPrivateProfileIntを呼ぶと、3が取得できることがありますか?
    4.0が取得されたタイミング(或いはその前後)で、iniファイルをファイルとして読み込み中身をDumpすると、期待した中身になっていますか?
    5.GetPrivateProfileIntまでに呼んでいるAPI、Method等の戻り値などError Checkはしていますか?

    2011年2月24日 10:58
  • レジストリやシステムINIを併用しないのであれば、GetPrivateProfileInt()のエミュレートはそんなに複雑な処理ではないので、下記のような代替関数を自作して、実際にどこでエラーが発生しているのかをログに残してチェックしてみてはいかがですか?

    std::wifstream::fail()によるチェックを定期的に挟むとか、そもそもセクションやキーは見つかっているのか、など……

    なお、厳密なINIファイルのフォーマットには従っていないので、そのへんはご自分でTR1.Regexを使うなりしてなんとかしてください。

     

    #include <fstream>
    #include <iostream>
    #include <string>
    #include <cstdlib>
    #include <exception>
    #include <cassert>
    #include <locale>
    #include <locale.h>
    #include <direct.h>
    #include <conio.h>
    
    void GetIntValueFromIniFile(const wchar_t* pFilePath, const wchar_t* pSectionName, const wchar_t* pKeyName, std::wstring& strValue, int& value, bool& isValueFound, bool& isValidFormat)
    {
    	strValue = L"";
    	value = 0;
    	isValueFound = false;
    	isValidFormat = false;
    	std::wifstream fs(pFilePath);
    	if (fs.fail())
    	{
    		throw std::runtime_error("Failed to open the file!");
    	}
    
    	const size_t sectionNameLen = wcslen(pSectionName);
    	const size_t keyNameLen = wcslen(pKeyName);
    	std::wstring strBuf;
    	while (!fs.eof())
    	{
    		std::getline(fs, strBuf);
    		if (_wcsnicmp(strBuf.c_str(), pSectionName, sectionNameLen) == 0)
    		{
    			while (!fs.eof())
    			{
    				std::getline(fs, strBuf);
    				if (_wcsnicmp(strBuf.c_str(), pKeyName, keyNameLen) == 0)
    				{
    					if (strBuf.length() <= keyNameLen + 1)
    					{
    						throw std::runtime_error("Target string line does not have enough length!");
    					}
    					if (strBuf[keyNameLen] != L'=')
    					{
    						throw std::runtime_error("Target string line does not have equal sign!");
    					}
    					isValueFound = true;
    					strValue = strBuf.substr(keyNameLen + 1);
    					wchar_t* pEndPtr = NULL;
    					value = wcstol(strValue.c_str(), &pEndPtr, 10);
    					if (pEndPtr != &strValue.c_str()[strValue.length()])
    					{
    						throw std::runtime_error("Failed to scan the string as integer!");
    					}
    					isValidFormat = true;
    					return;
    				}
    			}
    		}
    	}
    }
    
    void main()
    {
    	std::locale::global(std::locale(""));
    	_wsetlocale(LC_ALL, L"");
    	wchar_t fullPathBuf[1024] = {};
    	wchar_t* pBufferPtr = _wgetcwd(fullPathBuf, 1024);
    	assert(pBufferPtr != NULL);
    	std::wstring strFullPath(fullPathBuf);
    	strFullPath += L'\\';
    	strFullPath += L"test.ini";
    	std::wstring strValue;
    	int value = 0;
    	bool isValueFound = false;
    	bool isValidFormat = false;
    	try
    	{
    		GetIntValueFromIniFile(strFullPath.c_str(), L"[AAAA]", L"bbbbbb", strValue, value, isValueFound, isValidFormat);
    		//GetIntValueFromIniFile(strFullPath.c_str(), L"[日本語]", L"ソース", strValue, value, isValueFound, isValidFormat);
    	}
    	catch (const std::exception& err)
    	{
    		std::cout << "Exception: " << typeid(err).name() << ", " << err.what() << std::endl;
    	}
    	std::cout << "IsValueFound = " << std::boolalpha << isValueFound << std::endl;
    	std::cout << "IsValidFormat = " << std::boolalpha << isValidFormat << std::endl;
    	std::wcout << L"ValueStr = \"" << strValue << L"\"" << std::endl;
    	std::cout << "Value = " << value << std::endl;
    	std::cout << "Press any...\n";
    	_getch();
    }
    
    
    2011年2月24日 16:21
  • 私個人としては、ネットワークドライブとローカルドライブで同等の信頼性を期待するのは難しいのでは
    と言うのが正直な所です。ネットワークドライブの場合、ネットワーク越しでのアクセスになる関係上、
    アクセスがうまく行かない可能性がある前提でプログラムするべきだと考えています。

    例えば、アクセスがうまく行かない場合にリトライするとか、
    その時はうまく行かなくても再度読み込み動作を行って、それで読み込めれば
    その後は正常に動作する等々。

    処理にとって非常に大切な値を単純にネットワーク越しのGetPrivateProfileIntのみで
    よしとするのは信頼性にかけるのではと思います。
    どうしても取得がうまく行かないと処理が出来ないようなものなのであれば、
    取得出来ていない事を検出できる方法をとって、その上でうまくいかない場合は
    どうするのかと言う設計が必要なのではと思います。

    ちょっと大掛かりになりますけれど、
    ファイルその物はローカルに置いておいて起動時に特定のサーバー上のファイルと
    ローカルファイルの内容を付き合わせてチェックし、サーバー上のファイルが更新されていたら
    ローカルにコピーするような方法を取るの一つの手ではと思います。
    少なくともGetPrivateProfileIntのアクセス先がローカルであれば、
    ネットワーク絡みのアクセスエラーは気にしなくてよくなります。
    但し、上のファイルの同期を取る部分に関してはネットワーク越しになりますから
    ネットワークに起因するエラーを十分考慮した上で設計するべきだと思います。

     


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答の候補に設定 山本春海 2011年3月3日 6:22
    • 回答としてマーク 周パパ 2011年3月8日 0:50
    • 回答としてマークされていない 周パパ 2011年3月8日 1:03
    • 回答の候補の設定解除 周パパ 2011年3月8日 1:03
    2011年2月28日 2:42
  • Kozzさん、ご返信ありがとうございます。

    出張中で返信が遅れてしまい、大変失礼いたしました。
    1以外、指摘していたことは、すべて確認済みです。

    PATIOさんのご指摘にもあったように、「ネットワークに起因するエラーを十分考慮した上で設計するべき」なのだと思います。

    助言、ありがとうございました。

    2011年3月8日 0:58
  • syghさん、ご返信ありがとうございます。

    出張中で返信が遅れてしまい、大変失礼いたしました。

    自作の関数を作って準備はしているのですが、まだ適用するまでには至っていません。

    エラーを特定するためにも、syghさんの関数を参考にさせていただいて、調査してみようと思います。

    助言、ありがとうございました。

    2011年3月8日 1:04
  • PATIOさん、ご返信ありがとうございます。

    出張中で返信が遅れてしまい、大変失礼いたしました。

    「ネットワークに起因するエラーを十分考慮した上で設計するべき」なのだとは思いますが…

    不安定さを考慮してプログラムを作ったとして、例えば、リトライをするようにしたとして、リトライが起こる場合処理に時間がかかれば、ユーザーは「なぜ遅い?」と追求します。

    ネットワークの不安定さが原因と説明しても、「なぜ不安定なのか?」と。

    ネットワークドライブを割り当てて共有ドライブでの処理だけが不安定だと、それ以外で問題がなければ、ユーザーはなかなか信じてくれません。

    ネットワーク越しに単純なファイルIOは信頼性に欠けるとなれば、共有の機能はどこまで信頼性があるのでしょう?

    ご指摘いただいた「ローカルに置く」という案は検討中の案でもあり、今後の参考にさせていただきたいと思います。

    現状では、ユーザーも「プログラムの不具合というよりもネットワーク環境か?」という感じになってきています。

    ユーザーが望んでいるのは、スピードも含め期待どおりに動作することなので、共有ドライブへのアクセスが不安定な原因を探ったほうがよいのかもしれませんね。

    助言、ありがとうございました。

    2011年3月8日 1:35
  • ネットワークを介したアクセスの場合、ソフト面だけではなく、ネットワーク環境やHW面の要素も絡んでくるために
    状況はより複雑になります。
    例えばですが、内蔵HDDをユーザー外してしまうと言うケースはまず考えられませんし、
    一般的にそういう状況は考えませんが、ネットワークケーブルの断線やネットワークケーブルが抜けていたなんて
    事は容易に起こりえます。
    切れ掛かったネットワークケーブルが原因で不安定になるケースもありますし、
    ネットワークケーブルのコネクタ部分の爪が折れていて本体を動かした拍子に抜けていたなんて
    話も考えられると思います。

    また、ネットワーク環境に関しても実際の現場のトラフィックに適応した状況にあるかどうか、
    ネットワーク環境内にボトルネックが存在していて特定のセグメント内でアクセスが集中した場合に
    接続できない状況が発生する等、ネットワークであるが故の問題点はいくつも挙げられます。

    ネットワーク設計時には十分であった帯域も見直しが滞れば、不十分になっている可能性もあります。
    ネットワークはあくまでも複数のユーザーの共有資源ですから利用状況は刻々と変化します。
    ある時点では全く問題なく接続で来ていた物が特定のユーザーが大量のデータ転送を行ったために
    ネットワーク上の遅延が発生するなんてケースもありえます。

    通常ネットワーク上でのデータのやり取りに関してはある程度エラーが起こる事を前提に設計されており、
    その為のプロトコルなのですが、プロトコルでも吸収しきれないケースはありえます。
    仮にネットワーク上の共有ドライブにアクセスする時に頻繁に読み取りが出来ないケースがあるのであれば、
    ネットワーク環境として実際の現場の利用状況にあった設定がなされているのかと言う点は確認した方が
    良いと思います。帯域や同時接続数、HUBやルーターの性能限界等考えられる事がとても多いと思います。

    ネットワーク絡みの話をお客さんに説明するのは骨かもしれませんね。
    担当者が詳しい方なら解って貰えるかも知れませんけれど、詳しく無い方なら難しい話になりますし。
    ただ、ローカルに問題の設定ファイルを置いて見て全く問題が起きなくなったら納得してくれませんか。
    単純にプログラムを全く弄らずにローカルの設定ファイルを設けて設定ファイルの参照先をネットワークドライブか
    ローカルかを切り替えるだけで問題が起きなくなるとしたらわかってもらえそうな気がしますけれど。

    ユーザーからしたらうまく動いてくれれば良い話なので、逆にネットワーク共有にこだわる必要は無いようにも
    感じます。他の方法で同じ効果が出せるならそれでも良いわけですし。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    2011年3月8日 2:19
  • 今回、GetPrivateProfileInt がなぜか想定しない値を返すこと、それにもかかわらずに正常終了となることを問題提起されていたはずです。
    正直、ネットワークの物理的なトラブルによって、読み込みが正常終了しているのにもかかわらず、あり得ない値が取得されるという事態は、検知しようがないです。「ネットワークが不安定だから」という理由で片付けると、ほかのファイルアクセスの信頼性も担保できないのでネットワークを使うなという結論になりかねないように思います。

    確かに、切断されること、経路に問題が生じることは考慮すべきことです。
    これらは API などがエラーを上げること、例外をスローすることで検知し、適切なエラー処理を実装することでカバーする話だと思います。
    今回はエラーも上げない、例外も投げないのに結果が不正になるということで、ネットワーク環境の信頼性の問題よりも、OS / API の信頼性の話が先に出てくるのではないかと思います。
    もちろん、ネットワークに何らかの問題があって、OS / API のバグでそれを検知できないという可能性もありますが、まずは OS / API の層ですよね。

    さて、それを踏まえて何かできるかと言われると、難しいです。
    GetPrivateProfileInt を使わなかった場合にエラーを検知できる、あるいは正常に読み込めるというところであれば、GetPrivateProfileInt に不具合があると推定して、使わないと言うことで回避するのも一手だと考えます。
    やりたいことは原因究明ではなく、問題の解消、回避であろうからです。
    原因究明はコストばっかりかかって、「不具合ですが、直しません」とか言われたりすることもあり得ますので。

    # 仕事柄、社内のファイルサーバーを利用する身からすると、ネットワークトラブルでファイル I/O がエラーも上げず、
    # 不適切な動作をされたらファイルサーバー不信になりかねません、ははは…。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 周パパ 2011年3月11日 2:44
    2011年3月8日 14:35
    モデレータ
  • すいません、大事な所を読み落としていました。

    既定値として3を渡しているのに0を返していると言う部分を読み落としていたのでずれた話をしていました。
    確かに0を返しているとしたら関数仕様からしたらバグですね。
    そういう意味ではそういう目にあった事が無いので参考になる意見はだせません。すいません。

    そういう目にあった事が無いという背景には問題のiniファイルから情報を取り出す関数をネット越しに
    使用した事が無いという事情もあります。エクスプローラーのネット越しのファイルコピー等で
    結構痛い目にあっているのでネット越しにこれらの関数を使う事に不安があるというのも理由のひとつです。
    エクスプローラーのファイルコピーならコピー先の状態を確認して再オペで何とかなりますけれど、
    プログラムの中でこれをやられるとお手上げになってしまうので。
    実際の話、内部作成したコピーツールは涙ぐましいほどのコピー後チェックをしています。
    相手がLinuxのsbmだと顕著に出るみたいですが、Windowsサーバーなら完全にOKというわけでも無いようです。

    そういう意味では、私の所のネットワーク環境では事実上起こりえない話ではないです。
    高頻度で起こるわけでは有りませんけれど。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    2011年3月9日 0:44
  • Azuleanさん、ご返信ありがとうございます。

    >やりたいことは原因究明ではなく、問題の解消、回避であろうからです。

    そのとおりですね。

    先のことを考えれば原因究明も必要なのでしょうが、今現在優先すべきは問題の解消、回避であり、ユーザーも我々もそれを望んでいます。

    いろいろ助言をいただき、ありがとうございました。

    2011年3月11日 2:40
  • PATIOさん、ご返信ありがとうございます。

    再現はするのですが、確実に再現させる方法は見つかっておらず…

    とにかく何かしらの方法で早く回避したいと思います。

    プログラムでできる限りの対策をして、様子を見てみます。

    いろいろ助言をいただき、ありがとうございました。

    2011年3月11日 2:44