- PR -

Class_Terminate()が実行されるタイミングでプロパティは存在しますか?

投稿者投稿内容
koara
ベテラン
会議室デビュー日: 2005/09/16
投稿数: 96
投稿日時: 2006-08-28 14:51
お世話になっております、koaraと申します。

Windows XP Pro SP2
VB6.0 SP6
という環境で業務アプリを作っています。

Class_Terminate()内でクラスのプロパティの値を参照している処理で、
プロパティの値が空になっている時があります、
Class_Terminate()内では既にプロパティの値が参照できることは保証されていないのでしょうか?

具体的には下記のクラスで関数の処理が終了する間の排他制御をしようとしています。
LockFile.cls
引用:

Private suffix As String
Private Const LOCK_FILE = "c:\temp\locktest."

Public Sub MakeFile(ByVal ipAddress As String)

  suffix = Replace(ipAddress, ".", "_")

  Dim fno As Integer
  fno = FreeFile
  Open LOCK_FILE & suffix For Output As #fno
  Print #fno, Now & "," & App.EXEName
  Close #fno

End Sub

Public Function IsFile() As Boolean

  IsFile = True

  Dim fileName As String
  fileName = Dir(LOCK_FILE & "*")

  If fileName <> "" Then
    IsFile = False
  End If

End Function

'デストラクタ
Private Sub Class_Terminate()

  Kill LOCK_FILE & suffix

End Sub



下記のようにLockFile.clsを使用しています。
testFunction.bas
引用:

Public Sub testFunction()

  Dim Lock As New LockFile
  If Lock.IsFile Then
    Call Lock.MakeLock(Winsock1.LocalIP)
  Else
    MsgBox "他の場所で実行中です、暫く待ってもう一度実行してください。"
    End Sub
  End If

  処理....

End Sub



testFunction()開始直後にmakeFile()でロックファイルを作成し、
EndSubを抜けた後にClass_Terminate()が実行され
ロックファイルが削除されるという動作をさせてたいのですが、
既にsuffixが""の時があるようで
引用:

Private Sub Class_Terminate()

  Kill LOCK_FILE & suffix

End Sub


が失敗します、どこが問題なのでしょうか?
アドバイスよろしくお願いします。
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-08-28 15:12
引用:

koaraさんの書き込み (2006-08-28 14:51) より:

Dim Lock As New LockFile


この宣言と同時のインスタンス化による弊害 (VB6 以前特有) の可能性があります。

'Lock' の参照が、Nothing になる直前に Terminate イベントが発生するわけですが、
その後、'Lock' という変数のメンバを参照すると、暗黙的にインスタンス化されてしまいます。
(Nothing であるハズなのに、勝手にインスタンス化され参照できてしまえるわけです)

その後、参照が解放されると再度 Terminate イベントが発生します。
その際のインスタンスは、最初に作成されたインスタンスとは異なるため、
Suffix が長さ 0 の文字列になっているという可能性があるということです。
(わかりやすい説明ができなくて、申し訳ないです...)

# ちなみに、この可能性が捨てられないのは、"処理...." と省略されている部分が不明だからです。

あまり考えられないのですが、危険回避のためにも、

コード:

    Dim Lock As LockFile
    Set Lock = New LockFile


このように修正することをお勧めします。

それと、

引用:

  Else
    MsgBox "他の場所で実行中です、暫く待ってもう一度実行してください。"
    End Sub
  End If


誤記がある (正しくは Exit Sub) ということは、正しく転記されていないわけで、
転記できていない部分に何か潜んでいるようにも思えます。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
todo
ぬし
会議室デビュー日: 2003/07/23
投稿数: 682
投稿日時: 2006-08-28 15:54
コード:

If Lock.IsFile Then
    Call Lock.MakeLock(Winsock1.LocalIP)
  Else
    MsgBox "他の場所で実行中です、暫く待ってもう一度実行してください。"
    Exit Sub
  End If



Exit Sub のところで、suffixがセットされずに、Class_Terminate()が走るのでしょう。


[ メッセージ編集済み 編集者: todo 編集日時 2006-08-28 15:55 ]

[ メッセージ編集済み 編集者: todo 編集日時 2006-08-28 15:56 ]
koara
ベテラン
会議室デビュー日: 2005/09/16
投稿数: 96
投稿日時: 2006-08-28 17:22
じゃんぬねっとさん
レスありがとうございます。

引用:

Dim Lock As New LockFile

Dim Lock As LockFile
 Set Lock = New LockFile


の違いを考えていたらレスが遅くなってしまいました、すみません。
上記はどういう場合に違いがあるのでしょうか?

仰られている可能性というのはtestFunction()内で
再度testFunction()が呼び出される可能性のことだと解釈したのですが。
それでしたら無いと思います。

ただtestFunction()は連続して何回か実行される事があり、
そのような場合に前回のLockの参照が、Nothingにならず、
次のtestFunction()が呼び出されるということが起こるとすれば有り得ます。


testFunction()はシリアルラベルを発行するソフトの一部で、
商品バーコードを読み込み、枚数を入力するとDB上の在庫データを更新した後
シリアルを採番してラベルを発行します。
testFunction()はVBフォーム上の"DB書込み"ボタンに割り付けられております。
testFunction()の処理内容は

  1. ロックファイル作成
  2. DB トランザクション開始
  3. DB 更新
  4. DB コミット
  5. RS-232C接続のプリンタからラベル発行
  6. ロックファイル削除

という感じです。

既存のアプリケーションにファイルによる排他処理を追加していたのですが、
更新処理が複雑で、分岐処理の末端まで抜けの無いよう
ロックファイルの後処理をする自信がありませんでした。
そこで、デストラクタを利用することを考えたのですが甘かったようで、
ロックファイルが残ってしまうことがあります。

その原因がsuffixが""になっていて作ったロックファイルが分からなくなっているのでは
ないかと考え表題のような質問をしました。

引用:

誤記がある (正しくは Exit Sub) ということは、正しく転記されていないわけで、
転記できていない部分に何か潜んでいるようにも思えます。


じゃんぬねっとさんの仰るとおり、
そういう心構えで探さないと見つかるバグも見つけられないですよね。

End Subの部分は転記ミスです、
実際は
引用:

  Else
    MsgBox "他の場所で実行中です、暫く待ってもう一度実行してください。"
    Exit Sub
  End If



koara
ベテラン
会議室デビュー日: 2005/09/16
投稿数: 96
投稿日時: 2006-08-28 17:32
todoさん、レスありがとうございます。

正確に転記しなかった事をお詫びさせてください、
すみませんでした。
引用:

Private Sub Class_Terminate()

  Kill LOCK_FILE & suffix

End Sub


は実際
引用:

Private Sub Class_Terminate()
  If suffix <> "" Then
    Kill LOCK_FILE & suffix
  Else
    Exit Sub
  End If
End Sub


となっており、
Lock.IsFile = FALSE
の場合、Class_Terminate()でロックファイル削除の処理はされません。
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-08-28 17:36
引用:

koaraさんの書き込み (2006-08-28 17:22) より:

Dim Lock As New LockFile

Dim Lock As LockFile
Set Lock = New LockFile

の違いを考えていたらレスが遅くなってしまいました、すみません。
上記はどういう場合に違いがあるのでしょうか?


やはり、言葉では説明しにくいですね。
実際に組んで頂いた方がわかりやすいです。

コード:

    Dim Lock As New LockFile
    Set Lock = Nothing    ' ここで Nothing になっているのに
    Lock.MakeLock(...)    ' 勝手にインスタンス化されるものだから呼び出すことができる


このコードと、

コード:

    Dim Lock As LockFile
    Set Lock = New LockFile
    Set Lock = Nothing
    Lock.MakeLock(...)    ' インスタンスがないため呼び出すことができない


このコードでは、動作が違うことがわかるハズです。

引用:

仰られている可能性というのはtestFunction()内で
再度testFunction()が呼び出される可能性のことだと解釈したのですが。
それでしたら無いと思います。


そういうわけではないことは、上記を試されればわかるかと思います。

で、本題は todo さんのご指摘どおりである可能性があります。
なぜ、気付かなかったのだろう... orz

ローカル変数である 'Lock' は、Exit Sub (プロシージャ) を抜けると、
自動的に参照カウントがデクリメントされ、Terminate イベントが発生します。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
eternia
常連さん
会議室デビュー日: 2006/02/23
投稿数: 42
投稿日時: 2006-08-28 19:28
引用:

既にsuffixが""の時があるようで


引用:

Private Sub Class_Terminate()
  If suffix <> "" Then
    Kill LOCK_FILE & suffix
  Else
    Exit Sub
  End If
End Sub



上記のようになっているのであれば""は失敗しないと思いますが……
""になっていると思われた根拠は何でしょう?

あと、IsFile()の内容とメッセージボックスの内容が
一致していないようですが、ここは転記ミスではないですか?^^;
(色々と省かれている可能性があるので念のためです。)
koara
ベテラン
会議室デビュー日: 2005/09/16
投稿数: 96
投稿日時: 2006-08-29 09:38
じゃんぬねっとさん、ありがとうございます。

動作の違いは実行してみて分かったのですが、
どうしてそのようになるかが分からず悩んでいましたが
先ほど理由がわかりました。

引用:

【Dim x As New MyClass の動作について】
Visual Basic 6.0 では、このコードのある行ではオブジェクトは生成されずに、それを利用するタイミングに、オブジェクトがすでに作られているかどうかをチェックし、なければ作成されるというものでした。
http://www.microsoft.com/japan/msdn/net/vbtransitionguide/chapter4/chapter4_9.aspx



本題の件ですが、プログラムの要所でログを出力して動作を検証していたのですが
ログの内容と解釈が不十分で問題点を勘違いをしていました。

具体的には以下のようなログを残していました。
引用:

2006/08/28 10:11:30,vbのフォーム名,xxx_xxx_xxx_xxx(ipアドレス) ロックファイル作成<--a1
2006/08/28 10:11:32,vbのフォーム名,xxx_xxx_xxx_xxx ロックファイル削除<--a2
2006/08/28 10:11:38,vbのフォーム名,xxx_xxx_xxx_xxx ロックファイル作成 <--b1
2006/08/28 10:11:43,vbのフォーム名, ロックファイル不明 <--c1

a1:ロックファイル作成 ---> MakeFile()内でログを出力
a2:ロックファイル削除 ---> Class_Terminate()内でログを出力
c1:ロックファイル不明 ---> Class_Terminate()内でログを出力

a1とa2は正常時のペア(ロックファイル作成・削除で対という意味)です。



当初b1->c1という一連の動作で出力されたログだと思っていたのですが、
実際にはc1は全く別に出力されており、b1のロックファイルが作成されたまま消えないという現象が問題でした。

c1が出力されていた理由はtodoさん、じゃんぬねっとさん、eterniaさんがご指摘の通りです。
引用:

Private Sub Class_Terminate()
  If suffix <> "" Then
    Kill LOCK_FILE & suffix
  Else
    Exit Sub <----ここでc1:ロックファイル不明のログを出力
  End If
<----ここでa1:ロックファイル削除のログを出力
End Sub


b1がClass_Terminate()を呼ばずに終わる理由ですが、
ソースにEnd VBで終わる箇所があり気になっています。
End VBでも確実にClass_Terminate()が呼ばれるのか調べたいと思います。     

スキルアップ/キャリアアップ(JOB@IT)