tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルPenの操作でエラー(VBS2005)
記事No7585
投稿日: 2008/05/22(Thu) 11:45
投稿者ひでと
お世話になります。画像処理を勉強中です。
Penの設定でエラーが出てしまい、原因がつかめずこまっています。
Pen.Color,Pen.DashPattern,Pen.DashStyleの使い方ではないかという所までは判明しました。お力をお貸しください。
次のコードで上記のプロパティをいじるとエラーが発生します。

    Public Function Turn(ByVal Angle As Single) As clsCircle
        Dim my中心 As New clsPoint
        Dim 仮始点 As New clsPoint
        Dim 仮終点 As New clsPoint
        Dim dum As New clsCircle
        my中心 = 中心.Turn(Angle)
        dum.中心 = my中心
        dum.半径 = 半径
        仮始点 = 始点.Turn(Angle)
        仮終点 = 終点.Turn(Angle)
        dum.始点角 = Math.Atan2(仮始点.y - dum.中心.y, 仮始点.x - dum.中心.x) / 2 * 360 / Math.PI
        dum.終点角 = Math.Atan2(仮終点.y - dum.中心.y, 仮終点.x - dum.中心.x) / 2 * 360 / Math.PI
        If dum.始点角 < 0 Then dum.始点角 = 360 + dum.始点角
        If dum.終点角 < 0 Then dum.終点角 = 360 + dum.終点角
        'dum.pen = pen.Clone'(1)
        'dum.pen.Color = pen.Color'(2)
        dum.pen.DashPattern = pen.DashPattern '(3)
        dum.pen.DashStyle = pen.DashStyle '(4)
        Return dum
    End Function

この後ろの方の(1)から(4)までの組あわせで 別のプロシージャで
「保護されているメモリに読み取りまたは書き込み操作を行おうとしました。
他のメモリが壊れていることが考えられます。」
とのエラーが発生します。
エラーが発生しない場合を○×で表すと
(1)×
(2)○
(3)○
(4)○
(2),(3)×
(2),(4)○
(3),(4)○
となります。'(ColorとDashPatternの組あわせでもエラーがでるのが特に???)

原因と対処方法をよろしくお願いします。
検証可能なコードが作れなさそうなので 抜粋して記載させていただきますが、
他に必要そうな箇所が有りましたら追記いたしますので教えてください。

Public Class clsCircle
    Public 中心 As New clsPoint
    Public 始点角 As Single
    Public 終点角 As Single
    Public 半径 As Single
    Public pen As New Pen(Color.Black, 1)
    Private new始点 As New clsPoint
    Private new終点 As New clsPoint

    Public Function Turn(ByVal Angle As Single) As clsCircle
        Dim my中心 As New clsPoint
        Dim 仮始点 As New clsPoint
        Dim 仮終点 As New clsPoint
        Dim dum As New clsCircle
        my中心 = 中心.Turn(Angle)
        dum.中心 = my中心
        dum.半径 = 半径
        仮始点 = 始点.Turn(Angle)
        仮終点 = 終点.Turn(Angle)
        dum.始点角 = Math.Atan2(仮始点.y - dum.中心.y, 仮始点.x - dum.中心.x) / 2 * 360 / Math.PI
        dum.終点角 = Math.Atan2(仮終点.y - dum.中心.y, 仮終点.x - dum.中心.x) / 2 * 360 / Math.PI
        If dum.始点角 < 0 Then dum.始点角 = 360 + dum.始点角
        If dum.終点角 < 0 Then dum.終点角 = 360 + dum.終点角
        'dum.pen = pen.Clone'(1)
        'dum.pen.Color = pen.Color'(2)
        dum.pen.DashPattern = pen.DashPattern '(3)
        dum.pen.DashStyle = pen.DashStyle '(4)
        Return dum
    End Function

    Public Sub Drawn(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs, _
    ByVal 倍率 As Single, ByVal 描画中心 As clsPoint)
        Dim 左上Point As New clsPoint
        Dim x, y, width, height, startAngle, sweepAngle As Single
        左上Point.x = 中心.x - 半径
        左上Point.y = 中心.y + 半径
        x = 左上Point.GraficX(sender, 倍率, 描画中心)
        y = 左上Point.GraficY(sender, 倍率, 描画中心)
        width = 2 * 半径 * 倍率
        height = 2 * 半径 * 倍率
        If mRound(終点角, 8) > mRound(始点角, 8) Then
            startAngle = 360 - 始点角
            sweepAngle = -(終点角 - 始点角)
        Else
            startAngle = 360 - 終点角
            sweepAngle = 360 + (終点角 - 始点角)
        End If
        If width <= 0 Then Exit Sub
        e.Graphics.DrawArc(pen, x, y, width, height, startAngle, sweepAngle)'ここでエラーが発生
    End Sub
End Class


Public Class Form1
    Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim pen As New Pen(Color.Black, 1)
        pen.Width = 1
        pen.Color = Color.Black
        pen.DashStyle = Drawing2D.DashStyle.Solid
        With Me.ShapeDraw1
            pen.Color = Color.Black
            pen.DashStyle = Drawing2D.DashStyle.Solid
            .AddLines(30, 30, 100, 100, pen)
            pen.Color = Color.Red
            pen.DashPattern = New Single() {10.0F, 2.0F, 2.0F, 2.0F, 2.0F, 2.0F, 2.0F}
            .AddCircles(100, 100, 50, 90, 360, pen)
            '.AddCircles(200, 200, 50, pen)
            'Me.ShapeDraw1.IsSymmetry = True
            .Refresh()
        End With
    End Sub
End Class

[ツリー表示へ]
タイトルRe: Penの操作でエラー(VBS2005)
記事No7586
投稿日: 2008/05/22(Thu) 12:47
投稿者魔界の仮面弁士
> Penの設定でエラーが出てしまい、
Pen の解放処理がゴッソリ抜け落ちています。

Using ブロックを使うか、最後に Dispose を呼ぶかして、使用後の Pen を
きちんと破棄しておきましょう。

> 原因がつかめずこまっています。
ここに書かれていない部分に問題があるのでは無いでしょうか。

実験用に、独自のクラス等を介さない、小さなサンプルコードを作って、
Pen.DashPattern や Pen.DashStyle が期待動作するかどうかを
検証してみてください。

それで問題が起きるなら、GDI+ ライブラリに不整合が起きているのかも知れませんし、
実験コードでは正常動作するなら、どこかにコーディングミスがあるという事でしょう。


> 他に必要そうな箇所が有りましたら追記いたしますので教えてください。
「clsPoint」「始点」「終点」「ShapeDraw1」「mRound」が何者であるのか
読み取れませんでした。


ところで、GraficX というメソッドがありますが、Grafic ではなく Graphic  なのでは。

[ツリー表示へ]
タイトルRe^2: Penの操作でエラー(VBS2005)
記事No7587
投稿日: 2008/05/22(Thu) 13:43
投稿者ひでと
ありがとうございます。
> > Penの設定でエラーが出てしまい、
> Pen の解放処理がゴッソリ抜け落ちています。
>
> Using ブロックを使うか、最後に Dispose を呼ぶかして、使用後の Pen を
> きちんと破棄しておきましょう。

なるほど 開放処理ですね。
変数の寿命というのでしょうか、通常余り意識していなくて。
Pen には必須なのでしょうか?
開放処理ができていれば dum.pen=pen.Clone でPenの設定をそっくり移す事が
できますよね。

>
> > 原因がつかめずこまっています。
> ここに書かれていない部分に問題があるのでは無いでしょうか。

そうだとは思ったのですが、上記で教えていただいたPenの開放処理を考えながら
もう一度全体的に見直してみます。座標の計算でいっぱいいっぱいで
Penについては、後でなんとかなるだろうと甘くみてましたw
>
> 実験用に、独自のクラス等を介さない、小さなサンプルコードを作って、
> Pen.DashPattern や Pen.DashStyle が期待動作するかどうかを
> 検証してみてください。
>
> それで問題が起きるなら、GDI+ ライブラリに不整合が起きているのかも知れませんし、
> 実験コードでは正常動作するなら、どこかにコーディングミスがあるという事でしょう。
>
>
> > 他に必要そうな箇所が有りましたら追記いたしますので教えてください。
> 「clsPoint」「始点」「終点」「ShapeDraw1」「mRound」が何者であるのか
> 読み取れませんでした。
>
>
> ところで、GraficX というメソッドがありますが、Grafic ではなく Graphic  なのでは。
あ、気が付きませんでした。英語苦手の45歳です。

[ツリー表示へ]
タイトルRe^3: Penの操作でエラー(VBS2005)
記事No7588
投稿日: 2008/05/22(Thu) 15:07
投稿者魔界の仮面弁士
> 変数の寿命というのでしょうか、通常余り意識していなくて。
> Pen には必須なのでしょうか?
マネージリソース(.NET で管理される資源)自体は、ガベージコレクトによって
自動回収されるため、通常はあまり意識する必要はありません。

しかし、Dispose メソッド(正確には、IDisposable インターフェイス)を有する
クラスの場合だけは例外です。IDisposable なクラスの場合には、処理の最後に
Dispose を呼び出して、使い終わった内部リソースを破棄せねばなりません。
(この処理を自動的にやってくれるのが、Using ステートメントです)

なお、Dispose は「内部リソースが破棄される」だけで、
オブジェクトそのものが解放されるわけではありません。

> 開放処理ができていれば dum.pen=pen.Clone でPenの設定をそっくり移す事が
> できますよね。
Pen.Clone は「同じ設定のペンをもう一本作成する」処理です。
コピー後のペンもコピー元のペンも、使用後に破棄する必要があります。

[ツリー表示へ]
タイトルRe^4: Penの操作でエラー(VBS2005)
記事No7599
投稿日: 2008/05/23(Fri) 09:57
投稿者ひでと
遅くなりました。ありがとうございます。

> しかし、Dispose メソッド(正確には、IDisposable インターフェイス)を有する
> クラスの場合だけは例外です。IDisposable なクラスの場合には、処理の最後に
> Dispose を呼び出して、使い終わった内部リソースを破棄せねばなりません。
> (この処理を自動的にやってくれるのが、Using ステートメントです)
Disposeメソッドがあるものは 必須ということですね。分かりました。
(Usingはこれから見てみます)

>
> なお、Dispose は「内部リソースが破棄される」だけで、
> オブジェクトそのものが解放されるわけではありません。
>
> > 開放処理ができていれば dum.pen=pen.Clone でPenの設定をそっくり移す事が
> > できますよね。
> Pen.Clone は「同じ設定のペンをもう一本作成する」処理です。
> コピー後のペンもコピー元のペンも、使用後に破棄する必要があります。

破棄するタイミングがいまひとつ分からないのです。
実際の処理ではclsCiecleクラスにPenを持たせて、
それをShapeDrawクラスの中のコレクションに保存しています。
また、clsCircleクラスのTurnメソッドは同じclsCircleのインスタンスを返すメソッド(?)
として作っています。実際のShapeDrawクラスの中のコレクションを作る処理はForm1の
Button1_Clickで記載し、ShapeDraw(PicturBoxから継承)のPaintイベントで円弧を
描画する処理をしています。


PenをDisposeするタイミングですが、次のようにしてみました。(省略して記載)
ただ、これでいいのか?かなり不安なのです。アドバイスをお願いします。

Public Class ShapeDraw
    Public Circle As New clsCircle
    Public Circles As New List(Of clsCircle)

    Public Sub AddCircles(ByVal 中心x As Single, ByVal 中心y As Single, ByVal 半径 As Single, ByVal 始点角 As Single, ByVal 終点角 As Single, ByVal pen As Pen)
        Dim mCircle As New clsCircle
        With mCircle
            .pen = pen.Clone
        End With
        Circles.Add(mCircle)
    End Sub

Public Class clsCircle
    Public pen As New Pen(Color.Black, 1)

    Public Function Turn(ByVal Angle As Single) As clsCircle
        Dim dum As New clsCircle
        dum.pen = pen.Clone
        pen.Dispose()'ここでclsCircleのpenを破棄
        Return dum'ここではdum.penの破棄は行えないはず
    End Function


Public Class Form1
    Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim pen As New Pen(Color.Black, 1)
        pen.Width = 1
        pen.Color = Color.Black
        pen.DashStyle = Drawing2D.DashStyle.Solid
        With Me.ShapeDraw1
            pen.Color = Color.Red
            pen.DashPattern = New Single() {10.0F, 2.0F, 2.0F, 2.0F, 2.0F, 2.0F, 2.0F}
            .AddCircles(100, 100, 50, 90, 360, pen)

            pen.Color = Color.Green
            pen.DashStyle = Drawing2D.DashStyle.Solid
            .AddCircles(200, 200, 50, pen)
            .Refresh()
        End With
        pen.Dispose()'ここでPenの破棄
    '.AddCirclesでShapeDrawのCircles As New List(Of clsCircle)に保存されたPenも破棄しなければいけないのだろうか?
    End Sub
End Class

[ツリー表示へ]
タイトルRe^5: Penの操作でエラー(VBS2005)
記事No7601
投稿日: 2008/05/23(Fri) 11:39
投稿者よねKEN
> > しかし、Dispose メソッド(正確には、IDisposable インターフェイス)を有する
> > クラスの場合だけは例外です。IDisposable なクラスの場合には、処理の最後に
> > Dispose を呼び出して、使い終わった内部リソースを破棄せねばなりません。
> > (この処理を自動的にやってくれるのが、Using ステートメントです)
> Disposeメソッドがあるものは 必須ということですね。分かりました。
> (Usingはこれから見てみます)

Disposeメソッドを持つクラスで自分の管理下にあるインスタンスについてのみ
Disposeメソッドを発行した方がよい、です。

例えば、あるメソッドでNewしたPenのインスタンスでそのメソッドの中でしか
使用しないものであれば、そのメソッド内でDisposeするべきでしょう。
しかし、他のところから渡されたPenはDisposeすべきでしょうか、
するべきではないでしょうか。この判断は難しい場合もありますが、
誰がDisposeすべきか?を考えておくとよいです。

> PenをDisposeするタイミングですが、次のようにしてみました。(省略して記載)
> ただ、これでいいのか?かなり不安なのです。アドバイスをお願いします。
>
> Public Class ShapeDraw
>     Public Circle As New clsCircle
>     Public Circles As New List(Of clsCircle)
>
>     Public Sub AddCircles(ByVal 中心x As Single, ByVal 中心y As Single, ByVal 半径 As Single, ByVal 始点角 As Single, ByVal 終点角 As Single, ByVal pen As Pen)
>         Dim mCircle As New clsCircle
>         With mCircle
>             .pen = pen.Clone
>         End With
>         Circles.Add(mCircle)
>     End Sub
>
> Public Class clsCircle
>     Public pen As New Pen(Color.Black, 1)
>  
>     Public Function Turn(ByVal Angle As Single) As clsCircle
>         Dim dum As New clsCircle
>         dum.pen = pen.Clone

dum.penが持つ元々のインスタンスのDisposeが呼ばれていません。

>         pen.Dispose()'ここでclsCircleのpenを破棄

ここでpenをDisposeすると次にこのインスタンスのTurnメソッドを呼び出すときに
例外が起きるので問題があると思います。

>         Return dum'ここではdum.penの破棄は行えないはず
>     End Function

penはインスタンス変数ですので、このpenのDisposeを呼ぶタイミングは、
clsCircleクラスのインスタンスが破棄されるタイミングになるかと思います。
この点を厳密にやろうとすると、clsCircleクラス自身も
IDisposableインターフェースを実装し、Disposeメソッドを用意する。
そして、ObjectのFinalizeメソッドをオーバーライドする必要があります。

またpenフィールドは外部から設定できるようになっていますが、
設定不要であれば、ReadOnlyにしておいた方がよいと思います。
外部からpenに新たなインスタンスを設定する場合、
それまでに持っていたインスタンスのDiposeが呼ばれません。
#penフィールドにセットする側がセットする前に古いインスタンスの
#Disposeを実行する責任を持つのであれば問題ありません。

>
> Public Class Form1
>     Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
>         Dim pen As New Pen(Color.Black, 1)
>         pen.Width = 1
>         pen.Color = Color.Black
>         pen.DashStyle = Drawing2D.DashStyle.Solid
>         With Me.ShapeDraw1
>             pen.Color = Color.Red
>             pen.DashPattern = New Single() {10.0F, 2.0F, 2.0F, 2.0F, 2.0F, 2.0F, 2.0F}
>             .AddCircles(100, 100, 50, 90, 360, pen)
>
>             pen.Color = Color.Green
>             pen.DashStyle = Drawing2D.DashStyle.Solid
>             .AddCircles(200, 200, 50, pen)
>             .Refresh()
>         End With
>         pen.Dispose()'ここでPenの破棄

こちらはOKです。

>     '.AddCirclesでShapeDrawのCircles As New List(Of clsCircle)に保存されたPenも破棄しなければいけないのだろうか?

この点は、先ほど指摘したようにclsCircleがIDiposableを実装する場合、
clsCircleのインスタンスが不要になったら、clsCircleのDisposeメソッドを
呼ぶようにすることで解決します。

>     End Sub
> End Class


上記で指摘した内容はそれなりにややこしいと思います。
そこでもう一つの案なのですが、clsCircleはPenクラスのインスタンスを持つ必要があるでしょうか?という点です。
Penが必要になるのは描画するときですから、clsCircleにPenを保持する必要はなく、
描画するときにPenインスタンスを作って、描画の作業が終わったら即座にPenインスタンスを破棄すればよいです。

clsCircleを描画するのにたぶん色情報と線の太さが必要だろうと思いますが、
この情報だけをclsCircleが持てばよいのではないでしょうか。

[ツリー表示へ]
タイトルRe^5: Penの操作でエラー(VBS2005)
記事No7602
投稿日: 2008/05/23(Fri) 11:53
投稿者魔界の仮面弁士
> 破棄するタイミングがいまひとつ分からないのです。
よく分からないのであれば、
  「使う直前にその都度作成、使い終わったら即時破棄」
という方法を用いることができます。その方が実装難易度は下がります。

その方法を使いたく無い(都度作成するのではなく、使いまわしたい)のであれば、
Pen を管理するクラスの寿命をキチンと管理せねばならないので、多くの場合、
その管理クラスには、IDisopsable Finalize パターンを実装する事になるでしょう。
http://msdn.microsoft.com/ja-jp/library/b1yfkh5e.aspx


> 実際の処理ではclsCiecleクラスにPenを持たせて、
> それをShapeDrawクラスの中のコレクションに保存しています。
その場合、clsCiecle が Pen を管理する事になりますね。

その場合、clsCiecle を管理・作成するのが、どのクラスの役割なのかを
事前にはっきりさせておく必要があるでしょう。
(Form で作成/管理するのか、ShapeDraw が作成/管理するのか)


> また、clsCircleクラスのTurnメソッドは同じclsCircleのインスタンスを返すメソッド(?)
「同じインスタンス」という事は、その Turn メソッドの実装は、
 Public Function Turn() clsCircle
  Return Me
 End Function
という事になりますが…それはあまり意味のある実装には思えません。


> Button1_Clickで記載し、ShapeDraw(PicturBoxから継承)のPaintイベントで円弧を
> 描画する処理をしています。
その円弧が、PictureBox 以外に描画させる事は絶対にない、というのならば良いですが、
もし、その PictureBox 以外にも描画させたい場合(たとえば印刷処理など)が
生じた場合、同じような処理を再実装せねばならなくなるかも知れません。

もし、他の個所にも同様の描画処理が発生しえる場合は、描画処理部分だけを
別クラスにするという設計も方法あります。
たとえば、.NET 2.0 では、『ボタンを描画する処理』のために ButtonRenderer クラス、
『コンボボックスを描画する処理』のために ComboBoxRenderer クラスを用意していますが、
これらは Graphics クラスに対して処理するだけのクラスなので、特定の PictureBox などに
限定される事無く、フォームに直接描画したり、印刷したりする場合にも利用できます。

# どちらの実装方法が良いのかは、アプリの仕様次第なので、一概には言えませんが。


> Public Class ShapeDraw
>     Public Circle As New clsCircle
>     Public Circles As New List(Of clsCircle)
この実装は、安全では無いように思えます。

AddCircles 等を介することなく、呼び出し側から
 ShapeDraw1.Circles.Clear
 ShapeDraw1.Circles.Add(New clsCircle())
のように直接操作する事もできてしまいますから。


> Public Sub AddCircles(ByVal 中心x As Single, ByVal 中心y As Single, ByVal 半径 As Single, ByVal 始点角 As Single, ByVal 終点角 As Single, ByVal pen As Pen)
>    Dim mCircle As New clsCircle
>    With mCircle
>       .pen = pen.Clone
>    End With
>    Circles.Add(mCircle)
> End Sub

その実装の場合、pen を破棄するのは「ShapeDraw1.AddCircles を呼び出した側」の役割ですが、
pen.Clone を破棄するのは、「clsCircle クラス」の役割となりますね。
ShapeDraw 側は、Circles を破棄する場合(Circles.Clear とか、Circles.RemoveAt など)に、
clsCircle クラス(内の Pen)が解放させる義務が生じるので、お忘れなく。


>     Public Function Turn(ByVal Angle As Single) As clsCircle
>         Dim dum As New clsCircle
>         dum.pen = pen.Clone
>         pen.Dispose()'ここでclsCircleのpenを破棄
>         Return dum'ここではdum.penの破棄は行えないはず
>     End Function
あれ? 先ほどは、同じインスタンスを返すと言っていたような…。
でも実際には、別のインスタンスを返していますね。

もしかして、『同じclsCircleのインスタンスを返すメソッド』というのは、
「同じclsCircle型の異なるインスタンスを返すメソッド」のような意味だったのかな。

[ツリー表示へ]
タイトルよねKEN様、魔界の仮面弁士様ありがとうございます。
記事No7604
投稿日: 2008/05/23(Fri) 12:58
投稿者ひでと
よねKEN様、魔界の仮面弁士様ありがとうございます。
お二人にご説明いただいたことの1/3位しか、理解できません。
(もっと少ないかも。申し訳ないです)

なんとなく理解したこと
クラスのプロパティにPenを直接入れると、Disposeで開放処理が難しい。
その場合、開放処理をするのであればクラスが破棄されるタイミングでするべき。
クラス内部のプロシージャ内でPenを使うときは、そのプロシージャ内でDispose
することが望ましい。

クラスのプロパティにPenを直接入れるより、Penの持っているプロパティを設定する
変数(例 Wide,Color)をクラスのプロパティに作り、
Drawするときに、Penを作成し、そのプロパティを設定する方が楽である。
Drawが終わったら直ちに PenをDisposeすれば良い。

だいたいこん認識でよさそうな気がしました。
今 myPenクラスを別に作り、Penのプロパティの値をそのクラスで受ける。
Drwの時にmyPenから必要な値を取得するような方向で試してみようと思います。
ありがとうございました。
もう少し試してきます。

[ツリー表示へ]