Option Explicit

'===========================================================
' ■部品_ファイルを開く / OpenFile_ByPath
'   ・指定したパスの Excel ファイルを開く（開いたあと、ActiveWorkbook が、そのファイルに切り替わる）
'     → Opens the Excel file at the specified path (ActiveWorkbook switches to that file).
'
'   ・すでに同じファイルが開いている場合は、開き直さず、そのブックをアクティブにする
'     → If the same file is already open, it is not reopened; that workbook is activated.
'
'   ・開けた場合（すでに開いていた場合を含む）は True、開けなかった場合は False を返す
'     → Returns True if the file was opened (or was already open) and False if it could not be opened.
'
'   ★ この部品は Sub（プロシージャ）ではなく「Function（関数）」で、成功／失敗を Boolean で返す
'     → ★ This component is a Function (not a Sub) that returns success/failure as a Boolean.
'===========================================================

'===========================================================
' ■区分 / Classification
'   ・基礎区分：土台 ／ 用途区分：処理 ／ 操作対象区分：ファイル
'     → Base: foundation / Purpose: process / Target: file
'
' ■バージョン / Version
'   ・Version 1.0（2026-10-29）
'===========================================================

'===========================================================
' ■URL（WordPress部品ページ）
'   https://feelcodelab.com/vba_parts/openfile_bypath/
'===========================================================

'===========================================================
' ■前提条件（AI誤動作防止の最重要項目） / Preconditions
'   ★【最重要】この部品は Function（関数）であり、戻り値（Boolean）を返す
'     → 開けた場合は True、開けなかった場合は False（この場合は、エラーでは止まらない）
'     → 呼び出し側は、必ず戻り値を確認してから後続処理を行い、メッセージも呼び出し側で表示する
'     → ★ [MOST IMPORTANT] This component is a Function that returns a Boolean: True if the file was opened,
'        False if it could not be opened (no error is raised then). The caller must check the return value
'        before the following processing and show any message itself.
'
'   ★【最重要】ファイルを開いた直後に、ActiveWorkbook を変数へ控える
'     → ファイルを開くと、ActiveWorkbook が、開いたファイルに切り替わる。
'       開いたブックは、戻り値ではなく、呼び出し側が ActiveWorkbook から取得する
'       （Set wbSrc = ActiveWorkbook）。以降の部品には、その変数を渡す
'     → ★ [MOST IMPORTANT] Right after opening, keep ActiveWorkbook in a variable. The opened workbook is not
'        returned; the caller takes it from ActiveWorkbook (Set wbSrc = ActiveWorkbook) and passes that
'        variable to the following components.
'
'   ・ファイルのパスは、フルパス（ドライブ名またはネットワークパスから始まるパス）で指定する
'     → Specify the file path as a full path.
'
'   ・すでに同じファイルが開いている場合は、開き直さず、そのブックをアクティブにして True を返す
'     → 同じファイルかどうかは、フルパス（大文字・小文字は区別しない）で判定する
'     → 同じ名前で、別のフォルダのファイルが、すでに開いている場合は、開かずに False を返す
'       （Excel は同名のブックを同時に開けず、Workbooks.Open がエラーにならないまま、別のブックが
'         アクティブのまま残ることがある。取り違えを防ぐため）
'     → OneDrive / SharePoint のブックは、FullName が https:// の形になるため、同じ形のパスで指定する
'       （ローカルのパスで指定すると、同じファイルでも、別の場所のファイルと見なして False を返す）
'     → If the same file is already open, it is activated and True is returned; "the same file" is judged by
'        full path (case-insensitive). If a same-name file from another folder is already open, it returns
'        False without opening (Excel cannot open two workbooks with the same name, and Workbooks.Open may
'        not raise an error, leaving another workbook active). OneDrive / SharePoint workbooks have an
'        https:// FullName, so specify the path in the same form.
'
'   ・存在しない・フォルダ・壊れているなどで開けない場合も False を返す（原因は、イミディエイトウィンドウに出る）
'     → 存在しないことを、メッセージで区別したいときは、呼び出し側で部品_ファイル存在チェックを先に呼ぶ
'     → False is also returned if the file does not exist, is a folder, or is broken (the cause is printed to
'        the Immediate window). To tell "does not exist" apart in a message, call FileExists first.
'
'   ・読み取り専用・パスワード付きなどのファイルは、対象外（開けなければ False を返す）
'     → Read-only or password-protected files are out of scope (False if it cannot be opened).
'
'   ・設定の誤りは、False ではなく、原因を示すエラーで止まる（Err.Raise）
'       - パスが空文字（vbObjectError + 1012）
'       - パスにワイルドカード（* または ?）が含まれている（vbObjectError + 1013）
'     → Setting mistakes stop with an error (an empty path +1012, a wildcard in the path +1013).
'
'   ・Offset / Find / Resize / With は禁止
'     → Offset, Find, Resize, and With statements are prohibited.
'===========================================================

'===========================================================
' ■依存関係（部品同士） / Component Dependencies
'   ・特になし（他の部品を参照しない）
'     → None (it references no other component).
'
'   ・組み合わせ：f_OpenFile_ByPath → f_GetLastRow / f_SetRange_Fixed / f_AssignCell など
'     （開いた直後に ActiveWorkbook を変数へ控えて、その変数を、ブックの引数として渡す）
'     → Typical combination: [OpenFile_ByPath] -> [GetLastRow], [SetRange_Fixed], [AssignCell], etc.
'===========================================================

'===========================================================
' ■依存関係（シート・表） / Sheet Dependencies
'   ・特になし（ファイルを開くだけで、特定のシートを前提としない）
'     → No sheet-level dependencies (it only opens the file and assumes no specific sheet).
'===========================================================

'===========================================================
' ■技術解説 / Technical Notes
'
'   ・Workbook を返さず、Boolean を返す理由
'       → 「開けたか」を、他の部品と同じ形（If 部品_X(...) = False Then）で確認できるようにするため。
'          開いたブックは、呼び出し側が ActiveWorkbook を変数へ控えて使う。
'          Workbook を返す形にすると、失敗時の Nothing の確認が、別途必要になる。
'       → To check "was it opened" in the same form as other components. The caller keeps ActiveWorkbook in a
'          variable; returning a Workbook would require a separate Nothing check on failure.
'
'   ・すでに開いているファイルを、開き直さず、アクティブにする理由
'       → 同じファイルを開き直そうとすると、変更の破棄を確認するメッセージが出て、マクロが止まるおそれがある。
'          呼び出し側が「開いているかどうか」を気にしなくて済むようにするため。
'       → Reopening an already open file can show a "discard changes" prompt and stall the macro. The caller
'          does not need to care whether it is already open.
'
'   ・フルパスで同じファイルかを判定する理由
'       → ファイル名だけで判定すると、別のフォルダの、同じ名前のファイルを取り違える。
'       → Judging by file name alone would confuse same-name files in different folders.
'
'   ・ファイルの存在確認を、この部品の中で行わない理由
'       → 存在確認が必要かどうかは、呼び出し側のコードによる。必要なときは、部品_ファイル存在チェックを先に呼ぶ。
'          部品を分けることで、同じ確認を2回行わず、この部品も他の部品を参照しない（土台）。
'          存在しないファイルを渡すと、Workbooks.Open が失敗して False を返し、原因は Debug.Print に出る。
'       → Whether an existence check is needed depends on the caller; call FileExists first when needed. Keeping
'          the parts separate avoids a double check and keeps this part free of dependencies (foundation).
'          A missing file makes Workbooks.Open fail, so False is returned and the cause is printed.
'
'   ・同名で別フォルダのブックが開いているときに、開かずに False を返す理由
'       → 開こうとしても、エラーにならないまま、別のブックがアクティブのまま残ることがある。
'          True を返すと、呼び出し側が、意図しないブックへ後続処理を行うため、False にして原因を Debug.Print に出す。
'       → Opening may raise no error and leave another workbook active; returning True would let the caller
'          process the wrong workbook, so False is returned and the cause is printed.
'
'   ・確認を On Error GoTo より前に置く理由
'       → On Error GoTo ErrHandler の有効な範囲でエラーを出すと、False に変えられてしまい、原因が伝わらない。
'          確認を前に置くことで、この部品の Err.Raise が、呼び出し側へ届く。
'       → Raising inside the range where On Error GoTo is active would turn it into False and hide the cause;
'          the check is placed before it so that Err.Raise reaches the caller.
'
'   ・Offset / Find / Resize / With を禁止する理由
'       → 生成AIが誤って不正な範囲操作を行う可能性が高く、行数可変の処理で誤動作を引き起こしやすいため。
'       → These statements often cause misaligned or ambiguous range operations when generated by AI.
'
'   ・本部品が「土台」である理由
'       → 他の部品を参照しないため。
'       → It references no other component.
'===========================================================

'---------------------------------------------
' ■引数（型＋意味） / Arguments
'   vFilePath : 開きたいファイルのフルパス（String）
'               → Full path of the file to open (String).
'---------------------------------------------

'---------------------------------------------
' ■戻り値（型＋意味） / Return Value
'   Boolean : 開けた場合（すでに開いていた場合を含む）は True、開けなかった場合は False
'             → True if the file was opened (or was already open), otherwise False.
'---------------------------------------------

'---------------------------------------------
' ■エラー / Errors (Err.Raise)
'   ・vbObjectError + 1012：ファイルのパスが空文字
'   ・vbObjectError + 1013：ファイルのパスにワイルドカードが含まれている
'---------------------------------------------

'===========================================================
' ■禁止事項（AI向け） / Prohibitions
'   ・Function を Sub（プロシージャ）に変更することの禁止
'     → Do not convert this Function into a Sub; the Boolean return value is essential.
'
'   ・戻り値（Boolean）を確認せずに後続処理を行うことの禁止
'     → Do not proceed to the following processing without checking the Boolean return value.
'
'   ・開いた直後に ActiveWorkbook を変数へ控えずに、後続の部品へ ActiveWorkbook を渡すことの禁止
'     （後続でブックが切り替わると、取り違える）
'     → Do not pass ActiveWorkbook straight to following components without keeping it in a variable right
'        after opening (it may switch later and cause a mix-up).
'
'   ・ワイルドカード（* や ?）や、空文字のパスを渡すことの禁止
'     → Do not pass a path containing a wildcard (* or ?) or an empty path.
'
'   ・セル参照の直書き禁止（Range("A1") など）
'     → Hard-coded cell references are prohibited.
'
'   ・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：ファイルを開いて、最終行を取得する / Example 1: open a file and get its last row＞
'   Dim wbSrc As Workbook
'   Dim lastRow As Long
'
'   If 部品_ファイルを開く("E:\TEST\売上.xlsx") = False Then
'       MsgBox "売上.xlsx を開けませんでした"
'       Exit Sub
'   End If
'   Set wbSrc = ActiveWorkbook                          '開いた直後に控える / keep it right after opening
'
'   lastRow = 部品_最終行取得(wbSrc, "売上", 2)
'
'   ＜例2：開いたファイルへ、マクロのファイルから代入する / Example 2: assign from the macro file into an opened file＞
'   Dim wbDst As Workbook
'
'   If 部品_ファイルを開く("E:\TEST\テンプレート.xlsx") = False Then
'       MsgBox "テンプレートを開けませんでした"
'       Exit Sub
'   End If
'   Set wbDst = ActiveWorkbook
'
'   If 部品_代入_セル(ThisWorkbook, "入力", 6, 2, wbDst, "帳票", 3, 1) = False Then
'       MsgBox "代入に失敗しました"
'   End If
'
'   ＜例3：存在しないことを、メッセージで区別する / Example 3: tell "does not exist" apart in a message＞
'   If 部品_ファイル存在チェック("E:\TEST\売上.xlsx") = False Then
'       MsgBox "売上.xlsx が存在しません"
'       Exit Sub
'   End If
'   If 部品_ファイルを開く("E:\TEST\売上.xlsx") = False Then
'       MsgBox "売上.xlsx を開けませんでした"
'       Exit Sub
'   End If
'
'   ※ パスが空文字・ワイルドカード入りの場合は、False ではなく、原因を示すエラーで止まる
'      （起点の On Error で受けて、Err.Description を表示する）
'===========================================================

'===========================================================
' ■ラッパー関数（日本語名） / Wrapper Function
'   ・辞書の日本語名で呼び出すための窓口
'     → Entry point callable by the Japanese dictionary name.
'
'   ・内部では英語名の本体処理を呼び出し、結果（Boolean）をそのまま返す
'     → Internally calls the English-named core function and returns its result (Boolean) as it is.
'===========================================================
Function 部品_ファイルを開く( _
        ByVal vFilePath As String) As Boolean

    '本体処理（英語名）を呼び出し、結果をそのまま返す / Call core function and return its result
    部品_ファイルを開く = f_OpenFile_ByPath(vFilePath)

End Function

'===========================================================
' ■本体処理（英語名） / Core Function
'   ・Excel ファイルを開く（ActiveWorkbook が切り替わる）
'     → Opens the Excel file (ActiveWorkbook switches).
'
'   ・すでに開いている場合は、開き直さず、そのブックをアクティブにする
'     → If already open, it is not reopened; that workbook is activated.
'
'   ・開けた場合：True、開けなかった場合：False を返す
'     → Returns True if the file was opened and False if not.
'===========================================================
Function f_OpenFile_ByPath( _
        ByVal vFilePath As String) As Boolean

    Dim wb As Workbook
    Dim pos As Long
    Dim fileName As String

    f_OpenFile_ByPath = False

    '---------------------------------------------
    ' ■パスの確認（誤りは、原因を示すエラーで止める）
    '   ※ On Error GoTo より前に置く（Err.Raise を False に変えず、呼び出し側へ届けるため）
    '   / Check the path (stop with an error naming the cause);
    '     placed before On Error GoTo so that Err.Raise reaches the caller
    '---------------------------------------------
    If Len(Trim$(vFilePath)) = 0 Then
        Err.Raise vbObjectError + 1012, "f_OpenFile_ByPath", _
                  "ファイルのパス（vFilePath）が空文字です。"
    End If

    If InStr(vFilePath, "*") > 0 Or InStr(vFilePath, "?") > 0 Then
        Err.Raise vbObjectError + 1013, "f_OpenFile_ByPath", _
                  "ファイルのパスに、ワイルドカード（* または ?）は使えません（" & vFilePath & "）。"
    End If

    '---------------------------------------------
    ' ■エラー時は ErrHandler へ（False を返す） / On error, jump to ErrHandler (returns False)
    '---------------------------------------------
    On Error GoTo ErrHandler

    '---------------------------------------------
    ' ■ファイル名を取り出す（パスの最後の \ または / より後ろ）
    '   / Extract the file name (after the last \ or /)
    '---------------------------------------------
    pos = InStrRev(vFilePath, "\")
    If InStrRev(vFilePath, "/") > pos Then pos = InStrRev(vFilePath, "/")
    fileName = Mid$(vFilePath, pos + 1)

    '---------------------------------------------
    ' ■すでに同じファイルが開いているかを確認（フルパスで比較）
    '   同名で別の場所のブックが開いている場合は、取り違えを防ぐため、開かずに False を返す
    '   / Check whether the same file is already open (compare by full path);
    '     if a same-name workbook from another place is open, return False without opening it
    '---------------------------------------------
    For Each wb In Workbooks
        If StrComp(wb.FullName, vFilePath, vbTextCompare) = 0 Then
            wb.Activate                                 '開き直さず、アクティブにする / activate, do not reopen
            f_OpenFile_ByPath = True
            Exit Function
        End If
        If StrComp(wb.Name, fileName, vbTextCompare) = 0 Then
            Debug.Print "f_OpenFile_ByPath: 同名のブックが別の場所で開いています（" & wb.FullName & "）"
            Exit Function
        End If
    Next wb

    '---------------------------------------------
    ' ■ファイルを開く（ActiveWorkbook が切り替わる）
    '   / Open the file (ActiveWorkbook switches)
    '---------------------------------------------
    Workbooks.Open vFilePath

    f_OpenFile_ByPath = True
    Exit Function

ErrHandler:
    Debug.Print "f_OpenFile_ByPath: " & Err.Number & " " & Err.Description
    f_OpenFile_ByPath = False

End Function
