A family of Microsoft spreadsheet software with tools for analyzing, charting, and communicating data
There are three possible solutions other than the one posted above that I have seen in various forums:
- Using the data files in the AppData\Local\Microsoft\OneDrive folder. This works but involves multiple data files and requires some intricate parsing of data in those files that isn't obvious or easy.
- Using FSO or File System Object. This works in some situations but not all.
- Using the "Copy local path" function on the Info backstage panel. This is crude and not reliable since we have to use SendKeys and hope the keystroke events are executed in time for our code to get the path from the clipboard.
Back to the solution posted in this thread. I have modified it to produce the log file as a text file in the Desktop folder versus a tab in a workbook. This allows the code to be used in Word or other Office application. While this code, in my opinion, is the most robust of all of the methods identified thus far, it still may have issues and we'll fix them as they are found if possible. The latest version with all known issues resolved is included below.
Kevin
Public Function OneDriveLocalFilePath( _
Optional ByRef OneDriveFilePath As String, \_
Optional ByVal ReturnFolderPathOnly As Boolean, \_
Optional ByVal ReturnEmptyIfFileNotFound As Boolean \_
) 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.
'
' ReturnFolderPathOnly - Pass True to return the folder path only. This is the equivelant of
' ThisWorkbook.Path. Optional. If omitted then False is assumed.
'
' ReturnEmptyIfFileNotFound - Pass True to return an empty or null string if the file cannot be
' found, False to return an error message. Optional. If omitted then False is assumed.
'
' Notes
'
' Debugging information is written to a text file in the Dektop folder when the conditional
' compilation argument Debugging is set to -1. This argument can be set in this module or in the
' project properties dialog.
Const RegistryPath As String = "HKEY\_CURRENT\_USER\SOFTWARE\SyncEngines\Providers\OneDrive\"
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 LibraryType As String
Dim URLNameSpaceExtended As String
Dim LocalPartialPath As String
Dim PartialPathRootDirectory As String
Dim LocalPartialPathWithFirstFolder As String
Dim LocalPartialPathWithoutFirstFolder As String
Dim Pass As Long
Dim EntryCount As Long
Dim ExistsCount As Long
Dim Log As String
Dim LogFilePath As String
Dim FileNumber As Long
Dim FileLength As Long
OneDriveLocalFilePath\_WriteDebuggingLog Log, 0, "Entering OneDriveLocalFilePath"
' Default to the full name property of ThisWorkbook
If Len(OneDriveFilePath) = 0 Then
OneDriveFilePath = ThisWorkbook.FullName
End If
OneDriveLocalFilePath\_WriteDebuggingLog Log, 0, "OneDrive file path: " & OneDriveFilePath
' Determine 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")
For Pass = 1 To 2
ExistsCount = 0
OneDriveLocalFilePath\_WriteDebuggingLog Log, 0, "Evaluating registry entries in '" & RegistryPath & "'"
' Enumerate the key HKEY\_CURRENT\_USER\SOFTWARE\SyncEngines\Providers\OneDrive
If WinMgmtS.EnumKey(&H80000001, Mid(RegistryPath, InStr(RegistryPath, "\") + 1), RegistryKeys, Types) = 0 Then
EntryCount = 0
For Each RegistryKey In RegistryKeys
OneDriveLocalFilePath\_WriteDebuggingLog Log
' Each key has three values of interest:
'
' 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
On Error Resume Next
CID = WScript.RegRead(RegistryPath & RegistryKey & "\CID")
MountPoint = WScript.RegRead(RegistryPath & RegistryKey & "\MountPoint")
URLNamespace = WScript.RegRead(RegistryPath & RegistryKey & "\URLNamespace")
LibraryType = WScript.RegRead(RegistryPath & RegistryKey & "\LibraryType")
On Error GoTo 0
EntryCount = EntryCount + 1
OneDriveLocalFilePath\_WriteDebuggingLog Log, 0, "Evaluating registry entry " & EntryCount
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "Key:", (RegistryKey)
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "CID:", CID
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "URLNamespace:", URLNamespace
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "MountPoint:", MountPoint
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "LibraryType:", LibraryType
' Remove any trailing slash from the URL name space
If Right(URLNamespace, 1) = "/" Then
URLNamespace = Left(URLNamespace, Len(URLNamespace) - 1)
End If
' Determine the extended URL name space which may or may not include the CID
If Len(CID) = 0 Then
URLNameSpaceExtended = URLNamespace
Else
URLNameSpaceExtended = URLNamespace & "/" & CID
If Left(OneDriveFilePath, Len(URLNameSpaceExtended)) <> URLNameSpaceExtended Then
URLNameSpaceExtended = URLNamespace
End If
End If
' Looking for a local file path only if the base of the path being evaluated matches the extended URL name space
If Left(OneDriveFilePath, Len(URLNameSpaceExtended)) = URLNameSpaceExtended Then
LocalPartialPath = Mid(OneDriveFilePath, Len(URLNameSpaceExtended) + 2)
' It's not always clear if the first directory in the partial path is used locally, it may be a directory only in the cloud to distinguish directories shared by others
If InStr(LocalPartialPath, "/") > 0 Then
PartialPathRootDirectory = Left(LocalPartialPath, InStr(LocalPartialPath, "/") - 1)
Else
PartialPathRootDirectory = vbNullString
End If
If Pass = 1 Or Pass = 2 And LibraryType = "teamsite" And Right(MountPoint, Len(PartialPathRootDirectory)) = PartialPathRootDirectory Then
' Build two local partial paths: one with the first folder after the mount point, and the second without the first folder after the mount point
LocalPartialPathWithFirstFolder = Replace(Replace(Mid(OneDriveFilePath, Len(URLNameSpaceExtended) + 2), "/", "\"), "%20", Space(1))
LocalPartialPathWithoutFirstFolder = Mid(LocalPartialPathWithFirstFolder, InStr(LocalPartialPathWithFirstFolder, "\") + 1)
If LocalPartialPathWithFirstFolder = LocalPartialPathWithoutFirstFolder Then
LocalPartialPathWithFirstFolder = vbNullString
End If
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "Local partial path without first folder:", LocalPartialPathWithoutFirstFolder
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "Local partial path with first folder:", LocalPartialPathWithFirstFolder
If OneDriveLocalFilePath\_FileExists(MountPoint & "\" & LocalPartialPathWithoutFirstFolder, ConfirmedFilePath, ExistsCount, Log) Then Exit For
If Len(LocalPartialPathWithFirstFolder) > 0 Then
If OneDriveLocalFilePath\_FileExists(MountPoint & "\" & LocalPartialPathWithFirstFolder, ConfirmedFilePath, ExistsCount, Log) Then Exit For
End If
Else
If Pass = 2 Then
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "Not a team site or right part of directory of mount point does not match the first directory of the partial path"
End If
End If
Else
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "URL name space does not match base of OneDrive path"
End If
Next RegistryKey
' Return the confirmed file path if a valid path was found
If Len(ConfirmedFilePath) > 0 Then
If ReturnFolderPathOnly Then
Result = Left(ConfirmedFilePath, InStrRev(ConfirmedFilePath, "\") - 1)
Else
Result = ConfirmedFilePath
End If
Else
OneDriveLocalFilePath\_WriteDebuggingLog Log
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "No local path was found"
End If
Else
OneDriveLocalFilePath\_WriteDebuggingLog Log, 0, "No registry entries were found"
Result = OneDriveFilePath
End If
If ExistsCount < 2 Then Exit For
If Pass = 1 Then
OneDriveLocalFilePath\_WriteDebuggingLog Log
OneDriveLocalFilePath\_WriteDebuggingLog Log, 0, "More than one existing file was found in the first pass"
OneDriveLocalFilePath\_WriteDebuggingLog Log, 0, "Doing another pass and adding an extra check for a team site with matching share drive base folder"
End If
Next Pass
Else
' The path is not a URL so return it as-is
Result = OneDriveFilePath
End If
If ExistsCount = 0 Then
If Not ReturnEmptyIfFileNotFound Then
Result = "A local file path to an existing file was not found."
End If
End If
OneDriveLocalFilePath\_WriteDebuggingLog Log
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "OneDrive result: " & Result
#If Debugging Then
LogFilePath = CreateObject("Wscript.Shell").SpecialFolders("Desktop") & "\" & "OneDrive Local File Path Lof.txt"
On Error Resume Next
Kill LogFilePath
On Error GoTo 0
FileNumber = FreeFile
Open LogFilePath For Binary Access Read Write Lock Read Write As FileNumber
Put FileNumber, , Log
Close FileNumber
#End If
OneDriveLocalFilePath = Result
End Function
Private Function OneDriveLocalFilePath_FileExists( _
ByRef ProposedFilePath As String, \_
ByRef ConfirmedFilePath As String, \_
ByRef ExistsCount As Long, \_
ByVal Log As String \_
) As Boolean
' Tests if file path exists. Internal use only.
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "Checking proposed file path", ProposedFilePath
If ProposedFilePath = ConfirmedFilePath Then
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "File path has already been confirmed"
Exit Function
End If
If ExistingFile(ProposedFilePath) Then
ConfirmedFilePath = ProposedFilePath
ExistsCount = ExistsCount + 1
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "File path exists"
Else
OneDriveLocalFilePath\_WriteDebuggingLog Log, 1, "File path does not exist"
End If
End Function
Private Sub OneDriveLocalFilePath_WriteDebuggingLog( _
ByRef Log As String, \_
Optional ByVal Indent As Long, \_
Optional ByVal Message1 As String, \_
Optional ByVal Message2 As String \_
)
' Logs message to debugging log. Internal use only.
Const IndentSpace As Long = 2
Const SecondMessagePosition As Long = 44
Dim SpaceCount As Long
#If Not Debugging Then
Exit Sub
#End If
If Len(Message1) = 0 Then
Log = Log & vbCrLf
Else
If Len(Message2) > 0 Then
SpaceCount = SecondMessagePosition - ((Indent \* 2) + Len(Message1) + 1)
If SpaceCount > -1 Then
Message2 = Space(SpaceCount) & Message2
Else
Message2 = Message2
End If
End If
If Len(Log) > 0 Then
Log = Log & vbCrLf
End If
Log = Log & Space(Indent \* IndentSpace) & Message1 & Message2
End If
End Sub