[VBA]OneDriveで同期しているファイルまたはフォルダのURLをローカルパスに変換する関数
2023年6月20日
概要
Excel VBAのThisWorkbook.FullNameやThisWorkbook.Pathなどで絶対パスを取得する際、ExcelファイルがOneDriveに保存している場合、これらのメソッドはパスではなくURLを返すことがあります。例えば、"test.xlsm"というExcelファイルをCドライブ直下に保存した状態で、VBAからThisWorkbook.PathまたはThisWorkbook.FullNameを呼び出した場合、戻り値は"C:\test.xlsm"となります。しかし、ExcelファイルがOneDriveと同期している(OneDrive内のフォルダに保存している)場合、ThisWorkbook.Path及びThisWorkbook.FullNameはURLを返します。特に、個人向けOneDriveを使用している場合は"https://d.docs.live.net/CID番号/フォルダパス/test.xlsm"というURLが返ってきます。
この現象はExcelに限らず、WordにおけるThisDocument及びApplication.Documents、並びにPowerPointのApplication.PresentationsのPath及びFullNameでも発生します。この問題の根本的な解決法は(2020年04月29日時点で私が調べた限り)ありません。この問題は、OneDriveの使用を中止するか、OneDriveの設定を変更することでも対処可能ですが、マクロ開発者以外がマクロを使用する場合、現実的な対処法ではありません。
そこで、このURLをローカルパスに変換する関数を作成しました。なお、法人向けのOneDrive for Businessを使用していないので、OneDrive for Business向けのコードはテストしていません。動作報告をして下さる方をお待ちしております。OneDrive for Businessのコードはネコギシ様に動作確認して頂きました。
2023年06月20日追記
宮沢 勇人様よりPower Pointでは「Application.PathSeparator」が定義されていないため、コードを変更する必要がある旨のコメントを頂きました。Windows限定であればパス区切り文字「\」をハードコードすれば解決ですが、Macユーザーが困る可能性があるため、条件付きコンパイルのコードに変更しました。Macで動作報告している方、お待ちしております。
コード
' [VBA]OneDriveで同期しているファイルまたはフォルダのURLをローカルパスに変換する関数
' Copyright (c) 2020-2024 黒箱
' This software is released under the GPLv3.
' このソフトウェアはGNU GPLv3の下でリリースされています。
'* @fn Public Function OneDriveUrlToLocalPath(ByRef Url As String) As String
'* @brief OneDriveのファイルURL又はフォルダURLをローカルパスに変換します。
'* @param[in] Url OneDrive内に保存されたのファイル又はフォルダのURL
'* @return Variant ローカルパスを返します。引数Urlにローカルパスに"https://"以外から始まる文字列を指定した場合、引数Urlを返します。
'* @details OneDriveのファイルURL又はフォルダURLをローカルパスに変換します。本関数は、ExcelブックがOneDrive内に格納されている場合に、Workbook.Path又はWorkbook.FullNameがURLを返す問題を解決するためのものです。
'*
Public Function OneDriveUrlToLocalPath(ByRef Url As String) As String
Const OneDriveCommercialUrlPattern As String = "*my.sharepoint.com*" '法人向けOneDriveのURLか否かを判定するためのLike右辺値
'引数がURLでない場合、引数はローカルパスと判断してそのまま返す。
If Not (Url Like "https://*") Then
OneDriveUrlToLocalPath = Url
Exit Function
End If
'OneDriveのパスを取得しておく(パフォーマンス優先)。
Static PathSeparator As String
Static OneDriveCommercialPath As String
Static OneDriveConsumerPath As String
If (PathSeparator = "") Then
'PowerPointではApplication.PathSeparatorが定義されていないため修正。2023/06/20
'PathSeparator = Application.PathSeparator
#If Mac Then
PathSeparator = "/"
#Else
PathSeparator = Chr(92)
#End If
'法人向けOneDrive(OneDrive for Business)のパス
OneDriveCommercialPath = Environ("OneDriveCommercial")
If (OneDriveCommercialPath = "") Then OneDriveCommercialPath = Environ("OneDrive")
'個人向けOneDriveのパス
OneDriveConsumerPath = Environ("OneDriveConsumer")
If (OneDriveConsumerPath = "") Then OneDriveConsumerPath = Environ("OneDrive")
End If
'法人向けOneDrive:URL="https://会社名-my.sharepoint.com/personal/ユーザー名_domain_com/Documentsファイルパス")
Dim FilePathPos As Long
If (Url Like OneDriveCommercialUrlPattern) Then
FilePathPos = InStr(1, Url, "/Documents") + 10 '10 = Len("/Documents")
OneDriveUrlToLocalPath = OneDriveCommercialPath & Replace(Mid(Url, FilePathPos), "/", PathSeparator)
'個人向けOneDrive:URL="https://d.docs.live.net/CID番号/ファイルパス"
Else
FilePathPos = InStr(9, Url, "/") '9 == Len("https://") + 1
FilePathPos = InStr(FilePathPos + 1, Url, "/")
If (FilePathPos = 0) Then
OneDriveUrlToLocalPath = OneDriveConsumerPath
Else
OneDriveUrlToLocalPath = OneDriveConsumerPath & Replace(Mid(Url, FilePathPos), "/", PathSeparator)
End If
End If
End Function
プログラムの利用について
本プログラムのライセンスは「GPL-3.0(GNU General Public License version 3)」を適用しています。
本プログラムを私的に利用(個人利用、並びに企業またはその他の組織の内部利用)される方は、本プログラムを無償で利用できると考えて差し支え御座いません。その場合でも、本プログラム内の著作権及びライセンスの表示は削除せずに表示しておいて下さい。
その他の方は、GNU 一般公衆利用許諾書(GNU GENERAL PUBLIC LICENSE)バージョン3の内容をご理解頂き、適正な利用をお願い致します。同ライセンス以外での利用をご希望の方はお問い合わせフォームよりご連絡下さい。
本ホームページのプログラムを書籍またはホームページ等で一般公開したい方は、お問い合わせフォームよりご連絡下さい。
スポンサーリンク
この記事のトラックバックURL
スポンサーリンク
カテゴリー
スポンサーリンク
-
ホーム -
上へ
ディスカッション
コメント一覧
初めてコメントさせて頂きます。
ネコギシと申します。
勤務先の社内のみで使用するVBAマクロを作成中、
OneDrive上での動作確認をしていたところ、
OneDrive上の実行ファイルのフォルダ名が読み込めずに調べていて、
本ページにたどり着きました。
勝手ながら、黒箱さんのコードを使用して動作確認させて頂いたところ、
OneDrive for Bussessでもローカルパスに変更することが出来ました。
御礼と動作報告までにコメントを残させて頂きます。
有難うございました!
初めまして、黒箱です。
OneDrive for Bussessでの動作確認、誠にありがとうございました。大変助かります。
社内利用は歓迎致します。お役に立てたようでなによりです。
まつーらと申します。
エクセルで個人的な資料のデータベースを作成しており、CELL関数でフォルダのパスを取得して、
ハイパーリンクでPDF等のファイルを開くようにしていたのですが、
ONEDRIVEに移してからURLの取得となり、ブラウザが立ち上がるようになり困っておりました。
ネットで調べるも長いこと解決策が見つからなかったのですが、黒箱さんのページにたどり着き
ようやく解決しました。
素晴らしいコードをありがとうございました。
御礼と動作報告までにコメントを残させて頂きます。
初めまして、黒箱です。
動作確認のご報告、誠にありがとうございました。
CELL関数のパスもURLになることは知りませんでした。
情報提供ありがとうございました。
まさと申します。
最近、VBAで作成したマクロがなぜか消えてしまう事態になり、その原因自体はまだわかっていないのですが、
せっかく作ったマクロがまた消えてしまっては困るので、一括でエクスポートすることにしました。
しかし、エラーが出るのでパスを調べてみると//httpsで始まっていることに気づき、これをローカルパスに変換することはできないかと、ネットで調べているとこのサイトに行きつきました。
早速、利用させていただきました。助かりました。ありがとうございます。
まさ様
コメントありがとうございます。
本コードがお役に立てたようで、喜ばしい限りです。
しかし、マクロが消えるとは尋常ではない事態ですね。
コードを見ていないので何とも言えませんが、
コードが消える又はExcelマクロブックが壊れる原因で、
私が経験のあるものは下記の通りです。
1.VBProject、VBComponent、又はCodeModule等を扱っている場合は、その周辺のバグ
2.マクロでワークシートの追加及び削除を行っている場合は、その周辺のバグ
3.Excelブックを開く又は閉じるイベントでエラーを多発させ、Excelブックが正常に起動・終了できない状態で何度もExcelブックを保存した場合
4.派生クラス(Implementsしたクラス)で、基底クラスのメソッドを実装する際、アクセス修飾子が基底クラスと派生クラスで一致していない場合
5.ADOで自分自身にアクセスし、エラーにより何度も接続を正常に閉じなかった場合
6.COMオブジェクトと同名のクラスを作成した場合
何かの参考になれば幸いです。
黒箱さま
貴重な情報の提供ありがとうございます。
突然、Excelブックが保存できなくなったときは、正直「時間を返せ~」と思いました。
昨年MicrsoftOfficeを365に変えてから、ほぼ全てのファイルをOneDrive上に自動保存しているのですが、
それが何らかの要因になったと思っています。確証はないのですが。
黒箱さまが指摘してくれた内容で近いのは、3番目の「Excelブックを開く又は閉じるイベントでエラーを多発させ、Excelブックが正常に起動・終了できない状態で何度もExcelブックを保存した場合」でしょうか。
なので、不測の事態に備えて作成したマクロは一括でエクスポートするようにしました。
本来だったら、もっと早く準備すべきだったですね。
他の記事も参考にしたいと思います。丁寧なコメントありがとうございました。
黒箱様
LOYともうします。
すばらしい関数をありがとうございます。
EXCELのVBAでマクロを組んでいたところ、会社PCの環境は法人用One Drive環境だったためファイルが読み込めず
こちらにたどり着き、無事に解決できました。
ただ、1点だけどうしても解決できない問題があり、お知恵があればとおもって質問させていただきます。
EXCELのシートをPDFに出力しているのですが、いろいろと試しているのですが、パスに日本語が入っていない場合には問題なく動作するのですが、ファイルのパスに日本語が入っているとPDFを吐き出すときに、実行時エラー1004 ドキュメントを保存できませんでした、と表示され動作しません。
ほかに原因があるかもしれませんが、同じファイルを日本語でパスが入っていないところでマクロを実行すれば問題なく動作するので、日本語のファイルパスが問題ではないかと疑っています。
VBAの日本語のファイルパスはいろいろと問題があるようなので調べていはいるのですが、解決方法が見つからず、お知恵があればと思っています。
以上よろしくお願いいたします。
コメントありがとうございます。
ご質問の件ですが、問題のコードを見ないと何とも言えません。
これだけでは何なので、原因特定の指針と、原因不明の場合の対症療法について書いてみました。
原因特定の指針ですが、想像するに、疑わしいのは下記の要因かと思います。
(1)PDFをするための関数のバグ
海外製のライブラリを使用している場合に、たまに遭遇するバグです(最近は減りましたが)。
WorkSheetオブジェクトのExportAsFixedFormat()を使用している場合は、日本語は問題なく使用できます。
(2)無効なパスを指定している。
パスが絶対パスであることや、ファイルパスに使用できない無効な文字が含まれていないことを確認してください。
また、パスの長さが絶対パスで256文字を超えていないかなども確認して下さい。
(3)日本語パスを指定する際のロジックの違い
日本語パスを指定する場合と、英数字パスを指定する場合で、異なるコードを実行している場合、
そこに原因がある可能性があります。
(4)その他、ロジックのバグ
失礼ながら、LOY様のコード自体にバグがある可能性があります。
バグというのはエラーが発生した個所とは全く異なる場所に原因があることも多いため、
一度全体のソースコードを見直してみて下さい。
(5)その他、環境依存のエラー
Windows又はExcelのバージョン、フォルダの設定、その他インストールされているソフトウェアの問題など、
実行環境の違いによるエラーです。これが原因であるかを検証する方法は、
同じコードを別のPCで実行することです。可能であれば、できるだけ新しいWindows及びExcelを使用して、
可能な限り余計なソフトウェアがインストールされていないPCを使用することが理想です、
とりあえず、私の環境で下記のコードを実行してみましたが、特に問題はありませんでした。
Sub SaveActiveSheetAsPdf()
Dim Path As String
Path = OneDriveUrlToLocalPath(ThisWorkbook.Path) & "\あいうアイウアイウ亜胃鵜abcABC123.pdf"
Call ActiveSheet.ExportAsFixedFormat(xlTypePDF, Path)
End Sub
対症療法になりますが、一度英数字のパスでPDFを作成した後、実際に保存したい場所に移動することで、
望む結果が得らえれるかと思います。サンプルコードを下記に示します。
Sub SaveActiveSheetAsPdfEx()
Dim Path As String
Path = OneDriveUrlToLocalPath(ThisWorkbook.Path) & "\日本語ファイル.pdf"
Dim TempFilePath As String
TempFilePath = Environ("TEMP") & "\" & Format(Now, "yyyymmdd-hhmmss") & Int(Rnd() * 1000) & ".pdf"
Call ActiveSheet.ExportAsFixedFormat(xlTypePDF, TempFilePath)
Name TempFilePath As Path
End Sub
なお、どんな環境でも動くようにVBAの標準機能のみを使用しています。
LOY様がFileSystemObjectを使用することに抵抗が無ければ、
テンポラリフォルダのパス取得、ランダムファイル名の取得及びファイルの移動は、もっとスマートに実行できます。
黒箱様
早速コメントならびに詳細をありがとうございます。
作成しているコードはご提示いただいたようなコードで、以下のように書いています。
Sub makePDF_LA()
Dim exPath As String, FSO As Object, FileName As String
Set FSO = CreateObject(“Scripting.FileSystemObject”)
FileName = FSO.GetBaseName(ActiveWorkbook.Name)
exPath = OneDriveUrlToLocalPath(ThisWorkbook.Path) & “\” & FileName & “.pdf”
Call ActiveSheet.ExportAsFixedFormat(Type:=xlTypePDF, FileName:=exPath)
End Sub
ファイル名は英語なんですが、途中に日本語のフォルダーが2カ所入っているような状況です。
One Driveで会社全体で共有しているフォルダーなのでフォルダー名を変更することもできない状況です。
日本語フォルダーが入っているパスで実行するとCall ActiveSheet.ExportASFixedFormatでエラーがでます。
日本語フォルダーが入っていないパスで実行すると問題なくファイルが作成されます。
一時的にTEMPフォルダーに作成して後から移動することを考えてみます。
LOY様
ご提供いただいたコードを少し検証してみました。
日本語パスにすると異常が発生するとのことで、
下記のサイトに記載されていた全ての文字を使用して日本語パスを作成し、問題のコードを実行しました。
日本語の文字一覧
機種依存文字一覧
個人的に怪しいと思っていた下記の文字も、特に問題は起こりませんでした(他にも漢字類も試しました)。
(1)半角カタカナ
(2)小書きひらがな
(3)小書きカタカナ
(4)記号類(特にパスに使用できない文字の全角)
(5)機種依存
むしろ、小書きカタカナの一部がDIR関数で処理できないことが発見でした(UTF-16LEの問題だとは思いますが)。
これらのことから想像するに、今回の原因は、恐らく環境依存のエラーだと思います。
怪しいのはExcelのバージョン(というよりPDFアドインのバージョン)か、フォルダの設定ですかね?
とは言っても、会社では環境を変えることは事実上不可能だと思いますので、対症療法で行くしかないと思います。
私も会社の制限で様々な困難に直面することがありますが、その中でどうやって目的を達成するかが腕の見せ所かと思います。
重要なのは、上手くいかない方法に固執することなく、目的達成のための別ルートを考える発想力であると、個人的には思っております。
LOY様のご活躍を、陰ながら応援しております。
黒箱様
詳細なご検討、誠にありがとうございます。
半角カタカナなどは、さすがに使っていないのですが、半角カタカナですらとおるのですね。
ちょっとびっくりしました。
使っているExcelですが、今後のために念のため記載しておきます。
Microsoft Excel for Microsoft365 MSO(16.0.13127.21490) 32bit
OSは、Windows 10 20H2 ビルド19042.1288 です。
対処療法として、動作するパスのところでファイルを作成して、マクロを動かしてPDFを作成して
目的の作業をおこなったのちに、マニュアルでPDFを別のフォルダーに移動する、という方法で運用しようと思います。
この度は、詳細にご検討いただいて、本当に感謝しております。
ありがとうございました。
黒箱様
始めましてYBと申します。突然の質問失礼します。
OneDriveフォルダをローカルと同期した上で、同僚にエクセルのマクロを組んでもらいました。
しかし彼のOneDrive同期ファイルでは問題なくマクロが機能するのに、
私の環境では当エクセルのあるフォルダの一階層下のフォルダ内の別ファイルを検索するところでエラーが出てしまい、困っていました。
こちらのページに辿り着いて上記コードを追加してもらったところ、私の環境でもエラーがでなくなったものの別ファイルからデータが取得できていません。
試しに私のローカルにフォルダ毎コピーして実行したところ問題なく別ファイルからデータが取得できました。
同僚と私は同じように他のユーザーのOneDriveフォルダをローカルと同期した上で作業しているのに、何が違うのでしょうか?
何か思い当たるところはございますでしょうか?
YB様
コメントありがとうございます。
実環境を見ないと確実なことは言えませんが、
思い当たることといえば、ファイルのオンデマンド機能でしょうか。
ファイルのオンデマンド機能の詳細な説明は下記リンクの通りMicrosoftにお願いするとして、
この機能が有効になっていると、OneDriveフォルダに実体がないファイルが発生致します。
この場合、実体はオンライン上にあり、ローカルでファイルを開くなどしてユーザーが使用する動作をした際に、
初めてオンラインから実体がダウンロードされます。
実体の有無の見分け方は簡単で、ファイル(またはフォルダ)の横に、青い雲のマークが付いている場合は、ローカルに実体はありません。
これがチェックマークになっている場合は、ローカルに実体があります(下記リンクをご参照下さい)。
このオンデマンド機能は、VBAマクロを含むアプリケーションからのファイル参照では実体を持ってきてくれないので、
この機能が怪しいと思います。まずはこの部分について確認してみてください。
Windows 10 用 OneDrive ファイル オンデマンドでディスク領域を節約する
OneDriveアイコンの意味は?
突然のコメント失礼します。
上記の関数ですが、同期フォルダのショートカットを作成し、そのショートカット名を変更している場合、
httpとローカル上のパスで途中部分の名前が異なってしまい、上手く対応できません。
ここのところずっと悩んでいますが、全く解決策が浮かびません。
何かいいアイデアお持ちでは無いでしょうか。
rodeo540様
コメントありがとうございます。
ご質問の内容を確認させて下さい。
rodeo540様が指している「ショートカット」とは、Windows自体に備わっているショートカットでしょうか?
(一応、その前提で回答してみます)
ショートカット自体は、ファイルやフォルダのパス(例えばC:\Users\rodeo540\OneDrive」が書かれた”ファイル”です。
このショートカット”ファイル”自体の名前は任意なので、どんな名前でも構いませんが、このファイルとOneDriveのパスとは無関係です。
ご質問の意図と異なるかもしれませんが、その際は補足をお願い致します。
Good day, black box 🙂
Is there’s a condition for if the argument returns a URL? Because in this code below, it says that if thargument is not a URL, the argument is determined as a local path and returned as is.
‘引数がURLでない場合、引数はローカルパスと判断してそのまま返す。
If Not (Url Like “https://*”) Then
OneDriveUrlToLocalPath = Url
Exit Function
End If
Is there an Else condition for this item? Thank you in advance for your kind response 🙂
Good day, Josel.
Thank you for reading the Japanese article.
I’m not good at English.
I hope that this English will be conveyed.
This code is an early return pattern.
The Else condition is all code after “Static PathSeparator As String”.
This code expects a OneDrive URL or local folder path as an argument.
Because this function was developed to solve the following problems.
・The developer wanted to get a local path using ThisWorkbook.Path, etc., but the URL was returned.
So if you pass a local path as an argument, early return will return the local path. And if you specify a OneDrive URL, it will return the corresponding local path.
I just realized that after writing this.
This function doesn’t expect a non-OneDrive URL to be passed.
In that case, I think it should throw an error.
Thanks for the good question.
…Did you get the answer you wanted?
Yes, black box. Thank you so much for replying and clarifying the answer.
There is another question on my end, sorry to bother you. And I hope you can understand my japanese translated question.
Thank you again, 黒箱 🙂
ごきげんよう、
あなたのコードは私にとってはうまくいきます。 ただし、日本語の文字も数字に変換します。 どうすればこれに対処できますか? ありがとうございました。 これを読んでくれてありがとう:)
ごきげんよう、
あなたのコードは私にとってはうまくいきます。 ただし、日本語の文字も数字に変換します。 どうすればこれに対処できますか? ありがとうございました。 これを読んでくれてありがとう:)
hello Josel.
Thank you for writing your reply.
Now, regarding your reply written in Japanese, I couldn’t imagine the situation you are facing.
Please tell me more details.
My first thought is that the OneDrive URL may be URL encoded in your environment.
For example, encoding “Desktop(デスクトップ)” and “My Documents(マイドキュメント)” will be as follows.
デスクトップ %E3%83%87%E3%82%B9%E3%82%AF%E3%83%88%E3%83%83%E3%83%97
マイドキュメント %E3%83%9E%E3%82%A4%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88
In this case, you can solve this problem by decoding the URL.
If the situation you are facing is not URL encoding, please describe your situation in detail in English.
It would be great if you could show the your code . However, to ensure security, never write your CID or company name.
I look forward to your reply.
Hello again, 黒箱
Yes my OneDrive URL is URL encoded and I already solve it by using your codes.
But I faced another issue about retrieving the URL.
I should have retrieve the URL like this: https://company.sharepoint.com/teams/サンプル/Shared Documents/
But unfortunately I get this: https://company.sharepoint.com/teams/73616d706c65/Shared Documents/
Basically, my problem is that when I use the ThisWorkbook.Path method first before converting the URL, I get the above results (japanese characters turns into numbers)
Sample code:
Sub sampleCode()
Dim thisPath As String
Dim inputPath As String
Dim fso As Variant
thisPath = OneDriveUrlToLocalPath(ThisWorkbook.Path) ‘this method calls your provided code
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set Files = fso.GetFolder(inputPath).Files ‘this is when I get the runtime-error 76 path not found
…
End Function
Hope you understand what I wanted to point out, sorry to bother you again. Thank you so much 🙂
Hello again, 黒箱
Yes, I already decoded and converted the URL a local path by using your code.
However, I faced another issue about this. Below details is the expected and actual URL I needed.
Expected URL: https://company.sharepoint.com/teams/サンプル/Shared Documents
Actual URL: https://fujitsu.sharepoint.com/teams/73616d7c65/Shared Documents
I got the actual URL by using ‘ThisWorkbook.Path’ method
Hoping that you understand my question about this matter. I also didn’t know if my previous question got posted or not, sorry if it is duplicated in case both questions got posted.
Thank you so much and looking forward for your kind reply 🙂