- - PR -
インデックスが配列の境界外です。
投稿者 | 投稿内容 | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2009-04-13 11:52
ASP.NETで開発しているのですが、以下のコードで「System.IndexOutOfRangeException: インデックスが配列の境界外です。」というエラーが発生します。今まで6か月間動作していますが、このエラーは初めてで再現性が
ありません。6か月間毎日動作していて、先日1回発生しただけです。 別に配列も参照していないと思うのですが、どなたかエラー原因の可能性を教えて 頂けませんでしょうか。 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Public Shared Sub sMsgRead() Dim sb As StringBuilder Dim wRet As UInt32 Dim wSec() As String = {"INFO", "WARNING", "ERROR"} ←ここでエラーが発生します。 Dim wKey() As String = {"INF", "WAR", "ERR"} Dim wKeyStr As String Dim wLoop As Integer Dim wStr As String Dim wCnt As Integer ReDim mMsgI(0) ReDim mMsgW(0) ReDim mMsgE(0) mMsgCntI = 0 mMsgCntW = 0 mMsgCntE = 0 If Not File.Exists(mIniFileName) Then Exit Sub sb = New StringBuilder(BUFF_LEN) For wLoop = 0 To 2 wCnt = 0 Do While True wKeyStr = wKey(wLoop) + String.Format("{0,3:000}", wCnt + 1) wRet = GetPrivateProfileString(wSec(wLoop), wKeyStr, "X", sb, Convert.ToUInt32(sb.Capacity), mIniFileName) wStr = sb.ToString() If wStr = "X" Then Exit Do wCnt += 1 Select Case wLoop Case 0 mMsgCntI = wCnt ReDim Preserve mMsgI(wCnt) With mMsgI(wCnt - 1) .mMsgCd = wStr.Split(":")(0) .mMsgLevel = "I" .mMsgContent = wStr.Split(":")(1) End With Case 1 mMsgCntW = wCnt ReDim Preserve mMsgW(wCnt) With mMsgW(wCnt - 1) .mMsgCd = wStr.Split(":")(0) .mMsgLevel = "W" .mMsgContent = wStr.Split(":")(1) End With Case 2 mMsgCntE = wCnt ReDim Preserve mMsgE(wCnt) With mMsgE(wCnt - 1) .mMsgCd = wStr.Split(":")(0) .mMsgLevel = "E" .mMsgContent = wStr.Split(":")(1) End With End Select Loop Next End Sub | ||||||||
|
投稿日時: 2009-04-13 12:52
指定の箇所の記述はコンパイルが通る事から間違っていないと思います。
気になるのは 1.関数が Shared であること。 2.Web なので複数のユーザが同時に実行する可能性があること。 3.再現性がないこと。 というところでしょうか。 はっきりとはわかりませんが、静的なフィールドに複数ユーザが同時アクセスした関係とか・・・。 例えばユーザAによって wSec の配列数が確定した後にユーザBによって配列の初期化が起こったためとか。 | ||||||||
|
投稿日時: 2009-04-13 13:01
Kingさんのご指摘通り、Shared が怪しいと思うのですが
Sharedを使用しないで 単純にPublic Sub sMsgRead()すれば、ユーザーAを、ユーザーBも 各々の動作に影響されることなく使用できるという事になるのでしょうか。 初歩的な質問ですいません。 | ||||||||
|
投稿日時: 2009-04-13 13:39
wSecはローカル変数なので関係ないと思いますが、 mMsgI、mMsgW、mMsgEの3つ配列はローカル変数ではないようなので、 各アクセスで同じ変数が設定されるとしたら、 IndexOutOfRangeExceptionが起きる可能性がありますね。 sMsgReadメソッドはどこのクラスに定義されているんでしょう? mMsgIなどの変数(mの付いている変数全般)はどこでどのように定義しているのでしょう? sMsgReadメソッドがSharedかどうかというより、 ローカル変数以外の変数へのアクセスで、複数ユーザによる同時アクセス時に 同じ変数を設定する箇所があれば、問題が発生する可能性があります。 今提示されているコードだけからではその辺どうなっているかわからないので、 なんとも言えません。 | ||||||||
|
投稿日時: 2009-04-13 13:42
影響があるかないかで言えばあるでしょうね。
インスタンス生成なしで利用できていた関数が インスタンスを生成し単純に静的な変数なら共有するけど、そうでなければ各々のインスタンスを持ちます。 ただ Shared を使用しなかったら他に影響が出るのではないでしょうか。 インスタンス生成無しで利用できていた関数が インスタンスを生成しないと利用できなくなるわけですから。 それにこの事が原因だと決まったわけではないですし。 [ メッセージ編集済み 編集者: King 編集日時 2009-04-13 14:03 ] | ||||||||
|
投稿日時: 2009-04-13 14:54
よねKENさん、Kingさん回答ありがとうございます。
引用−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sMsgReadメソッドはどこのクラスに定義されているんでしょう? mMsgIなどの変数(mの付いている変数全般)はどこでどのように定義しているのでしょう? −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sMsgReadメソッドは、WsysComMsgというクラスの中で定義しています。 setIniPathもWsysComMsgというクラスの中で定義しています 同じクラスでm付きの変数を以下の様に定義しています。 '------------------------------------------------------------- ' メッセージデータ '------------------------------------------------------------- Public Structure tMsg Dim mMsgCd As String 'メッセージコード Dim mMsgLevel As String 'レベル Dim mMsgContent As String '内容 End Structure Private Shared mMsgI() As tMsg 'Infoメッセージ Private Shared mMsgW() As tMsg 'Warningメッセージ Private Shared mMsgE() As tMsg 'Errorメッセージ Private Shared mMsgCntI As Integer 'Infoメッセージ数 Private Shared mMsgCntW As Integer 'Warningメッセージ数 Private Shared mMsgCntE As Integer 'Errorメッセージ数 '------------------------------------------------------------- ' INIファイルPATH設定 '------------------------------------------------------------- Public Shared Sub setIniPath(ByVal pPath As String) mIniFileName = WSysCom.getPathAddBackslash(pPath) & INIFILE_NAME sMsgRead() End Sub '------------------------------------------------------------- ' メッセージ内容の取得 '------------------------------------------------------------- Public Shared ReadOnly Property getMsgContent(ByVal pMsgCd As String) As String Get Dim wMsgStr As String = "" wMsgStr = getMsgContent("I", pMsgCd) If wMsgStr = "" Or wMsgStr = pMsgCd Then wMsgStr = getMsgContent("W", pMsgCd) If wMsgStr = "" Or wMsgStr = pMsgCd Then wMsgStr = getMsgContent("E", pMsgCd) End If End If Return wMsgStr End Get End Property Public Shared ReadOnly Property getMsgContent(ByVal pMsgLevel As String, ByVal pMsgCd As String) As String Get Dim wLoop As Integer Dim wMsg() As tMsg Dim wCnt As Integer Dim wMsgStr As String = "" Select Case pMsgLevel Case "I" wMsg = mMsgI wCnt = mMsgCntI Case "W" wMsg = mMsgW wCnt = mMsgCntW Case "E" wMsg = mMsgE wCnt = mMsgCntE End Select For wLoop = 0 To wCnt - 1 If wMsg(wLoop).mMsgCd = pMsgCd Then wMsgStr = wMsg(wLoop).mMsgContent Exit For End If Next If wMsgStr = "" Then wMsgStr = pMsgCd End If Return wMsgStr End Get End Property 各画面からaspx.vbのPage_Load時に WSysComMsg.setIniPath(Request.PhysicalApplicationPath) と記述してm付きの変数に値を設定し、エラーが発生した 場合、aspxで以下の記述でメッセージを取得しています。 response.write("flg = vbMsgbox(""" & Syscom.WSyscomMsg.getMsgContent("E","RKTM003E") & """,msgOKOnly + msgInformation ,""エラーダイアログ"");" & vbCrLf) 全体的なソースの流れは、上記の様な感じです。 インスタンスを生成しないといけないのでしょうか。(原因は、Sharedとした場合) | ||||||||
|
投稿日時: 2009-04-13 21:24
よねKENさんのおっしゃるように Shared は関係ないと思います。
適当な事言って申し訳ありませんでした。 ただ Shared じゃ無くなったら解決するとしても なぜ現時点で Shared で実装しているのか、と言う事をはっきりさせておかないと 影響範囲等わかりませんし危ないと思います。 もし把握されてるならいいのですが。 | ||||||||
|
投稿日時: 2009-04-14 10:08
WsysComMsgというクラスはおそらくSharedな変数やメソッドしかなく、
このWebアプリ全体で共通の処理がまとめてあるのではありませんか? (ComはCommonの意味なのでしょうね・・・) ユーザAのアクセスにより、例えば、sMsgReadメソッドの以下の箇所を実行中だとしましょう。
同じタイミングでユーザBのアクセスがあり、getMsgContent(ByVal pMsgLevel As String, ByVal pMsgCd As String)プロパティの以下の箇所が実行中だとしましょう。
ユーザAのための処理で(A)に来た時点で、mMsgIは要素数1の配列になります。 しかし、mMsgCntIは前回の実行した際の値ですので、0とは限りません。仮にNとします。 この状態で、ユーザBのための処理は(B)の場所を実行しようとしています。 wMsg変数はmMsgI変数の指すオブジェクトを参照しているので、要素数1の配列です。 wCnt = mMsgCntIが実行されるとwCntはNとなります。 このように配列の要素数の実際の値と配列の要素数のカウントを保持している変数の間で不整合が起きます。 この例では、後のFor文の実行でN回ループするので、要素数1の配列の境界外をアクセスすることになり、IndexOutOfBoundExceptionが起きることになります。 #ASP.NETってマルチスレッドで動いているんですよね???>ASP.NETの偉い人 スレッドセーフな作りではないからだと思います。 修正の前に、まずは上記の説明を踏まえて、再現方法を確保するのが先決だと思います。 再現できなければ、修正してもその修正が正しいかどうかの担保が取れませんから。 その上で、スレッドセーフについて調べてみてください。 WSysComMsgクラスのようなSharedな変数、Sharedなメンバ(プロパティ、メソッド)を使っている箇所は全部インスタンス変数、メンバだけを使うように改造し、このクラスを使う各画面では、常にWSysComMsgのインスタンスを作って処理し、このインスタンスを一切使い回しをしないようにすれば、とりあえず今回のような問題は起きなくなると思います。 #他にも対処方法はいろいろあるでしょうけれど、ご提示のソースコードを見る限りでは、 #Sharedを一切使うな、という形にするのが安全そうな気がします。 WSysComMsgだけでなく、同様の作りになっている箇所があれば、それらも修正が必要でしょう。 「ASP.NET Shared変数」「ASP.NET static変数」で検索するといろいろ 共有変数を使った場合の問題点や対策案がわかると思います。 [ メッセージ編集済み 編集者: よねKEN 編集日時 2009-04-14 10:10 ] |