こんにちは。
VB6でロールバックさせる処理で、ロールバック実行時にこのようなエラーが出てしまいます。
「オブジェクトが閉じてる場合は、操作は許可されません。」と表示されてしまいます。
大体、コネクション管理がうまく行っていない時のメッセージなのは分かるのですが、ソースを見ても分かりません。同じようにトランザクション処理してる所はいっぱいあるのですが、こんなエラーは出ません。他コントロールのトランザクション処理を実行してもエラーが出ないことから、前後処理のコネクション開放漏れの可能性も薄く、このメソッド内の順番が悪いのかと思ってます。
On Error GoTo fail
'DBオープン
If MyDBopen(cn) = "-1" Then
Call MyDBClose(cn, rs)
Exit Function
End If
'登録開始
cn.BeginTrans
With Form
shoId = .Label_id.Caption
str = "Delete From tbl1 where ID='" & shoId & "'"
cn.Execute (str)'←①
str = "Insert into tbl1 values('" + shoId + "','" + .Label1.Caption + "')"
cn.Execute (str)
'コミット
cn.CommitTrans
Call MyDBClose(cn, rs)
Exit Function
End With
fail:
cn.RollbackTrans’←②
MsgBox "更新失敗しました。" + Space(5), vbCritical, "エラー"
Call MyDBClose(cn, rs)
こんな流れなんですが、①で例外感知、②で上のエラーが出ます。MyDBOpen & MyDBCloseは共通関数で乗せていませんが、エラーはなく、他処理も問題なく動いてます。また、①ですが、どんなSQLを置いても同じエラーが出ることからSQL自体を弾いてる気がしてます。
何かお気付きの方はよろしくお願いします。(ここでロールバックを実行させてるのにも問題がありますが・・)
追記です。
上の問題の箇所ですが、実装上の機能はDelete→Insertの編集画面なのですが、他の画面からも呼ばれる事もできます。
不思議な事に他画面からこの画面に入り、上記の更新処理を実行してもエラーメッセージは出ませんでした。
と言う事は、ロジック上は問題が無く、他画面のHide等メモリの問題でしょうか?
よろしくお願いします。
> と言う事は、ロジック上は問題が無く、他画面のHide等メモリの問題でしょうか?
それが分かるのは、掲示板を見ている第三者ではなく、
ソースを持っているぼぼさんだけだと思いますよ。
> ソースを見ても分かりません。
見ても分からなければ、デバッガの出番かと。
たとえばウォッチ式の「式の内容が変化したときに中断」などを使えば、Connection が
生成されたタイミングや破棄されたタイミング、Open されたタイミングや
Close されたタイミングを調査する事ができるかも知れません。
> ①で例外感知
それは、どのような内容の例外なのでしょうか?
たとえば、cn がグローバル変数になっていて、しかもそれが Form の Load 等から
利用されて、そこで cn が 閉じられた状態になってしまった、などとすれば
> ②で上のエラーが出ます。
にもなりますよね。まぁ、本当の理由は分かりませんけれども。
> MyDBOpen & MyDBCloseは共通関数で乗せていませんが、エラーはなく、他処理も問題なく動いてます。
そうかも知れませんが、それにしては、エラートラップが広域過ぎる点が気にかかります。
(今回の問題とは別問題だとは思いますが)
On Error GoTo fail がその位置にあるという事は、たとえば MyDBopen や MyDBClose などで
万一エラーが発生した場合、fail ルーチンにジャンプする事になるのですよね。
たとえば、変数 cn に Connction を設定したものの、肝心の cn.Open が失敗して
(ini ファイルの設定ミスやネットワークの一時的な障害などによる物など)、その時に
発生したエラーが拾われたとすれば、
> fail:
> cn.RollbackTrans
の部分で
> オブジェクトが閉じてる場合は、操作は許可されません。
のエラーに繋がって来る可能性も無いとは言えませんね。
で、そうした「接続すらしていないのにロールバック処理を走らせる」という状況を
避けるなら、fail: ルーチンの処理にもう少し手を加えて
(1) cn が空であるかどうかを判断する。(Nothing 判定)
(2) cn が接続済みかどうかを判断する。(State プロパティ判定など)
(3) それらの確認が取れてから RollbackTrans させる。
などといった手順を踏ませた方が良いかもしれません。
あるいはもし、MyDBopen 内できちんとエラー処理されているのであれば、
呼び出し側のその位置で、On Error を仕掛ける必要は無いはずです。
その他、本題から外れるような点として:
まず、戻り値を設定している箇所が見当たらないのに、これらの処理が、
Sub プロシージャではなく、Function プロシージャとして実装されている点が
私の目には奇妙にうつりました。
それから、接続処理が
If MyDBopen(cn) = "-1" Then
となっていますが、この String の "-1" という戻り値の意図も明確では無いと思います。
前後関係から、失敗時のコードなのかな? といった推測を立てる事はできますが、
他にどのようなコードが返される可能性があるのかは、伺い知る事ができませんよね。
> str = "Delete From tbl1 where ID='" & shoId & "'"
str は予約語では無いので、変数名として使用可能ですが、それ自体は
「Str 関数」と競合するので、あまり良い命名とは言えないかと。
また、このコードの場合、SQL インジェクションの問題も孕んでいますね。
(変数 shoId 内に、「'」の文字が混入していた場合の対策など)
魔界の仮面弁士さん、ありがとうございます。
おっしゃるようにstateでSQL実行直前で確認したら、切れてました。
DBオープンした時は、ちゃんと開いていたんですが、
With Form
shoId = .Label_id.Caption
を通過時に閉じてしまいました。
デバックポイントで追っても全く処理は無いのですが・・。
Form_LoadでGridの初期表示をセットしてますが、
DB接続は全くありませんし。
と言う事で、①のSQL実行直前に持ってきた所、正常に動くようになりました。
また、おっしゃるようにロールバック処理前には必ずコネクションチェックを入れるようにしました。
ありがとうございました、解決しました。
ちゃんと解決してください・・・。
編集 削除進捗はどうですか。
編集 削除