none
System.Printing名前空間のクラス利用時にメモリリークが発生する RRS feed

  • 質問

  • こんにちは、VB.net VS2008+WindowsXpで開発を行っています。
    現在、印刷管理用のアプリケーションを作成していますが、それにあたり、プリンタのキューを監視するためにSystem.Printing名前空間のPrintServerクラスとPrintQueueクラスを利用し、利用可能なプリンタ情報の取得と、各プリンタのステータス(一時停止、トナー切れなど)を取得しています。
    これまでは、Win32APIを直接利用し、プリンタのステータス情報を取得していましが、.NET Framework3.0より追加されたSystem.Printing名前空間を利用することにより、プリンタ情報の取得が容易になったこともあり、ソースの修正を行いました。

    すると、これまでは発生していなかったのですが、System.Printing名前空間のPrintServerクラスのインスタンス生成、Refreshメソッドなどの特定のメソッド利用時にメモリが上昇し、解放される気配がないことに気づきました。
    たとえば、画面上にボタンをひとつ配置し、そのボタンをクリックすると以下のソースを実行してみても確認できます。

      Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            ps = New PrintServer
        End Sub

    今回、動かしているアプリケーションは印刷キューを監視するための常駐プログラムのため、上記のように手動で起動するのに比べると、メソッドの実行回数も比べ物にならないため、あっという間にメモリリークしてしまいます(一定時間放置しておくと、OutOfMemory例外が実際に発生します)
    PrintServerオブジェクトのDispose、Nothing代入なども行いましたが、一向に改善されません。
    Gc.Collectでガベージコレクションを明示的に実行しても変化ありません。

    どなたかよい回避策、対応策をご存知の方がいらっしゃいましたらご教授願います。

    以下の箇所がメモリ増加の大きな原因となっていると思われます。
     '-------------------------------------------------------------------------------
        ' 2009/07/16 Add プリンタキューの情報を返す
        '
        ' [parameter]printername:プリンタ名
        ' [return]   プリンタキュー(存在しない場合はNULLを返す)
        '-------------------------------------------------------------------------------
        Private Function GetPrinterQueueObj(ByVal printerName As String) As PrintQueue
            Dim existFlg As Boolean = False
            Dim queue As PrintQueue = Nothing


            For Each pq As PrintQueue In _printserver.GetPrintQueues
                If pq.Name = printerName Then
                    existFlg = True
                    Exit For
                End If
            Next

            If existFlg Then
                queue = _printserver.GetPrintQueue(printerName)
            End If
            _printserver.Dispose()
            Return queue
        End Function

    2009年7月30日 5:13

回答

  • こんにちは
     
    このコードを使い、100msec毎に、あるプリンタのJOBSを取得するように実行してみましたが、特にリークするような事もありませんでした。
    GC.CollectionCountを見ていても、きちんとメモリの解放がされているようでした。

        Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
            Dim p As PrintQueue = GetPrinterQueueObj("実際のプリンタ名")
            Label2.Text = DateTime.Now.Ticks.ToString & " - JOBS:" & p.NumberOfJobs
        End Sub

    これで、タイマ値を100ミリ秒で10分ほど動かしていましたが、特に何も起きませんでした。
    _printserverを、グローバル、GetPrinterQueueObj内部、両方のスコープでやってみましたが特に問題は起きませんでした。


    以下の事はやっていないので、この結果意味の無い可能性があります。
    *複数のプリンタを切り替えながら、ということはやっていません。
    *プリンタはネットワークプリンタで、ローカルに直接繋がっているプリンタではありません。
    *思いっきり誤読してトンチンカンな事をしている可能性があります

    chonmage
    2009年7月30日 8:35

すべての返信

  • 自己レスです。
    確認不足でした、再度実行確認したところ、上記の部分に関してはある一定までメモリ上昇後、
    解放は行われているように見えます。
    現象は別の箇所で発生しているようですので、とりあえずクローズさせてください。

    ご確認いただいた方にはご迷惑をおかけし申し訳ありませんm(__)m
    2009年7月30日 8:33
  • こんにちは
     
    このコードを使い、100msec毎に、あるプリンタのJOBSを取得するように実行してみましたが、特にリークするような事もありませんでした。
    GC.CollectionCountを見ていても、きちんとメモリの解放がされているようでした。

        Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
            Dim p As PrintQueue = GetPrinterQueueObj("実際のプリンタ名")
            Label2.Text = DateTime.Now.Ticks.ToString & " - JOBS:" & p.NumberOfJobs
        End Sub

    これで、タイマ値を100ミリ秒で10分ほど動かしていましたが、特に何も起きませんでした。
    _printserverを、グローバル、GetPrinterQueueObj内部、両方のスコープでやってみましたが特に問題は起きませんでした。


    以下の事はやっていないので、この結果意味の無い可能性があります。
    *複数のプリンタを切り替えながら、ということはやっていません。
    *プリンタはネットワークプリンタで、ローカルに直接繋がっているプリンタではありません。
    *思いっきり誤読してトンチンカンな事をしている可能性があります

    chonmage
    2009年7月30日 8:35
  • こんにちは。フォーラムオペレーターの高橋春樹です。

    chonmageさん、こんにちは。
    ご確認ありがとうございました。

    ayanoriさん、初めまして。
    MSDNフォーラムのご利用ありがとうございます。
    今回、chonmageさんに問題ないことを検証して頂いたので、
    chonmageさんからの投稿に、回答マークを付けさせてもらいました。

    また、何かありましたら、MSDNフォーラムへのご投稿して頂ければと思います。

    今後ともMSDNフォーラムをよろしくお願いします(^-^)


    マイクロソフト株式会社 フォーラム オペレータ 高橋春樹
    2009年8月6日 2:17