Option Explicit

'===========================================================
' ■部品_最終列取得 / GetLastColumn
'   ・指定したブックの、指定したシートの、指定行の「データが入っている最後の列番号」を返す関数
'     → Returns the last used column number in the specified row of the specified sheet of the specified workbook.
'
'   ・xlToLeft を用いるため、空白が混在していても確実に最終列を取得できる
'     → Uses xlToLeft to reliably detect the last used column even when blank cells exist.
'
'   ・見出し行（表の1行目）の右端を調べて、範囲の終了列を決める処理などで使用することが多い
'     → Commonly used to check the right end of a header row and decide the end column of a range.
'===========================================================

'===========================================================
' ■区分 / Classification
'   ・基礎区分：土台 ／ 用途区分：取得 ／ 操作対象区分：シート
'     → Base: foundation / Purpose: get / Target: sheet
'
' ■バージョン / Version
'   ・Version 1.0（2026-10-27）
'===========================================================

'===========================================================
' ■URL（WordPress部品ページ）
'   https://feelcodelab.com/vba_parts/getlastcolumn/
'===========================================================

'===========================================================
' ■前提条件（AI誤動作防止の最重要項目） / Preconditions
'   ★【最重要】ブック（vWb）とシート名（vSheetName）は、必ず引数で指定する（省略できない）
'     → ThisWorkbook ＝ このマクロが入っているブック ／ ActiveWorkbook ＝ いまアクティブなブック
'     → ファイルを開く部品の後は、ActiveWorkbook が、開いたファイルに切り替わる。
'       開いた直後に、ActiveWorkbook を変数へ控えて、その変数を渡す
'     → ★ [MOST IMPORTANT] The workbook (vWb) and the sheet name (vSheetName) must always be specified
'        (they cannot be omitted). After the "open file" component runs, ActiveWorkbook switches to the opened
'        file, so keep it in a variable right after opening and pass that variable.
'
'   ・対象は横方向の1行である（行番号で指定する）
'     → The target is a single horizontal row, specified by row number.
'
'   ・どの行を調べるかによって結果が変わる（通常は、見出し行を指定する）
'     → The result depends on which row is checked (normally the header row).
'
'   ・データが1件もない場合は 1 が返る
'     → If no data exists, the function returns 1.
'
'   ・設定の誤りは、原因を示すエラーで止まる（Err.Raise）
'     → Setting mistakes stop with an error naming the cause (see "Errors" below).
'
'   ・Offset / Find / Resize / With は禁止
'     → Offset, Find, Resize, and With statements are prohibited.
'===========================================================

'===========================================================
' ■依存関係（部品同士） / Component Dependencies
'   ・なし（土台の部品。他の部品を参照しない。単独で使用可能）
'     → No required dependencies; this is a foundation component and is standalone.
'
'   ・他の部品から呼ばれる側（例：f_GetLastColumn → f_SetRange_Fixed に渡す終了列を作る）
'     → Commonly called by other components, or used to make the end column passed to [SetRange_Fixed].
'===========================================================

'===========================================================
' ■依存関係（シート・表） / Sheet Dependencies
'   ・特になし
'     → No sheet-level dependencies.
'===========================================================

'===========================================================
' ■技術解説 / Technical Notes
'
'   ・ブックとシート名を、必ず引数で受け取る理由
'       → シート名だけでは、どのブックのシートか決まらず、Worksheets("名前") は、そのときの
'          ActiveWorkbook のシートを指してしまう。ファイルを開くと ActiveWorkbook が切り替わるため、
'          取り違えが起きやすい。ブックを必ず引数で渡す形にすると、呼び出しの行に
'          「どのブックの、どのシートか」が書かれ、読むだけで意図が分かる。
'       → A sheet name alone does not identify the workbook, and Worksheets("name") would refer to the current
'          ActiveWorkbook, which switches when a file is opened. Always passing the workbook makes the call
'          itself say "which sheet of which workbook".
'
'   ・シートを、Worksheet ではなく、シート名で受け取る理由
'       → 存在しないシートを Worksheets("名前") で取得すると、部品に入る前に、呼び出し側で
'          「インデックスが有効範囲にありません」のエラーになる。名前で受け取り、部品の中で
'          存在確認を行うことで、どのシートが存在しないのかを示すエラーにできる。
'       → Getting a missing sheet with Worksheets("name") would raise "Subscript out of range" on the caller side
'          before the component runs; receiving the name and checking it inside lets the error name the sheet.
'
'   ・シートの存在確認を、部品の中に直接書いている理由
'       → 土台の部品は、他の部品（シート存在チェック）を参照しない決まりのため。
'          数行が他の土台と重複するが、1つの部品だけをコピーして使えることを優先している。
'       → A foundation component does not reference other components (SheetExists). The few duplicated lines
'          let this one component be copied and used on its own.
'
'   ・Cells(vRowNum, Columns.Count).End(xlToLeft).Column を使う理由
'       → Excelの最終列（16,384列）から左方向に検索することで、
'          空白が混在していても「最後にデータが入っている列」を確実に取得できる。
'       → Searching leftward from the rightmost column ensures reliable detection of the last used column
'          even when blank cells exist in the row.
'
'   ・データが1件もない場合に 1 を返す理由
'       → End(xlToLeft) は最終列から左に向かって最初に見つかったセルを返すため、
'          データがない場合は1列目が返る仕様となる。
'       → End(xlToLeft) returns the first non-empty cell from the right, so when no data exists, column 1 is returned.
'
'   ・列番号（Long）で返す理由
'       → 範囲設定_固定など、他の部品へ、そのまま列番号として渡せるようにするため。
'          列の英字（"D" など）に変換すると、使う側で再変換が必要になる。
'       → It can be passed directly as a column number to other components such as SetRange_Fixed.
'
'   ・Offset / Find / Resize / With を禁止する理由
'       → 生成AIが誤って不正な範囲操作を行う可能性が高く、
'          行数可変の処理で誤動作を引き起こしやすいため。
'       → These statements often cause misaligned or ambiguous range operations
'          when generated by AI, leading to unpredictable behavior.
'
'   ・本部品が「土台」である理由
'       → 範囲の終了列を決める多くの処理が最終列取得を必要とし、
'          しかも他の部品を参照せずに単独で動くため。
'       → Many processes that decide the end column need this, and it works on its own
'          without referencing other components.
'===========================================================

'---------------------------------------------
' ■引数（型＋意味） / Arguments
'   vWb        : 対象のブック（Workbook）※必須（省略できない）
'                → Target workbook (Workbook); required.
'
'   vSheetName : 対象のシート名（String）
'                → Target sheet name (String).
'
'   vRowNum    : 調べる行の行番号（Long）
'                → Row number of the row to check (Long).
'---------------------------------------------

'---------------------------------------------
' ■戻り値（型＋意味） / Return Value
'   Long : 指定行でデータが入っている最終列の列番号（データがないときは 1）
'          → Column number of the last used cell in the specified row (1 if there is no data).
'---------------------------------------------

'---------------------------------------------
' ■エラー / Errors (Err.Raise)
'   ・vbObjectError + 1010：ブック（vWb）が Nothing
'   ・vbObjectError + 1011：シート（vSheetName）が、ブックに存在しない
'   ・vbObjectError + 1007：行番号（vRowNum）が 1 未満
'   ・vbObjectError + 1009：行番号（vRowNum）が、シートの行数の上限を超えている
'---------------------------------------------

'===========================================================
' ■禁止事項（AI向け） / Prohibitions
'   ・ブックとシート名を省略することの禁止（必ず引数で指定する）
'     → Do not omit the workbook and the sheet name; always specify them.
'
'   ・Worksheets("名前") で取得したシートを、そのまま渡すことの禁止（シート名の文字列を渡す）
'     → Do not pass a sheet obtained with Worksheets("name"); pass the sheet name as a string.
'
'   ・セル参照禁止（Range("A1") など）
'     → Direct cell reference is prohibited; AI tends to generate incorrect fixed references.
'
'   ・Offset禁止
'     → Offset may cause misaligned row calculations.
'
'   ・Find禁止
'     → Find leads to unpredictable search behavior.
'
'   ・Resize禁止
'     → Resize may unintentionally expand ranges.
'
'   ・With禁止
'     → With blocks cause ambiguous object references.
'===========================================================

'===========================================================
' ■使用例（サンプルコード） / Example
'   ＜例1：マクロのファイル（ThisWorkbook）の「データ」シートの、2行目（見出し行）の最終列 / Example 1: macro file＞
'   Dim lastCol As Long
'
'   lastCol = 部品_最終列取得(ThisWorkbook, "データ", 2)
'
'   ＜例2：見出し行の右端までを、範囲にする / Example 2: make a range up to the right end of the header row＞
'   Dim lastRow As Long
'   Dim lastCol As Long
'   Dim rng As Range
'
'   lastRow = 部品_最終行取得(ThisWorkbook, "データ", 1)
'   lastCol = 部品_最終列取得(ThisWorkbook, "データ", 2)   '2行目の見出し行で最終列を取得 / by header row 2
'   Set rng = 部品_範囲設定_固定(ThisWorkbook, "データ", 2, 1, lastRow, lastCol)
'
'   ※ シート名が存在しない場合は、シート名とブック名を示すエラーで止まる
'      （起点の On Error で受けて、Err.Description を表示する）
'===========================================================

'===========================================================
' ■ラッパー関数（日本語名） / Wrapper Function
'   ・辞書の日本語名で呼び出すための窓口
'     → Entry point callable by the Japanese dictionary name.
'
'   ・内部では英語名の本体処理を呼び出し、結果をそのまま返す
'     → Internally calls the English-named core function and returns its result as it is.
'===========================================================
Function 部品_最終列取得( _
        ByVal vWb As Workbook, _
        ByVal vSheetName As String, _
        ByVal vRowNum As Long) As Long

    '本体処理（英語名）を呼び出し、結果をそのまま返す / Call core function and return its result
    部品_最終列取得 = f_GetLastColumn(vWb, vSheetName, vRowNum)

End Function

'===========================================================
' ■本体処理（英語名） / Core Function
'   ・指定したブックの、指定したシートの、指定行の最終列番号を返す
'     → Returns the last used column in the specified row of the specified sheet.
'
'   ・Cells(vRowNum, Columns.Count).End(xlToLeft).Column を使用
'     → Uses Cells(vRowNum, Columns.Count).End(xlToLeft).Column for detection.
'===========================================================
Function f_GetLastColumn( _
        ByVal vWb As Workbook, _
        ByVal vSheetName As String, _
        ByVal vRowNum As Long) As Long

    Dim ws As Worksheet

    '---------------------------------------------
    ' ■ブックが設定されているかを確認（Nothing の場合はエラーで止める）
    '   / Check that the workbook is set (stop with an error if Nothing)
    '---------------------------------------------
    If vWb Is Nothing Then
        Err.Raise vbObjectError + 1010, "f_GetLastColumn", _
                  "ブック（vWb）が設定されていません（Nothing）。"
    End If

    '---------------------------------------------
    ' ■シートの存在を確認（存在しない場合は、シート名とブック名を示すエラーで止める）
    '   / Check that the sheet exists (stop with an error naming the sheet and workbook)
    '---------------------------------------------
    On Error Resume Next
    Set ws = vWb.Worksheets(vSheetName)
    On Error GoTo 0

    If ws Is Nothing Then
        Err.Raise vbObjectError + 1011, "f_GetLastColumn", _
                  "シート「" & vSheetName & "」が、ブック「" & vWb.Name & "」に存在しません。"
    End If

    '---------------------------------------------
    ' ■行番号を確認（誤りは、原因を示すエラーで止める）
    '   / Check the row number (stop with an error naming the cause)
    '---------------------------------------------
    If vRowNum < 1 Then
        Err.Raise vbObjectError + 1007, "f_GetLastColumn", _
                  "行番号は 1 以上を指定してください（行番号=" & vRowNum & "）。"
    End If

    If vRowNum > ws.Rows.Count Then
        Err.Raise vbObjectError + 1009, "f_GetLastColumn", _
                  "行番号がシートの行数の上限（" & ws.Rows.Count & "）を超えています（行番号=" & vRowNum & "）。"
    End If

    '---------------------------------------------
    ' ■最終列の取得 / Get the last column
    '---------------------------------------------
    f_GetLastColumn = ws.Cells(vRowNum, ws.Columns.Count).End(xlToLeft).Column

End Function
