[[20231217170101]] 『DIMの整理を効率的に進めたい』(すすむ) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『DIMの整理を効率的に進めたい』(すすむ)

VBAのコードを継ぎ足し継ぎ足しで書いているとDIMで宣言した名前を削除したり追加したりしていくので
宣言だけして最終的にどこでも使用していない名前が出てきました。
最終的に不要なモノを削除したいのですが何か効率的な方法はありますか?

昔々のBASICでは変数がどの行番地で使用されているか一覧でDAMPするようなソフトがありました。
VBAでもそのようなツールがあれば良いのでしょうが?

アドバイスできる事あればお願いします。

< 使用 Excel:Excel2021、使用 OS:Windows11 >


"vba 未使用の変数"などのキーワードでネット検索しましょう。
(どなたさんも) 2023/12/17(日) 19:33:48

回答ありがとうございます。

ぐぐって私が希望するような(参考になりそうな)情報がありました。

「マクロ整理ツールv1.01」を利用する事にしました。

気になったのは

「MZ-ToolsというツールのReviewSourceCodeで
未使用の変数を確認することができました。」

とあるのですが、

そもそもMz-Toolの使い方が良く理解できていないのが原因ですが
Mz-Toolsのどこにあるのか?
Mz-Tool(8.0)内を色々見ていますが探し切れていません。

ご存じの方は、教えてください。

(すすむ) 2023/12/18(月) 06:21:52


 使ったことありませんが、検索すると
 初心者備忘録
 2015.10.5 新しくなったMZ-Tools
https://www.ka-net.org/blog/?p=6260
 
 がヒットしました。
 下の方のアニメーションが、未使用変数の洗い出しをしているところみたいですね

 Documentationを読むと
https://www.mztools.com/v8/onlinehelp/MZTools8Help.html?review_quality.htm
 Dead Code Review というので、detect some unused declarations in the source code するようです
(´・ω・`) 2023/12/18(月) 09:10:45

 アクティブなブックにあるすべてのモジュールについて、
 未使用変数を探して イミディエイトウインドウに出力するコードを書いてみました。

 personal.xlsb に入れておき、main プロシージャーを
 クイックアクセスツールバーにでも登録するとよいかも知れません。

 【仕様の概要】
 ・モジュールベース変数とプロシージャベースの変数が対象です。
 ・Public変数や、定数は除外しました(負荷の割に重要性は低いと思います。)
 ・クラスモジュールについては確認していません。

 ユーザーインターフェイスは拙劣ですが、
 コードがあるので、使用者がご自由に改変してください。

 Microsoft Visual Basic for Applications Extensibility x.x の参照設定が必要です。

 ==以下、参考コード ============
  Rem Microsoft Visual Basic for Applications Extensibility x.x の参照設定が必要です。

 Option Explicit
 Type proc
     name      As String
     code      As String
     vars      As String
     missing   As String
 End Type

 Dim re        As Object

 Sub main()
     Dim k     As Long

     '正規表現の用意
     Set re = CreateObject("VBScript.RegExp")
     re.Global = True
     re.MultiLine = True

     'ActiveWorkbookの各コンポーネントごとに処理
     With ActiveWorkbook.VBProject
         For k = 1 To .VBComponents.Count
             Call checkUnusedVariables(.VBComponents(k).name)
         Next
     End With
 End Sub

 'componentNameにある変数のうち、未使用のものをチェック
 Sub checkUnusedVariables(componentName As String)

     Dim cMod  As CodeModule
     Dim procs() As proc
     Dim pr    As Variant

     Dim p     As Long
     Dim dic   As Object
     Dim k     As Long
     Dim buf   As String

     '対象となるモジュールの指定
     Set cMod = ActiveWorkbook.VBProject.VBComponents(componentName).CodeModule

     '(1)プロシージャレベル変数の未使用変数 -----------------------------------------

     'プロシージャ名の取得
     Set dic = CreateObject("Scripting.Dictionary")
     With cMod
         For k = 1 To .CountOfLines
             If buf <> .ProcOfLine(k, 0) Then
                 buf = .ProcOfLine(k, 0)
                 dic(buf) = Empty
             End If
         Next
     End With

     p = -1
     For Each pr In dic
         p = p + 1
         ReDim Preserve procs(p) As proc
         procs(p).name = pr                                          'プロシージャ名
         procs(p).code = cMod.Lines(cMod.ProcStartLine(pr, 0), _
                                    cMod.ProcCountLines(pr, 0))      'コード
         procs(p).code = commentOut(procs(p).code)                   'コメントを削除した変数を保持
         procs(p).vars = getVariables(procs(p).code)                 '■変数名(カンマで連結)
         procs(p).missing = getMissings(procs(p).vars, procs(p).code)'■未使用の変数名(同上)

         If procs(p).missing <> "" Then
             Debug.Print componentName & " の "; procs(p).name & " の未使用変数は " & procs(p).missing
         End If
     Next

     '(2)モジュールベース変数のうち未使用変数 ---------------------------------------------
     Dim declarationLines As Long
     Dim missing As String
     Dim code  As String
     Dim vars As String

     declarationLines = cMod.CountOfDeclarationLines
     If declarationLines > 1 Then
         code = cMod.Lines(1, declarationLines)  '宣言セクションのコード
         code = commentOut(code)
         vars = getVariables(code)               'そこにある変数名たち

         Dim var As Variant
         Dim flag As Boolean
         For Each var In Split(vars, ",")
             flag = False
             For k = 0 To UBound(procs)
                 If InStr("," & procs(k).vars & ",", "," & var & ",") = 0 Then 'プロシージャレベルの変数ではなく
                     If existsInCode(var, procs(k).code) Then
                         flag = True
                         Exit For
                     End If
                 End If
             Next
             If flag = False Then
                 missing = missing & "," & var
             End If
         Next
         If missing <> "" Then
             Debug.Print componentName & " のモジュールレベル変数の未使用変数は " & Mid(missing, 2)
         End If
     End If
 End Sub

 'コード内の(1)文字列部分および(2)コメントを消去した文字列を返す
 Function commentOut(code As String) As String
     re.Pattern = """.*?"""
     code = re.Replace(code, "")

     re.Pattern = "('|Rem).*?$"
     commentOut = re.Replace(code, "")
 End Function

 'コードsから宣言された変数名を取り出す(Public,Constは除外。重要性低いと見做す)
 Function getVariables(s As String) As String
     Dim ary, e, ary2, e2
     Dim ans As String

     ary = Split(s, vbCrLf)
     For Each e In ary
         If InStr(e, "Dim") > 0 And InStr(e, "Dim""") = 0 Then '文字列としてのDimは除外
             ' Redim a(m, n) のような場合はカッコ部分を消去
             re.Pattern = "\(.*?\)"
             re.Global = True
             e = re.Replace(e, "")

             e = Replace(e, "ReDim Preserve", "")
             e = Replace(e, "ReDim", "")
             e = Replace(e, "Dim", "")

             '複数の変数を一行内に記述した場合
             If InStr(e, ",") > 0 Then
                 ary2 = Split(e, ",")
                 For Each e2 In ary2
                     ans = ans & "," & Split(Trim(e2), " ")(0)
                 Next
             Else
                 ans = ans & "," & Split(Trim(e), " ")(0)
             End If
         End If
     Next

     For Each e In Split("$,&,#,!", ",")     '型宣言文字への対応
         ans = Replace(ans, e, "")
     Next
     getVariables = Mid(ans, 2)
 End Function

 ' 変数たちvarsのうち、codeで使われていない変数を返す
 Function getMissings(vars$, code$) As String
     Dim var     As Variant
     Dim missing As String

     For Each var In Split(vars, ",")
         If existsInCode(var, code) = False Then missing = missing & "," & var
     Next
     getMissings = Mid(missing, 2)
 End Function

 'code の中に、(変数宣言以外で)変数varが使われていれば True、そうでなければ Falseを返す。
 Function existsInCode(var As Variant, code As String) As Boolean
     Dim matches As Object, m As Object

     '''re.Pattern = "^.*\b" & var & "\b" & ".*$"
     re.Pattern = "^.*[ (\r]" & var & "[ (),\r]" & ".*$" '''こちらに修正
     Set matches = re.Execute(code)
     For Each m In matches
         If InStr(m.Value, "Dim") = 0 Then    '宣言ではなく実際に使用された場合
             existsInCode = True
             Exit For
         End If
     Next
 End Function

 【補足】
 「VBAでVBEを操作する」
http://officetanaka.net/excel/vba/vbe/
 が参考になります。
 余り念入りに検証していないので、想定外のことが起こり得ます。
 ただし、元のコードには一切変更を加えていませんので、その点は安心ください。

 なんだか、ゴテゴテしたコードになってしまったようです。
 私なんか、未使用の変数があっても、それがどうした、という感じで余り気にしませんね。

 "VBAでVBEを操作"の練習に取り組んでみました。
 ちなみに、仕様追加とかは皆さんでお願いします。予定はありません。

 # 昔、こういった類のコードを提示して、ウイルスに悪用されるとお叱りをいただいたことがあります。
 (2023/12/19 コードを一部修正しました)
(xyz) 2023/12/18(月) 10:21:00

(´・ω・`)さん、Mz-Toolの参考記事を見つけていただいてありがとうございます。

チェックしてみましたが、以下のように但し書きがありました。

「VB/VBA では、private 宣言のみがレビューされます(パフォーマンス上の理由)」

C# and VB.NETでの使用を前提にしているようで
VBAでは、狭い範囲に限定されているようです。

(xyz)さん、Toolの作成ありがとうございます。

早速、利用させていただきます。

ド素人なので提示いただいたコードをどうにかして悪用するような知識は皆無なので安心してください。
(すすむ) 2023/12/18(月) 11:01:37


 その後いくつか気づいたことをメモします。こういうことがまだまだあるかもしれません。

 1. やはり文字列は消去しないといけないですね。
    a = "変数名" などのコードがあると、その変数名を使用したことになってしまいます。
    また、b = "'" & a などのコードがあると、
    いまの処理では '以下が強制的にコメントと見なされて消されてしまいます。
   (勿論、既存のコードは一切変わりませんが)
    コメントを消去する前に、まずは文字列を消去すれば、こうしたことは起きません。

 2. モジュールレベル変数と同一名のプロシージャレベル変数がある場合(*)、
    今のロジックだと、モジュールレベル変数の使用如何に拘わらず、
    自動的にモジュールレベル変数が使用されていると見做されています。
    これも理屈から言ったら対応が必要なんでしょう。
    (*)そもそもこういう使い方はしないほうがいいかも。

 追って、前に提示したコードを直接、変更してしまおうと思っています。 
 (12/19変更しました)  
(xyz) 2023/12/18(月) 23:26:09

xyzさん、コードの変更(修正)中との事、感謝します。

xlsmファイルを起動しておしえてもらったコードをpersonal.xlsb(標準モジュール) にコピペ後
プロシージャーをクイックアクセスツールバーに登録しました。

Macro1のアイコンをクリックしましたが以下のエラーが出ます。
  (そもそも利用の仕方がも違っている可能性もありますが。。。。)

マクロ’PERSONAL.XLSB!Module3.mainPERSONAL.XLSB!Module3.main’を実行できません。
このブックでマクロが使用できないか、または全てのマクロが無効になっている可能性があります。

以下の2点は修正済みですが他にチェックすべき点ありますか ?

1)トランスセンター > マクロの設定 では
 「VBAマクロを有効にする」 にチェック済み
 「VBAマクロが有効な場合にExcel4.0のマクロを有効にする」もチェック済み

2)トランスセンター >メッセージバーでは、
 「ActivX コントロールやマクロなどのアクティブ コンテンツがブロックされた場合、すべてのアプリケーションにメッセージバーを表示する」もチェック済み

(すすむ) 2023/12/19(火) 09:15:52


 紹介しましたサイトの
http://officetanaka.net/excel/vba/vbe/01.htm
 にある設定を確認してください。
 また、参照設定も必要です。
 (Microsoft Visual Basic for Applications Extensibility x.x の参照設定)

 なお、コードの修正は終了しています。掲載コードを書き替えました。

(xyz) 2023/12/19(火) 11:20:14


 セキュリティ関係では既に触れた程度の話しかないと思います。
 それでも引き続き同様の状態だとすると、失礼ながら、次のようなことを
 疑わざるを得ません。

 Personal.xlsbのModule3に mainというプロシージャが二つありませんか?
 ためしにこちらで実験すると、
 「このブックでマクロが使用できないか、または全てのマクロが無効になっている可能性があります。」
 と、まさにそちらと同様のエラーメッセージが表示されました。
 まさかと思いますが、念のため確認してください。

 Type proc .. End Type とか、
 Dim re  As Object とかのモジュールレベル変数、Typeを使っているので、
 新しいModule(Module4とか)を作成して、そこにコピーするほうが間違いがないでしょう。
 また、プロシージャ名もバッティングしにくく、名は体をあらわす"未使用変数抽出"などとすると
 よいかも知れません。
(xyz) 2023/12/21(木) 06:51:44


 宣言したままで使用されていない変数(Public変数は除外します)の一覧を表示するツールです。

 日本語変数名を使用したときに正常に機能していませんでした。(私は余り使わない)
 わかっていて放置するのも寝覚めが悪いので、修正しておきます。

 【判明したバグ】
 VBScriptの正規表現における"単語境界"(\b)は日本語と相性が悪い。
 つまり、日本語変数名のあとの半角スペース(の直前)が単語境界と認識されません。
 そこで単語境界(\b)の使用をやめました。過去発言のコードを直接変更しております。

(xyz) 2024/01/10(水) 23:40:16


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.