tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルVB2008で、単一のExcel.Shapesオブジェクトの取得方法
記事No9353
投稿日: 2009/09/07(Mon) 11:02
投稿者camputer
 こんにちは、質問させていただきます。どうぞよろしくお願いいたします。
 プログラム履歴役10年+VB履歴1年弱です。
開発環境はXP+VB2008+Excel2003になります。

 先日、オブジェクト開放の目的でアドバイスいただけて
      For Each xlSheet In xlSheets

      Next
のようなEach指定をすべて
            Dim iSheetMAX As Integer = xlSheets.Count
            For iSheet = 1 To iSheetMAX
        xlSheet = xlSheets(iSheet)

            Next
のように修正しております。

 そこでPPTやExcelでほとんどのコードを修正できたのですが、
        Dim xlShape As Excel.Shape
        Dim xlShapes As Excel.Shapes
についての場合のみ
    xlShape = xlShapes(i)
で、エラー「インターフェイス'Microsoft.Office.Interop.Excel.Shapes'には既定の
プロパティがないため、インデックス処理を実行できません。」となってしまいます。

 いろいろと調べておりますと、VB2005ではそれが出来たようなのですが、
http://msdn.microsoft.com/ja-jp/library/microsoft.office.interop.excel.shapes(office.11).aspx
に「単一の Shape オブジェクトを取得するには、Shapes(index) を使用します。」
とあります。
 VB2008でのやり方を調べてみましたが、発見できませんでした。
何かよさそうな方法を是非ともご教授いただきたくお願いいたします。
どうぞよろしくお願いします。

[ツリー表示へ]
タイトルRe: VB2008で、単一のExcel.Shapesオブジェクトの取得方法
記事No9354
投稿日: 2009/09/07(Mon) 13:58
投稿者魔界の仮面弁士
> で、エラー「インターフェイス'Microsoft.Office.Interop.Excel.Shapes'には既定の
> プロパティがないため、インデックス処理を実行できません。」となってしまいます。

Shapes にも既定メンバはありますが、それは「既定のプロパティ」ではなく
「既定のメソッド」となっています。この場合にはメンバ名を省略せずに、
xlShapes._Defauls(index) または xlShapes.Items(index) の構文で、
アクセスしてみてください。

[ツリー表示へ]
タイトルRe^2: VB2008で、単一のExcel.Shapesオブジェクトの取得方法
記事No9356
投稿日: 2009/09/07(Mon) 14:30
投稿者camputer
魔界の仮面弁士 様

 どうもありがとうございます!!
解決いたしました。ただそれでも何故かExcel.Exeが開放されなかったので、
myShapes.Countを一度Objectに入れてMRComObjectすると解決いたしました。

  Dim iShapeMax As Object = myShapes.Count
           '(↑何故かAs Integerでも開放されませんでした)
  For i = 1 To iShapeMax
    myShape = myShapes.Item(i)

  Next

  MRComObject(iShapeMax)
                
 (以前「PowerPointのオブジェクト開放ができません」で
魔界の仮面弁士様から教えていただいた
>一度 Object 型に受けてから解放という手もありますが、
>あまり良い方法でもないですね。
をヒントにやってみました。重ねてどうもありがとうございます。)



 VB界の超VIPの方にアドバイスいただけて非常に光栄です。
MSDNとGoogleと花ちゃんの過去ログで結構検索してみたつもりでしたが、
もしかすると難しくない問題だったのでしょうか。。。
上級者の方々がどのようにして、この膨大なメソッドやプロパティを把握してらっしゃるのか
分かりませんが、自分も早くこのような問題を解決できるようになりたいです。
 これでやっと、このループの中のコーディング方法の続きを考えることが出来ます。
どうもありがとうございました!

[ツリー表示へ]
タイトルRe^3: VB2008で、単一のExcel.Shapesオブジェクトの取得方法
記事No9357
投稿日: 2009/09/07(Mon) 15:36
投稿者魔界の仮面弁士
# 開放か解放かという話は、とりあえず横に置いておくとして:

> Dim iShapeMax As Object = myShapes.Count
Shapes.Count は、System.Int32 (Integer)を返します。
Integer は COM オブジェクトでは無い為、解放云々とは無関係ですよ。

そもそも「Integer 型を格納した Object」は、Marshal.ReleaseComObject に
渡すことができません。COM オブジェクト以外を渡すとエラーになりますから。


ループで処理する場合には、
>   For i = 1 To iShapeMax
>     myShape = myShapes.Item(i)
          :
          MRComObject(myShape)  '取得した myShape を破棄する
>   Next
のように実装します。


> 魔界の仮面弁士様から教えていただいた
>>一度 Object 型に受けてから解放という手もありますが、
>>あまり良い方法でもないですね。
> をヒントにやってみました。
そういう意味ではありません。

そこで受け取ろうとしていたオブジェクトは、列挙用の COM オブジェクト
(System.Runtime.InteropServices.ComTypes.IEnumVARIANT に相当)の事です。
.NET で管理されるオブジェクト(今回は Integer 型)を受け取っても、
Excel 解放の手助けとはなりません。

[ツリー表示へ]
タイトルRe^4: VB2008で、単一のExcel.Shapesオブジェクトの取得方法
記事No9359
投稿日: 2009/09/07(Mon) 17:12
投稿者camputer
 魔界の仮面弁士 様

 どうもありがとうございます!
> # 開放か解放かという話は、〜
↑どうもありがとうございました^^; このままずっと一生行くところでしたorz


 私の今の環境ですと、なぜか
  If myShape.Type = 6 Then

  Dim myShapeType As Integer = myShape.Type
  If myShapeType = 6 Then
としたり、
  Call DataGridView_Rows_Add(String1 As String, String2 As String, _
                               String3 As String, String4 As String, _
                               Integer1 As Integer, Integer2 As Integer)
                '↑引数はStringとIntegerのみです。
をコメントアウトしたりすると何故かExcel.EXEが消えます。
 Excel関連のObjectを含まないのコードでもExcel解放に関係あるのかな??と疑い
念のためコメントアウトする→しない→する→しないを繰り返してみると、
Excel.EXEが消える→残る→消える→残るを繰り返しましたので、
Excel関連Object以外のコードもすべて関係あるのかと思い込んでおりました。
 なので、
>.NET で管理されるオブジェクト(今回は Integer 型)を受け取っても、
>Excel 解放の手助けとはなりません。
についても、手助けになるものかと思い込んでおりました。
間違って覚えこむところでした。
ご指摘どうもありがとうございます。

 もしかするとスペックの低いPCだから(?)かもしれませんので今から、
Integerでひっかかるコードを再現してみて、別のPCで挙動を確認してみます。
再度ご報告させていただきます。
(Celeron 1.24GB →Pentium4 4.0GB で実行してみます。)

 まず御礼だけになってしまいますが、どうもありがとうございます!!

[ツリー表示へ]
タイトルRe^5: VB2008で、単一のExcel.Shapesオブジェクトの取得方法
記事No9360
投稿日: 2009/09/07(Mon) 20:07
投稿者魔界の仮面弁士
>  Excel関連のObjectを含まないのコードでもExcel解放に関係あるのかな??と疑い
> 念のためコメントアウトする→しない→する→しないを繰り返してみると、
> Excel.EXEが消える→残る→消える→残るを繰り返しましたので、
> Excel関連Object以外のコードもすべて関係あるのかと思い込んでおりました。

アプリを終了させた後で確認していませんか?

確実な解放処理を確認するときは、
 objExcelApplication.Quit() 'Excel.Application オブジェクトを閉じる
 MRComObject(objExcelApplication)  '使用していたオブジェクトを破棄
 MsgBox("一時停止。Excel.exe は終了していますか?")
などとして、アプリを終了させていない状態で確認してみてください。

正しく ReleaseComObject されていれば、最後の COM オブジェクトが
解放された段階で、EXCEL のプロセスも終了します。



なお、アプリを終了させた場合には、ReleaseComObject していなかったとしても、
すべての参照が未使用となった時点で、AppDomain の終了と共に COM ラッパーが
回収され、EXCEL も終了します。適切な順番で処理されていれば、ですけど。

http://hpcgi1.nifty.com/MADIA/vbnet/wwwlng.cgi?print+200908/09080001.txt

# 実際には RelaseComObject したとしても、AppDomain が残っている限りは、
# COM ラッパーが使用する COM 参照は、切断されたままメモリ上に
# 残っているのですが、それはとりあえず別の話。

[ツリー表示へ]
タイトルRe^6: VB2008で、単一のExcel.Shapesオブジェクトの取得方法
記事No9363
投稿日: 2009/09/08(Tue) 12:40
投稿者camputer
 魔界の仮面弁士 様

 どうもありがとうございます!!

 PCのスペックは関係なさそうでございました。
が、一個だけ分かりました!下のようにFor〜NextでObjectを色々扱っているのですが、
(簡単のため、宣言式や解放コードを省いて書かせていただきます)
    FName = Dir(Mypath & "*.xls", vbNormal)
        Do While FName <> ""
            Dim iSheetMax As Integer = xlSheets.Count
            For iSheet = 1 To iSheetMax
        xlSheet = xlSheets(iSheet)
                Dim iShapeMax As Integer = myShapes.Count
        For iShape = 1 To iShapeMax
                    myShape = myShapes.Item(iShape)

        Next
                Dim iChartMax As Integer = MyChartObjects.Count
                For iChart = 1 To iChartMax
             MyChartObject = MyChartObjects.Item(iChart)

                Next
       Next
        Next
    Loop

以前解放のテストをした時に上のコードで、
    For iChart = 1 To iChartMax
の部分を
    For Each MyChartObject in MyChartObjects

    Next
としておいても解放にひっかからなかったので、そのままにしておいた事が一因のようです。
 IntegerとStringでひっかかったコードを下のように2種類、再現してみましたが、
For Each MyChartObject in MyChartObjects の状態だと、
  test1-a:解放不可
  test1-b:解放可
  test2-a:解放不可
  test2-b:解放可
となるのが、
For iChart = 1 To iChartMaxだと、
  test1-a:解放可
  test1-b:解放可
  test2-a:解放可
  test2-b:解放可
になりました。

↓テスト内容でございます。普通は解放に影響しないコードだと思われるのですが。。
test1-a
                myShapes = xlSheet.Shapes
                myShapes.SelectAll()
                Dim iShapeMax As Integer = myShapes.Count 
                For i = 1 To iShapeMax
                    myShape = myShapes.Item(i)
                    
                    MRComObject(myShape)
                Next

                MRComObject(myShapes) : myShapes = Nothing

test1-b
                myShapes = xlSheet.Shapes
                myShapes.SelectAll()
                Dim iShapeMax As Object = myShapes.Count '←As Objectにして解放しました。
                For i = 1 To iShapeMax
                    myShape = myShapes.Item(i)

                    MRComObject(myShape)
                Next
                MRComObject(iShapeMax)
                MRComObject(myShapes) : myShapes = Nothing

test2-a
                If Len(tbox.AlternativeText) > 0 Then 〜

test2-b
                Dim tboxAlternativeText As String = tbox.AlternativeText
                If Len(tboxAlternativeText) > 0 Then 〜




>アプリを終了させていない状態で確認してみてください。
 終了直前にメッセージボックス起動状態で確認しております。どうもありがとうございます。
下のようなコードになります。

    Private Sub Excel業務効率化(ByRef Integer1 As Integer, ByVal String1 As String, ByRef Integer2 As Integer)
        Dim myApp As Excel.Application
        myApp = CreateObject("Excel.Application")
        Dim myBook As Excel.Workbook
        Dim myBooks As Excel.Workbooks = myApp.Workbooks
        Dim xlSheet As Excel.Worksheet
        Dim xlSheets As Excel.Sheets
      
        Dim tbox As Excel.Shape
        Dim myShape As Excel.Shape
        Dim myShapes As Excel.Shapes
        Dim myShapeUng As Excel.ShapeRange
        Dim FName As String


        FName = Dir(Mypath & "*.xls", vbNormal)
        Do While FName <> ""

     '<中略>(本案件はすべて、ここの解放中に起こっている現象でございます。。)

        Loop
        Me.Activate()

        MRComObject(myBooks) : myBooks = Nothing
        myApp.Quit() : MRComObject(myApp) : myApp = Nothing


    '↓VB花ちゃんのコード(http://hanatyan.sakura.ne.jp/dotnet/Excel01.htm)でも
       '確認させていただきました。
      
        '★☆★☆★☆★☆★☆ Debug 中は下記を実行して確認しながら進めて下さい
        Dim st As Integer = System.Environment.TickCount
        Do While System.Environment.TickCount - st < 5000
            Application.DoEvents()
            System.Threading.Thread.Sleep(500)
            If Process.GetProcessesByName("Excel").Length = 0 Then
                MessageBox.Show("Excel.EXE は解放されました。")
                Exit Do
            End If
        Loop
        If Process.GetProcessesByName("Excel").Length >= 1 Then
            MessageBox.Show("まだ Excel.EXE が起動しています。")
            '一度メッセージボックスを表示すると解放されるようなので再度確認
            If Process.GetProcessesByName("Excel").Length = 0 Then
                MessageBox.Show("Excel.EXE は解放されました。")
            End If
        End If

        MsgBox("完了", , "完了") '←花ちゃんのコードを入れさせていただく前は、
               'ここのメッセージを表示したままで確認しておりました。
    End Sub


 リンク先、どうもありがとうございました。まだ閲覧した事が無かったです。
今から勉強させていただきます。

 いまいち納得がいかない現象なのですが、
「For Each O in Os という因子を含んでいるにも関わらず偶然うまく解放できている
コード」に対して、Excelオブジェクトのプロパティから作られているStringやIntegerの
参照か何かがそれを邪魔する(?)ということでしょうか。。。??

 今後コーディングする上で、Com参照しているオブジェクトがいくつか
残っているかどうか確認する手段があればいいのですが。。
それと何より、魔界の仮面弁士様から以前「PowerPointのオブジェクト開放ができません」で
アドバイスいただいたような
>ゆえに私は、For Each ではなく For で呼び出しています
といった知識をもっと増やさなくてはいけませんね。。。

 このような意味の分からない現象に沢山のアドバイスをどうもありがとうございます。
あの魔界の仮面弁士様からこんなにご意見をいただけると思っておりませんでした。
どうもありがとうございます!! 

[ツリー表示へ]
タイトルRe^7: VB2008で、単一のExcel.Shapesオブジェクトの取得方法
記事No9381
投稿日: 2009/09/09(Wed) 16:41
投稿者camputer
 すいません、自分の説明が一箇所分かりにくそうだったので、補足させてください。。。

<コード1>
    For Each MyChartObject in MyChartObjects

    Next


<コード1’>
    Dim iChartMax As Integer = MyChartObjects.Count
    For iChart = 1 To iChartMax
    MyChartObject = MyChartObjects.Item(iChart)

    Next
に変更すると、<test1-a>、<test1-b>、<test2-a>、<test2-b>の
いずれの書き方で開放されるようになりました・・・という部分についてですが、
<コード1>と<test 〜>は同じサブルーチン内ですが、ぜんぜん違う場所にあります。
(どちらも同じ
       For iSheet = 1 To iSheetMax
                xlSheet = xlSheets(iSheet)
         '<ここの処理です>
       Next
 の中ではありますが。。。)


 なぜか分からないのですが、<コード1>の書き方だと、それとは別の場所にある
                If Len(tbox.AlternativeText) > 0 Then 〜
が原因でExcel.EXEが解放されず、
                Dim tboxAlternativeText As String = tbox.AlternativeText
                If Len(tboxAlternativeText) > 0 Then 〜
とすると開放されるようになります。
<コード1>を<コード1’>に書き換えると、なぜか
                If Len(tbox.AlternativeText) > 0 Then 〜
でも解放されるようになります。。。


 昨日の夜、
If myApp.Version <= 11 Then
を追加すると、この行でまたなぜか解放されなくなりました。。。orz
Integerのはずなのですが。。。???
下のようにするとExcel.EXEが解放されました。
    Dim myAppVersion As Object = myApp.Version
    If myAppVersion <= 11 Then
                
    End If   'If Err.Number = 0 Then
    MRComObject(myAppVersion)

 タイトルの内容を解決後も、沢山のアドバイスをいただき
本当にどうもありがとうございました。再三のアドバイスをいただけたおかげで
修正の方向がはっきりしましたので、なんとか自力で原因究明したく
この後も解明に努めます。解明できましたら、ご報告させていただきます。
 もしまた何かよさそうなテストの方法等ございましたら、アドバイスいただけると
幸いでございます。  Camputer.

[ツリー表示へ]