'Excel VBAで、特定のシート内にあるすべての図形の名称を取得し、
'同名の図形があった場合にはグループ化された図形も含めてユニークな名前にリネームしたいです。
'グループ図形を解除せずにグループ内の個々の図形の名前も取得・変更できるようにしたいです。
' また、同じ名前があったかどうかを確認するために Dictionary を使いたいのですが、
'再帰的な処理を行う場合、Dictionary を引数として渡すのと、パブリック変数として保持するのとでは
'どちらが望ましいかも理由付きで教えてください。 最後に、図形名の変更処理を実装した、
'具体的で再利用可能なサンプルコードを提示してください。図形名の変更履歴は不要です。
'GPT

Sub Rename()
    Dim ws As Worksheet
    Set ws = ActiveSheet

    Dim usedNames As Object
    Set usedNames = CreateObject("Scripting.Dictionary")

    Dim shp As Shape
    For Each shp In ws.Shapes
        
        MsgBox shp.Name
    Next shp
End Sub

Sub RenameAllShapesOnSheet()
    Dim ws As Worksheet
    Set ws = ActiveSheet

    Dim usedNames As Object
    Set usedNames = CreateObject("Scripting.Dictionary")

    Dim shp As Shape
    For Each shp In ws.Shapes
        Call RenameShapeRecursive(shp, usedNames)
    Next shp
End Sub

Sub RenameShapeRecursive(shp As Shape, usedNames As Object)
    Dim originalName As String
    Dim newName As String
    Dim prefix As String
    Dim numPart As Long

    originalName = shp.Name
    newName = originalName

    ' プレフィックスと数字に分解
    ParseNameParts originalName, prefix, numPart

    ' 名前がすでに使われていればインクリメントしてユニーク名生成
    Do While usedNames.exists(newName)
        numPart = numPart + 1
        newName = prefix & " " & numPart
    Loop

    ' 名前を変更し、使用済みに記録
    If newName <> originalName Then shp.Name = newName
    usedNames(newName) = True

    ' グループ内の図形も再帰的に処理
    If shp.Type = msoGroup Then
        Dim gShp As Shape
        For Each gShp In shp.GroupItems
            Call RenameShapeRecursive(gShp, usedNames)
        Next gShp
    End If
End Sub

Sub ParseNameParts(ByVal shapeName As String, ByRef prefix As String, ByRef number As Long)
    Dim i As Long
    Dim ch As String
    Dim nameLen As Long
    nameLen = Len(shapeName)

    ' 後ろから見て数字がどこまで続いているか判定
    For i = nameLen To 1 Step -1
        ch = Mid(shapeName, i, 1)
        If Not IsNumeric(ch) Then Exit For
    Next i

    If i < nameLen Then
        prefix = Trim(Left(shapeName, i))
        number = CLng(Mid(shapeName, i + 1))
    Else
        prefix = shapeName
        number = 1
    End If
End Sub

'ではそのシートに名前が重複する新たな図形を追加しようとしたときに、追加する図形の名前は変えずに、
'シート上に既にある図形名を変更するには?名前の変更手順は上記と同じです。
'またこの処理を開始するときにはシート上に重複する図形名はないものとします。
'-----------------------------------------------------------------------------
Sub AddShapeWithFixedName()
    Dim ws As Worksheet
    Set ws = ActiveSheet

    Dim fixedName As String
    fixedName = "テキスト ボックス 1"

    Dim usedNames As Object
    Set usedNames = CreateObject("Scripting.Dictionary")
    
    ' すでにある図形の名前をすべて Dictionary に登録
    Dim shp As Shape
    For Each shp In ws.Shapes
        usedNames(shp.Name) = True
    Next shp

    ' 追加予定の名前がすでに使われていれば、既存図形を改名する
    If usedNames.exists(fixedName) Then
        Set shp = ws.Shapes(fixedName)
        Call RenameShapeRecursive(shp, usedNames)
    End If

    ' 新しい図形を追加(名前は固定)
    Dim newShp As Shape
    Set newShp = ws.Shapes.AddTextbox( _
        Orientation:=msoTextOrientationHorizontal, _
        Left:=100, Top:=100, Width:=200, Height:=50)

    newShp.Name = fixedName
    newShp.TextFrame.Characters.Text = "追加された " & fixedName
End Sub


claude------------------------------------------------------------------------


' メイン処理:指定シートの全図形の名前をユニークにする
Public Sub RenameAllShapesToUnique(ws As Worksheet)
    Dim nameDict As Object
    Dim shp As Shape
    
    ' Dictionary作成
    Set nameDict = CreateObject("Scripting.Dictionary")
    
    ' 全図形を処理
    For Each shp In ws.Shapes
        Call ProcessShape(shp, nameDict)
    Next shp
    
    Debug.Print "図形名の重複チェックとリネーム処理が完了しました。"
End Sub

' 図形処理の再帰関数(引数でDictionaryを渡す)
Private Sub ProcessShape(shp As Shape, ByRef nameDict As Object)
    Dim originalName As String
    Dim newName As String
    Dim i As Long
    
    originalName = shp.Name
    
    ' グループ図形の場合は個々の図形も処理
    If shp.Type = msoGroup Then
        Debug.Print "グループ図形を検出: " & originalName
        
        ' グループ内の個々の図形を再帰処理
        For i = 1 To shp.GroupItems.Count
            Call ProcessShape(shp.GroupItems(i), nameDict)
        Next i
    End If
    
    ' 名前の重複チェックとリネーム
    newName = GenerateUniqueName(originalName, nameDict)
    
    If originalName <> newName Then
        shp.Name = newName
        Debug.Print "リネーム: " & originalName & " → " & newName
    Else
        Debug.Print "名前確認: " & originalName
    End If
    
    ' Dictionaryに追加
    nameDict(newName) = True
End Sub

' ユニークな名前を生成する関数
Private Function GenerateUniqueName(baseName As String, ByRef nameDict As Object) As String
    Dim counter As Long
    Dim candidateName As String
    
    candidateName = baseName
    counter = 1
    
    ' 重複がなくなるまでループ
    Do While nameDict.Exists(candidateName)
        candidateName = baseName & "_" & counter
        counter = counter + 1
    Loop
    
    GenerateUniqueName = candidateName
End Function

' 使用例1:アクティブシートの図形を処理
Public Sub Example_ProcessActiveSheet()
    Call RenameAllShapesToUnique(ActiveSheet)
End Sub

' 使用例2:特定シートの図形を処理
Public Sub Example_ProcessSpecificSheet()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets("Sheet1") ' シート名を指定
    Call RenameAllShapesToUnique(ws)
End Sub

' 使用例3:全シートの図形をまとめて処理
Public Sub Example_ProcessAllSheets()
    Dim ws As Worksheet
    
    For Each ws In ThisWorkbook.Worksheets
        Debug.Print "処理中のシート: " & ws.Name
        Call RenameAllShapesToUnique(ws)
    Next ws
End Sub

' デバッグ用:シート内の全図形名を表示
Public Sub ShowAllShapeNames(ws As Worksheet)
    Dim shp As Shape
    
    Debug.Print "=== シート「" & ws.Name & "」の図形一覧 ==="
    
    For Each shp In ws.Shapes
        Call DisplayShapeInfo(shp, 0)
    Next shp
End Sub

' 図形情報表示の再帰関数(デバッグ用)
Private Sub DisplayShapeInfo(shp As Shape, indentLevel As Long)
    Dim indent As String
    Dim i As Long
    
    ' インデント文字列作成
    indent = String(indentLevel * 2, " ")
    
    Debug.Print indent & "図形名: " & shp.Name & " (タイプ: " & GetShapeTypeName(shp.Type) & ")"
    
    ' グループ図形の場合は個々の図形も表示
    If shp.Type = msoGroup Then
        For i = 1 To shp.GroupItems.Count
            Call DisplayShapeInfo(shp.GroupItems(i), indentLevel + 1)
        Next i
    End If
End Sub

' 図形タイプ名を取得する補助関数
Private Function GetShapeTypeName(shapeType As Long) As String
    Select Case shapeType
        Case msoAutoShape: GetShapeTypeName = "オートシェイプ"
        Case msoGroup: GetShapeTypeName = "グループ"
        Case msoPicture: GetShapeTypeName = "画像"
        Case msoTextBox: GetShapeTypeName = "テキストボックス"
        Case msoChart: GetShapeTypeName = "グラフ"
        Case msoTable: GetShapeTypeName = "表"
        Case Else: GetShapeTypeName = "その他(" & shapeType & ")"
    End Select
End Function