C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: 一時ファイル作成後にアプリケーション設定取得ができない


(過去ログ 125 を表示中)

[トピック内 4 記事 (1 - 4 表示)]  << 0 >>

■74595 / inTopicNo.1)  一時ファイル作成後にアプリケーション設定取得ができない
  
□投稿者/ m-s (1回)-(2015/01/11(Sun) 14:57:10)

分類:[C#] 

[環境]
・Windows7 64bit
・Visual Studio Express 2013 for Windows Desktop
・Windowsフォームアプリケーション (C#)

[現象/疑問]
下に記載した条件で、一時ファイル作成後にアプリケーション設定を取得しようとすると、例外(SettingsPropertyNotFoundException)が発生します。
ただし、10回に1回くらいの確率で、例外が発生せずに動作することがあります。

色々試してみると、[条件]の下に記載した方法だと例外が発生しなくなったのですが(※1)
一時ファイルの作成と、アプリケーション設定との関連がわかりません。
(※1 5〜10回ほど実行した限りでは発生しなかった)

なぜ例外が発生したのでしょうか。
一時ファイルの作成と、アプリケーション設定とに何か関連があるのでしょうか。

[条件]
・.NET Framework 3.5
・Win32APIのGetTempFileNameで一時ファイル作成後に
  アプリケーション設定を取得しようとする
・一時ファイル作成前にはアプリケーション設定の取得を1度もしていない

-> プロジェクトのプロパティから「対象のフレームワーク」を 4.0 や 4.5 にすると
   例外は発生しませんでした。
-> 一時ファイル作成をWin32APIではなく System.IO.Path.GetTempFileName に置き換えた場合も
   例外は発生しませんでした。
-> 一時ファイル作成より前にアプリケーション設定を取得したことがあると
   一時ファイル作成後でも、アプリケーション設定を取得できました。

[例外 1]
型 'System.AccessViolationException' のハンドルされていない例外が System.dll で発生しました

追加情報:保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。

[例外 2] (自動生成されたSettingsクラスの中に以下の情報が入っていました)
'this.iCountRun' は、型 'System.Configuration.SettingsPropertyNotFoundException' の例外をスローしました。

[例外発生手順]
1) プロジェクト作成 (Windowsフォームアプリケーション)
2) 対象のフレームワークを「.NET Framework 3.5」に変更
3) アプリケーション設定に int型で「iCountRun」を設定(スコープ:ユーザ、初期値:0)
4) 自動生成された Form1 に下記コードを挿入
5) 実行 -> 起動時に例外が発生する

[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
private static extern uint GetTempFileName(string sPath, string sPrefix, uint uiUnique, StringBuilder bufFileName);

public Form1()
{
    InitializeComponent();

    // 試したこと その1
    // (1)の前にアプリケーション設定を取得すると
    // (2)でも問題なく取得できるようになる
    // int iCountDummy = Properties.Settings.Default.iCountRun;

    // (1) 一時ファイルを作成
    string sFileNameLog = GetTempFileName();
    System.Console.WriteLine(sFileNameLog);

    // 試したこと その2
    // もしかするとアプリケーション設定も一時ファイルを使用していて
    // タイミングが影響しているのかと思い Sleep を入れてみたが
    // 例外発生は解消されなかった
    // System.Threading.Thread.Sleep(3000);

    // (2) アプリケーション設定を取得(ここで例外発生)
    int iCount = Properties.Settings.Default.iCountRun;
    iCount++;

    Properties.Settings.Default.iCountRun = iCount;
    Properties.Settings.Default.Save();
}

private static string GetTempFileName()
{
    // 一時ファイルを作成する
    // 接頭辞を指定するためにWin32APIを使用
    string sPrefix = System.Windows.Forms.Application.ProductName;
    string sPathTemp = System.IO.Path.GetTempPath();
    StringBuilder bufFileName = new StringBuilder();

    uint uiRet = 0;

    // 試したこと その3
    // ↓この1文をコメントアウトすると例外が発生しなくなる
    uiRet = GetTempFileName(sPathTemp, sPrefix, 0, bufFileName);

    string sFileName = null;
    if (uiRet != 0)
    {
        sFileName = bufFileName.ToString();
    }

    // 試したこと その4
    // 上記の代わりに下記の方法で一時ファイルを作成した場合も
    // 例外は発生しなかった
    // string sFileName = System.IO.Path.GetTempFileName();

    return sFileName;
}


引用返信 編集キー/
■74596 / inTopicNo.2)  Re[1]: 一時ファイル作成後にアプリケーション設定取得ができない
□投稿者/ tinq (28回)-(2015/01/11(Sun) 15:48:05)
No74595 (m-s さん) に返信
・GetTempFileNameは成功していますか?失敗しているならDllImportにSetLastErrorをtrueにして、Marshal.GetLastWin32Errorを取得するとどうなりますか?
・DllImportにCharSetを追加
・StringBuilderのコンストラクタに十分な容量を指定(MAX_PATHの260ぐらい)
あたりではどうなるでしょうか。

どうしてもダメならPath.GetRandomFileNameでランダムな名前を取得して、自分でプレフィックスを追加し、同じファイル名がないのを確認して作成するのがいいかもしれません。
引用返信 編集キー/
■74597 / inTopicNo.3)  Re[1]: 一時ファイル作成後にアプリケーション設定取得ができない
□投稿者/ Azulean (406回)-(2015/01/11(Sun) 17:14:25)
No74595 (m-s さん) に返信
> [例外 1]
> 型 'System.AccessViolationException' のハンドルされていない例外が System.dll で発生しました
>
> 追加情報:保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。

AccessViolationException が発生すると言うことは、本来、確保しておくべきメモリを確保していなかったり、足りなかったり、あるいは間違った渡し方をすることでその関数の実行に失敗するか、他のオブジェクトなどが使っているメモリを壊してしまうかという被害が発生します。
今回のケースでは StringBuilder にあらかじめ確保しておく文字列バッファ(260 文字ぐらい?)を用意していなかったため、別の場所のメモリが破壊され、Settings クラスの動作の妨げになったのでしょう。

Win32API を呼ぶ場合、メモリ(バッファ)の確保はどうなるのかは気にしていただいた方が良いです。
あるいは多くのサンプルを確認してみて、確かそうなやり方を見つけるといったところでしょうか。
引用返信 編集キー/
■74598 / inTopicNo.4)  Re[2]: 一時ファイル作成後にアプリケーション設定取得ができない
□投稿者/ m-s (2回)-(2015/01/11(Sun) 17:49:59)
StringBuilderのコンストラクタに 260 を指定すると
例外が発生しなくなりました。

No74596 (tinq さん) に返信
> ・GetTempFileNameは成功していますか?失敗しているならDllImportにSetLastErrorをtrueにして、Marshal.GetLastWin32Errorを取得するとどうなりますか?
GetTempFileNameの戻り値は 0以外 のため、成功していると思います。
ファイルも作成されているため、正常終了しているように見えます。
SetLastError=trueを設定して、GetLastWin32Errorで値を取ると、0 が返ってきました。

> ・DllImportにCharSetを追加
CharSet.Auto、Ansi、None、Unicode を試してみましたが変化ありませんでした。

> ・StringBuilderのコンストラクタに十分な容量を指定(MAX_PATHの260ぐらい)
> あたりではどうなるでしょうか。
260を指定すると、例外が発生しなくなりました。

No74597 (Azulean さん) に返信
> AccessViolationException が発生すると言うことは、本来、確保しておくべきメモリを確保していなかったり、足りなかったり、あるいは間違った渡し方をすることでその関数の実行に失敗するか、他のオブジェクトなどが使っているメモリを壊してしまうかという被害が発生します。
> 今回のケースでは StringBuilder にあらかじめ確保しておく文字列バッファ(260 文字ぐらい?)を用意していなかったため、別の場所のメモリが破壊され、Settings クラスの動作の妨げになったのでしょう。
>
> Win32API を呼ぶ場合、メモリ(バッファ)の確保はどうなるのかは気にしていただいた方が良いです。
> あるいは多くのサンプルを確認してみて、確かそうなやり方を見つけるといったところでしょうか。
一時ファイル作成とアプリケーション設定との関連が不明だったのですが
メモリ破壊という説明ですっきりしました。
Win32APIを使用する際は、メモリの状態に気をつけるようにします。


GetTempFileNameでファイルは作成されていたことと、ファイル名も取れていたため
StringBuilderの容量の問題については気がつきませんでした。
(ファイル名が取得できていたため、指定しなくても勝手に拡張してくれるんだなぁと思ってました)

tinq さん、Azulean さん
ありがとうございました。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -