tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルExcelのプロセス
記事No5051
投稿日: 2007/03/05(Mon) 10:46
投稿者Jack
初歩的な質問で申し訳ありません
VB2005ExpressEditionからExcel2000を立ち上げデータ出力後に
Excelを閉じたいと思っているのですが、下記コードにて巧くプ
ロセスが消えてくれません…
どこか記述に誤りがあるようでしたらよろしくお願いいたします。

        Dim objExcel As Excel.Application
        Dim objBook As Excel.Workbook
        Dim strName As String  '開くファイルのパスを入力する

        MsgBox("出力する帳票を選択して下さい!", vbInformation, "確認")
        objExcel = CreateObject("Excel.Application")
        strName = objExcel.Application.GetOpenFilename("Excelファイル (*.xls), *.xls")
        objBook = objExcel.Workbooks.Open(strName)
        objBook.Worksheets("Sheet1").Cells(1, 1) = "Test"
        objBook.Close()
        objBook = Nothing
        objExcel.Quit()
        objExcel = Nothing
        MsgBox("出力終了", MsgBoxStyle.Information, "報告")

[ツリー表示へ]
タイトルRe: Excelのプロセス
記事No5053
投稿日: 2007/03/05(Mon) 10:55
投稿者よねKEN
残念ながら、VB6と同じ感覚でExcelなどのCOMを操作してはいけません。
まずは、こちらのサイトの"VB.NET Tips一覧"の"Excel・Word・他"をご覧ください。

[ツリー表示へ]
タイトルRe: Excelのプロセス
記事No5054
投稿日: 2007/03/05(Mon) 11:01
投稿者魔界の仮面弁士
# なぜ、Excel 解放問題に関する質問が無くならないのだろう…?

>         strName = objExcel.Application.GetOpenFilename("Excelファイル (*.xls), *.xls")
Application プロパティの使用は冗長です。
objExcel.Application は、objExcel 自身を返しますので。

>         objBook = objExcel.Workbooks.Open(strName)
Workbooks を変数にうけとり、最後に ReleaseComObject メソッドで解放しましょう。
VB6 や VBA とは、ActiveX の解放手順が全く異なることに注意して下さい。

>         objBook.Worksheets("Sheet1").Cells(1, 1) = "Test"
Worksheets を変数に受けましょう。
Worksheet も変数に受けましょう。
Range も変数に受けましょう。
そして、Range の Value プロパティに "Test" を代入し、それから最後に、
変数に受けた ActiveX オブジェクトを、ひとつひとつ解放していきましょう。

>         objBook.Close()
>         objBook = Nothing
>         objExcel.Quit()
>         objExcel = Nothing
これらの Nothing を代入する行為自体には、ほとんど意味がありません。
(これが VB6 であれば、少しは意味があるのですが)
Excel オブジェクトの解放の必要なのは、Marshal クラスの ReleaseComObject メソッドを呼び出すことです。

>         MsgBox("出力終了", MsgBoxStyle.Information, "報告")
最初は vbInformation 定数を使っていて、ここでは MsgBoxStyle.Information になっていますね。
どちらの定数を使っても(あるいは MessageBox.Show に切り替えても)結果は同じではありますが、
意識的に、コードに統一性を持たせる癖を付けておいた方が良いかと思いますよ。

[ツリー表示へ]
タイトルRe^2: Excelのプロセス
記事No5059
投稿日: 2007/03/05(Mon) 13:46
投稿者Jack
よねKENさん、魔界の仮面弁士さん初めまして
早速の御教授ありがとうございます。
お二方のアドバイスを参考に以下のようなソースに変更してみました
が、巧くいきません。
@でエラーが出る為、以降のソースが正しいかどうかも解りません
それと、魔界の仮面弁士さんのアドバイスにもありますWorkBooksの開放にあたり「ReleaseComObject」メソッドが表示されない為参照の仕方がおかしいとは思いま
すが是非宜しくお願いします。

        Dim objExcel As Excel.Application
        Dim objBooks As Excel.Workbooks
        Dim objBook As Excel.Workbook
        Dim objWSheets As Excel.Worksheets
        Dim objWSheet As Excel.Worksheet
        Dim objRange As Excel.Range
        Dim strName As String  '開くファイルのパスを入力する

        MsgBox("出力する帳票を選択して下さい!",MsgBoxStyle.Information, "確認")
        objExcel = CreateObject("Excel.Application")
        strName = objExcel.GetOpenFilename("Excelファイル (*.xls), *.xls")
        objBooks = objExcel.Workbooks()
        objBook = objBooks.Open(strName)
        objWSheets = objExcel.Worksheets() '@ココでエラーが出ます
        objWSheet = objWSheets("Sheet1")
        objRange = objWSheet.Cells(1, 1)
        objRange.Value = "TestTest"

        objRange = Nothing
        objWSheet = Nothing
        objWSheets = Nothing
        objBook.Close()
        objBooks.Close()
        objExcel.Quit()
        MsgBox("出力終了", MsgBoxStyle.Information, "報告")

[ツリー表示へ]
タイトルRe^3: Excelのプロセス
記事No5060
投稿日: 2007/03/05(Mon) 14:04
投稿者魔界の仮面弁士
> 「ReleaseComObject」メソッドが表示されない為
フルネームは、System.Runtime.InteropServices.Marshal.ReleaseComObject メソッドです。
このままでは長いので、名前空間の部分を Imports しておくと良いでしょう。

>         objWSheets = objExcel.Worksheets() '@ココでエラーが出ます
Sheets 型で受けてください。

>         objRange = objWSheet.Cells(1, 1)
Cells が「引数を取らないプロパティ」であることに注意して下さい。この場合は、
 objRange1 = objWSheet.Cells
 objRange2 = objRange1(1, 1)
のように、Range 型変数を 2 つ使うか、もしくは、
 objRange = objWSheet.Range("A1")
のように、Cells プロパティの代わりに Range プロパティを使うようにします。

>         objRange = Nothing
ここで、これらの変数に Nothing を代入してはいけません。.NET 側のメモリを解放する前に、
それらが参照していた ActiveX 側のメモリを、ReleaseComObject で解放する必要があるためです。

[ツリー表示へ]
タイトルRe^4: Excelのプロセス
記事No5062
投稿日: 2007/03/05(Mon) 15:38
投稿者Jack
魔界の仮面弁士さん、早速のアドバイスありがとうございます。
ソースはあまり変更しておりませんが(どう修正して良いのやら解らず、すみません)、
下記ソースに変更したところで、とりあえずWorkSheets(Sheetsになるのでしょうか?)
を参照する事はできエラーは出なくなりました。ありがとうございます。

        Dim objExcel As Excel.Application
        Dim objBooks As Excel.Workbooks
        Dim objBook As Excel.Workbook
        Dim objWsheets As Excel.Sheets
        'Dim objWSheets As Excel.Worksheets
        Dim objWSheet As Excel.Worksheet
        Dim objRangeA As Excel.Range
        Dim objRangeB As Excel.Range
        Dim strName As String  '開くファイルのパスを入力する

        MsgBox("出力する帳票を選択して下さい!", MsgBoxStyle.Information, "確認")
        objExcel = CreateObject("Excel.Application")
        strName = objExcel.GetOpenFilename("Excelファイル (*.xls), *.xls")
        objBooks = objExcel.Workbooks()
        objBook = objBooks.Open(strName)
        objWsheets = objExcel.Sheets()
        objWSheet = objWSheets("Sheet1")
        objRangeA = objWSheet.Cells
        objRangeB = objRangeA(1, 1)
        objRangeB.Value = "TestTestTest"

        '各オブジェクトの開放手順が解らず…
        objBook.Close()
        objBooks.Close()
        objExcel.Quit()

        MsgBox("出力終了", MsgBoxStyle.Information, "報告")

後プロセスの終了方法ですが、タスクマネージャを見る限りExcelのプロセス
は残っていないみたいです。
ただ、コレでは何となくすっきりしません(^^
オブジェクトの開放の前に、いろいろと勉強する必要ありそうです…、が、お願いします。
魔界の仮面弁士さんのおっしゃるように.Net側のメモリ開放の前にActivX側のメモリ開放
を行う必要があるということですが、そのあたりの手順とか理論がいまいち解っておりません。
勉強不足で申し訳ありませんが、そのあたりの手順について宜しくお願いできないでしょうか?

[ツリー表示へ]
タイトルRe^5: Excelのプロセス
記事No5063
投稿日: 2007/03/05(Mon) 16:36
投稿者Jack
こんにちは。
とりあえず、以下のようにソースを書き換えてみたところ
タスクマネージャから消えるタイミングが早くなりました…(謎)
こんな感じでよろしいのでしょうか?

        Dim objExcel As Excel.Application
        Dim objBooks As Excel.Workbooks
        Dim objBook As Excel.Workbook
        Dim objWsheets As Excel.Sheets
        Dim objWSheet As Excel.Worksheet
        Dim objRangeA As Excel.Range
        Dim objRangeB As Excel.Range
        Dim strName As String  '開くファイルのパスを入力する
        
        MsgBox("出力する帳票を選択して下さい!", MsgBoxStyle.Information, "確認")
        objExcel = CreateObject("Excel.Application")
        strName = objExcel.GetOpenFilename("Excelファイル (*.xls), *.xls")
        objBooks = objExcel.Workbooks()
        objBook = objBooks.Open(strName)
        objWsheets = objExcel.Sheets()
        objWSheet = objWSheets("Sheet1")
        objRangeA = objWSheet.Cells
        objRangeB = objRangeA(1, 1)
        objRangeB.Value = "TestTestTest"

        objExcel.Quit()
     
        System.Runtime.InteropServices.Marshal.ReleaseComObject(objRangeA)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(objRangeB)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(objWsheets)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(objWSheet)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(objBook)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(objBooks)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(objExcel)
        objRangeA = Nothing
        objRangeB = Nothing
        objWSheet = Nothing
        objWsheets = Nothing
        objBook = Nothing
        objBooks = Nothing
        objExcel = Nothing

        MsgBox("出力終了", MsgBoxStyle.Information, "報告")

[ツリー表示へ]
タイトルRe^6: Excelのプロセス
記事No5073
投稿日: 2007/03/06(Tue) 03:31
投稿者魔界の仮面弁士
> こんな感じでよろしいのでしょうか?
まぁ、良いんじゃ無いでしょうか。


あえて修正するとすれば:

>         objExcel = CreateObject("Excel.Application")
参照設定しているのに、ここでレイトバインドにする意味はあまり無いかと。
objExcel = New Excel.ApplicationClass() で良いんじゃないですかね。

# 可能であれば、Option Strict On モードでチェックしてみましょう。


>         objBooks = objExcel.Workbooks()
>         objRangeA = objWSheet.Cells
Workbooks と Cells。どちらもプロパティですが、一方は括弧を付けて、
他方は括弧を付けないという表記の揺れが見られますね。コードを統一しましょう。

ちなみに VB では、「引数の無いプロパティやメソッド」の呼び出し時には、
括弧を付けても付けなくても良い事になってはいるのですが、一般的には他言語に倣って、
メソッド呼び出しに括弧を付け、プロパティ呼び出しには括弧を付けない事が多いようです。

> objExcel.Quit()
> System.Runtime.InteropServices.Marshal.ReleaseComObject(objRangeA)
個人的には、最後に一括して解放するのではなく、
 objRangeB = objRangeA(1, 1)
 objRangeB.Value = "TestTestTest"
 Marshal.ReleaseComObject(objRangeB)
などのように、不要になった時点で破棄していく形を取った方が良いのではないかな、と。

[ツリー表示へ]
タイトルRe^7: Excelのプロセス
記事No5074
投稿日: 2007/03/06(Tue) 10:32
投稿者Jack
魔界の仮面弁士さん、おはようございます。
適切なアドバイスありがとうございます。
此方のTipsを参考に、各オブジェクトの開放について関数化してみたところです。

>参照設定しているのに、ここでレイトバインドにする意味はあまり無いかと。

なるほどです。
各オブジェクト変数の宣言も意識する必要がありますね。
先ほどレイトバインドとアーリーバインドの違いを知ったところです。
参照設定無しで各オブジェクトが機能するかどうか試してみたいと思います。
ありがとうございます。

># 可能であれば、Option Strict On モードでチェックしてみましょう。

Option strict On モード?ですか…。
型を厳密に扱うモードという事で、これも先ほど知りました(^^。
此方もCheckしてみますね。
ありがとうございます。

[ツリー表示へ]
タイトルRe^7: Excelのプロセス
記事No5082
投稿日: 2007/03/07(Wed) 13:56
投稿者Jack
魔界の仮面弁士さん、こんにちは。
レイトバインドとアーリーバインドについて御指摘いただき、早速
参照設定無しでExcelを操作しようと思ったのですが、これってインテ
リセンスが働かないので記述が大変ですね。
此方で紹介されているレイトバインドのメリット(Excelのヴァージョン
に左右される事が少ない)に惹かれ一応アーリーバインドで記述したソ
ースをコピーして変更を加え実行してみたらCom参照無しで巧く動作しま
した。
ところで、先日ご指摘のありました「Option Strict On」モードですが
「Option Strict On」すら何のことやら解らなかったのですが、とりあ
えずExcel参照設定ありで「Option Strict On」にて記述してみましたと
ころ、いたるところに「波線」がでて悶絶しました(^^
Ctype関数を追加しまくってなんとか波線は消えましたが…

OptionStrictOnモードですと型の宣言が明示的になり、不慮のデータ消失
等を防げたり、処理速度(パフォーマンス)が向上したりといったメリット
が考えられるそうですね。
ちなみに参照設定無しで「Option Strict On」による記述は可能なのでしょ
うか?

[ツリー表示へ]
タイトルRe^8: Excelのプロセス
記事No5083
投稿日: 2007/03/07(Wed) 14:31
投稿者魔界の仮面弁士
> 此方で紹介されているレイトバインドのメリット(Excelのヴァージョン
> に左右される事が少ない)に惹かれ一応アーリーバインドで記述したソ
> ースをコピーして変更を加え実行してみたらCom参照無しで巧く動作しま
> した。

逆に、そういった特殊な事情(複数バージョンのExcel に対応したいなど)が
無い限りは、極力、参照設定してアーリーバインドのコードにすべきかと。

> Ctype関数を追加しまくってなんとか波線は消えましたが…

CType ではなく、DirectCast を使いましょう。

> ちなみに参照設定無しで「Option Strict On」による記述は可能なのでしょ
> うか?

CallByName、もしくはリフレクションを利用することで対応可能です。

[ツリー表示へ]