The OneDrive Nightmare Continues: ThisWorkbook.Path and ThisWorkbook.FullName

Kevin Jones 7,265 Reputation points Volunteer Moderator
2024-01-25T07:46:09+00:00

I posted here about my OneDrive backup everything nightmare. Another bad dream has reared it's ugliness in VBA land: ThisWorkbook.Path and ThisWorkbook.FullName.

For some reason I absolutely cannot fathom, Excel returns a useless URL when reading these two properties in a workbook opened from any OneDrive or Sharepoint shared location. For example, ThisWorkbook.FullName value when opened from any location other than a OneDrive shared folder:

C:\Users\Public\Just Another Workbook.xlsx

But, put that workbook in any OneDrive shared folder, and this is returned:

https://datapros-my.sharepoint.com/personal/kjones\_dataautopros\_com/Documents/Just Another Workbook.xlsx

where the actual local path is:

C:\Users\Kevin Jones\OneDrive - Data Automation Professionals\Just Another Workbook.xlsx

Right off the bat it's painfully obvious that the URL is absolutely useless to anyone and everyone. One of the most common automation tasks that use ThisWorkbook.FullName or ThisWorkbook.Path is to navigate around inside the folder in which the workbook is located. There is no easy way to translate that URL into a local path. It is possible but it's a tricky operation - more on that later. What else is that URL good for? I have no idea.

The whole idea of sharing folders and files on a local drive is so that they can be used the same way as if they were in a non-shared folder. So we don't have to go to the online location and either download the file or open it in a browser. DropBox did it right. Google Drive did it right. So what were the Microsoft engineers thinking by exposing the URL versus the local path? Anyone have a clue?

A lot of people have said to turn off this OneDrive option. Even if that did work, it is no longer available in the current OneDrive UI/UX.

People have written URL parsers and file search routines to try to translate the URL into a local path. This StackOverflow thread got pretty crazy with ideas but none of the solutions worked in all scenarios. Remember that OneDrive supports one personal account, up to nine business accounts, and Sharepoint folders - each of these have different structures in their URLs without any obvious mapping to the folder on the local drive. I did find one possible solution in the StackOverflow thread that showed promise - I reworked it so that it worked in as many scenarios as I could find. I'm posting it below.

But, before we get to that, a message to Microsoft: Please refrain from serving up URLs in ThisWorkbook.FullName and ThisWorkbook.Path when the workbook is stored or opened on a local drive. It serves no purpose other than to annoy us and make our lives more difficult. It's also not reality as the workbook is, in fact, located on the local drive in a local folder - that's from where Excel opens it and saves it. Keep the URL an internal thing (I realize it's needed for real time collaboration) and, if you actually believe that someone out here can use it, add a new ThisWorkbook property such as SharedURL.

Below is the routine for getting the local path as it should be returned in FullName. It's been tested on multiple machines, Windows 10 and 11, and Sharepoint and OneDrive shared folders. Post to this thread if you make any improvements or fix any issues.

Public Function OneDriveLocalFilePath( _

        Optional ByRef OneDriveFilePath As String _

    ) As String

' Returns the local file path given a URL to a file stored in a OneDrive or Sharepoint folder. For

' some reason the Excel Workbook properties Path and FullName return URLs instead of local paths.

'

' OneDriveFilePath - Any valid local path or URL referencing a OneDrive file. If the path cannot be

'   resolved, the original path is returned.

    Dim WScript As Object

    Dim WinMgmtS As Object

    Dim Result As String

    Dim ProposedFilePath As String

    Dim ConfirmedFilePath As String

    Dim RegistryKey As Variant

    Dim RegistryKeys As Variant

    Dim Types As Variant

    Dim CID As String

    Dim MountPoint As String

    Dim URLNamespace As String

    Dim Path1 As String

    Dim Path2 As String

    Dim Directories As Variant

    Dim ParentDirectory As String

    ' Default to the full name property of ThisWorkbook

    If Len(OneDriveFilePath) = 0 Then

        OneDriveFilePath = ThisWorkbook.FullName

    End If

    ' Deterimine if the path is a URL or a local path

    If Left(OneDriveFilePath, 8) = "https://" Then

        ' WScript and Winmgmts are used to navigate the registry

        Set WScript = CreateObject("WScript.Shell")

        Set WinMgmtS = GetObject("Winmgmts:root\default:StdRegProv")

        ' Enumerate the key HKEY_CURRENT_USER\SOFTWARE\SyncEngines\Providers\OneDrive

        If WinMgmtS.EnumKey(&H80000001, "SOFTWARE\SyncEngines\Providers\OneDrive", RegistryKeys, Types) = 0 Then

            For Each RegistryKey In RegistryKeys

                ' Each key has three interesting values:

                '

                '   CID - Some hash code sometimes used in the path

                '   URLNameSpace - The URL to a parent directory in the cloud

                '   MountPoint - The local path OneDrive uses to mirror files found in the URLNameSpace address

                CID = vbNullString

                MountPoint = vbNullString

                URLNamespace = vbNullString

                ProposedFilePath = vbNullString

                ConfirmedFilePath = vbNullString

                On Error Resume Next

                CID = WScript.RegRead("HKEY_CURRENT_USER\SOFTWARE\SyncEngines\Providers\OneDrive" & RegistryKey & "\CID")

                MountPoint = WScript.RegRead("HKEY_CURRENT_USER\SOFTWARE\SyncEngines\Providers\OneDrive" & RegistryKey & "\MountPoint")

                URLNamespace = WScript.RegRead("HKEY_CURRENT_USER\SOFTWARE\SyncEngines\Providers\OneDrive" & RegistryKey & "\URLNamespace")

                On Error GoTo 0

                ' It's not always clear how for down the folder tree the URL and the mount point go so the file's parent is

            ' pulled from the OneDrivePath so that it can be compared with and without

                Directories = Split(OneDriveFilePath, "/")

                ParentDirectory = Directories(UBound(Directories) - 1)

                ' Remove any trailing slash from the URL name space

                If Right(URLNamespace, 1) = "/" Then

                    URLNamespace = Left(URLNamespace, Len(URLNamespace) - 1)

                End If

                ' Build two paths to test against: one without the CID and one with the CID

                Path1 = URLNamespace & "/"

                Path2 = URLNamespace & "/" & CID & "/"

                ' Try the path without the CID

                If Left(OneDriveFilePath, Len(Path1)) = Path1 Then

                    ' Try building the final local path from the mount point path and the unmatched end of the OneDrive path

                ' and return it if the file exists

                    ProposedFilePath = MountPoint & "" & Replace(Replace(Mid(OneDriveFilePath, Len(Path1) + 1), "/", ""), "%20", Space(1))

                    If ExistingFile(ProposedFilePath) Then

                        ConfirmedFilePath = ProposedFilePath

                        Exit For

                    End If

                    ' Try building the final local path from the mount point path and the unmatched end of the OneDrive path

                ' but without the first folder and return it if the file exists

                    If Right(MountPoint, Len(ParentDirectory)) = ParentDirectory Then

                        ProposedFilePath = Replace(Replace(Mid(OneDriveFilePath, Len(Path1) + 1), "/", ""), "%20", Space(1))

                        ProposedFilePath = Mid(ProposedFilePath, InStr(ProposedFilePath, "") + 1)

                        ProposedFilePath = MountPoint & "" & ProposedFilePath

                        If ExistingFile(ProposedFilePath) Then

                            ConfirmedFilePath = ProposedFilePath

                            Exit For

                        End If

                    End If

                End If

                ' Try building the final local path from the mount point path with the CID attached and the unmatched end

            ' of the OneDrive path and return it if the file exists

                If Left(OneDriveFilePath, Len(Path2)) = Path2 Then

                    ProposedFilePath = Replace(Replace(Mid(OneDriveFilePath, Len(Path2)), "/", ""), "%20", Space(1))

                    ProposedFilePath = Mid(ProposedFilePath, InStr(ProposedFilePath, "") + 1)

                    ProposedFilePath = MountPoint & "" & ProposedFilePath

                    If ExistingFile(ProposedFilePath) Then

                        ConfirmedFilePath = ProposedFilePath

                        Exit For

                    End If

                End If

            Next RegistryKey

        End If

        ' Return the confirmed file path if a valid path was found

        If Len(ConfirmedFilePath) > 0 Then

            Result = ConfirmedFilePath

        Else

            Result = OneDriveFilePath

        End If

    Else

        ' The path is not a URL so return it as-is

        Result = OneDriveFilePath

    End If

    OneDriveLocalFilePath = Result

End Function

Public Function ExistingFile( _

        ByVal FilePath As String _

    ) As Boolean

' Returns True if the file exists, False otherwise. This routine does not use the Dir technique as

' the Dir function resets any current Dir process.

'

' FilePath - Full path to the folder or file to be evaluated.

    Dim Attributes As Long

    On Error Resume Next

    Attributes = GetAttr(FilePath)

    ExistingFile = (Err.Number = 0) And (Attributes And vbDirectory) = 0

    Err.Clear

End Function

Kevin

Microsoft 365 and Office | Excel | For business | Windows

Locked Question. This question was migrated from the Microsoft Support Community. You can vote on whether it's helpful, but you can't add comments or replies or follow the question.

0 comments No comments

105 answers

Sort by: Newest
  1. Kevin Jones 7,265 Reputation points Volunteer Moderator
    2024-11-06T02:02:24+00:00

    I can't see anything wrong with your code other than the On Error Resume Next - I strongly recommend using it sparingly and only when you have to and resetting it with On Error GoTo 0 as soon it's no longer needed.

    Can you turn on debugging and post the log here?

    Kevin

    Was this answer helpful?

    0 comments No comments
  2. Anonymous
    2024-11-06T00:57:22+00:00

    Hi Kevin,

    I have a question regarding the use of your code.

    I run my VBA scripts from an Excel file located on local Win10 machine, which is on my organization's network.

    Basically, my script searches for specific TXT files on a network folder (mapped drive) and retrieves them for further processing on my Win10 machine.

    This has been working fine for several years now.

    Now I tried moving the TXT files to a SharePoint folder (with a path starting with 'https://...'), and tried to use your function to translate the path, but then I get the 'A local file path to an existing file was not found.' error.

    Below is the start of my script and the bold text is where "fdir" is loaded with the error message from your function.

    The sharepoint path being used is something like this "https://xxxxx.sharepoint.com/fldr1/fldr2/fldr3/fldr4/fldr5"

    (the "xxx" and "fldr" are substitutes for the actual names for confidentiality reasons.)

    Any help/advice would be appreciated.

    Thanks.

    Pat

    ==========================

    Private Sub select_and_convert()

    Dim FSO As Object, SHT1 As Object 
    
    Dim WB\_filename As String, FilePath As String, fdir As String, firstfile As String, Filename As String, dDir As String 
    
    Dim fullname As String, outputFilename As String 
    
    Dim fSize As Long, outfiles\_exist As Integer 
    
    On Error Resume Next 
    

    ' ------------ Initialization / Setup ---------------

    Set FSO = CreateObject("Scripting.FileSystemObject") 
    
    Set SHT1 = Worksheets("Sheet1") 
    
    outfiles\_exist = 0 
    
    outputFilename = "" 
    
    fdir = SHT1.TextBox\_src\_folder.Value    ' Folder where runAVL.txt and CEreport.txt are located 
    
    **fdir = OneDriveLocalFilePath(fdir, True)** 
    
    ChDir fdir 
    
    dDir = SHT1.TextBox\_dst\_folder.Value    ' Folder where the converted files (runAVL.xlsx and CEreport.xlsx are saved 
    

    ' ------------ START ---------------

    outputFile1 = Dir(dDir & "\CEreport.xlsx") 
    
    outputFile2 = Dir(dDir & "\runAVL.xlsx")
    

    ===============================

    Was this answer helpful?

    0 comments No comments
  3. Kevin Jones 7,265 Reputation points Volunteer Moderator
    2024-10-30T21:35:25+00:00

    The perusal of the registry has nothing to to with the application running the code. Can you run the code with debugging enabled in an Excel workbook on that machine?

    Kevin

    Was this answer helpful?

    1 person found this answer helpful.
    0 comments No comments
  4. Anonymous
    2024-10-30T17:29:46+00:00

    Hi Kevin.

    I have a Word document with VBA code, where I have the same issue with ThisDocument.Path.

    I have made what to me seems like obvious modifications to your code (including removing debugging rather than fixing it - I didn't know how), but the "For Each RegistryKey In RegistryKeys" loop finds nothing, so it doesn't work.

    Do you have any idea how one could adapt this to work in VBA for Word?

    A little background (just in case someone can suggest another way to achieve my real goal)

    I have a mailmerge project involving a word file and an excel file, residing in a SharePoint folder in the hope that several users may use it. Preparing the Word document for mailmerge, apparently a full path to the Excel workbook like C:\Users[username][tennant][sharepoint site][workbookname].xlsx (where each square parenthesis represents a specific value - so there are really no square parentheses!) is saved in the Word document. Here, [username] prevents it from working for another user. I have succesfully created a non-printing button in the Word document that runs a VBA script that recreates the mailmerge, including a filter needed in my project. However, I do this by prompting the user for his or her user name, assuming the rest of the path to be unchanged - and that may not always be the case. I would prefer to use ThisDocument.Path instead, as the word and excel files have the same path - only ... well, you know!

    Was this answer helpful?

    0 comments No comments
  5. Anonymous
    2024-07-03T07:08:24+00:00

    Hi Kevin. One final (I hope) thing that arguably might be an improvement:

    I will almost exclusively use the code to replace ThisWorkbook.Path, and I have several projects where this is called repeatedly, because other files in the project, created or referenced in different subs and functions in various modules, have paths relative to it, and there is no or little performance loss in using ThisWorkbook.Path every time, rather than saving it once and for all in a global variable, or passing it around as an argument.

    Performance is more of an issue with the code here, so my thought is this:

    Declare a global string variable, say "SavedThisWorkbookFullpath", and if the OneDriveFilePath argument is empty, initialize it to the value of SavedThisWorkbookFullpath, unless that is empty too, in which case SavedThisWorkbookFullpath and OneDriveFilePath are both initialized to ThisWorkbook.FullName.

    I've just timed this in my environment: Your routine runs 0.3 seconds. I wrote a little function that only calls yours if SavedThisWorkbookFullpath is empty; AFTER the first run that calls yours and sets SavedThisWorkbookFullpath, it runs 0.0000001 second. (To wit, a for loop just calling yours 1000 times ran 29.5 seconds; a for loop calling mine 100000001 times ran 11.3 seconds, starting the measurement AFTER the first call.)

    Was this answer helpful?

    1 person found this answer helpful.
    0 comments No comments