【VBA マクロ】OneDrive 内のパスを URL からローカルパスへ変換する方法

## はじめに
VBA マクロを使用していると、OneDrive のパスを取得しようとすると、https://xxxxx のパスを取得しまいエクスプローラーからアクセスできず、「パスが見つかりません」といったエラーが吐かれます。そこで、OneDrive のパスをローカルパスに変換してくれる VBA マクロの関数を作成しました。できるだけ汎用性を高め、共同で VBA マクロを使用することを前提としています。

## 実装
以下、ソースコードになります。具体的な流れは以下の通りです。
1. 関数 `GetPath` に引数として `url` を渡します。これは変換するパス（ローカルパスまたは URL）です。
1. `userName` 変数に `Environ("USERNAME")` を利用し、システム環境変数からユーザー名を代入します。
1. `oneDrivePath` 変数に、OneDrive のローカルパスを代入します。このパスは "C:\Users\ユーザー名\OneDrive - xxxxxx" の形で xxxxx の部分は、各々の環境により異なってくるかと思います。
1. `fso` という FileSystemObject のインスタンスを作成します。FileSystemObject は、ファイルシステムの操作（ファイルやフォルダの作成、削除、移動など）を行うためのオブジェクトになります。
1. 引数として与えられた `url` が "https:" から始まる場合（URL 形式である場合）に以下の操作を行います。
* OneDrive の各サブフォルダについてループを行います。
* 各サブフォルダの名前（`subfolder.Name`）を取得し、その名前が URL 内に存在する位置（`subfolderPosition`）を取得します。これには `InStr` 関数が使用されています。
* サブフォルダの名前が URL 内に存在する（`subfolderPosition > 0`）場合、URL からそのサブフォルダの名前を含む部分から後ろの部分（`relativePath`）を取得します。これには `Mid` 関数が使用されます。
* 取得した相対パス（`relativePath`）を OneDrive のルートパス（`oneDrivePath`）に追加して、完全なローカルパスを作成します。そしてこのパスを戻り値として返します。

具体例を挙げて説明していきましょう。 
例えば、次のような URL が引数として渡されたとします。
`https://onedrive.live.com/?id=Documents/Project/FileName&cid=xxxxxx`
この時、 "Documents/Project/FileName" は、OneDrive上のフォルダ構造を表します。この場合、"Documents"、"Project" はサブフォルダの名前で、"FileName" はファイルの名前です。
そして、VBA マクロは、この URL を次のようなローカルの OneDrive パスに変換します。
`C:\Users\ユーザー名\OneDrive - xxxxxx\Documents\Project\FileName`
具体的な処理は次の通りです。
<ul style="list-style-type:circle;">
<li>まず、URL が "https:" で始まるかどうかを確認します。</li>
<li>"https:" で始まる場合、URL に含まれる各サブフォルダ（この例では "Documents" と "Project"）がローカルのOneDrive のどのサブフォルダに対応するかを探します。これは OneDrive のルートフォルダからサブフォルダを一つずつ調べていくことで行います。</li>
<li>対応するサブフォルダが見つかった場合、そのサブフォルダ名を含む部分から URL の終わりまでの部分を取り出し、それをローカルの OneDrive パスに追加します。この結果が変換後のローカルパスになります。このようにして、URL がローカルパスに変換されます。これにより、OneDrive のウェブ上のパスとローカルのパスを相互に変換することができます。</li>
</ul>
6. 引数として与えられた `url` が "https:" から始まらない場合（つまり、既にローカルパスである場合）は、そのパスをそのまま戻り値として返します。



```vb:GetPath
' OneDrive内のパスの取得時にurlになる可能性があるパスをローカルパスに変換
Function GetPath(url As String) As String
    Dim oneDrivePath As String
    Dim userName As String
    Dim shortcut As Object
    Dim subfolder As Object
    Dim fso As Object  ' ファイルシステム操作をするためのオブジェクト
    userName = Environ("USERNAME")
    oneDrivePath = "C:\Users\" & userName & "\OneDrive - xxxxxx\"
    Dim folder As Object
    ' FileSystemObjectのインスタンスを作成
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set folder = fso.GetFolder(oneDrivePath)
    
    'パスにhttps:が含まれる場合
    If url Like "https:*" Then
        For Each subfolder In folder.SubFolders
            Dim subfolderName As String
            subfolderName = subfolder.Name

            Dim subfolderPosition As Integer
            subfolderPosition = InStr(1, url, subfolderName & "/") ' subfolderNameの位置を取得

            If subfolderPosition > 0 Then
                Dim relativePath As String
                relativePath = Mid(url, subfolderPosition) ' subfolderNameを含む部分からのパスを取得
                GetPath = oneDrivePath & relativePath ' OneDriveパスにその部分を追加
                Exit Function
            End If
        Next subfolder
    Else
        GetPath = url 'httpsを含まない場合はそのまま返す
    End If
End Function
```
## 最後に
VBA マクロを作ろとした時に、OneDrive のパスが取得できず、困っていました。そこで色々調べていったところ、OneDrive 内のパスをローカルパスに変換する方法が見つかりました。今回はそのソースコードを改良し、OneDrive のパスがどれだけ深くても、ローカルパスに変換してくれるようにしました。ぜひ VBA マクロを利用するときには、`GetPath` 関数を使ってみて下さい。

## 参考
[【ex24】OneDrive内のパスをVBAで参照](https://it-yobi.com/excel-b-noex24/)