tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトルエクセルの非表示シートを削除したい
記事No16638
投稿日: 2022/05/18(Wed) 17:27
投稿者jingles
独学でvb6を始めて1年です。
エクセルの非表示シートがあったら削除するプログラムを、エクセルVBAで確認し、vb6で使えるように
したかったのですが、変数の宣言がうまくいかず利用できません。
参照設定もMicrosoft DAO 3.6 object Libraryしか設定できない環境です。

Sub 非表示のワークシートをすべて削除する()
 If MsgBox("非表示のワークシートをすべて削除していいですか?", _
  vbYesNo + vbDefaultButton2) = vbNo Then Exit Sub
 Application.DisplayAlerts = False

 Dim sh As Worksheet
 For Each sh In Worksheets
  If sh.Visible = xlSheetHidden Then sh.Delete
 Next

 Application.DisplayAlerts = True
End Sub

を参考にしたのですが…
お詳しい方、助けていただけますか?

[ツリー表示へ]
タイトルRe: エクセルの非表示シートを削除したい
記事No16639
投稿日: 2022/05/20(Fri) 23:36
投稿者魔界の仮面弁士
> 独学でvb6を始めて1年です。
1998年9月に発売されたのち、2008年4月8日に延長サポートの期間を終えた化石環境ですね…。
自分はすっかり使わなくなってしまいました。

VBA との互換性は高いですが、Microsoft の公式資料も色々見れなくなっているので
細かい情報が今となっては拾い集めにくくなっているかもしれません。


> エクセルVBAで確認し
Excel は 32bit 版ですか? 64bit 版ですか?


> 参照設定もMicrosoft DAO 3.6 object Libraryしか設定できない環境です。
どういう意味でしょうか。参照設定の一覧にそれしかないという状況は考えにくいですし…
Excel VBA の参照設定に現れる内容と、VB6 の参照設定の内容に差がある、ということでしょうか。

仮に Office 側が 32bit 版だったとしても、基本ライブラリは別物なので、
既定の参照設定ライブラリの内容は一致しないのが普通です。
(もちろん、両方で使えるライブラリもあります)


なお、VB6 から Excel を操作する場合には、参照設定で
"Microsoft Excel xx.x Object Library" を使うことが一般的です。

ただし、インストールされている Office がストアアプリ版の場合は、VB6 からの操作が
できないことがあります。その場合は、デスクトップ アプリ版の Office に変更してみてください。
https://curio-shiki.com/blog/office/office-storeapps-to-desktop-version


> Sub 非表示のワークシートをすべて削除する()
VB6 においては、全角文字や半角カナでの命名は推奨されていません。
公式サイトの情報は、かなり昔に失われているので、以下は Internet Archive へのリンクですが:

KB418924: Visual Basic でプロジェクト名に日本語の文字を使用できない
https://bit.ly/3LB5MTs

上記を見ると、『変数、定数、およびプロシージャの名前』などに
『日本語の文字を使用することができません』と明確に記されています。

実際には使えてしまうことが多いのですが、特定の OS バージョンで、
一部の文字が問題を起こす事象は、過去に何度か報告されていました。

Grapecity: コントロールの名称に長音記号「ー」を使用できない場合がある
https://dev.grapecity.co.jp/support/kb/detail.asp?id=30479

まぁ VB6 に限らず、Office VBA の世界でも問題が報告されていたりはするのですが。
https://social.msdn.microsoft.com/Forums/ja-JP/4e6223e2-34d0-4a5f-bb61-109bc28040b6/windows-10-1249612540124721251912531-2004-201973847712391?forum=officesupportteamja


ちなみに VB.NET では、この制限が撤廃されています。
KB921451: Visual Studio .NET 以降の各プログラミング言語と共通言語ランタイムで識別子に日本語の文字を使用できます
https://bit.ly/3PvQT89


>  If MsgBox("非表示のワークシートをすべて削除していいですか?", _
>   vbYesNo + vbDefaultButton2) = vbNo Then Exit Sub
本来は「+ 演算子」ではなく、「Or 演算子」で結合すべきですね。


>  Application.DisplayAlerts = False
上記は VBA の構文ですね。VB6 でこの記述は NG です。

Excel VBA にとっての「Application」とは、今まさに操作中の自分自身を指し示しますが、
VB6 側にとってみれば、どの Excel インスタンスのことなのか曖昧です。そのため、
 Dim ExcelApp As Excel.Application
 Set ExcelApp = New Excel.Application
 ExcelApp.Visible = True
などの変数を用意しておき、この変数を通じて
 ExcelApp.DisplayAlerts = False
などと記述せねばならないのです。ご注意ください。
https://support.microsoft.com/ja-jp/topic/da8e586a-b2ab-371f-1245-aad6f37e68b6

Excel Object Library を参照設定している場合は文法上、変数なしでいきなり
 Application.DisplayAlerts = False
と書けてしまうのですが、これは「グローバル オブジェクトへの参照」という状態となり、
オブジェクト解放がうまくいかなかったり、期待動作しなくなるなどといった、
誤動作の原因となることが知られています。

この点は、このサイト(VBレスキュー)でも注意書きがありますね。
http://hanatyan.sakura.ne.jp/vb6/excel01.htm


>  Dim sh As Worksheet
>  For Each sh In Worksheets
これも NG。

『For Each sh In Worksheets』という表現は、VBA であれば
「For Each sh In ThisWorkbook.Worksheets」相当であると判断されますが、
VB6 は Excel 上で動いているわけでは無いため、いきなり Worksheets とだけ書かれても、
どのワークブック上の Worksheets なのかが明確になりません。

先程の Application の場合と同様、こちらも
 Dim ExcelBook As Excel.Workbook
 'Set ExcelBook = ExcelApp.Workbooks.Add()
 Set ExcelBook = ExcelApp.Workbooks.Open("C:\Test\Example.xlsx")
のように、Workbook 型の変数を受け取ってから、
 For Each sh In ExcelBook.Worksheets
のようにして、「どのワークブック上の Worksheets なのか」を明確にします。

あるいは、Worksheets のための変数も追加して
 Dim ExcelSheets As Excel.Sheets
 Set ExcelSheets = ExcelBook.Worksheets
 For Each sh In ExcelSheets
のように書いても良いですね。


>   If sh.Visible = xlSheetHidden Then sh.Delete
Visible プロパティが返す値は
 xlSheetVisible (または True)
 xlSheetHidden (または False)
 xlSheetVeryHidden
の 3 種類です。
https://www.relief.jp/docs/excel-vba-xlsheetveryhidden-xlsheethidden.html

もし、今回の「非表示シートの削除処理」において、
xlSheetHidden だけでなく xlSheetVeryHidden も削除対象とする場合は、
 If sh.Visible <> xlSheetVisible Then
のように記述した方が良いかも知れませんね。


> For Each sh In Worksheets
>  If sh.Visible = xlSheetHidden Then sh.Delete
For Each によるループの最中に、対象のコレクションの数が
増減するような操作を行うことは避けた方が良いでしょう。
列挙順に影響を与えてしまうかもしれませんので。

仮に今は問題なくても、Office のバージョンによっては、
異なる動作になってしまう可能性が無いとも言い切れないと思います。

下記は Worksheets ではなく Range をループさせる場合の話ですが、
本件の問題点が具体的に解説されています。
https://docs.microsoft.com/ja-jp/office/troubleshoot/excel/use-for-each-next-loop-to-delete-cell


そのためこのようなケースでは、多少手間ではあっても
(案1) コレクションの末尾から先頭に向かって、インデックス指定で列挙する
(案2) コレクションの各要素の名前を最初に集めておき、その名前を使って削除する
といった処理を行った方が安全ではないでしょうか。

'【案1】の例
For n = ExcelBook.Worksheets.Count To 1 Step -1
 Set sh = ExcelBook.Worksheets(n)
 If sh.Visible = xlSheetHidden Then
  sh.Delete '末尾から削除していけば、残りのインデックスがずれることもない
 End If
Next


'【案2】の例
'あらかじめ、ワークシート名の一覧を作っておく
Dim targets As VBA.Collection
Set targets = New VBA.Collection
For Each sh In ExcelBook.Worksheets
    targets.Add sh.Name
Next
'その一覧を通じて削除するのであれば、削除しても列挙順には影響を与えない
Dim vnt As Variant
For Each vnt In targets
 Set sh = ExcelBook.Worksheets(vnt)
 If sh.Visible = xlSheetHidden Then
  sh.Delete
 End If
Next
Set targets = Nothing

[ツリー表示へ]
タイトルRe^2: エクセルの非表示シートを削除したい
記事No16640
投稿日: 2022/05/24(Tue) 11:46
投稿者jingles
魔界の仮面弁士様
ご回答いただき有難うございます。
エクセルは2013年、32ビット版です。
vb6.0で作業をしていますが、参照設定のMicrosoft Excel 16.0 Object Libraryに
チェックを入れようとすると、"この名前は既にあるモジュール、プロジェクト、オブジェクトライブラリで使われています"と
言う警告がでてしまい追加ができません。ちなみに参照設定には、Microsoft DAO 3.6 Object Library
にチェックが入っています。

それで、Dim xlApp As Excel.Application、Dim xlBook As Excel.Workbook、Dim xlSheet As Excel.Worksheet等が使えなく、
何を使って作業すべきかが分からないのです。

でも、魔界の仮面弁士様にたくさんのヒントをいただいたので、1つ1つじっくり読み返して勉強し、
またご報告させていただきたいと思います。

お忙しいところ、本当に有難うございます。こういう質問の投稿が初めてなので、感動しています。
本当に有難うございました。

[ツリー表示へ]
タイトルRe^3: エクセルの非表示シートを削除したい
記事No16642
投稿日: 2022/05/24(Tue) 17:19
投稿者魔界の仮面弁士
> エクセルは2013年、32ビット版です。
> vb6.0で作業をしていますが、
標準 EXE プロジェクトを新規作成した状態でも、参照設定できない状況でしょうか。


> 参照設定のMicrosoft Excel 16.0 Object Libraryに
2013 で 16.0 ですか?
もしかしたら、開発環境の不整合が生じているのかもしれませんね。

"Microsoft Excel 16.0 Object Library" → Office 2016 / 2019
"Microsoft Excel 15.0 Object Library" → Office 2013
"Microsoft Excel 14.0 Object Library" → Office 2010
"Microsoft Excel 12.0 Object Library" → Office 2007
"Microsoft Excel 11.0 Object Library" → Office 2007
"Microsoft Excel 10.0 Object Library" → Office XP
"Microsoft Excel 9.0 Object Library" → Office 2000

Office 2013 とは別に、2016/2019 世代の Office 製品が
共存インストールされてたことはなかったでしょうか。

Office ライブラリは複数バージョンを切り替えて使えるような設計にはなっていないため、
開発環境は複数の Office バージョンを共存させないことが望ましいです。



> 参照設定には、Microsoft DAO 3.6 Object Library にチェックが入っています。
DAO は必須ライブラリでは無いため、一度それを外してみてください。

VB6 が手元に無いので確実なことは言えないのですが、
最低限必要なのは先頭 3 〜 4 個程度だったはず。

こんな名前だった気がするけどうろ覚え。
・Visual Basic For Applications
・Visual Basic objects and procedures
・Visual Basic runtime objects and procedures
・OLE Automation


> それで、Dim xlApp As Excel.Application、Dim xlBook As Excel.Workbook、Dim xlSheet As Excel.Worksheet等が使えなく、
> 何を使って作業すべきかが分からないのです。
どうしても参照設定が使えない場合は、レイトバインドで動くかどうかを試してみてださい。

Dim xlApp As Object
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = True
Dim xlBook As Object
Set xlBook = xlApp.Workbooks.Add()
Dim xlSheet As Object
Set xlSheet = xlBook.Worksheets(1)
xlSheet.Range("A1").Value = "Hello World!"


「定数を自分で宣言しなければならない」
「WithEvents 変数によるイベント処理が使えない」
などのデメリットはありますが、複数バージョンが混在した環境でも
動作させられる可能性があります。

[ツリー表示へ]
タイトルRe^2: エクセルの非表示シートを削除したい
記事No16641
投稿日: 2022/05/24(Tue) 14:00
投稿者jingles
魔界の仮面弁士様
度々恐れ入ります。教えていただいた内容はとても分かりやすく、理解できました。
早速プログラムに書いてみたのですが、デバックすると、"ユーザー定義型は定義されていません"となり、
下記の ExcelApp As Excel.Applicationの部分が反転します。

    Dim ExcelApp As Excel.Application
    Dim ExcelBook As Excel.Workbook
    Dim ExcelSheets As Excel.Sheets
    
    Set ExcelApp = New Excel.Application
    ExcelApp.Visible = True
    ExcelApp.DisplayAlerts = False
    
    Set ExcelBook = ExcelApp.WorkBooks.Add()
    Set ExcelBook = ExcelApp.WorkBooks.Open(L_File$)
    Set ExcelSheets = ExcelBook.WorkSheets
    
    For n = ExcelBook.WorkSheets.Count To 1 Step -1
    Set sh = ExcelBook.WorkSheets(n)
        If sh.Visible = xlsheetHidden Then
            sh.Delete
        End If
    Next

どこかに問題があるでしょうか。

[ツリー表示へ]
タイトルRe^3: エクセルの非表示シートを削除したい
記事No16643
投稿日: 2022/05/24(Tue) 17:24
投稿者魔界の仮面弁士
> デバックすると
バグ(bug)を取り除く作業は
デバック (de-back) ではなく
デバッグ (debug) ですね。


> "ユーザー定義型は定義されていません"となり、
> 下記の ExcelApp As Excel.Applicationの部分が反転します。

Excel の参照設定が無い場合には、そのようなエラーになりますね。

本来は参照設定を見直すべきですが、
参照設定を追加せずにレイトバインドで実装することもできます。

(1) 「As Excel.何某」を「As Object」に変更
(2) 「= New Excel.Application」を「= CreateObject("Excel.Application")」に変更
(3) 「 = xlsheetHidden」を「 = False」に変更



> どこかに問題があるでしょうか。
エラーでは無いですが、下記の部分が問題ですね。
>    Set ExcelBook = ExcelApp.WorkBooks.Add()
>    Set ExcelBook = ExcelApp.WorkBooks.Open(L_File$)

.Add している行は不要ではありませんか?

仮に、L_File$ と新規ブックの 2 つ開く必要があるのだとしても、
同じ変数を使いまわす必然性が無いはず。

[ツリー表示へ]
タイトルRe^4: エクセルの非表示シートを削除したい
記事No16644
投稿日: 2022/05/24(Tue) 17:47
投稿者jingles
魔界の仮面弁士様

素早いご回答、有難うございます!
確かにそうですね。再びチャレンジします!
有難うございました!

[ツリー表示へ]