Get URL for file stored in OneDrive with Excel VBA - excel

My Exel VBA saves a pdf file to OneDrive locally "C:\Users\Name\OneDrive\FileName.pdf".
I need to find some code that gives med the URL to this file, so that it can be typed into a cell. The URL is used to create a QR code, so that anyone can read the pdf-file.
For now I have to find the URL manually and paste it in to the spreadsheet, before VBA creates the QR-code.
I am working in Office 365, but the .xlsm-file will be distributed to user with different Excel versions.
I've been struggling with this for a while, so I'm very happy if anyone can help.
CODE:
Sub QrLabelCreate()
'STEP 1:
'Excel VBA put data into a word-document, and export it to pdf-file (saved to OneDrive):
.ActiveDocument.ExportAsFixedFormat _
OutputFileName:="C:Users\Name\OneDrive\MyMap\" & ID & ".pdf", _
ExportFormat:=wdExportFormatPDF
'STEP 2: THE PROBLEM
'====== I am not able to create code that gives me the URL to the pdf-file. ==========
'STEP 3:
'The URL is pasted into the spreadsheet, and VBA creates the QR-code.
End Sub

Doing this generally is not easy at all, but luckily it is related to the more common problem of finding the local path when given the URL.
That's why I can now offer a kind of solution here.
Note that this solution does not create a OneDrive 'share' link, to create such a link you need to use the Microsoft Graph API! The links created by this function will only work for the account that owns the remote folder that's being synchronized.
To use my solution, copy the following function into any standard code module:
'Function for converting OneDrive/SharePoint Local Paths synchronized to
'OneDrive in any way to an OneDrive/SharePoint URL, containing for example
'.sharepoint.com/sites, my.sharepoint.com/personal/, or https://d.docs.live.net/
'depending on the type of OneDrive account and synchronization.
'If no url path can be found, the input value will be returned unmodified.
'Author: Guido Witt-Dörring
'Source: https://gist.github.com/guwidoe/6f0cbcd22850a360c623f235edd2dce2
Public Function GetWebPath(ByVal path As String, _
Optional ByVal rebuildCache As Boolean = False) _
As String
#If Mac Then
Const vbErrPermissionDenied As Long = 70
Const vbErrInvalidFormatInResourceFile As Long = 325
Const ps As String = "/"
#Else
Const ps As String = "\"
#End If
Const vbErrFileNotFound As Long = 53
Static locToWebColl As Collection, lastTimeNotFound As Collection
Static lastCacheUpdate As Date
Dim webRoot As String, locRoot As String, vItem As Variant
Dim s As String, keyExists As Boolean
If path Like "http*" Then GetWebPath = path: Exit Function
If Not locToWebColl Is Nothing And Not rebuildCache Then
locRoot = path: GetWebPath = ""
If locRoot Like "*" & ps Then locRoot = Left(locRoot, Len(locRoot) - 1)
Do
On Error Resume Next: locToWebColl locRoot: keyExists = _
(Err.Number = 0): On Error GoTo -1: On Error GoTo 0
If keyExists Or InStr(locRoot, ps) = 0 Then Exit Do
locRoot = Left(locRoot, InStrRev(locRoot, ps) - 1)
Loop
If InStr(locRoot, ps) > 0 Then _
GetWebPath = Replace(Replace(path, locRoot, _
locToWebColl(locRoot)(1), , 1), ps, "/"): Exit Function
If Not lastTimeNotFound Is Nothing Then
On Error Resume Next: lastTimeNotFound path
keyExists = (Err.Number = 0): On Error GoTo -1: On Error GoTo 0
If keyExists Then
If DateAdd("s", 10, lastTimeNotFound(path)) > Now() Then _
GetWebPath = path: Exit Function
End If
End If
GetWebPath = path
End If
Dim cid As String, fileNum As Long, line As Variant, parts() As String
Dim tag As String, mainMount As String, relPath As String, email As String
Dim b() As Byte, n As Long, i As Long, size As Long, libNr As String
Dim parentID As String, folderID As String, folderName As String
Dim folderIdPattern As String, fileName As String, folderType As String
Dim siteID As String, libID As String, webID As String, lnkID As String
Dim odFolders As Object, cliPolColl As Object, libNrToWebColl As Object
Dim sig1 As String: sig1 = StrConv(Chr$(&H2), vbFromUnicode)
Dim sig2 As String: sig2 = ChrW$(&H1) & String(3, vbNullChar)
Dim vbNullByte As String: vbNullByte = MidB$(vbNullChar, 1, 1)
#If Mac Then
Dim utf16() As Byte, utf32() As Byte, j As Long, k As Long, m As Long
Dim charCode As Long, lowSurrogate As Long, highSurrogate As Long
ReDim b(0 To 3): b(0) = &HAB&: b(1) = &HAB&: b(2) = &HAB&: b(3) = &HAB&
Dim sig3 As String: sig3 = b: sig3 = vbNullChar & vbNullChar & sig3
#Else
ReDim b(0 To 1): b(0) = &HAB&: b(1) = &HAB&
Dim sig3 As String: sig3 = b: sig3 = vbNullChar & sig3
#End If
Dim settPath As String, wDir As String, clpPath As String
#If Mac Then
s = Environ("HOME")
settPath = Left(s, InStrRev(s, "/Library/Containers")) & _
"Library/Containers/com.microsoft.OneDrive-mac/Data/" & _
"Library/Application Support/OneDrive/settings/"
clpPath = s & "/Library/Application Support/Microsoft/Office/CLP/"
#Else
settPath = Environ("LOCALAPPDATA") & "\Microsoft\OneDrive\settings\"
clpPath = Environ("LOCALAPPDATA") & "\Microsoft\Office\CLP\"
#End If
#If Mac Then
Dim possibleDirs(0 To 11) As String: possibleDirs(0) = settPath
For i = 1 To 9: possibleDirs(i) = settPath & "Business" & i & ps: Next i
possibleDirs(10) = settPath & "Personal" & ps: possibleDirs(11) = clpPath
If Not GrantAccessToMultipleFiles(possibleDirs) Then _
Err.Raise vbErrPermissionDenied
#End If
Dim oneDriveSettDirs As Collection: Set oneDriveSettDirs = New Collection
Dim dirName As Variant: dirName = Dir(settPath, vbDirectory)
Do Until dirName = ""
If dirName = "Personal" Or dirName Like "Business#" Then _
oneDriveSettDirs.Add dirName
dirName = Dir(, vbDirectory)
Loop
#If Mac Then
s = ""
For Each dirName In oneDriveSettDirs
wDir = settPath & dirName & ps
cid = IIf(dirName = "Personal", "????????????????", _
"????????-????-????-????-????????????")
If dirName = "Personal" Then s = s & "//" & wDir & "GroupFolders.ini"
s = s & "//" & wDir & "global.ini"
fileName = Dir(wDir, vbNormal)
Do Until fileName = ""
If fileName Like cid & ".ini" Or _
fileName Like cid & ".dat" Or _
fileName Like "ClientPolicy*.ini" Then _
s = s & "//" & wDir & fileName
fileName = Dir
Loop
Next dirName
If Not GrantAccessToMultipleFiles(Split(Mid(s, 3), "//")) Then _
Err.Raise vbErrPermissionDenied
#End If
If Not locToWebColl Is Nothing And Not rebuildCache Then
s = ""
For Each dirName In oneDriveSettDirs
wDir = settPath & dirName & ps
cid = IIf(dirName = "Personal", "????????????????", _
"????????-????-????-????-????????????")
If Dir(wDir & "global.ini") <> "" Then _
s = s & "//" & wDir & "global.ini"
fileName = Dir(wDir, vbNormal)
Do Until fileName = ""
If fileName Like cid & ".ini" Then _
s = s & "//" & wDir & fileName
fileName = Dir
Loop
Next dirName
For Each vItem In Split(Mid(s, 3), "//")
If FileDateTime(vItem) > lastCacheUpdate Then _
rebuildCache = True: Exit For
Next vItem
If Not rebuildCache Then
If lastTimeNotFound Is Nothing Then _
Set lastTimeNotFound = New Collection
On Error Resume Next: lastTimeNotFound.Remove path: On Error GoTo 0
lastTimeNotFound.Add Item:=Now(), Key:=path
Exit Function
End If
End If
lastCacheUpdate = Now()
Set lastTimeNotFound = Nothing
Set locToWebColl = New Collection
For Each dirName In oneDriveSettDirs
wDir = settPath & dirName & ps
If Dir(wDir & "global.ini", vbNormal) = "" Then GoTo NextFolder
fileNum = FreeFile()
Open wDir & "global.ini" For Binary Access Read As #fileNum
ReDim b(0 To LOF(fileNum)): Get fileNum, , b
Close #fileNum: fileNum = 0
#If Mac Then
b = StrConv(b, vbUnicode)
#End If
For Each line In Split(b, vbNewLine)
If line Like "cid = *" Then cid = Mid(line, 7): Exit For
Next line
If cid = "" Then GoTo NextFolder
If (Dir(wDir & cid & ".ini") = "" Or _
Dir(wDir & cid & ".dat") = "") Then GoTo NextFolder
If dirName Like "Business#" Then
folderIdPattern = Replace(Space(32), " ", "[a-f0-9]")
ElseIf dirName = "Personal" Then
folderIdPattern = Replace(Space(16), " ", "[A-F0-9]") & "!###*"
End If
Set cliPolColl = New Collection
fileName = Dir(wDir, vbNormal)
Do Until fileName = ""
If fileName Like "ClientPolicy*.ini" Then
fileNum = FreeFile()
Open wDir & fileName For Binary Access Read As #fileNum
ReDim b(0 To LOF(fileNum)): Get fileNum, , b
Close #fileNum: fileNum = 0
#If Mac Then
b = StrConv(b, vbUnicode)
#End If
cliPolColl.Add Key:=fileName, Item:=New Collection
For Each line In Split(b, vbNewLine)
If InStr(1, line, " = ", vbBinaryCompare) Then
tag = Left(line, InStr(line, " = ") - 1)
s = Mid(line, InStr(line, " = ") + 3)
Select Case tag
Case "DavUrlNamespace"
cliPolColl(fileName).Add Key:=tag, Item:=s
Case "SiteID", "IrmLibraryId", "WebID"
s = Replace(LCase(s), "-", "")
If Len(s) > 3 Then s = Mid(s, 2, Len(s) - 2)
cliPolColl(fileName).Add Key:=tag, Item:=s
End Select
End If
Next line
End If
fileName = Dir
Loop
fileNum = FreeFile
Open wDir & cid & ".dat" For Binary Access Read As #fileNum
ReDim b(0 To LOF(fileNum)): Get fileNum, , b: s = b: size = LenB(s)
Close #fileNum: fileNum = 0
Set odFolders = New Collection
For Each vItem In Array(16, 8)
i = InStrB(vItem, s, sig2)
Do While i > vItem And i < size - 168
If MidB$(s, i - vItem, 1) = sig1 Then
i = i + 8: n = InStrB(i, s, vbNullByte) - i
If n < 0 Then n = 0
If n > 39 Then n = 39
folderID = StrConv(MidB$(s, i, n), vbUnicode)
i = i + 39: n = InStrB(i, s, vbNullByte) - i
If n < 0 Then n = 0
If n > 39 Then n = 39
parentID = StrConv(MidB$(s, i, n), vbUnicode)
i = i + 121: n = -Int(-(InStrB(i, s, sig3) - i) / 2) * 2
If n < 0 Then n = 0
#If Mac Then
utf32 = MidB$(s, i, n)
ReDim utf16(LBound(utf32) To UBound(utf32))
j = LBound(utf32): k = LBound(utf32)
Do While j < UBound(utf32)
If utf32(j + 2) = 0 And utf32(j + 3) = 0 Then
utf16(k) = utf32(j): utf16(k + 1) = utf32(j + 1)
k = k + 2
Else
If utf32(j + 3) <> 0 Then Err.Raise _
vbErrInvalidFormatInResourceFile
charCode = utf32(j + 2) * &H10000 + _
utf32(j + 1) * &H100& + utf32(j)
m = charCode - &H10000
highSurrogate = &HD800& + (m \ &H400&)
lowSurrogate = &HDC00& + (m And &H3FF)
utf16(k) = CByte(highSurrogate And &HFF&)
utf16(k + 1) = CByte(highSurrogate \ &H100&)
utf16(k + 2) = CByte(lowSurrogate And &HFF&)
utf16(k + 3) = CByte(lowSurrogate \ &H100&)
k = k + 4
End If
j = j + 4
Loop
ReDim Preserve utf16(LBound(utf16) To k - 1)
folderName = utf16
#Else
folderName = MidB$(s, i, n)
#End If
If folderID Like folderIdPattern Then
odFolders.Add VBA.Array(parentID, folderName), folderID
End If
End If
i = InStrB(i + 1, s, sig2)
Loop
If odFolders.Count > 0 Then Exit For
Next vItem
fileNum = FreeFile()
Open wDir & cid & ".ini" For Binary Access Read As #fileNum
ReDim b(0 To LOF(fileNum)): Get fileNum, , b
Close #fileNum: fileNum = 0
#If Mac Then
b = StrConv(b, vbUnicode)
#End If
Select Case True
Case dirName Like "Business#"
mainMount = "": Set libNrToWebColl = New Collection
For Each line In Split(b, vbNewLine)
webRoot = "": locRoot = ""
Select Case Left$(line, InStr(line, " = ") - 1)
Case "libraryScope"
parts = Split(line, """"): locRoot = parts(9)
If locRoot = "" Then libNr = Split(line, " ")(2)
folderType = parts(3): parts = Split(parts(8), " ")
siteID = parts(1): webID = parts(2): libID = parts(3)
If mainMount = "" And folderType = "ODB" Then
mainMount = locRoot: fileName = "ClientPolicy.ini"
On Error Resume Next
webRoot = cliPolColl(fileName)("DavUrlNamespace")
On Error GoTo 0
Else
fileName = "ClientPolicy_" & libID & siteID & ".ini"
On Error Resume Next
webRoot = cliPolColl(fileName)("DavUrlNamespace")
On Error GoTo 0
End If
If webRoot = "" Then
For Each vItem In cliPolColl
If vItem("SiteID") = siteID And vItem("WebID") = _
webID And vItem("IrmLibraryId") = libID Then
webRoot = vItem("DavUrlNamespace"): Exit For
End If
Next vItem
End If
If webRoot = "" Then Err.Raise vbErrFileNotFound
If locRoot = "" Then
libNrToWebColl.Add VBA.Array(libNr, webRoot), libNr
Else
locToWebColl.Add VBA.Array(locRoot, webRoot, email), _
locRoot
End If
Case "libraryFolder"
locRoot = Split(line, """")(1): libNr = Split(line, " ")(3)
For Each vItem In libNrToWebColl
If vItem(0) = libNr Then
s = "": parentID = Left(Split(line, " ")(4), 32)
Do
On Error Resume Next: odFolders parentID
keyExists = (Err.Number = 0): On Error GoTo 0
If Not keyExists Then Exit Do
s = odFolders(parentID)(1) & "/" & s
parentID = odFolders(parentID)(0)
Loop
webRoot = vItem(1) & s: Exit For
End If
Next vItem
locToWebColl.Add VBA.Array(locRoot, webRoot, email), locRoot
Case "AddedScope"
parts = Split(line, """")
relPath = parts(5): If relPath = " " Then relPath = ""
parts = Split(parts(4), " "): siteID = parts(1)
webID = parts(2): libID = parts(3): lnkID = parts(4)
fileName = "ClientPolicy_" & libID & siteID & lnkID & ".ini"
On Error Resume Next
webRoot = cliPolColl(fileName)("DavUrlNamespace") & relPath
On Error GoTo 0
If webRoot = "" Then
For Each vItem In cliPolColl
If vItem("SiteID") = siteID And vItem("WebID") = _
webID And vItem("IrmLibraryId") = libID Then
webRoot = vItem("DavUrlNamespace") & relPath
Exit For
End If
Next vItem
End If
If webRoot = "" Then Err.Raise vbErrFileNotFound
s = "": parentID = Left(Split(line, " ")(3), 32)
Do
On Error Resume Next: odFolders parentID
keyExists = (Err.Number = 0): On Error GoTo 0
If Not keyExists Then Exit Do
s = odFolders(parentID)(1) & ps & s
parentID = odFolders(parentID)(0)
Loop
locRoot = mainMount & ps & s
locToWebColl.Add VBA.Array(locRoot, webRoot, email), locRoot
Case Else
Exit For
End Select
Next line
Case dirName = "Personal"
For Each line In Split(b, vbNewLine)
If line Like "library = *" Then _
locRoot = Split(line, """")(3): Exit For
Next line
On Error Resume Next
webRoot = cliPolColl("ClientPolicy.ini")("DavUrlNamespace")
On Error GoTo 0
If locRoot = "" Or webRoot = "" Or cid = "" Then GoTo NextFolder
locToWebColl.Add VBA.Array(locRoot, webRoot & "/" & cid, email), _
locRoot
If Dir(wDir & "GroupFolders.ini") = "" Then GoTo NextFolder
cid = "": fileNum = FreeFile()
Open wDir & "GroupFolders.ini" For Binary Access Read As #fileNum
ReDim b(0 To LOF(fileNum)): Get fileNum, , b
Close #fileNum: fileNum = 0
#If Mac Then
b = StrConv(b, vbUnicode)
#End If
For Each line In Split(b, vbNewLine)
If InStr(line, "BaseUri = ") And cid = "" Then
cid = LCase(Mid(line, InStrRev(line, "/") + 1, 16))
folderID = Left(line, InStr(line, "_") - 1)
ElseIf cid <> "" Then
locToWebColl.Add VBA.Array(locRoot & ps & odFolders( _
folderID)(1), webRoot & "/" & cid & "/" & _
Mid(line, Len(folderID) + 9), email), _
locRoot & ps & odFolders(folderID)(1)
cid = "": folderID = ""
End If
Next line
End Select
NextFolder:
cid = "": s = "": email = "": Set odFolders = Nothing
Next dirName
Dim tmpColl As Collection: Set tmpColl = New Collection
For Each vItem In locToWebColl
locRoot = vItem(0): webRoot = vItem(1): email = vItem(2)
If Right(webRoot, 1) = "/" Then webRoot = Left(webRoot, Len(webRoot) - 1)
If Right(locRoot, 1) = ps Then locRoot = Left(locRoot, Len(locRoot) - 1)
tmpColl.Add VBA.Array(locRoot, webRoot, email), locRoot
Next vItem
Set locToWebColl = tmpColl
GetWebPath = GetWebPath(path, False): Exit Function
End Function
You can then easily convert the local path to the corresponding OneDrive URL like this:
'Requires the function GetWebPath! (https://stackoverflow.com/a/74165973/12287457)
Dim oneDriveUrl as String
oneDriveUrl = GetWebPath(yourLocalPath)
Your code could look like this:
Sub QrLabelCreate()
Dim localPath as String
localPath = "C:Users\Name\OneDrive\MyMap\" & ID & ".pdf"
'STEP 1:
'Excel VBA put data into a word-document, and export it to pdf-file (saved to OneDrive):
.ActiveDocument.ExportAsFixedFormat _
OutputFileName:=localPath, _
ExportFormat:=wdExportFormatPDF
'STEP 2: THE PROBLEM
'====== I am not able to create code that gives me the URL to the pdf-file. ==========
'Requires the function GetWebPath! (https://stackoverflow.com/a/74165973/12287457)
Dim oneDriveUrl as String
oneDriveUrl = GetWebPath(localPath)
'STEP 3:
'The URL is pasted into the spreadsheet, and VBA creates the QR-code.
End Sub
I want to point out that this is also possible using the excellent VBA-FileTools
library by #Cristian Buse (GitHub), as he already pointed out in the comments! If you import his library, you can convert the path to an URL in exactly the same way as with the function I provided in this answer:
'Requires the library VBA-FileTools! (https://github.com/cristianbuse/VBA-FileTools)
Dim oneDriveUrl as String
oneDriveUrl = GetWebPath(yourLocalPath)

You can use the VBA "ENVIRON" command to get the "OneDrive" environment variable that contains the local root to the current user's OneDrive folders.
For example:
Sub ShowOneDrivePath()
Dim OutputFilePath As String
OutputFilePath = Environ("OneDrive") & "\MyMap\MyPdfName.pdf"
Debug.Print "OneDrive file path is:" & OutputFilePath
End Sub

Related

How to save the active workbook in another folder in Excel VBA?

I am trying to automatically save my active workbook into another folder on my computer and if there is already a file with the name of my workbook in that folder, then it should be saved with "_v1"/"_v2" and so on at the end of its name.
I have found this code but it works just for the current folder, where the workbook is saved.
Sub SaveNewVersion_Excel()
Dim FolderPath As String
Dim myPath As String
Dim SaveName As String
Dim SaveExt As String
Dim VersionExt As String
Dim Saved As Boolean
Dim x As Long
TestStr = ""
Saved = False
x = 2
VersionExt = "_v"
On Error GoTo NotSavedYet
myPath = "O:\Operations\Department\Data Bank Coordinator\_PROJECTS_\QC BeadRegion Check\Multi Ref Archiv"
myFileName = Mid(myPath, InStrRev(myPath, "\") + 1, InStrRev(myPath, ".") - InStrRev(myPath, "\") - 1)
FolderPath = Left(myPath, InStrRev(myPath, "\"))
SaveExt = "." & Right(myPath, Len(myPath) - InStrRev(myPath, "."))
On Error GoTo 0
If InStr(1, myFileName, VersionExt) > 1 Then
myArray = Split(myFileName, VersionExt)
SaveName = myArray(0)
Else
SaveName = myFileName
End If
If FileExist(FolderPath & SaveName & SaveExt) = False Then
ActiveWorkbook.saveAs FolderPath & SaveName & SaveExt
Exit Sub
End If
Do While Saved = False
If FileExist(FolderPath & SaveName & VersionExt & x & SaveExt) = False Then
ActiveWorkbook.saveAs FolderPath & SaveName & VersionExt & x & SaveExt
Saved = True
Else
x = x + 1
End If
Loop
Exit Sub
NotSavedYet:
MsgBox "This file has not been initially saved. " & _
"Cannot save a new version!", vbCritical, "Not Saved To Computer"
End Sub
Function FileExist(FilePath As String) As Boolean
Dim TestStr As String
On Error Resume Next
TestStr = Dir(FilePath)
On Error GoTo 0
If TestStr = "" Then
FileExist = False
Else
FileExist = True
End If
End Function
It works for the current folder but when I change the folder path it doesn't work.
I would very much appreciate it if you could help me.
Thanks!
Sergiu
I've assumed the new folder is "D:_PROJECTS_\Multi Ref Archiv" and that if the existing file is zzzz_v07.xlsm then you want this saved as zzzz_v08.xlsm even when there are no previous versions in the folder. I added the leading zero so they sort nicely!
Sub SaveNewVersion_Excel2()
Const FOLDER = "D:\_PROJECTS_\Multi Ref Archiv" ' new location
Const MAX_FILES = 99
Dim oFSO As Object, oFolder As Object, bOK As Boolean, res As Variant
Set oFSO = CreateObject("Scripting.FileSystemObject")
Dim sFilename As String, sFilename_v As String
' filename only
sFilename = ThisWorkbook.Name
' check folder exists
If Not oFSO.folderexists(FOLDER) Then
bOK = MsgBox(FOLDER & " does not exist. Do you want to create ?", vbYesNo, "Confirm")
If bOK Then
oFSO.createFolder FOLDER
MsgBox "OK created " & FOLDER, vbInformation
Else
Exit Sub
End If
End If
' get next name
sFilename_v = Next_v(sFilename)
' check if exists
Dim i As Integer: i = 1
Do While oFSO.fileexists(FOLDER & "\" & sFilename_v) = True And i <= MAX_FILES
i = i + 1
sFilename_v = Next_v(sFilename_v)
Loop
' check loop ok
If i > MAX_FILES Then
MsgBox "More than " & MAX_FILES & " files already exist", vbExclamation
Exit Sub
End If
sFilename_v = FOLDER & "\" & sFilename_v
' confirm save
res = MsgBox("Do you want to save to " & sFilename_v, vbYesNo, "Confirm")
If res = vbYes Then
ActiveWorkbook.SaveAs sFilename_v
MsgBox "Done", vbInformation
End If
End Sub
Function Next_v(s As String)
Const ver = "_v"
Dim i As Integer, j As Integer, ext As String, rev As Integer
i = InStrRev(s, ".")
j = InStrRev(s, ver)
ext = Mid(s, i)
' increment existing _v if exists
If j > 0 Then
rev = Mid(s, j + 2, i - j - 2)
s = Left(s, j - 1)
Else
rev = 0
s = Left(s, i - 1)
End If
Next_v = s & ver & Format(rev + 1, "00") & ext
End Function
You can move all of the logic out to a separate function, then you only need to call that to get the "correct" name to save as.
'Pass in the full path and filename
' Append "_Vx" while the passed filename is found in the folder
' Returns empty string if the path is not valid
Function NextFileName(fPath As String)
Const V As String = "_V"
Dim fso, i, p, base, ext
Set fso = CreateObject("scripting.filesystemobject")
'valid parent folder?
If fso.folderexists(fso.GetParentFolderName(fPath)) Then
p = fPath
ext = fso.getextensionname(p)
base = Left(p, Len(p) - (1 + Len(ext))) 'base name without extension
i = 1
Do While fso.fileexists(p)
i = i + 1
p = base & (V & i) & "." & ext
Loop
End If
NextFileName = p
End Function

Excel's fullname property with OneDrive

If I want to use the open Workbook object to get the fullname of an Excel file after saving it, but that file has been synchronized to OneDrive, I get a "https" address instead of a local one, which other programs cannot interpret.
How do I get the local filename of a file like this?
Example:
Save a file to "C:\Users\user\OneDrive - Company\Documents".
OneDrive does its synchronization.
Querying Workbook.FullName now shows as "https://..."
Universal Solution & Meta-Analysis of All Solutions
TLDR:
For the solution, skip to the section The Solutions
For the meta-analysis, skip to the section Testing and comparison of solutions
Background
In the last few months, #Cristian Buse (GitHub) and I performed extensive research and work on the problem in question, which led to the discovery of a large number of cases no previously available solution could solve. Because of this, we started refining our own solutions.
Unfortunately, throughout development, our solutions grew very complex. Describing how exactly they work would go far beyond the scope of a single StackOverflow answer.
For those who are interested in these technicalities, here are links to the threads we used to discuss our progress: Thread 1, Thread 2. The total volume of these threads is approximately 40,000 words or 150 pages. Luckily, it is not necessary to understand any of that to harvest the fruits of our efforts.
In the end, both of us created independent solutions:
#Cristian Buse developed his solution as part of one of his excellent VBA Libraries, to be specific, the Library VBA-FileTools. It is implemented elegantly and stepping through his code is the best way of comprehending how the solution works in detail. Furthermore, his library provides a bunch of other very useful functionalities.
My own solution comes in the form of a standalone function without any dependencies. This is useful if this problem occurs in a small project where no additional functionality is required. Because implementing the desired universal functionality is complex, it is very long and convoluted for a single procedure. I do not recommend trying to comprehend the solution by reading the code of this function.
As of now (27th Jan. 2023), this is the only solution for this problem that also works on macOS!
The Solutions
NOTE: Should you encounter any bugs with our solutions, please report them here or on GitHub! In that case, I recommend you use this solution in the meantime, as it is the next most accurate solution available.
Solution 1 - Library (currently Windows only)
Import this library: VBA-FileTools
from GitHub into your project. Getting the local name of your workbook is then as easy as:
GetLocalPath(ThisWorkbook.FullName)
Solution 2 - Standalone Function (works on Windows and macOS)
Copy this function, either from GitHub Gist or from this answer directly, into any standard code module. The version on GitHub Gist includes more information and some comments in and on the code.
Getting the local name of your workbook now works in the same way as with Solution 1:
GetLocalPath(ThisWorkbook.FullName)
Note that this function also offers some optional parameters, but they should almost never be needed. (See Gist for more information)
If you are using MacOS, this is currently the only solution for you. Every other solution analyzed in this post will not work at all on macOS!
Here is the code of the function:
'This Function will convert a OneDrive/SharePoint Url path, e.g. Url containing
'https://d.docs.live.net/; .sharepoint.com/sites; my.sharepoint.com/personal/...
'to the locally synchronized path on your current pc or mac, e.g. a path like
'C:\users\username\OneDrive\ on Windows; or /Users/username/OneDrive/ on MacOS,
'if you have the remote directory locally synchronized with the OneDrive app.
'If no local path can be found, the input value will be returned unmodified.
'Author: Guido Witt-Dörring
'Source: https://gist.github.com/guwidoe/038398b6be1b16c458365716a921814d
Public Function GetLocalPath(ByVal path As String, _
Optional ByVal rebuildCache As Boolean = False, _
Optional ByVal returnAll As Boolean = False, _
Optional ByVal preferredMountPointOwner As String = "") _
As String
#If Mac Then
Const vbErrPermissionDenied As Long = 70
Const vbErrInvalidFormatInResourceFile As Long = 325
Const ps As String = "/"
#Else
Const ps As String = "\"
#End If
Const vbErrFileNotFound As Long = 53
Static locToWebColl As Collection, lastTimeNotFound As Collection
Static lastCacheUpdate As Date
Dim resColl As Object, webRoot As String, locRoot As String
Dim vItem As Variant, s As String, keyExists As Boolean
Dim pmpo As String: pmpo = LCase(preferredMountPointOwner)
If Not locToWebColl Is Nothing And Not rebuildCache Then
Set resColl = New Collection: GetLocalPath = ""
For Each vItem In locToWebColl
locRoot = vItem(0): webRoot = vItem(1)
If InStr(1, path, webRoot, vbTextCompare) = 1 Then _
resColl.Add Key:=vItem(2), _
Item:=Replace(Replace(path, webRoot, locRoot, , 1), "/", ps)
Next vItem
If resColl.Count > 0 Then
If returnAll Then
For Each vItem In resColl: s = s & "//" & vItem: Next vItem
GetLocalPath = Mid(s, 3): Exit Function
End If
On Error Resume Next: GetLocalPath = resColl(pmpo): On Error GoTo 0
If GetLocalPath <> "" Then Exit Function
GetLocalPath = resColl(1): Exit Function
End If
If Not lastTimeNotFound Is Nothing Then
On Error Resume Next: lastTimeNotFound path
keyExists = (Err.Number = 0): On Error GoTo 0
If keyExists Then
If DateAdd("s", 1, lastTimeNotFound(path)) > Now() Then _
GetLocalPath = path: Exit Function
End If
End If
GetLocalPath = path
End If
Dim cid As String, fileNum As Long, line As Variant, parts() As String
Dim tag As String, mainMount As String, relPath As String, email As String
Dim b() As Byte, n As Long, i As Long, size As Long, libNr As String
Dim parentID As String, folderID As String, folderName As String
Dim folderIdPattern As String, fileName As String, folderType As String
Dim siteID As String, libID As String, webID As String, lnkID As String
Dim odFolders As Object, cliPolColl As Object, libNrToWebColl As Object
Dim sig1 As String: sig1 = StrConv(Chr$(&H2), vbFromUnicode)
Dim sig2 As String: sig2 = ChrW$(&H1) & String(3, vbNullChar)
Dim vbNullByte As String: vbNullByte = MidB$(vbNullChar, 1, 1)
#If Mac Then
Dim utf16() As Byte, utf32() As Byte, j As Long, k As Long, m As Long
Dim charCode As Long, lowSurrogate As Long, highSurrogate As Long
ReDim b(0 To 3): b(0) = &HAB&: b(1) = &HAB&: b(2) = &HAB&: b(3) = &HAB&
Dim sig3 As String: sig3 = b: sig3 = vbNullChar & vbNullChar & sig3
#Else
ReDim b(0 To 1): b(0) = &HAB&: b(1) = &HAB&
Dim sig3 As String: sig3 = b: sig3 = vbNullChar & sig3
#End If
Dim settPath As String, wDir As String, clpPath As String
#If Mac Then
s = Environ("HOME")
settPath = Left(s, InStrRev(s, "/Library/Containers")) & _
"Library/Containers/com.microsoft.OneDrive-mac/Data/" & _
"Library/Application Support/OneDrive/settings/"
clpPath = s & "/Library/Application Support/Microsoft/Office/CLP/"
#Else
settPath = Environ("LOCALAPPDATA") & "\Microsoft\OneDrive\settings\"
clpPath = Environ("LOCALAPPDATA") & "\Microsoft\Office\CLP\"
#End If
#If Mac Then
Dim possibleDirs(0 To 11) As String: possibleDirs(0) = settPath
For i = 1 To 9: possibleDirs(i) = settPath & "Business" & i & ps: Next i
possibleDirs(10) = settPath & "Personal" & ps: possibleDirs(11) = clpPath
If Not GrantAccessToMultipleFiles(possibleDirs) Then _
Err.Raise vbErrPermissionDenied
#End If
Dim oneDriveSettDirs As Collection: Set oneDriveSettDirs = New Collection
Dim dirName As Variant: dirName = Dir(settPath, vbDirectory)
Do Until dirName = ""
If dirName = "Personal" Or dirName Like "Business#" Then _
oneDriveSettDirs.Add dirName
dirName = Dir(, vbDirectory)
Loop
#If Mac Then
s = ""
For Each dirName In oneDriveSettDirs
wDir = settPath & dirName & ps
cid = IIf(dirName = "Personal", "????????????????", _
"????????-????-????-????-????????????")
If dirName = "Personal" Then s = s & "//" & wDir & "GroupFolders.ini"
s = s & "//" & wDir & "global.ini"
fileName = Dir(wDir, vbNormal)
Do Until fileName = ""
If fileName Like cid & ".ini" Or _
fileName Like cid & ".dat" Or _
fileName Like "ClientPolicy*.ini" Then _
s = s & "//" & wDir & fileName
fileName = Dir
Loop
Next dirName
If Not GrantAccessToMultipleFiles(Split(Mid(s, 3), "//")) Then _
Err.Raise vbErrPermissionDenied
#End If
If Not locToWebColl Is Nothing And Not rebuildCache Then
s = ""
For Each dirName In oneDriveSettDirs
wDir = settPath & dirName & ps
cid = IIf(dirName = "Personal", "????????????????", _
"????????-????-????-????-????????????")
If Dir(wDir & "global.ini") <> "" Then _
s = s & "//" & wDir & "global.ini"
fileName = Dir(wDir, vbNormal)
Do Until fileName = ""
If fileName Like cid & ".ini" Then _
s = s & "//" & wDir & fileName
fileName = Dir
Loop
Next dirName
For Each vItem In Split(Mid(s, 3), "//")
If FileDateTime(vItem) > lastCacheUpdate Then _
rebuildCache = True: Exit For
Next vItem
If Not rebuildCache Then
If lastTimeNotFound Is Nothing Then _
Set lastTimeNotFound = New Collection
On Error Resume Next: lastTimeNotFound.Remove path: On Error GoTo 0
lastTimeNotFound.Add Item:=Now(), Key:=path
Exit Function
End If
End If
lastCacheUpdate = Now()
Set lastTimeNotFound = Nothing
Set locToWebColl = New Collection
For Each dirName In oneDriveSettDirs
wDir = settPath & dirName & ps
If Dir(wDir & "global.ini", vbNormal) = "" Then GoTo NextFolder
fileNum = FreeFile()
Open wDir & "global.ini" For Binary Access Read As #fileNum
ReDim b(0 To LOF(fileNum)): Get fileNum, , b
Close #fileNum: fileNum = 0
#If Mac Then
b = StrConv(b, vbUnicode)
#End If
For Each line In Split(b, vbNewLine)
If line Like "cid = *" Then cid = Mid(line, 7): Exit For
Next line
If cid = "" Then GoTo NextFolder
If (Dir(wDir & cid & ".ini") = "" Or _
Dir(wDir & cid & ".dat") = "") Then GoTo NextFolder
If dirName Like "Business#" Then
folderIdPattern = Replace(Space(32), " ", "[a-f0-9]")
ElseIf dirName = "Personal" Then
folderIdPattern = Replace(Space(16), " ", "[A-F0-9]") & "!###*"
End If
fileName = Dir(clpPath, vbNormal)
Do Until fileName = ""
If InStr(1, fileName, cid) And cid <> "" Then _
email = LCase(Left(fileName, InStr(fileName, cid) - 2)): Exit Do
fileName = Dir
Loop
Set cliPolColl = New Collection
fileName = Dir(wDir, vbNormal)
Do Until fileName = ""
If fileName Like "ClientPolicy*.ini" Then
fileNum = FreeFile()
Open wDir & fileName For Binary Access Read As #fileNum
ReDim b(0 To LOF(fileNum)): Get fileNum, , b
Close #fileNum: fileNum = 0
#If Mac Then
b = StrConv(b, vbUnicode)
#End If
cliPolColl.Add Key:=fileName, Item:=New Collection
For Each line In Split(b, vbNewLine)
If InStr(1, line, " = ", vbBinaryCompare) Then
tag = Left(line, InStr(line, " = ") - 1)
s = Mid(line, InStr(line, " = ") + 3)
Select Case tag
Case "DavUrlNamespace"
cliPolColl(fileName).Add Key:=tag, Item:=s
Case "SiteID", "IrmLibraryId", "WebID"
s = Replace(LCase(s), "-", "")
If Len(s) > 3 Then s = Mid(s, 2, Len(s) - 2)
cliPolColl(fileName).Add Key:=tag, Item:=s
End Select
End If
Next line
End If
fileName = Dir
Loop
fileNum = FreeFile
Open wDir & cid & ".dat" For Binary Access Read As #fileNum
ReDim b(0 To LOF(fileNum)): Get fileNum, , b: s = b: size = LenB(s)
Close #fileNum: fileNum = 0
Set odFolders = New Collection
For Each vItem In Array(16, 8)
i = InStrB(vItem, s, sig2)
Do While i > vItem And i < size - 168
If MidB$(s, i - vItem, 1) = sig1 Then
i = i + 8: n = InStrB(i, s, vbNullByte) - i
If n < 0 Then n = 0
If n > 39 Then n = 39
folderID = StrConv(MidB$(s, i, n), vbUnicode)
i = i + 39: n = InStrB(i, s, vbNullByte) - i
If n < 0 Then n = 0
If n > 39 Then n = 39
parentID = StrConv(MidB$(s, i, n), vbUnicode)
i = i + 121: n = -Int(-(InStrB(i, s, sig3) - i) / 2) * 2
If n < 0 Then n = 0
#If Mac Then
utf32 = MidB$(s, i, n)
ReDim utf16(LBound(utf32) To UBound(utf32))
j = LBound(utf32): k = LBound(utf32)
Do While j < UBound(utf32)
If utf32(j + 2) = 0 And utf32(j + 3) = 0 Then
utf16(k) = utf32(j): utf16(k + 1) = utf32(j + 1)
k = k + 2
Else
If utf32(j + 3) <> 0 Then Err.Raise _
vbErrInvalidFormatInResourceFile
charCode = utf32(j + 2) * &H10000 + _
utf32(j + 1) * &H100& + utf32(j)
m = charCode - &H10000
highSurrogate = &HD800& + (m \ &H400&)
lowSurrogate = &HDC00& + (m And &H3FF)
utf16(k) = CByte(highSurrogate And &HFF&)
utf16(k + 1) = CByte(highSurrogate \ &H100&)
utf16(k + 2) = CByte(lowSurrogate And &HFF&)
utf16(k + 3) = CByte(lowSurrogate \ &H100&)
k = k + 4
End If
j = j + 4
Loop
ReDim Preserve utf16(LBound(utf16) To k - 1)
folderName = utf16
#Else
folderName = MidB$(s, i, n)
#End If
If folderID Like folderIdPattern Then
odFolders.Add VBA.Array(parentID, folderName), folderID
End If
End If
i = InStrB(i + 1, s, sig2)
Loop
If odFolders.Count > 0 Then Exit For
Next vItem
fileNum = FreeFile()
Open wDir & cid & ".ini" For Binary Access Read As #fileNum
ReDim b(0 To LOF(fileNum)): Get fileNum, , b
Close #fileNum: fileNum = 0
#If Mac Then
b = StrConv(b, vbUnicode)
#End If
Select Case True
Case dirName Like "Business#"
mainMount = "": Set libNrToWebColl = New Collection
For Each line In Split(b, vbNewLine)
webRoot = "": locRoot = ""
Select Case Left$(line, InStr(line, " = ") - 1)
Case "libraryScope"
parts = Split(line, """"): locRoot = parts(9)
If locRoot = "" Then libNr = Split(line, " ")(2)
folderType = parts(3): parts = Split(parts(8), " ")
siteID = parts(1): webID = parts(2): libID = parts(3)
If mainMount = "" And folderType = "ODB" Then
mainMount = locRoot: fileName = "ClientPolicy.ini"
Else: fileName = "ClientPolicy_" & libID & siteID & ".ini"
End If
On Error Resume Next
webRoot = cliPolColl(fileName)("DavUrlNamespace")
On Error GoTo 0
If webRoot = "" Then
For Each vItem In cliPolColl
If vItem("SiteID") = siteID And vItem("WebID") = _
webID And vItem("IrmLibraryId") = libID Then
webRoot = vItem("DavUrlNamespace"): Exit For
End If
Next vItem
End If
If webRoot = "" Then Err.Raise vbErrFileNotFound
If locRoot = "" Then
libNrToWebColl.Add VBA.Array(libNr, webRoot), libNr
Else: locToWebColl.Add VBA.Array(locRoot, webRoot, email), _
locRoot
End If
Case "libraryFolder"
locRoot = Split(line, """")(1): libNr = Split(line, " ")(3)
For Each vItem In libNrToWebColl
If vItem(0) = libNr Then
s = "": parentID = Left(Split(line, " ")(4), 32)
Do
On Error Resume Next: odFolders parentID
keyExists = (Err.Number = 0): On Error GoTo 0
If Not keyExists Then Exit Do
s = odFolders(parentID)(1) & "/" & s
parentID = odFolders(parentID)(0)
Loop
webRoot = vItem(1) & s: Exit For
End If
Next vItem
locToWebColl.Add VBA.Array(locRoot, webRoot, email), locRoot
Case "AddedScope"
parts = Split(line, """")
relPath = parts(5): If relPath = " " Then relPath = ""
parts = Split(parts(4), " "): siteID = parts(1)
webID = parts(2): libID = parts(3): lnkID = parts(4)
fileName = "ClientPolicy_" & libID & siteID & lnkID & ".ini"
On Error Resume Next
webRoot = cliPolColl(fileName)("DavUrlNamespace") & relPath
On Error GoTo 0
If webRoot = "" Then
For Each vItem In cliPolColl
If vItem("SiteID") = siteID And vItem("WebID") = _
webID And vItem("IrmLibraryId") = libID Then
webRoot = vItem("DavUrlNamespace") & relPath
Exit For
End If
Next vItem
End If
If webRoot = "" Then Err.Raise vbErrFileNotFound
s = "": parentID = Left(Split(line, " ")(3), 32)
Do
On Error Resume Next: odFolders parentID
keyExists = (Err.Number = 0): On Error GoTo 0
If Not keyExists Then Exit Do
s = odFolders(parentID)(1) & ps & s
parentID = odFolders(parentID)(0)
Loop
locRoot = mainMount & ps & s
locToWebColl.Add VBA.Array(locRoot, webRoot, email), locRoot
Case Else
Exit For
End Select
Next line
Case dirName = "Personal"
For Each line In Split(b, vbNewLine)
If line Like "library = *" Then _
locRoot = Split(line, """")(3): Exit For
Next line
On Error Resume Next
webRoot = cliPolColl("ClientPolicy.ini")("DavUrlNamespace")
On Error GoTo 0
If locRoot = "" Or webRoot = "" Or cid = "" Then GoTo NextFolder
locToWebColl.Add VBA.Array(locRoot, webRoot & "/" & cid, email), _
locRoot
If Dir(wDir & "GroupFolders.ini") = "" Then GoTo NextFolder
cid = "": fileNum = FreeFile()
Open wDir & "GroupFolders.ini" For Binary Access Read As #fileNum
ReDim b(0 To LOF(fileNum)): Get fileNum, , b
Close #fileNum: fileNum = 0
#If Mac Then
b = StrConv(b, vbUnicode)
#End If
For Each line In Split(b, vbNewLine)
If InStr(line, "BaseUri = ") And cid = "" Then
cid = LCase(Mid(line, InStrRev(line, "/") + 1, 16))
folderID = Left(line, InStr(line, "_") - 1)
ElseIf cid <> "" Then
locToWebColl.Add VBA.Array(locRoot & ps & odFolders( _
folderID)(1), webRoot & "/" & cid & "/" & _
Mid(line, Len(folderID) + 9), email), _
locRoot & ps & odFolders(folderID)(1)
cid = "": folderID = ""
End If
Next line
End Select
NextFolder:
cid = "": s = "": email = "": Set odFolders = Nothing
Next dirName
Dim tmpColl As Collection: Set tmpColl = New Collection
For Each vItem In locToWebColl
locRoot = vItem(0): webRoot = vItem(1): email = vItem(2)
If Right(webRoot, 1) = "/" Then webRoot = Left(webRoot, Len(webRoot) - 1)
If Right(locRoot, 1) = ps Then locRoot = Left(locRoot, Len(locRoot) - 1)
tmpColl.Add VBA.Array(locRoot, webRoot, email), locRoot
Next vItem
Set locToWebColl = tmpColl
GetLocalPath = GetLocalPath(path, False, returnAll, pmpo): Exit Function
End Function
How Do the Solutions Work?
Both solutions get all of the required information for translating the UrlPath/WebPath to a LocalPath from the OneDrive settings files inside of the directory %localappdata%\Microsoft\OneDrive\settings\....
This means, contrary to most other solutions online, the registry is not used! The reasons for this are explained in the Gist repository of Solution 2.
The following files will be read:
(Wildcards: * - zero or more characters; ? - one character)
????????????????.dat
????????????????.ini
global.ini
GroupFolders.ini
????????-????-????-????-????????????.dat
????????-????-????-????-????????????.ini
ClientPolicy*.ini
All of the .ini files can be read easily as they use UTF-16 encoding.
The .dat files are much more difficult to decipher, because they use a proprietary binary format. Luckily, the information we need can be extracted by looking for certain byte-patterns inside these files and copying and converting the data at a certain offset from these "signature" bytes.
Data from all of these files is used, to create a "dictionary" of all the local mount points on your pc, and their corresponding WebPath. For example, for your personal OneDrive, such a local mount point could look like this:
C:\Users\Username\OneDrive, and the corresponding WebPath could look like this: https://d.docs.live.net/f9d8c1184686d493.
This "dictionary" can then be used to "translate" a given WebPath to a local path by replacing the part that is equal to one of the elements of the dictionary with the corresponding local mount point. For example, this WebPath: https://d.docs.live.net/f9d8c1184686d493/Folder/File.xlsm will be correctly "translated" to C:\Users\Username\OneDrive\Folder\File.xlsm
Because all possible WebPaths for the local machine can be translated by the same dictionary, it is implemented as Static in both solutions. This means it will only be written the first time the function is called, all subsequent function calls will find the "dictionary" already initialized leading to shorter run time.
Testing and Comparison of Solutions
I conducted extensive testing of all solutions I could find online. A selection of these tests will be presented here.
This is a list of some of the tested solutions:
Nr.
Author
Solution
Tests passed
1
Koen Rijnsent
https://stackoverflow.com/a/71753164/12287457
0/46
2
Cooz2, adapted for Excel by LucasHol
https://social.msdn.microsoft.com/Forums/office/en-US/1331519b-1dd1-4aa0-8f4f-0453e1647f57/how-to-get-physical-path-instead-of-url-onedrive
0/46
3
Julio Garcia
https://stackoverflow.com/a/74360506/12287457
0/46
4
Claude
https://stackoverflow.com/a/64657459/12287457
0/46
5
Variatus
https://stackoverflow.com/a/68568909/12287457
0/46
6
MatChrupczalski
https://social.msdn.microsoft.com/Forums/office/en-US/1331519b-1dd1-4aa0-8f4f-0453e1647f57/how-to-get-physical-path-instead-of-url-onedrive
1/46
7
Caio Silva
https://stackoverflow.com/a/67318424/12287457 and https://stackoverflow.com/a/67326133/12287457
2/46
8
Alain YARDIM
https://stackoverflow.com/a/65967886/12287457
2/46
9
tsdn
https://stackoverflow.com/a/56326922/12287457
2/46
10
Peter G. Schild
https://stackoverflow.com/a/60990170/12287457
2/46
11
TWMIC
https://stackoverflow.com/a/64591370/12287457
3/46
12
Horoman
https://stackoverflow.com/a/60921115/12287457
4/46
13
Philip Swannell
https://stackoverflow.com/a/54182663/12287457
4/46
14
RMK
https://stackoverflow.com/a/67697487/12287457
5/46
15
beerockxs
https://stackoverflow.com/a/67582367/12287457
5/46
16
Virtuoso
https://stackoverflow.com/a/33935405/12287457
5/46
17
COG
https://stackoverflow.com/a/51316641/12287457
5/46
18
mohnston
https://stackoverflow.com/a/68569925/12287457
5/46
19
Tomoaki Tsuruya (鶴谷 朋亮)
https://tsurutoro.com/vba-trouble2/
5/46
20
Greedo
https://gist.github.com/Greedquest/ 52eaccd25814b84cc62cbeab9574d7a3
6/45
21
Christoph Ackermann
https://stackoverflow.com/a/62742852/12287457
6/46
22
Schoentalegg
https://stackoverflow.com/a/57040668/12287457
6/46
23
Erlandsen Data Consulting
https://www.erlandsendata.no/?t=vbatips&p=4079
7/46
24
Kurobako (黒箱)
https://kuroihako.com/vba/onedriveurltolocalpath/
7/46
25
Tim Williams
https://stackoverflow.com/a/70610729/12287457
8/46
26
Erik van der Neut
https://stackoverflow.com/a/72709568/12287457
8/46
27
Ricardo Diaz
https://stackoverflow.com/a/65605893/12287457
9/46
28
Iksi
https://stackoverflow.com/a/68963896/12287457
11/46
29
Gustav Brock, Cactus Data ApS
https://stackoverflow.com/a/70521246/12287457
11/46
30
Ricardo Gerbaudo
https://stackoverflow.com/a/69929678/12287457
14/46
31
Guido Witt-Dörring Short solution
https://stackoverflow.com/a/72736924/12287457
24/46
32
Ion Cristian Buse
https://github.com/cristianbuse/VBA-FileTools
46/46
33
Guido Witt-Dörring Universal Solution
https://gist.github.com/guwidoe/ 038398b6be1b16c458365716a921814d
46/46
Each line in the table in the below image represents one solution in the above table and they can be correlated using the solution number.
Likewise, each column represents a test case, they can be correlated to this test-table by using the test-number. Unfortunately Stack Overflow doesn't allow answers long enough to include the table of test cases directly in this post.
All of this testing was done on Windows. On macOS, every solution except for Nr 33 would pass 0/46 tests. I suspect that my solution (Nr 33) would also pass every test on macOS, however I was not able to test this rigorously.
Most solutions pass very few tests. Many of these tests are relatively dificult to solve, some are absolute edge cases, such as tests Nr 41 to 46, that test how a solution deals with OneDrive folders that are synced to multiple different local paths, which can only happen if multiple Business OneDrive accounts are logged in on the same PC and even then needs some special setup. (More information on that can be found here in Thread 2)
Test Nr 22 contains various Unicode emoji characters in some folder names, this is why many solutions fail with error here.
Another reason why many solutions perform poorly is that the environment variables Environ("OneDrive"), Environ("OneDriveCommercial"),Environ("OneDriveConsumer"), which many solutions build on, are not reliable, especially when you have multiple business OneDrive accounts logged in at the same time, like I do. Note that even if they always returned their expected values, it would be way to little information to solve all cases.
If you have another different solution you would like me to test, let me know and I'll add it to this section.
I found a thread online which contained enough information to put something simple together to solve this. I actually implemented the solution in Ruby, but this is the VBA version:
Option Explicit
Private Function Local_Workbook_Name(ByRef wb As Workbook) As String
Dim Ctr As Long
Dim objShell As Object
Dim UserProfilePath As String
'Check if it looks like a OneDrive location
If InStr(1, wb.FullName, "https://", vbTextCompare) > 0 Then
'Replace forward slashes with back slashes
Local_Workbook_Name = Replace(wb.FullName, "/", "\")
'Get environment path using vbscript
Set objShell = CreateObject("WScript.Shell")
UserProfilePath = objShell.ExpandEnvironmentStrings("%UserProfile%")
'Trim OneDrive designators
For Ctr = 1 To 4
Local_Workbook_Name = Mid(Local_Workbook_Name, InStr(Local_Workbook_Name, "\") + 1)
Next
'Construct the name
Local_Workbook_Name = UserProfilePath & "\OneDrive\" & Local_Workbook_Name
Else
Local_Workbook_Name = wb.FullName
End If
End Function
Private Sub testy()
MsgBox ActiveWorkbook.FullName & vbCrLf & Local_Workbook_Name(ActiveWorkbook)
End Sub
Horoman's version (2020-03-30) is good because it works on both private and commercial OneDrive. However it crashed on me because the line "LocalFullName = oneDrivePath & Application.PathSeparator & endFilePath" inserts a slash between oneDrivePath & endFilePath. Moreover, one should really try out paths "OneDriveCommercial" and "OneDriveConsumer" before "OneDrive". So here's the code that works for me:
Sub TestLocalFullName()
Debug.Print "URL: " & ActiveWorkbook.FullName
Debug.Print "Local: " & LocalFullName(ActiveWorkbook.FullName)
Debug.Print "Test: " & Dir(LocalFullName(ActiveWorkbook.FullName))
End Sub
Private Function LocalFullName$(ByVal fullPath$)
'Finds local path for a OneDrive file URL, using environment variables of OneDrive
'Reference https://stackoverflow.com/questions/33734706/excels-fullname-property-with-onedrive
'Authors: Philip Swannell 2019-01-14, MatChrupczalski 2019-05-19, Horoman 2020-03-29, P.G.Schild 2020-04-02
Dim ii&
Dim iPos&
Dim oneDrivePath$
Dim endFilePath$
If Left(fullPath, 8) = "https://" Then 'Possibly a OneDrive URL
If InStr(1, fullPath, "my.sharepoint.com") <> 0 Then 'Commercial OneDrive
'For commercial OneDrive, path looks like "https://companyName-my.sharepoint.com/personal/userName_domain_com/Documents" & file.FullName)
'Find "/Documents" in string and replace everything before the end with OneDrive local path
iPos = InStr(1, fullPath, "/Documents") + Len("/Documents") 'find "/Documents" position in file URL
endFilePath = Mid(fullPath, iPos) 'Get the ending file path without pointer in OneDrive. Include leading "/"
Else 'Personal OneDrive
'For personal OneDrive, path looks like "https://d.docs.live.net/d7bbaa#######1/" & file.FullName
'We can get local file path by replacing "https.." up to the 4th slash, with the OneDrive local path obtained from registry
iPos = 8 'Last slash in https://
For ii = 1 To 2
iPos = InStr(iPos + 1, fullPath, "/") 'find 4th slash
Next ii
endFilePath = Mid(fullPath, iPos) 'Get the ending file path without OneDrive root. Include leading "/"
End If
endFilePath = Replace(endFilePath, "/", Application.PathSeparator) 'Replace forward slashes with back slashes (URL type to Windows type)
For ii = 1 To 3 'Loop to see if the tentative LocalWorkbookName is the name of a file that actually exists, if so return the name
oneDrivePath = Environ(Choose(ii, "OneDriveCommercial", "OneDriveConsumer", "OneDrive")) 'Check possible local paths. "OneDrive" should be the last one
If 0 < Len(oneDrivePath) Then
LocalFullName = oneDrivePath & endFilePath
Exit Function 'Success (i.e. found the correct Environ parameter)
End If
Next ii
'Possibly raise an error here when attempt to convert to a local file name fails - e.g. for "shared with me" files
LocalFullName = vbNullString
Else
LocalFullName = fullPath
End If
End Function
I have adjusted the function provided by others to account for some additional constraints:
When you share files via a team site, it's not "my.sharepoint.com/" but "sharepoint.com/" that you should use to determine if it's a commercial version.
It is better to count the slashes rather than using the position of "/Documents" because, for example in French, the document folder is called "Documents partages". It is preferable to count 4 slashes for commercial use and 2 slashes for personal use.
If the SharePoint folder added as a shortcut to OneDrive is not at the root, the local address on the hard drive does not contain parent folders on the SharePoint.
Here is the code that takes my changes into account:
Public Function AdresseLocal$(ByVal fullPath$)
'Finds local path for a OneDrive file URL, using environment variables of OneDrive
'Reference https://stackoverflow.com/questions/33734706/excels-fullname-property-with-onedrive
'Authors: Philip Swannell 2019-01-14, MatChrupczalski 2019-05-19, Horoman 2020-03-29, P.G.Schild 2020-04-02
Dim ii&
Dim iPos&
Dim oneDrivePath$
Dim endFilePath$
Dim NbSlash
If Left(fullPath, 8) = "https://" Then
If InStr(1, fullPath, "sharepoint.com/") <> 0 Then 'Commercial OneDrive
NbSlash = 4
Else 'Personal OneDrive
NbSlash = 2
End If
iPos = 8 'Last slash in https://
For ii = 1 To NbSlash
iPos = InStr(iPos + 1, fullPath, "/")
Next ii
endFilePath = Mid(fullPath, iPos)
endFilePath = Replace(endFilePath, "/", Application.PathSeparator)
For ii = 1 To 3
oneDrivePath = Environ(Choose(ii, "OneDriveCommercial", "OneDriveConsumer", "OneDrive"))
If 0 < Len(oneDrivePath) Then Exit For
Next ii
AdresseLocal = oneDrivePath & endFilePath
While Len(Dir(AdresseLocal, vbDirectory)) = 0 And InStr(2, endFilePath, Application.PathSeparator) > 0
endFilePath = Mid(endFilePath, InStr(2, endFilePath, Application.PathSeparator))
AdresseLocal = oneDrivePath & endFilePath
Wend
Else
AdresseLocal = fullPath
End If
End Function
...which builds on the work of the different contributors.
It's possible to improve on Virtuoso's answer to reduce (though not eliminate) the chance that the function returns a "wrong" file location. The problem is that there are various URLs that a workbook's .FullName can be. These are three I'm aware of:
A URL associated with the user's OneDrive
A URL associated with the user's OneDrive for Business
A URL associated with somebody else's OneDrive in the case that that other person has "shared" the file (in which case you open the file via File > Open > Shared with me)
On my PC I can get the relevant local folders to map the first two URLs via the OneDriveConsumer and OneDriveCommercial environment variables, that exist in addition to the OneDrive environment variable, so the code below makes use of these. I'm not aware that it's possible to handle the "Shared with Me" files and the code below will return their https://-style location.
Private Function Local_Workbook_Name(ByRef wb As Workbook) As String
Dim i As Long, j As Long
Dim OneDrivePath As String
Dim ShortName As String
'Check if it looks like a OneDrive location
If InStr(1, wb.FullName, "https://", vbTextCompare) > 0 Then
'Replace forward slashes with back slashes
ShortName = Replace(wb.FullName, "/", "\")
'Remove the first four backslashes
For i = 1 To 4
ShortName = Mid(ShortName, InStr(ShortName, "\") + 1)
Next
'Loop to see if the tentative LocalWorkbookName is the name of a file that actually exists, if so return the name
For j = 1 To 3
OneDrivePath = Environ(Choose(j, "OneDrive", "OneDriveCommercial", "OneDriveConsumer"))
If Len(OneDrivePath) > 0 Then
Local_Workbook_Name = OneDrivePath & "\" & ShortName
If Dir(Local_Workbook_Name) <> "" Then
Exit Function
End If
End If
Next j
'Possibly raise an error here when attempt to convert to a local file name fails - e.g. for "shared with me" files
End If
Local_Workbook_Name = wb.FullName
End Function
Unfortunately, if files exist with identical paths within both the OneDrive folder and the OneDrive for Business folder, then the code can't distinguish between them, and may return the "wrong one". I don't have a solution for that.
I like the Version from TWMIC with the use of the Registry. All other Version did not work at my oneDrive for Business. There are some folders where the name is slightly different to the URL, for example in the URL are partly no spaces but in the folder there are. If it is from Teams and in the Team Name are spaces then this is a problem. Even the Folder Names from Teams are different than the URL, depending which folder level in Teams you are syncing.
The Version from TWMIC is tagged as dangerous at my work computer and i can't use it, very sad about that.
So i made a Version which reads the ini File from oneDrive for Busines, if it is OneDrive for Business...
Public Function AdresseLocal$(ByVal fullPath$)
'Finds local path for a OneDrive file URL, using environment variables of OneDrive and loading the settings ini File of OneDrive
'Reference https://stackoverflow.com/questions/33734706/excels-fullname-property-with-onedrive
'Authors: Philip Swannell 2019-01-14, MatChrupczalski 2019-05-19, Horoman 2020-03-29, P.G.Schild 2020-04-02, Iksi 2021-08-28
Dim ScreenUpdate As Boolean
Dim ii&
Dim iPos&
Dim DatFile$, SettingsDir$, Temp$
Dim oneDrivePath$, oneDriveURL$
Dim endFilePath$
If Left(fullPath, 8) = "https://" Then
If InStr(1, fullPath, "sharepoint.com") <> 0 Then 'Commercial OneDrive
'Find the correct settings File, I'm not sure if it is always in Folder Business1, so trying to find a Folder Business and then Business1, 2 ....
'First find *.dat File, seems to be only one of that type, the correct ini File is the same Name than the dat File
DatFile = Dir(Environ("LOCALAPPDATA") & "\Microsoft\OneDrive\settings\Business\*.dat")
If DatFile <> "" Then SettingsDir = Environ("LOCALAPPDATA") & "\Microsoft\OneDrive\settings\Business\"
For ii = 1 To 9
Temp = Dir(Environ("LOCALAPPDATA") & "\Microsoft\OneDrive\settings\Business" & ii & "\*.dat")
If Temp <> "" Then
If SettingsDir = "" Then
DatFile = Temp
SettingsDir = Environ("LOCALAPPDATA") & "\Microsoft\OneDrive\settings\Business" & ii & "\"
Else
MsgBox "There is more than one OneDrive settings Folder!"
End If
End If
Next
'Open ini File without showing
ScreenUpdate = Application.ScreenUpdating
Application.ScreenUpdating = False
Workbooks.OpenText Filename:= _
SettingsDir & Left(DatFile, Len(DatFile) - 3) & "ini" _
, Origin:=xlWindows, StartRow:=1, DataType:=xlDelimited, TextQualifier _
:=xlDoubleQuote, ConsecutiveDelimiter:=True, Tab:=False, Semicolon:= _
False, Comma:=False, Space:=True, Other:=False, TrailingMinusNumbers:=True
ii = 1
Do While Cells(ii, 1) = "libraryScope"
'Search the correct URL which fits to the fullPath and then search the corresponding Folder
If InStr(fullPath, Cells(ii, 9)) = 1 Then
oneDriveURL = Cells(ii, 9)
If Cells(ii, 15) <> "" Then
oneDrivePath = Cells(ii, 15)
Else
iPos = Cells(ii, 3)
Do Until Cells(ii, 1) = "libraryFolder"
ii = ii + 1
Loop
Do While Cells(ii, 1) = "libraryFolder"
If Cells(ii, 4) = iPos Then
oneDrivePath = Cells(ii, 7)
Exit Do
End If
ii = ii + 1
Loop
End If
Exit Do
End If
ii = ii + 1
Loop
ActiveWorkbook.Close False
Application.ScreenUpdating = ScreenUpdate
endFilePath = Mid(fullPath, Len(oneDriveURL) + 1)
Else 'Personal OneDrive
'For personal OneDrive, path looks like "https://d.docs.live.net/d7bbaa#######1/" & file.FullName
'We can get local file path by replacing "https.." up to the 4th slash, with the OneDrive local path obtained from registry
iPos = 8 'Last slash in https://
For ii = 1 To 2
iPos = InStr(iPos + 1, fullPath, "/") 'find 4th slash
Next ii
endFilePath = Mid(fullPath, iPos) 'Get the ending file path without OneDrive root. Include leading "/"
End If
endFilePath = Replace(endFilePath, "/", Application.PathSeparator)
If Len(oneDrivePath) <= 0 Then
For ii = 1 To 3 'Loop to see if the tentative LocalWorkbookName is the name of a file that actually exists, if so return the name
oneDrivePath = Environ(Choose(ii, "OneDriveCommercial", "OneDriveConsumer", "OneDrive")) 'Check possible local paths. "OneDrive" should be the last one
Next ii
End If
AdresseLocal = oneDrivePath & endFilePath
While Len(Dir(AdresseLocal, vbDirectory)) = 0 And InStr(2, endFilePath, Application.PathSeparator) > 0
endFilePath = Mid(endFilePath, InStr(2, endFilePath, Application.PathSeparator))
AdresseLocal = oneDrivePath & endFilePath
Wend
Else
AdresseLocal = fullPath
End If
End Function
For me this works great!
Easy Fix (early 2019) - For anyone else having this issue:
OneDrive > Settings > Office:
- Uncheck 'Use Office applications to sync Office files that I open'
This lets excel save the file in the typical "C:\Users[UserName]\OneDrive..." file format instead of the UNC "https:\" format.
Short solution
The solution presented in the following does not work in absolutely all cases, but it probably works in more than 99% of real-world scenarios. If you are looking for a solution that covers even the edge cases, please look at this universal solution.
An advantage of this solution compared to the above linked universal solution is its simplicity and therefore its lower likelihood to break because of OneDrive/Windows updates.
The function to convert the "WebPath" to a local path looks like this:
Public Function GetLocalPath(ByVal Path As String) As String
Const HKCU = &H80000001
Dim objReg As Object, rPath As String, subKeys(), subKey
Dim urlNamespace As String, mountPoint As String, secPart As String
Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\." & _
"\root\default:StdRegProv")
rPath = "Software\SyncEngines\Providers\OneDrive\"
objReg.EnumKey HKCU, rPath, subKeys
For Each subKey In subKeys
objReg.GetStringValue HKCU, rPath & subKey, "UrlNamespace", urlNamespace
If InStr(Path, urlNamespace) > 0 Then
objReg.GetStringValue HKCU, rPath & subKey, "MountPoint", mountPoint
secPart = Replace(Mid(Path, Len(urlNamespace)), "/", "\")
Path = mountPoint & secPart
Do Until Dir(Path, vbDirectory) <> "" Or InStr(2, secPart, "\") = 0
secPart = Mid(secPart, InStr(2, secPart, "\"))
Path = mountPoint & secPart
Loop
Exit For
End If
Next
GetLocalPath = Path
End Function
To now get the local full name of your workbook, just use GetLocalPath(ThisWorkbook.FullName)
Very helpful, thanks. I had a similar issue, but with a folder name rather than a filename. Consequently I modified it slightly. I made it work for folder names AND filenames (doesn't have to be a workbook). In case it's helpful, code is below:
Public Function Local_Name(theName As String) As String
Dim i As Integer
Dim objShell As Object
Dim UserProfilePath As String
' Check if it looks like a OneDrive location.
If InStr(1, theName, "https://", vbTextCompare) > 0 Then
' Replace forward slashes with back slashes.
Local_Name = Replace(theName, "/", "\")
'Get environment path using vbscript.
Set objShell = CreateObject("WScript.Shell")
UserProfilePath = objShell.ExpandEnvironmentStrings("%UserProfile%")
' Trim OneDrive designators.
For i = 1 To 4
Local_Name = Mid(Local_Name, InStr(Local_Name, "\") + 1)
Next i
' Construct the name.
Local_Name = UserProfilePath & "\OneDrive\" & Local_Name
Else
' (must already be local).
Local_Name = theName
End If
End Function
This is really great stuff. I have run into this problem on some windows 10 machines but not others and it seems to come and go. I tried everything resetting OneDrive, changing the configuration etc. The only thing I tried that at least works on my machine is to use Fullname=CurDir & FileName, instead of FullName= activeworkbook.Path & FileName.
This returned the full local name without the https stuff and I was able to open my file ok.
I have the same problem as you.
But I have solved that problem.
The first I turn off OneDrive before I running the script.
you can add this script on the first script into your vba/module:
Call Shell("cmd.exe /S /C" & "%LOCALAPPDATA%\Microsoft\OneDrive\OneDrive.exe /shutdown")
and then, on your last script on your vba/module you can insert this for activate your OneDrive:
Call Shell("cmd.exe /S /C" & "start %LOCALAPPDATA%\Microsoft\OneDrive\OneDrive.exe /background")
I am using Windows10 on that script.
Instead of using the variable ThisWorkbook.Path use Environ("OneDrive").
Option Explicit
'
Function TransferURL(wbkURL As String) As String
' Converts the URL of a OneDrive into a path.
' Returns the path's name.
Dim oFs As Object
Dim oFl As Object
Dim oSubFl As Object
Dim pos As Integer
Dim pathPart As String
Dim oneDrive As String
Dim subFl As String
Set oFs = CreateObject("Scripting.FileSystemObject")
' Check the version of OneDrive.
If VBA.InStr(1, _
VBA.UCase(wbkURL), "MY.SHAREPOINT.COM") = 0 Then
oneDrive = "OneDriveConsumer"
Else
oneDrive = "OneDriveCommercial"
End If
Set oFl = oFs.GetFolder(Environ(oneDrive))
' Iteration over OneDrive's subfolders.
For Each oSubFl In oFl.SUBFOLDERS
subFl = "/" & VBA.Mid(oSubFl.Path, _
VBA.Len(Environ(oneDrive)) + 2) & "/"
' Check if part of the URL.
If VBA.InStr(1, _
wbkURL, subFl) > 0 Then
' Determine the path after OneDrive's folder.
pos = VBA.InStr(1, _
wbkURL, subFl)
pathPart = VBA.Mid(VBA.Replace(wbkURL, "/", _
Application.PathSeparator), pos)
End If
Next
TransferURL = Environ(oneDrive) & pathPart
End Function
Call the function by:
' Check if path specification as URL.
If VBA.Left(VBA.UCase(oWbk.Path), _
5) = "HTTPS" Then
' Call ...
pathName = TransferURL(oWbk.Path)
End If
The differentiation between OneDriveConsumer and OneDriveCommercial is derived from:
https://social.msdn.microsoft.com/Forums/en-US/1331519b-1dd1-4aa0-8f4f-0453e1647f57/how-to-get-physical-path-instead-of-url-onedrive?forum=officegeneral
Edited by MatChrupczalski Thursday, May 9, 2019 5:45 PM
Option Explicit
Private coll_Locations As Collection ' using Collection but could just as easily use Dictionary
Public Const HKEY_CURRENT_USER = &H80000001
'
Public Function getOneDrv_PathFor(ByVal sPath As String, Optional ByVal sType As String = "") As String
' convert start of passed in path from URL to Local or vice.versa, (for OneDrive Sync'd folders)
' sType : if starts L(ocal) return local path, if starts U(rl) then return URL Path, else return other mode to that passed in
Dim sPathNature As String
Dim vKey As Variant
Dim Slash As String, Slash2 As String
getOneDrv_PathFor = sPath ' return unchanged if no action required or recognised
sType = UCase(Left(sType, 1))
If sType <> "L" And sType <> "U" Then sType = ""
sPathNature = IIf(Left(sPath, 4) = "http", "U", "L")
If sType <> "" And sType = sPathNature Then Exit Function ' nothing to do
If coll_Locations Is Nothing Then get_Locations
For Each vKey In coll_Locations
If InStr(1, sPath, vKey, vbTextCompare) = 1 Then
Slash = IIf(sPathNature = "U", "/", "\")
Slash2 = IIf(Slash = "/", "\", "/")
getOneDrv_PathFor = coll_Locations(vKey) & Replace(Mid(sPath, Len(vKey) + 1), Slash, Slash2)
Exit For
End If
Next
End Function
Private Sub get_Locations()
' collect possible OneDrive: URL vs Local paths
Dim oWMI As Object
Dim sRegPath As String, arrSubKeys() As Variant, vSubKey As Variant
Dim sServiceEndPointUri As String, sUserFolder As String
Set coll_Locations = New Collection
Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
sRegPath = "Software\Microsoft\OneDrive\Accounts\"
oWMI.EnumKey HKEY_CURRENT_USER, sRegPath, arrSubKeys
For Each vSubKey In arrSubKeys
oWMI.GetStringValue HKEY_CURRENT_USER, sRegPath & vSubKey, "ServiceEndPointUri", sServiceEndPointUri
oWMI.GetStringValue HKEY_CURRENT_USER, sRegPath & vSubKey, "UserFolder", sUserFolder
If sServiceEndPointUri <> "" And sUserFolder <> "" Then
If Right(sServiceEndPointUri, 5) = "/_api" Then sServiceEndPointUri = Left(sServiceEndPointUri, Len(sServiceEndPointUri) - 4) & "Documents/"
sUserFolder = sUserFolder & "\"
coll_Locations.Add Item:=sServiceEndPointUri, Key:=sUserFolder
coll_Locations.Add Item:=sUserFolder, Key:=sServiceEndPointUri
End If
Next
'listOneDrv_Locations
Set oWMI = Nothing
End Sub
Public Sub listOneDrv_Locations()
' to list what's in the collection
Dim vKey As Variant
' Set coll_Locations = Nothing
If coll_Locations Is Nothing Then get_Locations
For Each vKey In coll_Locations
Debug.Print vKey, coll_Locations(vKey)
Next
End Sub
Then to get the LocalPath would be
strLocalPath = getOneDrv_PathFor(strCurrentPath, "Local")
I know the question was tagged with VBA, but I found this while I was trying to solve with C#. I wrote a version similar to #TWMIC answer as the following:
string LocalPath( string fullPath )
{
if ( fullPath.StartsWith( "https://", StringComparison.InvariantCultureIgnoreCase ) )
{
// So Documents/ location works below
fullPath = fullPath.Replace( "\\", "/" );
var userAccounts = Microsoft.Win32.Registry.CurrentUser
.OpenSubKey(#"Software\Microsoft\OneDrive\Accounts\");
if (userAccounts != null)
{
foreach (var accountName in userAccounts.GetSubKeyNames())
{
var account = userAccounts.OpenSubKey(accountName);
var endPoint = account.GetValue("ServiceEndPointUri") as string;
var userFolder = account.GetValue("UserFolder") as string;
if (!string.IsNullOrEmpty(endPoint) && !string.IsNullOrEmpty(userFolder))
{
if (endPoint.EndsWith("/_api"))
{
endPoint = endPoint.Substring(0, endPoint.Length - 4) + "documents/";
}
if (fullPath.StartsWith(endPoint, StringComparison.InvariantCultureIgnoreCase))
{
return Path.Combine(userFolder, fullPath.Substring(endPoint.Length));
}
}
}
}
}
return fullPath;
}
I guess there is a little bug in the code of JK2017: The"ShortName"-variable has to be rebuilt at every start of these 3 versions of OneDrive. So ist has to be inside the 'For i = 1 To 3' loop.
I have also added the choise to get only the path instead of the full filename.
Private Function Local_Workbook_Name(ByRef wb As Workbook, Optional bPathOnly As Boolean = False) As String
'returns local wb path or nothing if local path not found
Dim i As Long, x As Long
Dim OneDrivePath As String
Dim ShortName As String
Dim testWbkPath As String
Dim OneDrivePathFound As Boolean
'Check if it looks like a OneDrive location
If InStr(1, wb.FullName, "https://", vbTextCompare) > 0 Then
'loop through three OneDrive options
For i = 1 To 3
'Replace forward slashes with back slashes
ShortName = Replace(wb.FullName, "/", "\")
'Remove the first four backslashes
For x = 1 To 4
ShortName = RemoveTopFolderFromPath(ShortName)
Next
'Choose the version of Onedrive
OneDrivePath = Environ(Choose(i, "OneDrive", "OneDriveCommercial", "OneDriveConsumer"))
If Len(OneDrivePath) > 0 Then
'Loop to see if the tentative LocalWorkbookName is the name of a file that actually exists, if so return the name
Do While ShortName Like "*\*"
testWbkPath = OneDrivePath & "\" & ShortName
If Not (Dir(testWbkPath)) = vbNullString Then
OneDrivePathFound = True
Exit Do
End If
'remove top folder in path
ShortName = RemoveTopFolderFromPath(ShortName)
Loop
End If
If OneDrivePathFound Then Exit For
Next i
Else
If bPathOnly Then
Local_Workbook_Name = RemoveFileNameFromPath(wb.FullName)
Else
Local_Workbook_Name = wb.FullName
End If
End If
If OneDrivePathFound Then
If bPathOnly Then
Local_Workbook_Name = RemoveFileNameFromPath(testWbkPath)
Else
Local_Workbook_Name = testWbkPath
End If
End If
End Function
Function RemoveTopFolderFromPath(ByVal ShortName As String) As String
RemoveTopFolderFromPath = Mid(ShortName, InStr(ShortName, "\") + 1)
End Function
Function RemoveFileNameFromPath(ByVal ShortName As String) As String
RemoveFileNameFromPath = Mid(ShortName, 1, Len(ShortName) - InStr(StrReverse(ShortName), "\"))
End Function
The different number of slashes "/" could be related with different versions of OneDrive (private/professional). Compare MatChrupczalski post on the msdn website:
https://social.msdn.microsoft.com/Forums/en-US/1331519b-1dd1-4aa0-8f4f-0453e1647f57/how-to-get-physical-path-instead-of-url-onedrive?forum=officegeneral
Therefore I adapted the function to the following:
Sub TestMySolution()
MsgBox ActiveWorkbook.FullName & vbCrLf & LocalFullName(ActiveWorkbook.FullName)
End Sub
' 29.03.2020 Horoman
' main parts by Philip Swannell 14.01.2019
' combined with parts from MatChrupczalski 19.05.2019
' using environment variables of OneDrive
Private Function LocalFullName(ByVal fullPath As String) As String
Dim i As Long, j As Long
Dim oneDrivePath As String
Dim endFilePath As String
Dim iDocumentsPosition As Integer
'Check if it looks like a OneDrive location
If InStr(1, fullPath, "https://", vbTextCompare) > 0 Then
'for commercial OneDrive file path seems to be like "https://companyName-my.sharepoint.com/personal/userName_domain_com/Documents" & file.FullName)
If InStr(1, fullPath, "my.sharepoint.com") <> 0 Then
'find "/Documents" in string and replace everything before the end with OneDrive local path
iDocumentsPosition = InStr(1, fullPath, "/Documents") + Len("/Documents") 'find "/Documents" position in file URL
endFilePath = Mid(fullPath, iDocumentsPosition) 'get the ending file path without pointer in OneDrive
Else
'for personal onedrive it looks like "https://d.docs.live.net/d7bbaa#######1/" & file.FullName, _
' by replacing "https.." with OneDrive local path obtained from registry we can get local file path
'Remove the first four backslashes
endFilePath = Mid(fullPath, 9) ' removes "https://" and with it two backslashes
For i = 1 To 2
endFilePath = Mid(endFilePath, InStr(endFilePath, "/") + 1)
Next
End If
'Replace forward slashes with back slashes (URL type to Windows type)
endFilePath = Replace(endFilePath, "/", Application.PathSeparator)
'Loop to see if the tentative LocalWorkbookName is the name of a file that actually exists, if so return the name
For j = 1 To 3
oneDrivePath = Environ(Choose(j, "OneDrive", "OneDriveCommercial", "OneDriveConsumer"))
If Len(oneDrivePath) > 0 Then
LocalFullName = oneDrivePath & Application.PathSeparator & endFilePath
If Dir(LocalFullName) <> "" Then
Exit Function 'that is it - WE GOT IT
End If
End If
Next j
'Possibly raise an error here when attempt to convert to a local file name fails - e.g. for "shared with me" files
LocalFullName = ""
End If
LocalFullName = fullPath
End Function
Have fun.
Hallo this is how I do it, I found my the path through "SOFTWARE\SyncEngines\Providers\OneDrive":
private static string GetLocalPath(string url)
{
try
{
var oneDriveKey = Registry.CurrentUser.OpenSubKey(#"Software\SyncEngines\Providers\OneDrive");
if (oneDriveKey != null)
{
foreach (var subKeyName in oneDriveKey.GetSubKeyNames())
{
var subKey = oneDriveKey.OpenSubKey(subKeyName);
if (subKey != null)
{
var urlNameSpace = subKey.GetValue("UrlNamespace").ToString().Trim('/');
if (url.Contains(urlNameSpace) && subKey.GetValue("MountPoint") is string localLibraryPath)
{
string restOfDocumentPath = url.Substring(urlNameSpace.Length);
restOfDocumentPath = restOfDocumentPath.Replace('/', '\\');
return localLibraryPath + restOfDocumentPath;
}
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return string.Empty;
}
Here's a small improvement on Philip Swannell's improvement of Virtuoso's original answer for when the number of "\" to remove from the path is more than 4 / varies (depending on the file, i found i needed to remove 5 or sometimes 6 of these). The shortcomings mentioned by Philip are still there though.
Private Function Local_Workbook_Name(ByRef wb As Workbook) As String
'returns local wb path or nothing if local path not found
Dim i As Long
Dim OneDrivePath As String
Dim ShortName As String
Dim testWbkPath As String
Dim OneDrivePathFound As Boolean
'Check if it looks like a OneDrive location
If InStr(1, wb.FullName, "https://", vbTextCompare) > 0 Then
'Replace forward slashes with back slashes
ShortName = Replace(wb.FullName, "/", "\")
'Remove the first four backslashes
For i = 1 To 4
ShortName = RemoveTopFolderFromPath(ShortName)
Next
'loop through three OneDrive options
For i = 1 To 3
OneDrivePath = Environ(Choose(i, "OneDrive", "OneDriveCommercial", "OneDriveConsumer"))
If Len(OneDrivePath) > 0 Then
'Loop to see if the tentative LocalWorkbookName is the name of a file that actually exists, if so return the name
Do While ShortName Like "*\*"
testWbkPath = OneDrivePath & "\" & ShortName
If Not (Dir(testWbkPath)) = vbNullString Then
OneDrivePathFound = True
Exit Do
End If
'remove top folder in path
ShortName = RemoveTopFolderFromPath(ShortName)
Loop
End If
If OneDrivePathFound Then Exit For
Next i
Else
Local_Workbook_Name = wb.FullName
End If
If OneDrivePathFound Then Local_Workbook_Name = testWbkPath
End Function
Function RemoveTopFolderFromPath(ByVal ShortName As String) As String
RemoveTopFolderFromPath = Mid(ShortName, InStr(ShortName, "\") + 1)
End Function
Call me a hacker but the http reference on my machine is always the same so I looked at the local reference on my hard drive where the OneDrive could be found
Lets say that was C:\MyOneDrive\OneDrive then took all the other parts of the workbook path that weren't needed and added on the local part. Then switched the slash direction
folder = "C:\MyOneDrive\OneDrive" & Right(Application.ActiveWorkbook.Path, Len(Application.ActiveWorkbook.Path) - 72) & "\"
folder = Replace(folder, "/", "\")
My two lines covered all the cases on my machine!!
I solved this problem be creating a symbolic link (mklink /d). Opening files through a desktop shortcut to the link meant that WB.FullName always returned the file path using the symbolic link.
As you all seem to work on Windows-System you can also use the filescripting object:
Debug.Print
Debug.Print "ThisWorkbook.Path: "; ThisWorkbook.Path
Debug.Print "ThisWorkbook.FullName: "; ThisWorkbook.FullName
With CreateObject("Scripting.FileSystemObject")
Debug.Print "Scripting.fso: "; .GetAbsolutePathName(ThisWorkbook.Name)
End With
I solved this without VBA.
instead i used Powerquery.
first i use this formula in a cell, to get the path without filename and worksheetname.
=LEFT(CELL("filename";E8);FIND("[";CELL("filename";E8))-1)
Then i import the path as a table in powerquery: "Råfilsti"
I then have another query that has this as it's source, here i do some datawrangling on the https file path. I hardcoded my local onedrive path in the query but you can copy paste your onedrive root folder into a cell in excel and call that as a parameter to use in powerquery.
Then load that query into a table in the workbook.

IE11 Webbrowser made program quit suddently, VB6

The code is downloading Youtube videos, and the compiled exe runs well on IE9 and 10, but I upgrade to IE11 yesterday, Win7 x64.
I run the program in VB6 with F5 button, the program runs smoothly. But when I compile it to EXE, once use the webbrowser open the YouTube page, the program shutdown suddently.
What's the different between debug run the program in VB6 IDE mode and EXE mode? Can I fix the problem?
Private Sub wb2_DocumentComplete(ByVal pDisp As Object, URL As Variant)
On Error Resume Next
If wbStop = True Then Exit Sub
If (pDisp Is wb2.Object) Then
Dim xxx As Integer
Dim tmpFmt As String
If strHD = "&fmt=22" Then
tmpFmt = "18|"
ElseIf strHD = "&fmt=" Then
tmpFmt = "22|"
Else
tmpFmt = ",5|"
End If
timer39.Enabled = True
Dim bb As Boolean
bb = True
Dim sstr As String
Dim coolstr As String
Dim ccstr As String
Dim ddstr As String
urlstr = ""
Dim url18 As String
Dim url22 As String
Dim url34 As String
Dim url35 As String
Dim url37 As String
Dim url38 As String
Dim itag As String
Dim itagB As Boolean
Dim hd As Integer
Dim bbb As Boolean
Dim mmm As Long
Dim aaa As Boolean
aaa = False
Dim tType As String
tType = ""
bbb = False
wb2.Silent = True
Dim xbb As Boolean
Dim strSig As String
Dim BoolSig As Boolean
strSig = ""
'Download Video
If onoff = True Then
coolstr = ""
For k = 0 To wb2.Document.All.Length - 1
If wb2.Document.All.Item(k).tagName = "HEAD" Then
hd = k
Exit For
End If
Next k
coolstr = wb2.Document.All.Item(hd).innerhtml & " " & wb2.Document.body.innerhtml
'coolstr = wb2.Document.all.item(0).
Text2.Text = "1"
LV.ListItems(1).ListSubItems(1).Text = "Analysing HD Video URL 10%"
Debug.Print coolstr
coolstr = URLDecode(coolstr)
coolstr = Replace(coolstr, "\u0026", "&")
'coolstr = URLDecode(coolstr)
'coolstr = URLDecode(coolstr)
'coolstr = URLDecode(coolstr)
'coolstr = URLDecode(coolstr)
'Debug.Print coolstr
Open "c:\ylog.txt" For Output As #3
Print #3, coolstr
Close #3
urlstr = ""
Text2.Text = "2"
LV.ListItems(1).ListSubItems(1).Text = "Analysing HD Video URL 20%"
itagB = False
itag = "itag=37"
Url_Encode_pos = 1
For I = 1 To Len(coolstr) - 7
If UCase(Mid(coolstr, I, 7)) = UCase(itag) Then
Url_Encode_pos = I
itagB = True
Exit For
End If
Next I
Text2.Text = itag
If itagB = False Then
itag = "itag=22"
For I = 1 To Len(coolstr) - 7
If UCase(Mid(coolstr, I, 7)) = UCase(itag) Then
Url_Encode_pos = I
itagB = True
Exit For
End If
Next I
End If
Text2.Text = itag
If itagB = False Then
itag = "itag=18"
For I = 1 To Len(coolstr) - 7
If UCase(Mid(coolstr, I, 7)) = UCase(itag) Then
Url_Encode_pos = I
itagB = True
Exit For
End If
Next I
End If
Text2.Text = itag
If itagB = False Then Exit Sub
For I = 1 To Len(coolstr) - 40
If UCase(Mid(coolstr, I, 40)) = UCase("\/\/s.ytimg.com\/yts\/jsbin\/html5player") Then
For js = I + 40 To Len(coolstr) - 40
If Mid(coolstr, js, 1) <> Chr(34) Then
urlJs = urlJs & Mid(coolstr, js, 1)
Else
Exit For
End If
Next js
Exit For
End If
Next I
urlJs = "http:\/\/s.ytimg.com\/yts\/jsbin\/html5player" & urlJs
urlJs = Replace(urlJs, "\/", "/")
Debug.Print urlJs
LV.ListItems(1).ListSubItems(1).Text = "Analysing HD Video URL 30%"
xbb = False
Text2.Text = ""
lstSig.Clear
lstURL.Clear
urlstr = ""
For I = Url_Encode_pos To Len(coolstr)
If Mid(coolstr, I, 1) <> "," Then
urlstr = urlstr & Mid(coolstr, I, 1)
Else
Exit For
End If
Next I
For I = Url_Encode_pos - 1 To 1 Step -1
If Mid(coolstr, I, 1) <> "," Then
urlstr = Mid(coolstr, I, 1) & urlstr
Else
Exit For
End If
Next I
urlstr = URLDecode(urlstr)
urlstr = URLDecode(urlstr)
urlstr = URLDecode(urlstr)
urlstr = Replace(urlstr, Chr(34) & "url_encoded_fmt_stream_map" & Chr(34) & ": " & Chr(34), "")
urlstr = Replace(urlstr, Chr(38) & " ", "")
urlstr = Replace(urlstr, Chr(38) & Chr(38), Chr(38))
Debug.Print urlstr
urlstr = Trim(urlstr)
If Mid(urlstr, 1, 2) = "s=" Then urlstr = "signature=" & Right(urlstr, Len(urlstr) - 2)
ss = ""
For I = 1 To Len(urlstr)
If Mid(urlstr, I, 4) <> "url=" Then
ss = ss & Mid(urlstr, I, 1)
Else
urlstr = Right(urlstr, Len(urlstr) - I - 3)
Exit For
End If
Next I
Debug.Print ss
urlstr = urlstr & "&" & ss
urlstr = Replace(urlstr, "sig=", "signature=")
urlstr = Replace(urlstr, "&s=", "&signature=")
urlstr = Replace(urlstr, "?s=", "?signature=")
If InStr(1, urlstr, "signature=") = 0 Then Exit Sub
urlstr = Replace(urlstr, "&" & itag, "")
urlstr = Replace(urlstr, "?" & itag, "?")
'If InStr(1, urlstr, "itag") = 0 Then urlstr = urlstr & "&" & itag
urlstr = urlstr & "&" & itag
Debug.Print urlstr
Label2.Caption = "sig: " & lstSig.ListCount
Label3.Caption = "URL: " & lstURL.ListCount
Text2.Text = "&" & itag
LV.ListItems(1).ListSubItems(1).Text = "Analysing HD Video URL 40%"
'urlstr = Replace(urlstr, "\u0026", Chr(38))
Debug.Print urlstr
Open "c:\urllog.txt" For Output As #3
Print #3, urlstr
Close #3
tmpstr = ""
If urlstr = "" Then Exit Sub
Text2.Text = "&" & itag
LV.ListItems(1).ListSubItems(1).Text = "Analysing HD Video URL 70%"
timerOut.Enabled = False
TimerCheck.Enabled = False
Debug.Print urlstr
'Debug.Print urlstr
'For I = 1 To Len(sstr) - 4
' If Mid(sstr, I, 4) = "amp;" Then
' sstr = Mid(sstr, 1, I - 1) & Mid(sstr, I + 4, Len(sstr) - 1)
'
' Exit For
' End If
'Next I
'Debug.Print sstr
Dim comd As String
picPro.Visible = True
psb.Value = 0
Shape1.Visible = True
imgPB.Visible = True
'frmDrag.lblPro.Visible = True
Label1.Caption = "Downloading..."
LV.ListItems(1).ListSubItems(1).Text = "Downloading..."
Label1.Visible = True
asked = False
bb = True
sstr = ""
For I = 1 To Len(wb2.LocationURL) - 2
If Mid(wb2.LocationURL, I, 2) = "v=" Then
bb = False
For m = I + 2 To Len(wb2.LocationURL)
If Mid(wb2.LocationURL, m, 1) <> "=" And Mid(wb2.LocationURL, m, 1) <> "&" Then
sstr = sstr & Mid(wb2.LocationURL, m, 1)
Else
Exit For
End If
'Debug.Print sstr
Next m
Exit For
End If
'Debug.Print wb2.LocationURL
'Debug.Print sstr
'Debug.Print I
Next I
'Debug.Print sstr
'Debug.Print wb2.LocationURL
'If strHD = "&fmt=18" Then
' Debug.Print urlstr
' Debug.Print wbStop
' Debug.Print mmm
'End If
If bb = False Then
videoid = sstr
Else
End If
'Debug.Print videoid
'urlstr = "http://www.youtube.com/get_video?asv=&video_id=" & videoid & "&t=" & urlstr
'urlstr = "http://www.youtube.com/get_video?video_id=" & videoid & "&t=" & urlstr
'If url38 <> "" Then urlstr = Right(url38, Len(url38) - 4)
'If url34 <> "" Then urlstr = Right(url34, Len(url34) - 4)
'If url35 <> "" Then urlstr = Right(url35, Len(url35) - 4)
'If url18 <> "" Then urlstr = Right(url18, Len(url18) - 4)
'If url22 <> "" Then urlstr = Right(url22, Len(url22) - 4)
'If url37 <> "" Then urlstr = Right(url37, Len(url37) - 4)
xcv = False
Text2.Text = ""
Debug.Print urlstr
Text2.Text = Text2.Text & "urlstr: " & urlstr & vbCrLf
'urlstr = Right(url18, Len(url18) - 4)
'urlstr = Right(urlstr, Len(urlstr) - 4)
If Bitag = False Then
'urlstr = DecodeSigURL(urlstr)
wbStop = True
sigUrl = urlstr
wb2.Navigate "http://www.google.com"
wb2.Stop
Debug.Print urlJs
inetSig.URL = urlJs
inetSig.Execute , "Get"
Exit Sub
End If
Debug.Print urlstr
wbStop = True
wb2.Navigate "http://www.google.com"
wb2.Stop
'wb2.Navigate "about:blank"
'wb2.Visible = False
Label2.Caption = "document"
Inet3.URL = urlstr
Inet3.Execute , "Get"
Exit Sub
End If
End If
End Sub
At last I found the problem. It is the VPN. If I visit the site by VPN, the problem will be solved.

How to ignore error and continue with the Loop?

I get runtime errror 53: file not found for the 27th-28th item. Any idea what is wrong?
The error lies with:
"FileCopy Source:=SourcePath, Destination:=DestinationPath"
Option Base 1
Sub LoopThroughFolder()
Const FileSpec As String = "*.xls"
Dim y As Integer
Dim MyFolder As String
Dim MyFile As String
Dim iDot As Integer
Dim FileRoot As String
Dim FileExt As String
Dim SourcePath As String
Dim DestinationPath As String
Dim ArrayData() As Variant
Dim Series() As Integer
'Capture the filename information
For y = 2009 To 2030
ReDim Preserve ArrayData(12, y)
ReDim Preserve Series(12, y)
MyFolder = ActiveWorkbook.Path & "\" & y & "\"
i = 1
MyFile = Dir(MyFolder & FileSpec)
Do While Len(MyFile) > 0
iDot = InStrRev(MyFile, ".")
If iDot = 0 Then
FileRoot = MyFile
FileExt = ""
Else
FileRoot = Left(MyFile, iDot - 1)
FileExt = Mid(MyFile, iDot - 1)
End If
MyFile = Dir
ArrayData(i, y) = FileRoot
i = i + 1
Loop
Next y
'Conversion from MMMYY to numerical sequence
a = 1
BasicPath = ActiveWorkbook.Path
For y = 2009 To 2030
For i = 1 To 12
If Not IsEmpty(ArrayData(i, y)) Then
Series(i, y) = a
a = a + 1
SourcePath = BasicPath & "\" & y & "\" & ArrayData(i, y) & ".xls"
DestinationPath = BasicPath & "\output\" & "Bill_Summary_Report_" & Series(i, y) & ".xls"
FileCopy Source:=SourcePath, Destination:=DestinationPath
Else
x = 0
End If
Next i
Next y
End Sub
try
Sub LoopThroughFolder()
on error resume next
.....
I have added a function fileExist which will true if path exist. Before this line "FileCopy Source:=SourcePath, Destination:=DestinationPath" is called it always better to checkif they exist and if yes then proceed with filecopy.
Option Base 1
Sub LoopThroughFolder()
Const FileSpec As String = "*.xlsm"
Dim y As Integer
Dim MyFolder As String
Dim MyFile As String
Dim iDot As Integer
Dim FileRoot As String
Dim FileExt As String
Dim SourcePath As String
Dim DestinationPath As String
Dim ArrayData() As Variant
Dim Series() As Integer
'Capture the filename information
For y = 2009 To 2030
ReDim Preserve ArrayData(12, y)
ReDim Preserve Series(12, y)
MyFolder = ActiveWorkbook.path & "\" & y & "\"
i = 1
MyFile = Dir(MyFolder & FileSpec)
Do While Len(MyFile) > 0
iDot = InStrRev(MyFile, ".")
If iDot = 0 Then
FileRoot = MyFile
FileExt = ""
Else
FileRoot = Left(MyFile, iDot - 1)
FileExt = Mid(MyFile, iDot - 1)
End If
MyFile = Dir
ArrayData(i, y) = FileRoot
i = i + 1
Loop
Next y
'Conversion from MMMYY to numerical sequence
a = 1
BasicPath = ActiveWorkbook.path
For y = 2009 To 2030
For i = 1 To 12
If Not IsEmpty(ArrayData(i, y)) Then
Series(i, y) = a
a = a + 1
SourcePath = BasicPath & "\" & y & "\" & ArrayData(i, y) & ".xls"
DestinationPath = BasicPath & "\output\" & "Bill_Summary_Report_" & Series(i, y) & ".xls"
If fileExist(SourcePath) And fileExist(DestinationPath) Then
FileCopy Source:=SourcePath, Destination:=DestinationPath
End If
Else
x = 0
End If
Next i
Next y
End Sub
Function fileExist(path As String) As Boolean
On Error Resume Next
Dim file As String
file = Dir(path)
If file <> "" Then fileExist = True
On Error GoTo 0
End Function

error handling in VBA

i have a sub that looks like this:
Sub open_esy(filename, p As String, p1 As Integer)
Dim fileLocation As String
Dim iFileNum As Integer, findblank
Dim letter_temp0 As String, letter0 As String, letter1 As String
Dim i As Integer
Dim j As Integer
i = 16
If Dir("\\Tecan3\output\" & filename & "*esy") <> "" Then
fileLocation = "\\Tecan3\output\" & Dir("\\Tecan3\output\" & filename & "*esy")
ElseIf Dir("\\Tecan_2\output on tecan 2\" & filename & "*esy") <> "" Then
fileLocation = "\\Tecan_2\output on tecan 2\" & Dir("\\Tecan_2\output on tecan 2\" & filename & "*esy")
ElseIf Dir("\\Tecan1\tecan #1 output\" & filename & "*esy") <> "" Then
fileLocation = "\\Tecan1\tecan #1 output\" & Dir("\\Tecan1\tecan #1 output\" & filename & "*esy")
Else
MsgBox "file " & filename & "not found"
Exit Sub
End If
'open the batch file
''''old iFileNum = FreeFile()
''''old Open fileLocation For Input As #1
''''old Do While Not EOF(iFileNum)
''''old Line Input #iFileNum, stext
Dim fso As New FileSystemObject
Dim fld As Folder
Dim ts As textstream
Set ts = fso.OpenTextFile(fileLocation, ForReading)
While Not ts.AtEndOfStream
stext = ts.ReadLine
letter0 = Mid(stext, 1, 3)
If letter0 <> "A01" And letter0 <> "B01" And letter0 <> "C01" And letter0 <> "D01" And letter0 <> "E01" And letter0 <> "F01" And letter0 <> "G01" And letter0 <> "H01" And letter0 <> "I01" Then
'letter1 = Mid(stext, 7, InStr(8, stext, " ") - 7)
letter1 = Mid(stext, 7, InStr(8, stext, " ") - InStr(1, stext, " ") - 3)
Windows("Batch_XXXX revised.xlsm").Activate
Call ProcessVialPosition(letter0, i)
Cells(i, 3) = letter1
i = i + 1
End If
Wend
ts.Close
''''old Loop
''''old Close #1
Cells(2, 2) = filename
Cells(1, 2) = p
Cells(1, 4) = p1
save_template ("\\Centos5\ls-data\Interface\TF1\THC worklists\" & filename & "_THC" & ".txt")
End Sub
and for some reason it exists out of it at seemingly random points
how do i catch where it exists this sub and how do i catch the error?
You need some error handling code!
Sub open_esy(filename, p As String, p1 As Integer)
On Error Goto Err_open_esy
... your sub here ...
Exit_open_esy:
Exit Sub
Err_open_esy:
... your error handling code here ...
... you can grab line numbers too if you insert them above ...
MyUniversalErrorHandler(Err.Number, Err.Description, Erl)
'Erl is the error line number from the above sub/function
End Sub
Are you asking how to track an error that seemingly is not being raised? If so to disable all error handling in the IDE click Tools->Options->General->Break on all Errors
Failing that you will need to set a breakpoint and step throught the code.

Resources