tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルEXCELへの印字部分処理に関して
記事No6370
投稿日: 2007/09/28(Fri) 19:11
投稿者ジーク
昨日はお世話になりました
本日、Excelの印字部分での処理について
ご教授いただきたく書き込みました

現在、Excelへ印字をする部分の関数を作成していたのですが
プロセス内にExcelが残ってしまう現象が発生しています。

当サイトにある注意点や解放方法により解放を行っているのですが
印字部分のみ、どうもうまくいきません(その他印刷部分などはうまくいっています)


〜〜〜関数のコードは下記になります〜〜〜

Private Sub OUT_XLS_Data(ByVal pXlsWorkSheet As Excel.Worksheet, _
                             ByVal pStrXlsCellName As String, _
                             ByVal pDicXlsNames As Dictionary(Of String, String))

        Dim xlsRange As Excel.Range = Nothing
        Dim xlsRangeObj As Excel.Range = Nothing

        Dim intCellObjCol As Integer = 0
        Dim intCellObjRow As Integer = 0

        Dim strTmp As String = ""
        Dim intLoopCount As Integer = 0

        Try

            'pDicXlsNamesにはExcel内のセル定義名が入っています
            If pDicXlsNames.ContainsKey(pStrXlsCellName) = False Then
                Exit Try
            End If


            '定義が指定されているセルの位置情報を取得します
            xlsRangeObj = pXlsWorkSheet.Range(pStrXlsCellName)
            intCellObjRow = xlsRangeObj.Row
            intCellObjCol = xlsRangeObj.Column

            For intLoopCount = 0 To 10

                Select Case pStrXlsCellName
                    Case "NAME"
                        strTmp = "NAME_DATA" & intLoopCount
                    Case "ADDRESS"
                        strTmp = "ADDRESS" & intLoopCount
                End Select
        
                '縦位置を修正したセル情報を設定し印字します
                xlsRange = xlsRangeObj(intCellObjRow + intLoopCount, intCellObjCol)
                xlsRange.Value = strTmp

                MRComObject(xlsRange)

            Next intLoopCount

            MRComObject(xlsRangeObj)


        Catch ex As Exception
            MsgBox(ex.Message)

        Finally
            MRComObject(intCellObjRow)
            MRComObject(intCellObjCol)

            MRComObject(xlsRange)
            MRComObject(xlsRangeObj)

            MRComObject(pDicXlsNames)
            MRComObject(pStrXlsCellName)
            MRComObject(pXlsWorkSheet)

        End Try

    End Sub

〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜

ご迷惑をおかけしますが
ご教授の程、よろしくお願いします

[ツリー表示へ]
タイトルRe: EXCELへの印字部分処理に関して
記事No6373
投稿日: 2007/09/28(Fri) 21:12
投稿者花ちゃん
>      xlsRange = xlsRangeObj(intCellObjRow + intLoopCount, intCellObjCol)
>      xlsRange.Value = strTmp
多分、問題はここでは。


>             MRComObject(intCellObjRow)
>             MRComObject(intCellObjCol)

これは、解放する必要がないのでは。(単なるInteger 型の変数なので)

[ツリー表示へ]
タイトルRe^2: EXCELへの印字部分処理に関して
記事No6374
投稿日: 2007/09/28(Fri) 22:18
投稿者魔界の仮面弁士
> >             MRComObject(intCellObjRow)
> >             MRComObject(intCellObjCol)
> これは、解放する必要がないのでは。(単なるLong型の変数なので)

ん? Integer型ですよね? (^^;


ところで。

ここのサイトで紹介されている MRComObject メソッドに、少し手を加えてみました。

これなら、
 MRComObject(xlsRange)
はコンパイルが通りますが、今回のような
 MRComObject(intCellObjRow)
はコンパイルエラーになってくれるので、間違いを見つけやすくなります。


また、元のコードは、引数が ByRef Object だったために、Option Strict On の時に
呼び出しにくかったのですが、このコードではその点も改善されています。



'VB2005/2008 用
''' <summary>
''' COMオブジェクトの参照カウントをデクリメントします。
''' </summary>
''' <typeparam name="T">(省略可能)</typeparam>
''' <param name="objCom">
''' COM オブジェクト持った変数を指定します。
''' このメソッドの呼出し後、この引数の内容は Nothing となります。
''' </param>
''' <param name="force">
''' すべての参照を強制解放する場合はTrue、現在の参照のみを減ずる場合はFalse。
''' </param>
Public Shared Sub MRComObject(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)
  If objCom Is Nothing Then
    Return
  End If
  Try
    If System.Runtime.InteropServices.Marshal.IsComObject(objCom) Then
      If force Then
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(objCom)
      Else
        System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom)
      End If
    End If
  Finally
    objCom = Nothing
  End Try
End Sub

[ツリー表示へ]
タイトルRe^2: EXCELへの印字部分処理に関して
記事No6410
投稿日: 2007/10/05(Fri) 10:18
投稿者ジーク
レスを頂いたのに書き込みが遅れ、申し訳ありません

>花ちゃんさん
ご返答ありがとうございます

最初はEXCELのオブジェクトだけ解放していたのですが
何が原因で解放されないのかが不明だった為
とにかく全ての変数などを解放しようと思い記述していました


>魔界の仮面弁士さん
ご返答ありがとうございます
作成して頂いた MRComObject メソッドを早速使用させて頂きました

しかし、関連するxlsBookの Saved や SaveAs が
「基になる RCW から分割された COM オブジェクトを使うことはできません。」
と表示されエラーになってしまいます。
また、当サイトに記載されているMRComObject メソッドを
使用させて頂いた時と同様にExcelのプロセスが解放されませんでした。

申し訳ありませんが引き続きご教授頂ければと思います
よろしくお願いします

[ツリー表示へ]
タイトルRe^3: EXCELへの印字部分処理に関して
記事No6411
投稿日: 2007/10/05(Fri) 15:07
投稿者魔界の仮面弁士
> しかし、関連するxlsBookの Saved や SaveAs が
> 「基になる RCW から分割された COM オブジェクトを使うことはできません。」
> と表示されエラーになってしまいます。

使用中のオブジェクトは解放してはいけません。
「処理が終わった後」で呼び出してください。

# 通常は、Application,Workbooks,Workbook,……の順で生成するので、
# 解放は、……,Workbook,Workbooks,Application の逆順で解放することになるかと。


> 申し訳ありませんが引き続きご教授頂ければと思います
修正後のソースを提示してみてください。

[ツリー表示へ]
タイトルRe^4: EXCELへの印字部分処理に関して
記事No6425
投稿日: 2007/10/12(Fri) 13:46
投稿者ジーク
ご返答ありがとうございます
> 修正後のソースを提示してみてください。

    Public Sub ExcelPrint()

        Dim strFilePath As String = "Excelファイル名"

        Dim xlsApp As Object = Server.CreateObject("Excel.Application")
        Dim xlsBooks As Excel.Workbooks = xlsApp.Workbooks
        Dim xlsBook As Excel.Workbook = xlsBooks.Open(strFilePath)
        Dim xlsSheets As Excel.Sheets = xlsBook.Worksheets
        Dim xlsSheet As Excel.Worksheet = xlsSheets.Item(1)

        Dim dicXlsName As New Dictionary(Of String, String)


        Dim strScript As String = ""
        Dim strMsg As String = ""

        Dim pd As New System.Drawing.Printing.PrintDocument                 'PrintDocumentの作成
        Dim defaultPrinterName As String = pd.PrinterSettings.PrinterName   'プリンタ名の取得

        Try
            With xlsApp
                .Visible = False            'Excelを非表示
                .DisplayAlerts = False      'メッセージの非表示
                .ScreenUpdating = False     '再描画無効
            End With

            SetDictionary_XlsNameData(xlsBook, dicXlsName)

            '印字するデータを設定します
            Get_Name_Data()


            OUT_XLS_Data(xlsSheet, "NAME", dicXlsName)
            OUT_XLS_Data(xlsSheet, "Addr", dicXlsName)


            'EXCELを保存したことにする
            xlsBook.Saved = True

            '通常使うプリンターへ印刷する
            xlsSheet.PrintOut(ActivePrinter:=defaultPrinterName)

        Catch ex As Exception
            'エラー処理
            strMsg = ex.Message

            MsgBox(strMsg)

        Finally
            'ここで使用したEXCEL関連のオブジェクトの解放を行います
            MRComObject(xlsSheet)
            MRComObject(xlsSheets)
            xlsBook.Close(False)
            MRComObject(xlsBook)
            MRComObject(xlsBooks)
            xlsApp.Quit()
            MRComObject(xlsApp)
            MRComObject(dicXlsName)

        End Try
    End Sub

----------------------------------------------------------------------
    Private Sub OUT_XLS_Data(ByVal pXlsWorkSheet As Excel.Worksheet, _
                             ByVal pStrXlsCellName As String, _
                             ByVal pDicXlsNames As Dictionary(Of String, String))

        Dim udt_RptData_Test() As udt_RptData_Test_Tag = Nothing

        Dim xlsRange As Excel.Range = Nothing
        Dim xlsRangeObj As Excel.Range = Nothing


        Dim intCellObjCol As Integer = 0
        Dim intCellObjRow As Integer = 0

        Dim strBuff As String = ""
        Dim intLoopCount As Integer

        Try

            udt_RptData_Test = DirectCast(ViewState("TestData"), udt_RptData_Test_Tag())

            If pDicXlsNames.ContainsKey(pStrXlsCellName) = False Then
                Exit Try
            End If



            If Not udt_RptData_Test Is Nothing Then
                xlsRangeObj = pXlsWorkSheet.Range(pStrXlsCellName)
                intCellObjRow = xlsRangeObj.Row
                intCellObjCol = xlsRangeObj.Column

                For intLoopCount = LBound(udt_RptData_Test) To UBound(udt_RptData_Test)
                    'For intLoopCount = 0 To 0


                    Select Case pStrXlsCellName
                        Case "NAME"
                            strBuff = udt_RptData_Test(intLoopCount).strName
                        Case "Addr"
                            strBuff = udt_RptData_Test(intLoopCount).strAddr
                    End Select

                    xlsRange = xlsRangeObj(intCellObjRow + intLoopCount, intCellObjCol)
                    xlsRange.Value = strBuff

                    MRComObject(xlsRange)

                Next intLoopCount

                MRComObject(xlsRangeObj)

            End If

        Catch ex As Exception
            MsgBox(ex.Message)

        Finally

            MRComObject(xlsRange)
            MRComObject(xlsRangeObj)

            MRComObject(pStrXlsCellName)
            MRComObject(pXlsWorkSheet)

        End Try

    End Sub

上記のように使用し
魔界の仮面弁士さんに提示していただいた解放関数を
使用するとエラーが発生及び解放が行われません

長々と書いてしまいましたが宜しくお願いします

[ツリー表示へ]
タイトルRe^5: EXCELへの印字部分処理に関して
記事No6439
投稿日: 2007/10/14(Sun) 01:47
投稿者魔界の仮面弁士
> Dim xlsApp As Object = Server.CreateObject("Excel.Application")

……うん? Server.CreateObject ということは、もしかして、ASP.NET でしょうか。

だとすれば、Excel ライブラリはサーバサイドに対応した設計にはなっていませんし、
ライセンス的にもおすすめできるものではありません。別の手法を検討した方が良いかと。
http://support.microsoft.com/kb/257757/ja

# ExcelCreator とか、SpreadsheetGear とか、XML スプレッドシートを XSLT で生成するとか。


> MsgBox(strMsg)
あれ? MsgBox を使っているという事は、Webアプリでもなさそうですね。
うーん。分からなくなってしまいました。


とりあえず、普通のデスクトップアプリだという前提で回答します。


> SetDictionary_XlsNameData(xlsBook, dicXlsName)
この部分は、ブラックボックスですね。

上記メソッドの内容については、掲示板には一切書かれていないので、もしも
その中で、何か問題のある処理が行われていたとしても、こちらでは判断できません。

念のため、上記の行を削除(コメントアウト)した場合に、プロセスが
残ってしまうのかどうか、検証された方が良いかも知れません。


> '印字するデータを設定します
> Get_Name_Data()
データの「設定」なのに「Get」という点に奇妙さを感じますが、それはさておき。

コードの全体像が見えないので判断に困りますが、
このメソッドは、戻り値も引数指定も無いのですか?

データの読み書きを行うメソッドであるなら、
 Get_Name_Data(foo)     'fooを使ってデータを編集する または fooにデータが埋め込まれる
または
 foo = Get_Name_Data()  '戻り値 foo にデータが返される
の形式になりそうな気がするのですけれども。


> Finally
>   'ここで使用したEXCEL関連のオブジェクトの解放を行います
>     MRComObject(xlsSheet)
>     MRComObject(xlsSheets)
>     xlsBook.Close(False)
>     MRComObject(xlsBook)
>     MRComObject(xlsBooks)
>     xlsApp.Quit()
>     MRComObject(xlsApp)
>     MRComObject(dicXlsName)
>
> End Try
このコードは危険だと思いますよ。

もし、処理の途中でエラーが発生していた場合には、Finally ブロックに入るわけですが、
エラーの発生位置によっては、たとえば「xlsBook が Nothing 状態」の可能性もあるわけです。

そうした場合、xlsBook.Close のメソッドが失敗しますから、さらにそこで
エラーが発生し、それ以降の解放処理が行われなくなってしまうかと。


> Private Sub OUT_XLS_Data(…
最初のコードと比べると、微妙に変わってますね。(^^;

[ツリー表示へ]