Showing posts with label Excel. Show all posts
Showing posts with label Excel. Show all posts

Monday, 2 September 2013

VBA Modules: Outlook/Access/Excel: modProcedures v2.14

Read this for full information on these modules

One major down side of the OL_BPMProcess and modAppsFirefox routines was that they locked up the Outlook session while they're running.  This module came about because I was fed up of losing Outlook for an entire Monday while it was busy waiting to download huge CSV files, particularly as the routines would invariably get stuck somewhere random and unexpected and I'd have to set them going again on Monday morning.  Storing the URL and other variables from the email triggers allows me to move on, and pick up those routines where they left off, much later on, when it's more convenient for me.

A few tweaks to the other modules have allowed me to force all downloads to complete during weekends and evenings (my routines usually run on Sundays), but to never wait for too long during business hours (in case something got stuck and I have to fix an issue on Monday).  Manually running the procedures doesn't invoke the same protections, so I can still consciously forego Outlook during the day if I want to run these saved procedures, or I can just wait until I leave the office at night or go on lunch.

Firefox (for me) will only download a maximum of 6 simultaneous streams so there are protections built into modFirefox for that too.  Rather than start downloading the file, store the Excel or Access procedure and pick those up later when the file has downloaded, it just stores the Download procedure for triggering later on.

When the procedure is running it updates the worksheet to "In Progress" then to "OK" when completed.  (You can choose to rerun Failed procedures, but it's not really recommended, owing to the way the routines are stored and will run -- if it fails once it'll usually fail again.  I usually delete all the procedures from the file once a week just for good housekeeping.)

This module should be added to an Excel workbook with sheets named Excel, Access, and Download with the appropriate column headings in place:

Excel:
mpTimestamp mpURL mpDownloadingFile mpMoveToFile mpPathFile mpMacro rResult rTimestamp
Access:
mpTimestamp mpURL mpDownloadingFile mpMoveToFile mpPathFile mpMacro rResult rTimestamp
Download:
mpTimestamp mpURL mpApp mpMoveToFile mpPathFile mpMacro rResult rTimestamp

'modProcedures
'v2.14 2013-12-02 13:05

'===========================================================================
' HELP CONTACT
'===========================================================================
' Code is provided without warranty and can be stolen and amended as required.
'   Tom Parish
'   TJP@tomparish.me.uk
'   http://baldywrittencod.blogspot.com
'   DGF Help Contact: see BPMHelpContact module
'=========================================================================

'===========================================================================
' modProcedures
'===========================================================================
'   mp_Procedure_Store: stores list of procedures to be run later, prevents
'   indefinite hangs when downloading large files from Outlook.
'
'   mp_Run_Procedures: runs stored list of procedures sequentially (or just one)

'===========================================================================
' Additional modules required:
'===========================================================================
'   modAppsOffice
'
'  [automation requires modAppsFireFox v3.00 and OL_BPMprocess v6.00, but not used here]

'===========================================================================
' Additional References required:
'===========================================================================
'   Microsoft Excel Object Library

'===========================================================================
' External applications required:
'===========================================================================
'   Microsoft Outlook   (for automation)
'   Microsoft Access    (for Access functions)
'   Microsoft Excel     (for Excel functions)

'=========================================================================
' VERSION HISTORY
'=========================================================================
'   v2.14   mpDefaultStoreWorkbook changed to C:\SHARES\ as more robust than UNC
'   v2.13   mp_Run_Procedures: recalcs after each item is run
'   v2.12   mp_Run_Procedures: bugfix, resets mpApp on Downloads sheet
'   v2.11   mp_Run_Procedures: bugfix, doesn't run Excel/Access if nothing to do
'   v2.10   mpXLapp, prevents conflict with modAppsOffice
'   v2.09   mp_Run_Procedures: bugfix prevents WB.Open Read-Only
'   v2.08   mp_Procedure_Store: bugfix prevents WB.Open Read-Only
'   v2.07   mp_Procedure_Store: bugfix prevents looping for Download proc
'   v2.06   mp_Procedure: bugfixes
'   v2.05   mp_Procedure_Store: bugfix for Downloads store
'   v2.04   mp_Run_Procedures: will Download then run relevant App procedure
'   v2.03   mp_Procedure: bugfix if mpDownloadedFile not found
'   v2.02   mp_Procedure_Store: bugfix
'   v2.01   mp_Procedure_Store: bugfix
'   v2.00   mp_Procedure_Store now stores URL and allows queued Downloads
'   v1.01   errorhandlers, bugfix in mp_run_Procedures
'   v1.00   mp_Procedure_Store, mp_Run_Procedures, mp_Procedure

Option Explicit

Private mpXLapp As Excel.Application  '2.10 bugfix prevents issues with modAppsOffice XLapp

Private Const mpDefaultStoreWorkbook As String = "C:\SHARES\VS\ProcStore.xlsb"  'v2.13
'can specify a different WB when storing procedures

Public Enum mpApps
'v2.00 2013-08-27 13:05
'sheet number matched to each application type
    mpExcel = 1
    mpAccess = 2
    mpDownload = 3  'v2.00
End Enum

Function mp_Procedure_Store(ByVal mpURL As String, ByVal mpDownloadingFile As String, ByVal mpMoveToFile As String _
    , ByVal mpApp As mpApps, ByRef mpPathFile As String, Optional ByVal mpMacro As String _
    , Optional ByVal mpProceduresWorkbook As String = mpDefaultStoreWorkbook) As Boolean
'v2.08 2013-09-16 13:05
'runs Excel, opens mpProceduresWorkbook, records downloading filename and procedure details for later

'SYNTAX
' Store Excel procedure:
'   mp_Procedure_Store "http://url.com", "C:\Users\bpmgb\Downloads\downloadingfile.xls", "C:\Users\bpmgb\Downloads\Downloaded File.xls", mpExcel, "C:\Users\bpmgb\Downloads\", "runtest.xlsm", ""
' Store Access procedure:
'   mp_Procedure_Store "http://url.com", "C:\Users\bpmgb\Downloads\downloadingfile.xls", "C:\Users\bpmgb\Downloads\Downloaded File.csv", mpAccess, "C:\Users\bpmgb\Downloads\", "runtest.accdb", "runtest"
' Store Download procedure:  mpDownloadingFile = "" qualifies
'   mp_Procedure_Store "http://url.com", "", "C:\Users\bpmgb\Downloads\Downloaded File.xls", mpExcel, "C:\Users\bpmgb\Downloads\", "runtest.xlsm", ""

mp_Procedure_Store = True

Dim WB As Excel.Workbook, pwSheet As Excel.Worksheet, pwRow As Excel.Range
Dim rTimestamp As Excel.Range, rDownloadingFile As Excel.Range, rMoveToFile As Excel.Range _
    , rPathFile As Excel.Range, rMacro As Excel.Range, rDLfiles As Excel.Range _
    , rURL As Excel.Range, rApp As Excel.Range, a As Byte

'run Excel (or use this Excel session for testing)
Dim mpNative As Boolean
On Error Resume Next
If UCase(ThisWorkbook.FullName) <> UCase(mpProceduresWorkbook) Then  'v2.08 stops looping
    Set mpXLapp = XLlaunch(True, False)
Else
    Set mpXLapp = Application
End If
On Error GoTo 0

With mpXLapp
    .DisplayAlerts = False

'open WB (if not open), store details for processing later
    If mpXLapp <> Application Then
        Set WB = .Workbooks.Open(mpProceduresWorkbook)
    Else
    If UCase(ThisWorkbook.FullName) = UCase(mpProceduresWorkbook) _
    Then Set WB = ThisWorkbook _
    Else: Set WB = .Workbooks.Open(mpProceduresWorkbook)
    End If
  
    With WB
        If mpApp <> mpDownload And mpDownloadingFile <> "" Then  'v2.07 bugfix, doesn't loop
        'check this file is not already being downloaded on Excel and Access sheets
            For a = mpApps.mpExcel To mpApps.mpAccess
                Set pwSheet = .Sheets(a)
                With pwSheet
                    Set rDLfiles = .Columns(mpXLapp.Match("mpDownloadingFile", .Rows(1), 0))  'v2.01
                    If mpXLapp.CountIf(rDLfiles, mpDownloadingFile) > 0 Then
                    'error: this mpDownloadingFile file is already being used for another procedure?
                    'store this procedure to try downloading it again later from source URL
                        mp_Procedure_Store mpURL, "", mpMoveToFile, mpApp, mpPathFile, mpMacro, mpProceduresWorkbook
                        GoTo CleanUp
                    End If
                End With
            Next a
        End If
        'this is a new file being downloaded / to be downloaded
        If mpDownloadingFile = "" Then Set pwSheet = .Sheets(3) Else Set pwSheet = .Sheets(mpApp)  'v2.05
        With pwSheet
            Set pwRow = .Rows(mpXLapp.CountA(.Columns(1)) + 1)
            Set rTimestamp = pwRow.Columns(mpXLapp.Match("mpTimestamp", .Rows(1), 0))
                rTimestamp.Value = Now()
            Set rURL = pwRow.Columns(mpXLapp.Match("mpURL", .Rows(1), 0))
                rURL.Value = mpURL
        If mpDownloadingFile = "" Then   'v2.05 'store app, only for Download sheet
            Set rApp = pwRow.Columns(mpXLapp.Match("mpApp", .Rows(1), 0))
                rApp.Value = mpApp
        Else  'store downloading file name, not for Download sheet
            Set rDownloadingFile = pwRow.Columns(mpXLapp.Match("mpDownloadingFile", .Rows(1), 0))
                rDownloadingFile.Value = mpDownloadingFile
        End If
            Set rMoveToFile = pwRow.Columns(mpXLapp.Match("mpMoveToFile", .Rows(1), 0))
                rMoveToFile.Value = mpMoveToFile
            Set rPathFile = pwRow.Columns(mpXLapp.Match("mpPathFile", .Rows(1), 0))
                rPathFile.Value = mpPathFile
            Set rMacro = pwRow.Columns(mpXLapp.Match("mpMacro", .Rows(1), 0))
                rMacro.Value = mpMacro
        End With
CleanUp:
On Error Resume Next
        If mpNative = False Then .Close SaveChanges:=True Else .Save
        Set pwSheet = Nothing
        Set WB = Nothing
    End With
    If mpNative = False Then .Quit
    Set mpXLapp = Nothing
End With

End Function

Sub mp_Run_Procedures(ByRef mpApp As mpApps, ByVal bRerun As Boolean _
    , Optional ByVal mpProceduresWorkbook As String = mpDefaultStoreWorkbook)
'v2.13 2013-11-26 16:00
'runs all Procedures that have not yet been completed OK
'[and reruns all failed Procedures if bRerun = True]
'SYNTAX:  mp_Run_Procedures mpExcel, False

Dim WB As Excel.Workbook, pwSheet As Excel.Worksheet, pwRow As Excel.Range, pwRows() As Long
Dim rURL As Range, rApp As Range, rDownloadingFile As Excel.Range, rMoveToFile As Excel.Range _
    , rPathFile As Excel.Range, rMacro As Excel.Range _
    , rResult As Excel.Range, rTimestamp As Excel.Range, rTimestamps As Excel.Range
Dim mpDownloadingFile As String, mpMoveToFile As String _
    , mpPathFile As String, mpMacro As String, mpFileExt As String, mpFileName As String, r As Long
Dim b As Integer, c As Integer, d As Integer

Const rOK As String = "OK"
Const rFail As String = "Fail"
Const rIP As String = "In Progress"

'run Excel (use this WB preferably)  'v2.09
Dim mpNative As Boolean
If UCase(ThisWorkbook.FullName) = UCase(mpProceduresWorkbook) Then
'NB: differences in path e.g. UNC vs Local will cause to open again Read-Only
    Set mpXLapp = Application
    mpNative = True
Else
    Set mpXLapp = XLlaunch(True, False)
End If

With mpXLapp
    .DisplayAlerts = False

'open WB, store details for processing later
    If mpNative = True Then 'v2.09
        Set WB = ThisWorkbook
    Else
        Set WB = .Workbooks.Open(mpProceduresWorkbook)
    End If
  
    With WB
        Set pwSheet = .Sheets(mpApp)
        With pwSheet
            Set rTimestamps = .Cells(1, 1)
            Set rTimestamps = .Range(rTimestamps, rTimestamps.End(xlDown))
            r = rTimestamps.Cells.Count  'total rows (timestamps) in table incl. header
            Set rTimestamps = .Columns(mpXLapp.Match("rTimestamp", .Rows(1), 0))  'in/complete rows
            Set rResult = .Columns(mpXLapp.Match("rResult", .Rows(1), 0))   'result (allows rerun)
            Set rURL = .Columns(mpXLapp.Match("mpURL", .Rows(1), 0))  'v2.04 URL for download or in case need to download again
            If mpApp = mpApps.mpDownload Then 'v2.04
                Set rApp = .Columns(mpXLapp.Match("mpApp", .Rows(1), 0))
                Set rDownloadingFile = .Columns(mpXLapp.Match("mpMoveToFile", .Rows(1), 0))  'v2.12 bugfix in Downloads process
            Else
                Set rDownloadingFile = .Columns(mpXLapp.Match("mpDownloadingFile", .Rows(1), 0))
            End If
            Set rMoveToFile = .Columns(mpXLapp.Match("mpMoveToFile", .Rows(1), 0))
            Set rPathFile = .Columns(mpXLapp.Match("mpPathFile", .Rows(1), 0))
            Set rMacro = .Columns(mpXLapp.Match("mpMacro", .Rows(1), 0))
            For r = 2 To r  'all reports, ignore header row
                If rTimestamps.Cells(r) = "" Then
                    If (bRerun = True And rResult.Cells(r) <> "OK") Or rResult.Cells(r) <> "Fail" Then 'v2.04
                    'run this procedure
                        mpMoveToFile = rMoveToFile.Cells(r).Value
                        b = InStr(mpMoveToFile, ".")
                        Do While b > 0
                            c = b
                            b = InStr(c + 1, mpMoveToFile, ".")
                        Loop
                        If c > 0 Then mpFileExt = Mid(mpMoveToFile, c, 8)
                        b = InStr(mpMoveToFile, "\")
                        Do While b > 0
                            d = b
                            b = InStr(d + 1, mpMoveToFile, "\")
                        Loop
                        If d > 0 Then mpFileName = Mid(mpMoveToFile, d + 1, Len(mpMoveToFile) - d)
                        mpPathFile = rPathFile.Cells(r).Value
                        mpMacro = rMacro.Cells(r).Value
                        rResult.Cells(r).Value = "In Progress"
                        If mpApp = mpApps.mpDownload Then
                        'Downloads: mp_Procedure will use URL to download file, then run procedure in relevant App
                            mpApp = rApp.Cells(r).Value
                            mpDownloadingFile = rURL.Cells(r).Value  'starts with "http://" or "https://"
                        Else
                        'Apps: mp_Procedure will check if downloading file is finished yet, then run procedure in relevant App
                            mpDownloadingFile = rDownloadingFile.Cells(r).Value
                        End If
                        If mp_Procedure(mpApp, mpDownloadingFile, mpMoveToFile, mpPathFile, mpMacro) = True _
                        Then rResult.Cells(r).Value = "OK" _
                        Else: rResult.Cells(r).Value = "Fail"
                        rTimestamps.Cells(r).Value = Now()
                        mpApp = .Index  'v2.12 bugfix, resets mpApp
                    End If
                End If
                Application.Calculate  'v2.13, updates admin sheet counts
            Next r
        End With
        If mpNative = False Then .Close SaveChanges:=True Else .Save
        Set pwSheet = Nothing
        Set WB = Nothing
    End With
    If mpNative = False Then .Quit
    Set mpXLapp = Nothing
End With

End Sub

Private Function mp_Procedure(ByRef mpApp As mpApps, ByRef mpDownloadingFile As String, ByRef mpMoveToFile As String _
    , ByVal mpPathFile As String, ByVal mpMacro As String) As Boolean
'v2.11 2013-09-23 11:18
'only run from mp_Run_Procedures

On Error GoTo ErrorHandler  'mp_Procedure = False

Dim mp_DownloadedFile As String, mp_FileExt As String, b As Byte, c As Byte

'v2.04 if URL is specified, download file then change variables and run relevant procedure
'       NB: mpApp is never 3 at this point
If Left(mpDownloadingFile, 7) = "http://" Or Left(mpDownloadingFile, 8) = "https://" Then  'v2.05 handles https
    b = InStr(mpMoveToFile, ".")  'check end of string for file ext
    Do Until b = 0
        mp_FileExt = Mid(mpMoveToFile, b, 1 + Len(mpMoveToFile) - b)  '".csv"
        c = b
        b = InStr(b + 1, mpMoveToFile, ".") 'check end of string for file ext
    Loop
    mpDownloadingFile = modAppsFirefox.ff_GetDownload(mpDownloadingFile, mp_FileExt, mpMoveToFile, True)  'True always waits for completion
End If

'original code from modFireFox, second part is changed for this procedure
'whilst downloading, file.ext placeholder will exist with 0 bytes, file.ext.part is temporary downloading file
'on completion, file.ext wil be deleted, then file.ext.part will be renamed, so file.ext will be >0 bytes
    Dim f1 As Double
On Error Resume Next
    If Dir(mpDownloadingFile) = "" Then GoTo ErrorHandler
    f1 = FileLen(mpDownloadingFile)
    Do While f1 = 0
    'when download completes, mpDownloadingFile is deleted (i.e. error) then reappears with f1 > 0
        f1 = FileLen(mpDownloadingFile)
    Loop
On Error GoTo 0

On Error GoTo ErrorHandler  'mp_Procedure = False

'rename downloaded file if specified
'NB: renaming .csv to .xls will cause you problems!
    If mpMoveToFile <> "" Then
    'use specified filename
        mp_DownloadedFile = mpMoveToFile
    Else
    'use default download filename
        Const cDefaultFilename As String = "Downloaded Web Server Report."  ' & mp_FileExt
        Const cPrd As String = "."
        mp_FileExt = Replace(LCase(mp_FileExt), cPrd, "")
        mp_DownloadedFile = cDefaultFilename & mp_FileExt
    End If
'before moving/renaming download file, kill target file
    If mpDownloadingFile <> mp_DownloadedFile Then 'v2.06 not necessary where filenames the same
        If Dir(mpDownloadingFile) <> "" And Dir(mp_DownloadedFile) <> "" Then Kill mp_DownloadedFile  'v2.06 stops deletion where files not found
    'rename/move downloaded file
        Name mpDownloadingFile As mp_DownloadedFile
    End If
  
'validity check to confirm download completed
    If Dir(mp_DownloadedFile) = "" Then
    'downloaded file doesn't exist, something went wrong
        MsgBox "Downloaded file not found", vbCritical, "Error in mp_DownloadedFile"
        GoTo ErrorHandler
    End If

'run relevant application (only if required)  'v2.11
    If mpPathFile <> "" Then
        If mpApp = 1 Then run_Excel "", mpPathFile, mpMacro, False
        If mpApp = 2 Then run_Access "", mpPathFile, mpMacro
    End If
  
mp_Procedure = True
Exit Function
ErrorHandler:
End Function

Monday, 5 August 2013

VBA Modules: Excel: xlUtils v2.19


Read this for full information on these modules

This module is my real timesaver.  A bunch of Excel functions that I use all the time and as I can't stand repitition, I've automated and streamlined most of what I do.  Too many functions to explain here, but most are simple and self-explanatory.  Some are stolen from other resources, but most I've just thrown together myself because the vanilla "solutions" online weren't very n00b-friendly, or I just couldn't understand why someone would write half a solution and not spend an extra 10 minutes coding it properly.  I've posted a lot of these on various support functions, so if you see my name around the place with some code attached, you'll probably find my suggested code has found its way here, after a bit of tweaking.

Note that this uses Chip Pearson's modKeyState module.

'xlUtils
'v2.19 2014-01-16 13:01

'===========================================================================
' HELP CONTACT
'===========================================================================
' Code is provided without warranty and can be stolen and amended as required.
'   Tom Parish
'   TJP@tomparish.me.uk
'   http://baldywrittencod.blogspot.com
'   DGF Help Contact: see BPMHelpContact module
'=========================================================================

'===========================================================================
' xlUtils
'===========================================================================
'   various MS Excel wizardry

'===========================================================================
' Additional modules required:
'===========================================================================
'   modKeyState

'=========================================================================
' VERSION HISTORY
'=========================================================================
'v2.19 2014-01-16 13:01 - xlU_SafeToQuitExcel: option to quit/close automatically
'v2.18 2013-12-10 17:04 - xlU_SafeToQuitExcel: bugfix in numbers where PERSONAL.XLSB is not open
'   v2.17   xlU_Transfer_Ranges: bugfix
'   v2.16   xlU_Ranges_Change_Scope: bugfix when converting to Workbook
'   v2.15   added xlU_WorkbookIsReadOnly
'   v2.14   added xlU_SafeToQuitExcel
'   v2.13   added xlU_Ranges_Change_Scope
'   v2.12   added xlU_UpdateLinks
'   v2.11   added xlU_Transfer_Ranges
'   v2.10   added xlU_Convert_File
'   v2.09   added xlU_ValidationList
'   v2.08   added xlU_Export_Single_Sheets
'           added xlU_Protect_All_Sheets
'   v2.07   added xlU_BreakLinks
'   v2.06   xlU_Ranges_Set_To_Column_1_Data_Rows: bugfix (private const)
'   v2.05   added xlU_Numeric_To_Text
'           xlU_Clean_Special: removes needless _ from end of string
'           xlU_Clean_Special: bugfix, VB removes '
'           removed xlU_Check_Special (now xlSharePoint.SP_Check_Special)
'           added from xlRanges: xlU_Ranges_Add_Named_After_Column_Headers
'           added from xlRanges: xlU_Ranges_Set_To_Column_1_Data_Rows
'   v2.04   added Private constants
'   v2.03b  added xlU_Pause_for_Timeout (BETA - doesn't work?)
'   v2.02   added xlU_Find_And_Replace_Text
'   v2.01   bugfix xlU_Check_Special; annotations changed
'   v2.00   option to specify wb/ws in various macros
'           improved xlU_Clean_Special code and annotations
'           improved xlU_EmptyFolder warning msgbox
'           improved xlU_Remove_Spaces warning msgbox
'   v1.09   improved xlU_Remove_Spaces
'           Capitalised Macro Names
'           removed Public from Subs and Functions
'           annotations improved
'   v1.08   bugfix in xlU_Check_Special
'           improved xlU_SheetExists
'   v1.07   added xlU_Check_Special
'           added xlU_RemoveAllConnections
'           improved xlU_removespaces
'   v1.06   added xlU_reset_comment_sizes
'   v1.05   xlU_cut_multiple_rows_to_new_location

Option Explicit

Private Const cSpc As String = " "
Private Const cFsl As String = "/"
Private Const cBsl As String = "\"
'Private Const cHyp As String = "-"
'Private Const cAst As String = "*"
'Private Const cPrd As String = "."
'Excel <=2003
Private Const xxls As String = ".xls"   'FileFormat:=56, Office 2003 macro enabled workbook
Private Const xxlt As String = ".xlt"   'FileFormat:=??, Office 2003 macro enabled template
Private Const x2k3 As String = " (2003)" 'added to filename during zip upload, i.e. "Report Name (2003).zip"
'Excel >2003
Private Const xxlx As String = ".xlsx"  'FileFormat:=51, Office 2007/10 workbook
Private Const xxlm As String = ".xlsm"  'FileFormat:=52, Office 2007/10 macro enabled workbook
Private Const xxlb As String = ".xlsb"  'FileFormat:=??, Office 2007/10 binary workbook
Private Const xxtm As String = ".xltm"  'FileFormat:=??, Office 2007/10 macro enabled template
'Other
Private Const xcsv As String = ".csv"   'FileFormat:=6, CSV file

Enum xluScopeChangeType
    xluWorksheetToWorkbook
    xluWorkbookToWorksheet
End Enum


Sub xlU_SelectLotsOfSheets(ByVal str As String, Optional ByRef wb As Workbook)
'v2.00 2013-05-22 11:51
'selects all sheets where ws.Name contains str
'or selects all if str = ""

If wb Is Nothing Then Set wb = ActiveWorkbook

Dim blnASU As Boolean
blnASU = Application.ScreenUpdating
Application.ScreenUpdating = False

Dim blnreplace As Boolean, sh As Worksheet
blnreplace = True
For Each sh In wb.Worksheets
    If InStr(1, sh.Name, str) Or str = "" Then
        sh.Select blnreplace
        blnreplace = False
    End If
Next sh

Application.ScreenUpdating = blnASU

End Sub

Sub xlU_ShowAllObjects(Optional ByRef ws As Worksheet)
'v2.00 2013-05-22 11:51
'shows all objects on sheet and sets to Move and Size with cells
'(permits deletion of columns if hidden comments prevent this)

If ws Is Nothing Then Set ws = ActiveSheet

Dim blnASU As Boolean
blnASU = Application.ScreenUpdating
Application.ScreenUpdating = False

Dim s As Shape
On Error Resume Next
For Each s In ws.Shapes
    s.Placement = xlMoveAndSize
Next

Application.ScreenUpdating = blnASU

End Sub

Sub xlU_DeleteAllObjects(Optional ByRef ws As Worksheet)
'v2.00 2013-05-22 11:51

If ws Is Nothing Then Set ws = ActiveSheet

Dim blnASU As Boolean
blnASU = Application.ScreenUpdating
Application.ScreenUpdating = False

Dim s As Shape
On Error Resume Next
For Each s In ws.Shapes
    s.Delete
Next

Application.ScreenUpdating = blnASU

End Sub

Sub xlU_ShowAllSheets(Optional ByRef wb As Workbook)
'v2.00 2013-05-22 11:51

If wb Is Nothing Then Set wb = ActiveWorkbook

Dim blnASU As Boolean
blnASU = Application.ScreenUpdating
Application.ScreenUpdating = False

Dim s As Byte
For s = 1 To wb.Sheets.Count
    wb.Sheets(s).Visible = True
Next s

Application.ScreenUpdating = blnASU

End Sub

Function xlU_Remove_Spaces(Optional ByVal str As String) As String
'v2.00 2013-05-22 12:23
'...from END of string only
'works through Selection range if str not specified

Const pp As String = "WARNING: cannot undo this action"
Const tt As String = "xlU_Remove_Spaces"
If MsgBox(pp, vbOKCancel Or vbCritical, tt) = vbCancel Then Exit Function

Dim bln As Boolean
bln = Application.ScreenUpdating
Application.ScreenUpdating = False

If str <> vbNullString Then
    Do Until Not Right(str, 1) = " "
        str = Left(str, Len(str) - 1)
    Loop
    xlU_Remove_Spaces = str
Else
'string not specified, so work on all cells in Selection
    Dim r As Range
    For Each r In Selection.Cells
        Do Until Not Right(r.Value, 1) = " "
            r.Value = Left(r.Value, Len(r) - 1)
        Loop
    Next r
End If

Application.ScreenUpdating = bln

End Function

Sub xlU_Exit_Design_Mode()
'v1.00
'always exits
'!! running any blank macro will also do this?

With Application.CommandBars("Exit Design Mode").Controls(1)
    If .State = msoButtonDown Then .Execute
End With
   
End Sub

Sub xlU_Enter_Design_Mode()
'v1.00
'!! sometimes enters, sometimes toggles?

Application.CommandBars("Exit Design Mode").Controls(1).Execute
'Application.CommandBars.FindControl(ID:=1605).Execute
   
End Sub

Sub xlU_EmptyFolder(ByVal fdr As String, Optional ByVal AlsoRmDir As Boolean _
    , Optional ByVal DoMsgs As Boolean)
'v2.00 2013-05-22 12:22
'delete all files from specified folder [and remove folder]

Const pp As String = "WARNING: cannot undo this action"
Const tt As String = "xlU_EmptyFolder"
If MsgBox(pp, vbOKCancel Or vbCritical, tt) = vbCancel Then Exit Sub

Const cBsl As String = "\"
Const cFsl As String = "/"
Const cSdS As String = "*.*"
Const emsg As String = "xlU_EmptyFolder failed: path not valid, no slashes"

Dim fn As String, sl As String
If InStr(fdr, cBsl) > 0 Then sl = cBsl
If InStr(fdr, cFsl) > 0 Then sl = cFsl
If sl = vbNullString Then
    If DoMsgs = True Then MsgBox emsg, vbCritical, tt
    Exit Sub
End If

If Right(fdr, 1) <> sl Then fdr = fdr & sl

fn = Dir(fdr & cSdS)
Do While fn <> vbNullString
On Error Resume Next
    Kill fdr & fn
On Error GoTo 0
    fn = Dir
Loop  'all files in folder

If AlsoRmDir = True Then RmDir fdr

End Sub

Function xlU_Clean_Special(ByVal str As String, Optional ByVal CropLength As Boolean = True _
    , Optional ByVal OnlyFilename As Boolean _
    , Optional ByVal OnlyVBObjectName As Boolean) As String
'v2.05 2013-07-25 19:31
'removes invalid special characters from path/file/VBObject name string
' CropLength:=True stops message box warnings and autocrops string to 128 chars
' OnlyFilename:=True also removes slashes \ /
' OnlyVBObjectName:=True also removes slashes, spaces, hyphens, commas, periods

'constants (commented if defined below)
'Const scUS As String = "_"
Const VBNameLength = 35
Const SharePointFileNameLength = 128
'consistency checks are done below for the following constants
Const uniMin As Byte = 0    'first universally forbidden character, MUST BE ZERO
Const uniMax As Byte = 13   'last universally forbidden character, number set according to how many forbidden characters
Const slaFSL As Byte = 14   'first slash, always Fsl, AWAYS uniMax+1
Const slaBSL As Byte = 15   'last slash, always Bsl, AWAYS slaFSL+1
Const vboMin As Byte = 16   'first VBA forbidden character, AWAYS slaBSL+1
Const vboMax As Byte = 22   'last VBA forbidden character, number set according to how many forbidden characters

Dim b As Integer, c As Integer, pp As String
Const tt As String = "ERROR in xlU_Clean_Special"
Dim sc(uniMin To vboMax) As String
sc(uniMin) = "~"     'unimin referenced specifically below
sc(uniMin + 1) = Chr(34) 'Chr(34) = " (quotemark)
sc(uniMin + 2) = "#"
sc(uniMin + 3) = "%"
sc(uniMin + 4) = "&"
sc(uniMin + 5) = "*"
sc(uniMin + 6) = ":"
sc(uniMin + 7) = "<"
sc(uniMin + 8) = ">"
sc(uniMin + 9) = "?"
sc(uniMin + 10) = "{"
sc(uniMin + 11) = "|"
sc(uniMin + 12) = "}"
If uniMin + 13 <> uniMax Then   'consistency check
    pp = "uniMin + 13 <> uniMax"
    MsgBox pp, vbCritical, tt
    End
End If
sc(uniMax) = ".."
'slashes for filenames and VB Object names (NOT paths)
sc(slaFSL) = "/"
sc(slaBSL) = "\"
'hyphen & space & comma & period & brackets & apostrophe for VB Object names
sc(vboMin) = "-"
sc(vboMin + 1) = " "
sc(vboMin + 2) = ","
sc(vboMin + 3) = "."
sc(vboMin + 4) = "("
sc(vboMin + 5) = "'"
If vboMin + 6 <> vboMax Then    'consistency check
    pp = "vboMin + 6 <> vboMax"
    MsgBox pp, vbCritical, tt
    End
End If
sc(vboMax) = ")"

'remove special characters from all
For b = uniMin To uniMax
    str = Replace(str, sc(b), vbNullString)
Next b

'check filename length (length AFTER the LAST slash max 128 chars)
b = InStr(1, str, sc(slaFSL))  'look for fwd slash
If b > 0 Then
    str = Replace(str, sc(slaBSL), sc(slaFSL))  'remove all back slashes
    Do Until b = 0  'until last slash found
        c = b       'c is position of last slash
        b = b + 1                   'next position
        b = InStr(b, str, sc(slaFSL))   'next position
    Loop
Else  'no fwd slashes
    b = InStr(1, str, sc(slaBSL))  'look for back slash
    If b > 0 Then
        str = Replace(str, sc(slaFSL), sc(slaBSL))  'remove all fwd slashes
        Do Until b = 0  'until last slash found
            c = b       'c is position of last slash
            b = b + 1                   'next position
            b = InStr(b, str, sc(slaBSL))   'next position
        Loop
    End If
End If
'c is position of last slash, or 0 if no slashes
If Len(str) - c > SharePointFileNameLength Then
    If CropLength = True Then
        str = Left(str, VBNameLength)
    Else
        pp = "WARNING: filename > " & SharePointFileNameLength & " chars" & vbLf & vbLf & str
        MsgBox pp, vbCritical, tt
        End
    End If
End If

If OnlyFilename = True Or OnlyVBObjectName = True Then
'swap all slashes for spaces - NOT suitable for paths!
    For b = slaFSL To slaBSL
        c = InStr(str, sc(b))
        Do While c > 0
            str = Left(str, c - 1) & Replace(Right(str, Len(str) - c), sc(b), cSpc)
            c = InStr(str, sc(b))
        Loop
    Next b
End If

If OnlyVBObjectName = True Then
'swap hyphens & spaces & periods for underscore in VB object name
    Const scUS As String = "_"
    For b = slaFSL To vboMax
        str = Replace(str, sc(b), scUS)
    Next b
'then remove invalid characters from start of string
    Dim c1 As String
    c1 = Left(str, 1)
    Do While c1 = scUS Or c1 = sc(18) Or IsNumeric(c1)
        str = Right(str, Len(str) - 1)
        c1 = Left(str, 1)
    Loop
'remove double underscore
    Do While InStr(str, scUS & scUS) > 0
        str = Replace(str, scUS & scUS, scUS)
    Loop
    'check object name length
    If Len(str) > VBNameLength Then
        If CropLength = True Then
            str = Left(str, VBNameLength)
        Else
            pp = "WARNING: object name > 35 chars"
            MsgBox pp, vbCritical, tt
        End If
    End If
End If

'remove needless underscores from end of string
Do While Right(str, 1) = scUS
    str = Left(str, Len(str) - 1)
Loop

xlU_Clean_Special = str

End Function

Function xlU_SheetExists(ByVal wsname As String _
    , Optional ByRef wb As Workbook) As Boolean
'v1.08 2013-04-12 15:00
'returns TRUE if the sheet exists in the active (or specified) workbook

If wb Is Nothing Then Set wb = ActiveWorkbook

xlU_SheetExists = False
On Error GoTo NoSuchSheet
If Len(wb.Sheets(wsname).Name) > 0 Then
    xlU_SheetExists = True
    Exit Function
End If

NoSuchSheet:
End Function

Function xlU_FileFolderExists(ByVal strFullPath As String) As Boolean
'v1.00
'Author       : Ken Puls (www.excelguru.ca)
'URL          : http://www.excelguru.ca/node/30
'Macro Purpose: Check if a file or folder exists

    On Error GoTo Skip
    If Not Dir(strFullPath, vbDirectory) = vbNullString Then xlU_FileFolderExists = True
   
Skip:
    On Error GoTo 0

End Function

Function xlU_GetSpecialFolderNames(Optional ByVal DoDebug As Boolean = True)
'v2.00 2013-05-22 12:06
'examples of legacy code only, better to use modSpecialFolders
'DoDebug = True [default] outputs to Immediate window
'DoDebug = False pops up series of MsgBoxes

Dim objFolders As Object
Set objFolders = CreateObject("WScript.Shell").SpecialFolders

Select Case DoDebug
    Case True
        Debug.Print objFolders("desktop")
        Debug.Print objFolders("allusersdesktop")
        Debug.Print objFolders("sendto")
        Debug.Print objFolders("startmenu")
        Debug.Print objFolders("recent")
        Debug.Print objFolders("favorites")
        Debug.Print objFolders("mydocuments")
    Case False
        MsgBox objFolders("desktop")
        MsgBox objFolders("allusersdesktop")
        MsgBox objFolders("sendto")
        MsgBox objFolders("startmenu")
        MsgBox objFolders("recent")
        MsgBox objFolders("favorites")
        MsgBox objFolders("mydocuments")
End Select

End Function

Sub xlU_Reset_Comment_Sizes(Optional ByRef ws As Worksheet _
    , Optional ByVal CmtHeight As Single _
    , Optional ByVal CmtWidth As Single)
'v1.06 2013-03-19 11:18
'resets all comments on ActiveSheet
'NB: superseded by version in Delivery KPI Template v3.13b

If ws Is Nothing Then Set ws = ActiveSheet

Dim c As Comment, ch As Single, cw As Single
If CmtHeight = 0 Then ch = 60 Else ch = CmtHeight
If CmtWidth = 0 Then cw = 100 Else ch = CmtWidth

For Each c In ws.Comments
    c.Shape.Height = ch
    c.Shape.Width = cw
Next c

End Sub

Sub xlU_Cut_Multiple_Rows_to_New_Location(Optional ByVal DeleteEmptyRows As Boolean _
    , Optional ByRef ws As Worksheet)
'v2.00 2013-05-22 13:10

If ws Is Nothing Then Set ws = ActiveSheet

Dim xl As Application
Set xl = Application

Const RangeToCut As String = "temp_range"
MsgBox "Highlight all rows to copy, and then name that range 'temp_range'", vbOKCancel

Dim BlankRow As Integer
BlankRow = CInt(InputBox("Enter a blank row number (normally somewhere below the bottom of the table) where the rows will be pasted"))
If BlankRow = 0 Then BlankRow = ActiveSheet.Columns(1).Rows(ActiveSheet.Rows.Count).End(xlUp).Offset(1)

xl.Calculation = xlCalculationManual
Dim blnASU As Boolean
blnASU = xl.ScreenUpdating
xl.ScreenUpdating = False

Dim rp As Range, rC As Range
On Error GoTo Error_norange
For Each rC In ws.Range(RangeToCut).Rows
On Error GoTo 0
    Set rp = ws.Rows(BlankRow)
    If rp.Cells(1).Value <> "" Then GoTo Error_notblank
    If rC.Cells(1).Value <> "" Then
    'ignores blank rows
        rp.EntireRow.Insert
        rC.EntireRow.Copy
        rp.PasteSpecial xlPasteAll
xl.CutCopyMode = False
    End If
    'clear [and delete] rc row
    rC.EntireRow.ClearContents
    If DeleteEmptyRows = True Then rC.EntireRow.Delete
Next rC
GoTo DoTheRest

Error_norange:
    MsgBox "range name " & RangeToCut & " not found"
GoTo DoTheRest

Error_notblank:
    MsgBox "blank row not blank"
GoTo DoTheRest

DoTheRest:
xl.ScreenUpdating = blnASU
xl.Calculation = xlCalculationSemiautomatic
End Sub

Sub xlU_RemoveAllConnections(ByRef wb As Workbook)
'v1.07 2013-04-10 10:32
'removes active connections EXCEPT first connection (necessary for pivot functions)
'source: http://vbcity.com/forums/t/163459.aspx

If wb.Connections.Count > 1 Then
Dim i As Integer
For i = 2 To wb.Connections.Count
    wb.Connections.Item(1).Delete
Next i
Else
'MsgBox wb.Connections.Count
End If

End Sub

Sub xlU_RemoveUnusedConnections(ByRef wb As Workbook)
'v1.08b 2013-04-12 14:46
'removes only inactive connections
MsgBox "xlu_RemoveUnusedConnections doesn't work yet, causes big problems, don't run it"
Exit Sub  '!!

If wb.Connections.Count > 0 Then
Dim i As Integer
For i = 1 To wb.Connections.Count
    With wb.Connections.Item(i)
        Debug.Print .Name
        .Refresh
        '.Delete
    End With
Next i
Else
'MsgBox wb.Connections.Count
End If

End Sub

Sub xlU_Find_And_Replace_Text(ByVal OldString As String, ByVal NewString As String _
    , Optional ByRef RangeToFindAndReplace As Range)
'v2.02 2013-06-11 13:18
'bypasses sheet protection restrictions on normal text-only f&r

Dim s As String, spre As String, ssuf As String, c As Range, b As Byte
If RangeToFindAndReplace Is Nothing Then Set RangeToFindAndReplace = Selection
For Each c In RangeToFindAndReplace
    s = c.Value
    b = InStr(s, OldString)
    If b > 0 Then
        spre = Left(s, b - 1)
        ssuf = Mid(s, b + Len(OldString), Len(s))
        s = spre & NewString & ssuf
        c.Value = s
    End If
Next c

End Sub

Function xlU_Pause_for_Timeout(Optional ByVal TimeOutInSecs As Long) As Boolean
'v2.03b 2013-07-10 16:58
'reports False if Shift is held down
'...except it doesn't work!??

Dim t As Long

With Application

    For t = 0 To TimeOutInSecs - 1
        .StatusBar = "HOLD SHIFT TO BYPASS | updating in " & TimeOutInSecs - t & "..."
        .Wait Now() + TimeSerial(0, 0, 1)
        If modKeyState.IsShiftKeyDown = True Then
            xlU_Pause_for_Timeout = False
            .StatusBar = "UPDATE CANCELLED BY USER"
            Exit Function
        End If
    Next t

    .StatusBar = False

End With

xlU_Pause_for_Timeout = True

End Function

Sub xlU_Ranges_Set_To_Column_1_Data_Rows()
'v2.06 2013-07-26 16:40
'Adjusts length of all named ranges on query sheets to match length of data table in column 1

'range names to ignore (can be changed here, or more added in code below):
Const ex1 As String = "_FilterDatabase"
Const ex2 As String = "v_"

Application.Calculation = xlCalculationManual

Dim r As Range, nm As Name, n As String, wn As String, rw() As Long, wmax As Byte, c As Long, qt As QueryTable
wmax = ActiveWorkbook.Sheets.Count
'one rw per sheet
ReDim rw(wmax) As Long

For Each nm In ActiveWorkbook.Names
    n = nm.Name
    wn = Mid(nm, 2, Len(nm))
    wn = Left(wn, InStr(wn, "!") - 1)
    wn = Replace(wn, "'", "")
    For Each qt In ThisWorkbook.Sheets(wn).QueryTables
    'count rows in column 1 for this sheet and adjust ranges to cover
        c = Sheets(wn).Columns(1).EntireColumn.Cells(Sheets(wn).Rows.Count).End(xlUp).Row
        rw(Sheets(wn).Index) = c
       
        If InStr(nm, "REF!") > 0 Then
        'range reference error, delete
            nm.Delete
       
        ElseIf InStr(nm.Name, ex1) > 0 Then
        'query reference, ignore it
       
        ElseIf InStr(nm.Name, ex2) > 0 Then
        'validation table, ignore it
       
        ElseIf Range(nm).Rows.Count <> c Then
        'range is wrong length
            Sheets(wn).Select
        'find length of data set from Column 1
            Set r = Range(nm).EntireColumn.Rows(rw(Sheets(wn).Index))
            Set r = Range(r, r.EntireColumn.Cells(1))
        're-add range name
            nm.Delete
            ActiveWorkbook.Names.Add n, r
        End If
    Next qt
Next nm

End Sub

Sub xlU_Ranges_Add_Named_After_Column_Headers(Optional ByRef xlU_Worksheet As Worksheet _
    , Optional IncludeHeaders As Boolean = True)
'v2.05 2013-07-25 19:29
'Adds range names according to column headers and length of data table in column 1

Dim rHeader As Range, rColumn As Range, r As Long, rName As String
If xlU_Worksheet Is Nothing Then Set xlU_Worksheet = ActiveSheet
With xlU_Worksheet
'count rows in column 1
r = Application.CountA(.Columns(1).EntireColumn)
If .Columns(1).Cells(.Rows.Count).End(xlUp).Row <> r Then
    MsgBox "Column 1 must have continuous data otherwise counts fail", vbCritical, "Error in xlU_Add_Ranges_Named_After_Column_Headers"
    Exit Sub
End If
Const rOffset As Byte = 1
If IncludeHeaders = False Then r = r - rOffset 'excludes header

For Each rHeader In .Rows(1).Cells
    If rHeader.Value = "" And rHeader.Offset(0, 1).Value = "" Then Exit Sub
    If IncludeHeaders = False Then
        Set rColumn = .Range(rHeader.Cells.Offset(1), rHeader.Cells.Offset(r))
    Else
        Set rColumn = .Range(rHeader.Cells(1), rHeader.Cells(r))
    End If
    rName = xlU_Clean_Special(.Name & "_" & rHeader.Value, True, , True)
    ThisWorkbook.Names.Add rName, rColumn
Next rHeader
End With

End Sub

Sub xlU_Numeric_To_Text(ByRef xlU_Range As Range)
'v2.05 2013-07-25 19:46
'NB: Excel may convert to numeric before this point, e.g. when entering 07 will convert to 7 and thus '7

Dim s As String, r As Range
Const cApo As String = "'"
For Each r In xlU_Range.Cells
    If IsNumeric(r.Value) Then
        s = cApo & r.Text
        r.Value = s
    End If
Next r

End Sub

Function xlU_BreakLinks(ByRef wb As Workbook) As Boolean
'v2.07 2013-07-31 14:06
'reports False if no Links to break or any other error, otherwise True

xlU_BreakLinks = True
On Error GoTo ErrorHandler
Dim lnk As Variant, i As Integer
With wb
    lnk = .LinkSources(xlLinkTypeExcelLinks)
On Error GoTo NothingToBreak
    For i = 1 To UBound(lnk)
On Error GoTo ErrorHandler
        .BreakLink lnk(i), xlLinkTypeExcelLinks
    Next i
End With
NothingToBreak:
Exit Function

ErrorHandler:
xlU_BreakLinks = False
On Error GoTo 0
End Function

Sub xlU_Export_Single_Sheets(ByVal OutputPath As String _
    , ByVal OutputXLSX As Boolean, ByVal OutputXLS As Boolean, ByVal OutputCSV As Boolean _
    , Optional ByVal xlU_Password As String)
'v2.08 2013-07-31 16:16
'saves a copy of each sheet as a separate lookup file (XLSX and/or XLS and/or CSV)
'filename for each file is [ThisWorkbook.Name & " " & Sheet.Name].[xlsx|xls|csv]

Const v7 As Byte = 12
If Val(Application.Version) < v7 Then
    MsgBox "Only works in Excel 2007 or later", vbCritical, "xlU_Export_Single_Sheets"
    Exit Sub
End If

xlU_Protect_All_Sheets True, xlU_Password

Application.ScreenUpdating = False

Const cDDD As String = "..."
Const cSep As String = "  |  "
Const cSpc As String = " "
Const cBsl As String = "\"
Const cFsl As String = "/"
Const cURL As String = "http:"
Dim wb As Workbook, fn As String, shn As String, rw As Integer, f As Byte, p As Byte

'original filename
fn = Replace(Replace(ThisWorkbook.Name, ".xlsm", ""), " (master)", "")  'no extension, remove " (master)"

'fix potential path errors
'convert OutputPath to UNC (URL doesn't work) and force connection
OutputPath = Replace(OutputPath, cFsl, cBsl)
OutputPath = Replace(OutputPath, cURL, "")
If Right(OutputPath, 1) <> cBsl Then OutputPath = OutputPath & cBsl
Shell "explorer " & OutputPath, vbHide

Dim aSBorig, aSBbase As String, aSBdft As String
aSBorig = Application.StatusBar
aSBdft = fn
aSBbase = aSBdft & cSep & "Saving separate single-sheet files"
Application.StatusBar = aSBbase

Dim s As Worksheet
For Each s In ThisWorkbook.Sheets
    With s
        Application.StatusBar = aSBbase & cSep & .Name & cDDD
On Error Resume Next
        .Unprotect xlU_Password
On Error GoTo 0
        shn = .Name
        .Copy  'to new wb
        Set wb = ActiveWorkbook
On Error Resume Next
        .Protect xlU_Password
On Error GoTo 0
    End With
    With wb
        xlU_BreakLinks wb
        With .Sheets(1)
            rw = Application.CountA(.Columns(1)) + 1
            .Rows(rw & ":" & .Rows.Count).Delete
            .Protect xlU_Password
        End With
        Application.DisplayAlerts = False
        If OutputXLSX = True Then .SaveAs FileName:=OutputPath & fn & cSpc & shn & ".xlsx", FileFormat:=51 'Logis Station Groups Logis Air Station Codes.xlsx
        If OutputXLS = True Then .SaveAs FileName:=OutputPath & fn & cSpc & shn & ".xls", FileFormat:=56  'Logis Station Groups Logis Air Station Codes.xls
        If OutputCSV = True Then .SaveAs FileName:=OutputPath & fn & cSpc & shn & ".csv", FileFormat:=6   'Logis Station Groups Logis Air Station Codes.csv
        .Close
        Application.DisplayAlerts = True
    End With
    Set wb = Nothing
Next s

Application.ScreenUpdating = True
Application.StatusBar = aSBorig

xlU_Protect_All_Sheets True, xlU_Password

End Sub

Sub xlU_Protect_All_Sheets(ByVal DoProtect As Boolean, Optional xlU_Password As String)
'v2.08 2013-07-31 15:51

On Error Resume Next
Dim s As Worksheet
For Each s In ThisWorkbook.Sheets
With s
    If DoProtect = True Then .Protect xlU_Password Else .Unprotect xlU_Password
End With
Next s
On Error GoTo 0

End Sub

Function xlU_TransferValidationList(ByRef vSource As Range, ByRef vTarget As Range) As Boolean
'v2.09 2013-08-02 13:05
'returns True if transferred OK
'max 1 cell in vTarget and vSource
'could easily be adapted to copy validations across a whole row from a source row
'e.g.:
'    With Target.EntireRow
'        For i = 1 To .Cells.Count  'NB: this is very inefficient! Limit this to e.g. columns with headers
'            xlU_TransferValidationList .Cells(i), Domains.Rows(2).Cells(i)
'        Next i
'    End With

On Error GoTo ErrorHandler
Dim c As Byte
c = vSource.Cells.Count
If c > 1 Then GoTo ErrorHandler
With vTarget.Validation
    On Error Resume Next
    .Delete
    On Error GoTo ErrorHandler
    .Add Type:=vSource.Validation.Type, AlertStyle:=vSource.Validation.AlertStyle, Operator:= _
        vSource.Validation.Operator, Formula1:=vSource.Validation.Formula1
    .IgnoreBlank = vSource.Validation.IgnoreBlank
    .InCellDropdown = vSource.Validation.InCellDropdown
    .InputTitle = vSource.Validation.InputTitle
    .InputMessage = vSource.Validation.InputMessage
    .ErrorTitle = vSource.Validation.ErrorTitle
    .ErrorMessage = vSource.Validation.ErrorMessage
    .ShowInput = vSource.Validation.ShowInput
    .ShowError = vSource.Validation.ShowError
End With
    
xlU_TransferValidationList = True
    
ErrorHandler:
End Function

Function xlU_Transfer_Ranges(ByRef srcws As Worksheet, tgtws As Worksheet)
'v2.17 2013-12-03 17:33
'takes all worksheet ranges from src ws and replicates on tgt ws

On Error Resume Next
Dim rng As Range, rn As String, nm As Name
For Each nm In srcws.Names
    tgtws.Names.Add Replace(nm.Name, srcws.Name, tgtws.Name), Replace(nm.Value, srcws.Name, tgtws.Name)
Next nm

End Function

Function xlU_Convert_File(ByRef PathFile As String _
    , ByVal FileFormat As XlFileFormat, ByVal SourceIsXML As Boolean _
    , Optional ByVal DeleteOriginal As Boolean) As Boolean
'v2.10 2013-08-08 14:40
'opens any compatible format file (must be specified if source in XML format)
'saves as chosen output file format
'will output to CSV, XLS, XLSX, XLSM (add more types below if required)

On Error GoTo ErrorHandler
Dim tempPathFile As String, newPathFile As String, x1 As Byte, x2 As Byte

newPathFile = PathFile
x1 = InStr(newPathFile, ".")
x2 = x1
Do While x2 > 0
    x1 = x2
    x2 = InStr(x2 + 1, newPathFile, ".")
Loop
If x1 > 0 Then newPathFile = Left(newPathFile, x1 - 1)  'removes extension

tempPathFile = newPathFile & ".temp"
If Dir(tempPathFile) <> "" Then Kill tempPathFile  'target already exists
FileCopy PathFile, tempPathFile  'copy to target

If FileFormat = 6 Then
    newPathFile = newPathFile & xcsv
ElseIf FileFormat = 56 Then
    newPathFile = newPathFile & xxls
ElseIf FileFormat = 51 Then
    newPathFile = newPathFile & xxlx
ElseIf FileFormat = 52 Then
    newPathFile = newPathFile & xxlm
Else
    MsgBox "FileFormat " & FileFormat & " not allowed, but can add to macro code if required", vbCritical, "xlU_Convert_File"
End If

Application.DisplayAlerts = False
If SourceIsXML Then
    Workbooks.OpenXML FileName:=tempPathFile, LoadOption:=xlXmlLoadImportToList
Else
    Workbooks.Open tempPathFile
End If
ActiveWorkbook.SaveAs newPathFile, FileFormat
ActiveWorkbook.Close False
If DeleteOriginal = True Then Kill PathFile
Kill tempPathFile  'doesn't delete temp file if there's an error
xlU_Convert_File = True
Exit Function

ErrorHandler:
End Function

Function xlU_UpdateLinks(Optional wb As Workbook) As Boolean
'v2.12 2013-08-27 11:36
'source: http://www.thecodecage.com/forumz/microsoft-excel-forum/212417-excel-visual-basic-applications-update-all-external-links.html

On Error GoTo ErrorHandler
xlU_UpdateLinks = True

If wb Is Nothing Then Set wb = ActiveWorkbook
Dim Links As Variant
Dim i As Integer
With wb
    Links = .LinkSources(xlExcelLinks)
    If Not IsEmpty(Links) Then
        For i = 1 To UBound(Links)
            .UpdateLink Links(i), xlLinkTypeExcelLinks
        Next i
    End If
End With

Exit Function

ErrorHandler:
xlU_UpdateLinks = False
End Function

Function xlU_Ranges_Change_Scope(ByVal xluScopeChange As xluScopeChangeType _
    , ByVal xluWorksheet As Worksheet, ByVal xluWorkbook As Workbook _
    , Optional ByVal xluDeleteOriginal As Boolean = False) As Boolean
'v2.16 2013-12-03 09:59
'changes scope of Range Names from Worksheet to Workbook, or vice versa
'only creates Name in xluWorksheet if Name refers to xluWorksheet, but can easily run for each sheet in turn

On Error Resume Next

Dim nm As Name, newWBname As String
If xluScopeChange = xluWorksheetToWorkbook Then
    For Each nm In xluWorksheet.Names
        If InStr(nm.Value, "#REF!") = 0 Then
            newWBname = Mid(nm.Name, InStr(nm.Name, "!") + 1, Len(nm.Name)) 'v2.16
            xluWorkbook.Names.Add newWBname, nm.Value                       'v2.16
            If xluDeleteOriginal Then nm.Delete
        End If
    Next nm
   
ElseIf xluScopeChange = xluWorkbookToWorksheet Then
    For Each nm In xluWorkbook.Names
        If InStr(nm.Value, xluWorksheet.Name) > 0 And InStr(nm.Value, "#REF!") = 0 Then
            xluWorksheet.Names.Add nm.Name, nm.Value
            If xluDeleteOriginal Then nm.Delete
        End If
    Next nm

End If

End Function

Function xlU_SafeToQuitExcel(Optional ByVal QuitIfSafeOtherwiseCloseThisWorkbookWithoutSaving As Boolean) As Boolean
'v2.19 2014-01-16 13:01 - option to quit/close automatically
'v2.18 2013-12-10 17:04 - bugfix in numbers where PERSONAL.XLSB is not open
'v2.14 2013-10-30 12:41
'True if all open Workbooks (including this one but excluding PERSONAL.XLSB) are saved
'False if any workbooks need saving (not safe to quit)

    Dim wb As Workbook, t As Integer
    t = 0
    For Each wb In Workbooks
        If UCase(wb.Name) <> "PERSONAL.XLSB" And wb.Saved <> True Then t = t + 1
    Next wb
    If t <= 1 Then xlU_SafeToQuitExcel = True
   
    If QuitIfSafeOtherwiseCloseThisWorkbookWithoutSaving Then
    'NB: if necessary, workbook should be saved prior to running this function
        Application.DisplayAlerts = False
        If xlU_SafeToQuitExcel Then Application.Quit
        ThisWorkbook.Close False
    End If

End Function

Function xlU_WorkbookIsReadOnly(Optional wb As Workbook) As Boolean
'v2.15 2013-10-30 16:12
'Workbook.ReadOnly works for local files, but isn't reliable with SharePoint files

If wb Is Nothing Then
    Set wb = ActiveWorkbook
Else
    wb.Activate
End If

If InStr(1, Application.Caption, "Read-Only") > 1 _
Or InStr(1, ActiveWindow.Caption, "Read-Only") > 1 _
Then xlU_WorkbookIsReadOnly = True

End Function

VBA Modules: Excel: xlSharePoint v5.07

Read this for full information on these modules

Now this is pretty advanced stuff.  It's essential for working with Excel files on Sharepoint -- which is "quirky" at best and "really annoying" at worst.  Why MS decided to make it so awkward to interface with their own corporate site hosting package, I have no idea, but hey, that's their prerogative.

I do not recommend using this module without first getting a REALLY good understanding of how SharePoint works; the various site setup options make this a bit of an inexact science, but if your SP site setup is simple (i.e. no Versioning) and you have at least Contribute access to the relevant folders, it should work OK for you.

Note that this module is currently in beta simply because I've not tested it for SP Document Libraries that have Versioning, which requires mandatory CheckOut/CheckIn of files.  It should work OK though, so please do let me know if you get it working or if you have any issues.

Also note that the macros will try to force UNC connection via Explorer, and will use them if possible.  I make no guarantees whatsoever that this module will work with SharePoint via URLs.  Although I have in the past had some success, URLs are just not as reliable, and they behave strangely (maybe a corporate network thing).  UNC is a much better way of accessing SharePoint, so use those addresses if you can.  You might need to bludgeon your local IT department to help you with that.  Windows XP should let you use UNC via inbuilt Windows SharePoint Services (WSS), but Windows 7 seems to be much better at it.  If you have Windows Server you might need to install WSS 3.0 to access via UNC.  Even then, it might not work correctly!

I've tried to link back to the various online sources in the code, but there are too many to mention here, and I've found that in most cases the vanilla code hasn't worked as expected, and I've needed to completely rebuild it to make it work reliably.

'xlSharePoint
'v5.07 2014-01-16 14:56

'===========================================================================
' HELP CONTACT
'===========================================================================
' Code is provided without warranty and can be stolen and amended as required.
'   Tom Parish
'   TJP@tomparish.me.uk
'   http://baldywrittencod.blogspot.com
'   DGF Help Contact: see BPMHelpContact module
'=========================================================================

'*** BETA ***
'SP_Open_and_CheckIn untested with SharePoint Document Libraries that force CheckOut

'always export to \\GBMNCWSA050\BPMpublic\VBA Modules\

' ***********************************************
' *****  WARNING: v3+ incompatible with v2  *****
' ***********************************************

' *****************************************************
' *****  WARNING: SETTINGS BELOW MUST BE AMENDED  *****
' *****************************************************

'===========================================================================
' xlSharePoint
'===========================================================================
'   This module handles various SharePoint functions with either UNC
'   or URL addresses, depending on user settings.
'
'   Works for Office 2007/2010 and Windows 7
'   (2003 and XP sort of works, but is buggy)

'===========================================================================
' Additional modules required:
'===========================================================================
'   modAppsOffice v4
'   modCheckUsers v2
'   modKeyState
'   modZip v6
'
'   Code included from other modules:
'      [modSpecialFolders]
'      [xlShellAndWait]

'===========================================================================
' Additional References required:
'===========================================================================
'   Microsoft Excel Object Library (if not running from Excel)

'===========================================================================
' External applications required:
'===========================================================================
'   Microsoft Outlook   (for Outlook functions)
'   Microsoft Access    (for Access functions)
'   Microsoft Excel     (for Excel functions)

'=========================================================================
' VERSION HISTORY
'=========================================================================
'v5.07 2014-01-16 14:56 - ShellAndWait now Private, resolves conflict with xlShellAndWait module
'v5.06 2014-01-13 17:45 - ThisWorkbook code updated to allow VBA weekly update automation
'v5.05 2014-01-10 14:27 - SP_Force_Connection - bugfix for zips (remove "" from ends)
'v5.04 2014-01-09 17:11 - SP_CloseExplorerWindow - bugfix
'v5.03 2014-01-08 10:24 - SP_Force_Connection - now accepts filenames at end of path
'v5.02 2013-12-18 10:28 - SP_Force_Connection - added option for default UNCpath
'   v5.01   xlShellAndWait - late binding bugfix
'   v5.00   SP_Force_Connection/SP_CloseExplorerWindow: major improvement
'           to closing Explorer window opened during forced UNC connection
'*************************************************************************
' previous versions should be upgraded to v5.00
'*************************************************************************
'   v4.10   SP_Force_Connection: result changed to Boolean
'   v4.09   Workbook_Open routine added to ThisWorkbook module code at end
'   v4.08   bugfix in ThisWorkbook module code at end
'   v4.07b  SP_Open_and_CheckIn bugfix: Workbooks(fn).CanCheckIn
'   v4.06   spXLapp, stops reliance on XLapp in modAppsOffice
'   v4.05a  improved ThisWorkbook module code at end
'   v4.05   SP_Upload_from_2007: safer Quit routine
'   v4.04   improved ThisWorkbook module code at end
'   v4.03   SP_Upload_from_2007: added option to Break Links
'   v4.02   SP_Force_Connection: less brutal, checks Dir(UNCpath) first
'   v4.01   added SP_Force_Connection, uses ShellAndWait
'           added code from xlShellAndWait
'   v4.00   added code from modSpecialFolders
'   v3.03   SP_Upload_from_2007: shortened option variable names; added
'               validity check; all output variables optional
'   v3.02   SP_Upload_from_2007: added AlsoSaveCopyToSecondaryPath
'   v3.01   SP_Upload_from_2007: added XLSX; process improvements
'*** SP_Open_and_CheckIn BETA ***
'*** untested with folders that force CheckOut / CheckIn when publishing ***
'   v3.01b  SP_Open_and_CheckIn runs in background
'   v3.00b  added SP_Open_and_CheckIn
'   v3.00   SP_Upload_from_2007: allows Publish to SharePoint
'           retired SPCheckUpload (superseded)
'           retired fextn and textn (only used in CIRF)
'           renamed SP_pth_sl and SP_fn_val (conflict with CIRF)
'           renamed SP_Upload_XLS_and_XLSM to SP_Upload_from_2007
'*************************************************************************
' WARNING: previous versions not compatible with v3 and must be upgraded
'*************************************************************************
'   v2.07   SP_Upload_XLS_and_XLSM: temp pth changed to \\UserDocs\BPM Tools\temp\
'           SP_Upload_XLS_and_XLSM: allows Publish of XLS and XLSM
'   v2.06   SP_Upload_XLS_and_XLSM: added CSV option
'           removed SPpthS, not needed
'           annotations (additional modules)
'   v2.05   added SP_Check_Special (moved from xlUtils.xlU_Check_Special)
'   v2.04   renamed file extension constants (more consistent)
'           SP_Upload_from_2003: renamed from SP_Upload and error handlers improved
'   v2.03   added SP_Upload_XLS_and_XLSM
'   v2.02   changed Public constants & functions to Private (conflicts)
'   v2.01   removed ftyp=52 as misleading coding; updated annotations
'   v2.00   module name changed (was modSharePoint but needs Excel library)
'   v1.16   bugfix: Application.Statusbar for non-Excel applications
'   v1.15   code tidy up, annotations improved, no functional change
'   v1.14   added cPrd "."
'   v1.13   added xzip extension
'   v1.12   added xxlx, xxlm, xxlb extensions
'   v1.11   added SP_Upload
'   v1.10   added fn_SPpth
'   v1.09   added warning at top of module
'   v1.08   added SPdom, SP_OfferToCheckInAllWorkbooks
'   v1.07   renamed module and macros, iMacroName to SPMacroName
'   v1.06   SPUseCheckOut improvements, but still quite buggy
'   v1.05   bugfixes, cleanups
'   v1.04   bugfixes, cleanups
'   v1.03   bugfixes, cleanups
'   v1.02   added SPUseCheckOut

' *****************************************************
' *****  WARNING: SETTINGS BELOW MUST BE AMENDED  *****
' *****************************************************

Option Explicit
Option Compare Text  'for ShellAndWait

'*** SYNTAX:
'*** Use fn_SPpth(SomeURL) in code to convert SPpth URL into UNC and force connection (if possible for this user)
'*** --> [SomeURL] should always be a PUBLIC SharePoint location to prevent user access errors
'*** --> ideally has Read permission for "NT Authority\Authenticated Users" (or equivalent generic public access group)
'*** Specify your 'parent' top level SharePoint SITE here as URL
    Public Const SPpth As String _
        = "http://ishare.dhl.com/sites/DGFUK/"
    'Public Const SPpthUNC As String = fn_SPpth(SPpth)

'*** Specify your 'parent' top level SharePoint DOMAIN here (must be same as above)
    Public Const SPdom As String = "ishare.dhl.com"

Private spXLapp As Excel.Application    'v4.06 prevents reliance on & conflicts with modAppsOffice

Private Const cURL As String = "http:"  'If Left(SPpth, 5) = cURL Then SPsetsl = cFsl Else cBsl
'Private Const cUNC As String = "\\"    'not necessary

Private Const xzip As String = ".zip"   'zip file
'Excel <=2003
Private Const xxls As String = ".xls"   'FileFormat:=56, Office 2003 macro enabled workbook
Private Const xxlt As String = ".xlt"   'FileFormat:=??, Office 2003 macro enabled template
Private Const x2k3 As String = " (2003)" 'added to filename during zip upload, i.e. "Report Name (2003).zip"
'Excel >2003
Private Const xxlx As String = ".xlsx"  'FileFormat:=51, Office 2007/10 workbook
Private Const xxlm As String = ".xlsm"  'FileFormat:=52, Office 2007/10 macro enabled workbook
Private Const xxlb As String = ".xlsb"  'FileFormat:=??, Office 2007/10 binary workbook
Private Const xxtm As String = ".xltm"  'FileFormat:=??, Office 2007/10 macro enabled template
Private Const xcsv As String = ".csv"   'FileFormat:=6, CSV file

'Office 2010: Val(Application.Version) = 14  Office 2007: Application.Version = "12.0"  Office 2003: Application.Version = "11.0"
'Private Const v3 As Byte = 11
Private Const v7 As Byte = 12  '>=v7 proves 2007/2010
'Private Const v10 As Byte = 14

Private Const cSpc As String = " "
Private Const cHyp As String = "-"
Private Const cFsl As String = "/"
Private Const cBsl As String = "\"
Private Const cAst As String = "*"
Private Const cPrd As String = "."
Private Const wsA As String = "admin"

'==========================================================================
'==========================================================================
'==========================================================================
'==========================================================================

'
'   Code from modSpecialFolders module:
'

'http://answers.microsoft.com/en-us/office/forum/office_2010-customize/how-2-refer-to-desktop/97eba910-54c9-409f-9454-6d7c8d54d009
Private Declare Function SHGetSpecialFolderLocation _
                         Lib "shell32" (ByVal hwnd As Long, _
                                        ByVal nFolder As Long, ppidl As Long) As Long

Private Declare Function SHGetPathFromIDList _
                         Lib "shell32" Alias "SHGetPathFromIDListA" _
                             (ByVal Pidl As Long, ByVal pszPath As String) As Long

Private Declare Sub CoTaskMemFree Lib "ole32" (ByVal pvoid As Long)

'Desktop
Private Const CSIDL_INTERNET = &H1 'Internet Explorer (icon on desktop)
Private Const CSIDL_PROGRAMS = &H2 'Start Menu\Programs
Private Const CSIDL_CONTROLS = &H3 'My Computer\Control Panel
Private Const CSIDL_PRINTERS = &H4 'My Computer\Printers
Private Const CSIDL_PERSONAL = &H5 'My Documents
Private Const CSIDL_FAVORITES = &H6 '<user name>\Favorites
Private Const CSIDL_STARTUP = &H7 'Start Menu\Programs\Startup
Private Const CSIDL_RECENT = &H8 '<user name>\Recent
Private Const CSIDL_SENDTO = &H9 '<user name>\SendTo
Private Const CSIDL_BITBUCKET = &HA '<desktop>\Recycle Bin
Private Const CSIDL_STARTMENU = &HB '<user name>\Start Menu
Private Const CSIDL_DESKTOPDIRECTORY = &H10 '<user name>\Desktop
Private Const CSIDL_DRIVES = &H11 'My Computer
Private Const CSIDL_NETWORK = &H12 'Network Neighborhood
Private Const CSIDL_NETHOOD = &H13 '<user name>\nethood
Private Const CSIDL_FONTS = &H14 'Windows\fonts
Private Const CSIDL_TEMPLATES = &H15
Private Const CSIDL_COMMON_STARTMENU = &H16 'All Users\Start Menu
Private Const CSIDL_COMMON_PROGRAMS = &H17 'All Users\Programs
Private Const CSIDL_COMMON_STARTUP = &H18 'All Users\Startup
Private Const CSIDL_COMMON_DESKTOPDIRECTORY = &H19 'All Users\Desktop
Private Const CSIDL_APPDATA = &H1A '<user name>\Application Data
Private Const CSIDL_PRINTHOOD = &H1B '<user name>\PrintHood
Private Const CSIDL_LOCAL_APPDATA = &H1C '<user name>\Local Settings\Application Data (non roaming)
Private Const CSIDL_ALTSTARTUP = &H1D 'non localized startup
Private Const CSIDL_COMMON_ALTSTARTUP = &H1E 'non localized common startup
Private Const CSIDL_COMMON_FAVORITES = &H1F
Private Const CSIDL_INTERNET_CACHE = &H20
Private Const CSIDL_COOKIES = &H21
Private Const CSIDL_HISTORY = &H22
Private Const CSIDL_COMMON_APPDATA = &H23 'All Users\Application Data
Private Const CSIDL_WINDOWS = &H24 'Windows Directory
Private Const CSIDL_SYSTEM = &H25 'System Directory
Private Const CSIDL_PROGRAM_FILES = &H26 'C:\Program Files
Private Const CSIDL_MYPICTURES = &H27 'C:\Program Files\My Pictures
Private Const CSIDL_PROFILE = &H28 'USERPROFILE
Private Const CSIDL_SYSTEMX86 = &H29 'x86 system directory on RISC
Private Const CSIDL_PROGRAM_FILESX86 = &H2A 'x86 C:\Program Files on RISC
Private Const CSIDL_PROGRAM_FILES_COMMON = &H2B 'C:\Program Files\Common
Private Const CSIDL_PROGRAM_FILES_COMMONX86 = &H2C 'x86 Program Files\Common on RISC
Private Const CSIDL_COMMON_TEMPLATES = &H2D 'All Users\Templates
Private Const CSIDL_COMMON_DOCUMENTS = &H2E 'All Users\Documents
Private Const CSIDL_COMMON_ADMINTOOLS = &H2F 'All Users\Start Menu\Programs\Administrative Tools
Private Const CSIDL_ADMINTOOLS = &H30 '<user name>\Start Menu\Programs\Administrative Tools
Private Const CSIDL_CONNECTIONS = &H31 'Network and Dial-up Connections
Private Const MAX_PATH = 260
Private Const NOERROR = 0

'==========================================================================
'==========================================================================
'==========================================================================
'==========================================================================

'
'   Code from xlShellAndWait module:
'

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' modShellAndWait
' By Chip Pearson, chip@cpearson.com, www.cpearson.com
' This page on the web site: www.cpearson.com/Excel/ShellAndWait.aspx
' 9-September-2008
'
' This module contains code for the ShellAndWait function that will Shell to a process
' and wait for that process to end before returning to the caller.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Declare Function WaitForSingleObject Lib "kernel32" ( _
    ByVal hHandle As Long, _
    ByVal dwMilliSeconds As Long) As Long

Private Declare Function OpenProcess Lib "kernel32.dll" ( _
    ByVal dwDesiredAccess As Long, _
    ByVal bInheritHandle As Long, _
    ByVal dwProcessId As Long) As Long

Private Declare Function CloseHandle Lib "kernel32" ( _
    ByVal hObject As Long) As Long

Private Const SYNCHRONIZE = &H100000

Private Enum ShellAndWaitResult
    Success = 0
    Failure = 1
    TimeOut = 2
    InvalidParameter = 3
    SysWaitAbandoned = 4
    UserWaitAbandoned = 5
    UserBreak = 6
End Enum

Private Enum ActionOnBreak
    IgnoreBreak = 0
    AbandonWait = 1
    PromptUser = 2
End Enum

Private Const STATUS_ABANDONED_WAIT_0 As Long = &H80
Private Const STATUS_WAIT_0 As Long = &H0
Private Const WAIT_ABANDONED As Long = (STATUS_ABANDONED_WAIT_0 + 0)
Private Const WAIT_OBJECT_0 As Long = (STATUS_WAIT_0 + 0)
Private Const WAIT_TIMEOUT As Long = 258&
Private Const WAIT_FAILED As Long = &HFFFFFFFF
Private Const WAIT_INFINITE = -1&

'==========================================================================
'==========================================================================
'==========================================================================
'==========================================================================

Private Function SpecFolder(ByVal lngFolder As Long) As String
Dim lngPidlFound As Long
Dim lngFolderFound As Long
Dim lngPidl As Long
Dim strPath As String

strPath = Space(MAX_PATH)
lngPidlFound = SHGetSpecialFolderLocation(0, lngFolder, lngPidl)
If lngPidlFound = NOERROR Then
    lngFolderFound = SHGetPathFromIDList(lngPidl, strPath)
    If lngFolderFound Then
        SpecFolder = Left$(strPath, _
                           InStr(1, strPath, vbNullChar) - 1)
    End If
End If
CoTaskMemFree lngPidl
End Function

'==========================================================================
'==========================================================================
'==========================================================================
'==========================================================================

Private Function ShellAndWait(ShellCommand As String, _
                    TimeOutMs As Long, _
                    ShellWindowState As VbAppWinStyle, _
                    BreakKey As ActionOnBreak) As ShellAndWaitResult
'v1.01 2013-12-17 15:58 - late binding for non-Excel Application use
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' ShellAndWait
'
' This function calls Shell and passes to it the command text in ShellCommand. The function
' then waits for TimeOutMs (in milliseconds) to expire.
'
'   Parameters:
'       ShellCommand
'           is the command text to pass to the Shell function.
'
'       TimeOutMs
'           is the number of milliseconds to wait for the shell'd program to wait. If the
'           shell'd program terminates before TimeOutMs has expired, the function returns
'           ShellAndWaitResult.Success = 0. If TimeOutMs expires before the shell'd program
'           terminates, the return value is ShellAndWaitResult.TimeOut = 2.
'
'       ShellWindowState
'           is an item in VbAppWinStyle specifying the window state for the shell'd program.
'
'       BreakKey
'           is an item in ActionOnBreak indicating how to handle the application's cancel key
'           (Ctrl Break). If BreakKey is ActionOnBreak.AbandonWait and the user cancels, the
'           wait is abandoned and the result is ShellAndWaitResult.UserWaitAbandoned = 5.
'           If BreakKey is ActionOnBreak.IgnoreBreak, the cancel key is ignored. If
'           BreakKey is ActionOnBreak.PromptUser, the user is given a ?Continue? message. If the
'           user selects "do not continue", the function returns ShellAndWaitResult.UserBreak = 6.
'           If the user selects "continue", the wait is continued.
'
'   Return values:
'            ShellAndWaitResult.Success = 0
'               indicates the the process completed successfully.
'            ShellAndWaitResult.Failure = 1
'               indicates that the Wait operation failed due to a Windows error.
'            ShellAndWaitResult.TimeOut = 2
'               indicates that the TimeOutMs interval timed out the Wait.
'            ShellAndWaitResult.InvalidParameter = 3
'               indicates that an invalid value was passed to the procedure.
'            ShellAndWaitResult.SysWaitAbandoned = 4
'               indicates that the system abandoned the wait.
'            ShellAndWaitResult.UserWaitAbandoned = 5
'               indicates that the user abandoned the wait via the cancel key (Ctrl+Break).
'               This happens only if BreakKey is set to ActionOnBreak.AbandonWait.
'            ShellAndWaitResult.UserBreak = 6
'               indicates that the user broke out of the wait after being prompted with
'               a ?Continue message. This happens only if BreakKey is set to
'               ActionOnBreak.PromptUser.

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Dim TaskID As Long
Dim ProcHandle As Long
Dim WaitRes As Long
Dim Ms As Long
Dim MsgRes As VbMsgBoxResult
Dim SaveCancelKey As XlEnableCancelKey  'NB: only works in Excel
Dim ElapsedTime As Long
Dim Quit As Boolean
Const ERR_BREAK_KEY = 18
Const DEFAULT_POLL_INTERVAL = 500
Dim XLapp As Object  'v1.01
If InStr(Application.Name, "Excel") > 0 Then Set XLapp = Application Else Set XLapp = CreateObject("Excel.Application")

If Trim(ShellCommand) = vbNullString Then
    ShellAndWait = ShellAndWaitResult.InvalidParameter
    Exit Function
End If

If TimeOutMs < 0 Then
    ShellAndWait = ShellAndWaitResult.InvalidParameter
    Exit Function
ElseIf TimeOutMs = 0 Then
    Ms = WAIT_INFINITE
Else
    Ms = TimeOutMs
End If

Select Case BreakKey
    Case AbandonWait, IgnoreBreak, PromptUser
        ' valid
    Case Else
        ShellAndWait = ShellAndWaitResult.InvalidParameter
        Exit Function
End Select

Select Case ShellWindowState
    Case vbHide, vbMaximizedFocus, vbMinimizedFocus, vbMinimizedNoFocus, vbNormalFocus, vbNormalNoFocus
        ' valid
    Case Else
        ShellAndWait = ShellAndWaitResult.InvalidParameter
        Exit Function
End Select

On Error Resume Next
Err.Clear
TaskID = Shell(ShellCommand, ShellWindowState)
If (Err.Number <> 0) Or (TaskID = 0) Then
    ShellAndWait = ShellAndWaitResult.Failure
    Exit Function
End If

ProcHandle = OpenProcess(SYNCHRONIZE, False, TaskID)
If ProcHandle = 0 Then
    ShellAndWait = ShellAndWaitResult.Failure
    Exit Function
End If


On Error GoTo ErrH:
SaveCancelKey = XLapp.EnableCancelKey
XLapp.EnableCancelKey = xlErrorHandler
WaitRes = WaitForSingleObject(ProcHandle, DEFAULT_POLL_INTERVAL)
Do Until WaitRes = WAIT_OBJECT_0
    DoEvents
    Select Case WaitRes
        Case WAIT_ABANDONED
            ' Windows abandoned the wait
            ShellAndWait = ShellAndWaitResult.SysWaitAbandoned
            Exit Do
        Case WAIT_OBJECT_0
            ' Successful completion
            ShellAndWait = ShellAndWaitResult.Success
            Exit Do
        Case WAIT_FAILED
            ' attach failed
            ShellAndWait = ShellAndWaitResult.Success
            Exit Do
        Case WAIT_TIMEOUT
            ' Wait timed out. Here, this time out is on DEFAULT_POLL_INTERVAL.
            ' See if ElapsedTime is greater than the user specified wait
            ' time out. If we have exceed that, get out with a TimeOut status.
            ' Otherwise, reissue as wait and continue.
            ElapsedTime = ElapsedTime + DEFAULT_POLL_INTERVAL
            If Ms > 0 Then
                ' user specified timeout
                If ElapsedTime > Ms Then
                    ShellAndWait = ShellAndWaitResult.TimeOut
                    Exit Do
                Else
                    ' user defined timeout has not expired.
                End If
            Else
                ' infinite wait -- do nothing
            End If
            ' reissue the Wait on ProcHandle
            WaitRes = WaitForSingleObject(ProcHandle, DEFAULT_POLL_INTERVAL)
           
        Case Else
            ' unknown result, assume failure
            ShellAndWait = ShellAndWaitResult.Failure
            Quit = True
    End Select
Loop

CloseHandle ProcHandle
XLapp.EnableCancelKey = SaveCancelKey
Exit Function

ErrH:
Debug.Print "ErrH: Cancel: " & XLapp.EnableCancelKey
If Err.Number = ERR_BREAK_KEY Then
    If BreakKey = ActionOnBreak.AbandonWait Then
        CloseHandle ProcHandle
        ShellAndWait = ShellAndWaitResult.UserWaitAbandoned
        XLapp.EnableCancelKey = SaveCancelKey
        Set XLapp = Nothing
        Exit Function
    ElseIf BreakKey = ActionOnBreak.IgnoreBreak Then
        Err.Clear
        Resume
    ElseIf BreakKey = ActionOnBreak.PromptUser Then
        MsgRes = MsgBox("User Process Break." & vbCrLf & _
            "Continue to wait?", vbYesNo)
        If MsgRes = vbNo Then
            CloseHandle ProcHandle
            ShellAndWait = ShellAndWaitResult.UserBreak
            XLapp.EnableCancelKey = SaveCancelKey
        Else
            Err.Clear
            Resume Next
        End If
    Else
        'Debug.Print "Unknown value of 'BreakKey': " & CStr(BreakKey)
        CloseHandle ProcHandle
        XLapp.EnableCancelKey = SaveCancelKey
        ShellAndWait = ShellAndWaitResult.Failure
    End If
Else
    ' some other error. assume failure
    CloseHandle ProcHandle
    ShellAndWait = ShellAndWaitResult.Failure
End If

XLapp.EnableCancelKey = SaveCancelKey
Set XLapp = Nothing

End Function


'==========================================================================
'==========================================================================
'==========================================================================
'==========================================================================

Function SP_Upload_from_2007(ByRef WBToUpload As Workbook, ByVal UploadPath As String _
    , ByVal OutputFilename As String _
    , Optional ByVal spPublishXLSM As Boolean = False _
    , Optional ByVal spPublishXLSX As Boolean = False _
    , Optional ByVal spPublishXLS As Boolean = False _
    , Optional ByVal spUploadZipXLSM As Boolean = False _
    , Optional ByVal spUploadZipXLSX As Boolean = False _
    , Optional ByVal spUploadZipXLS As Boolean = False _
    , Optional ByVal spUploadWorksheetAsCSV As Worksheet = Nothing _
    , Optional ByVal AllowEvents As Boolean = False, Optional ByVal QuitWhenDone As Boolean = False _
    , Optional ByVal AlsoCopyXLSToSharedPath As String = "" _
    , Optional ByVal AlsoSaveCopyToSecondaryPath As String = "" _
    , Optional ByVal AlsoBreakLinks As Boolean = True) _
As String
'v4.06 2013-09-23 10:28
'[uploads copies of XLSM file as XLSM, XLSX, XLS, unzipped]
'[uploads as XLSM, XLSX, XLS, zipped]
'[uploads as raw CSV, unzipped]
'[also copies XLS to specified path]
'[also saves copy to specified path]
'--> returns text error message if unsuccessful (good for message boxes)

'!! may not always publish versions correctly, need to wait until Upload Center
'!! has finished uploading before CheckIn/Out works OK?


'=================================================================================
'  SEE END OF THIS MODULE FOR TYPICAL CODE FOR THISWORKBOOK MODULE
'=================================================================================
'        xlSharePoint.SP_Upload_from_2007 WBToUpload:=ThisWorkbook _
'            , UploadPath:=ipth _
'            , OutputFilename:=ofn _
'            , spPublishXLSM:=True _
'            , spPublishXLSX:=True _
'            , spPublishXLS:=True _
'            , spUploadZipXLSM:=True _
'            , spUploadZipXLSX:=True _
'            , spUploadZipXLS:=True _
'            , spUploadWorksheetAsCSV:=Nothing _
'            , AllowEvents:=False, QuitWhenDone:=False _
'            , AlsoCopyXLSToSharedPath:=xpth _
'            , AlsoSaveCopyToSecondaryPath:=spth _
'            , AlsoBreakLinks:=True
'=================================================================================

'validity check, must specify at least one output option
If spPublishXLSM = False _
    And spPublishXLSX = False _
    And spPublishXLS = False _
    And spUploadZipXLSM = False _
    And spUploadZipXLSX = False _
    And spUploadZipXLS = False _
    And spUploadWorksheetAsCSV Is Nothing _
Then GoTo ErrorHandlerNothingToDo

'convert UploadPath to UNC (URL doesn't work) and force connection
UploadPath = Replace(UploadPath, cFsl, cBsl)
UploadPath = Replace(UploadPath, cURL, "")
If Right(UploadPath, 1) <> cBsl Then UploadPath = UploadPath & cBsl
SP_Force_Connection UploadPath   'NB: this will close all open Explorer windows

If Val(Application.Version) < v7 Then
    MsgBox "Error: only for use with Excel 2007/2010.  Use SP_Upload_from_2003", vbCritical, "SP_Upload_from_2007"  'v4.05
    Exit Function
End If

Dim blnAEE As Boolean
With Application
    blnAEE = .EnableEvents
    If QuitWhenDone = True Then
        .StatusBar = "WARNING:  HOLD SHIFT to prevent Excel application quitting when done"
        .Wait Now() + TimeSerial(0, 0, 2)
        If IsShiftKeyDown = True Then
            QuitWhenDone = False
            .StatusBar = "Will NOT quit when done"
        Else
            .StatusBar = "WARNING:  Excel will Quit when done.  Hold SHIFT to attempt to cancel Quit"
        End If
    End If
    .DisplayAlerts = False
    .EnableEvents = AllowEvents
End With

Dim pth As String, fn As String, wbTemp As Workbook, p As Byte

With WBToUpload
   
    If QuitWhenDone = True Then If InStr(Application.Caption, "Read-Only") = 0 Then .Save  'this file in situ - will quit when done
   
'!! multiple "." in file name will cause unexpected behaviour here, maybe errors
    p = InStr(OutputFilename, ".")
    If p = 0 Then p = Len(OutputFilename) + 1
    fn = Left(OutputFilename, p - 1)    'fn without file extension
    If fn & xxlm = ThisWorkbook.Name Then
    'safety net - workaround is to call master file "(master)"
        MsgBox "OutputFilename (" & OutputFilename & xxlm & ") must differ from ThisWorkbook.Name (" & ThisWorkbook.Name & ")" & vbLf & vbLf & "Simple workaround is to include '(master)' in master file name", vbCritical, ""
        End
    End If

'save XLSM [and XLSX] [and XLS] files to temp folder (will be deleted later)
On Error GoTo ErrorHandlerTempFailed
'Shift+F8 over this line when reviewing
    pth = SpecFolder(CSIDL_PERSONAL) & "\BPM Tools\"  'v3.00
    If Dir(pth, vbDirectory) <> "." Then MkDir pth
    pth = pth & "temp\"
    If Dir(pth, vbDirectory) <> "." Then MkDir pth
Application.DisplayAlerts = False
    .SaveCopyAs pth & fn & xxlm  'save XLSM copy to temp folder
    If spPublishXLS = True Or spPublishXLSX = True Or spUploadZipXLS = True Or spUploadZipXLSX = True _
    Or AlsoBreakLinks = True Then
        Set spXLapp = modAppsOffice.XLlaunch
spXLapp.EnableEvents = AllowEvents
spXLapp.DisplayAlerts = False
On Error Resume Next
        Set wbTemp = spXLapp.Workbooks.Open(pth & fn & xxlm)
        If wbTemp Is Nothing Then
            spXLapp.Workbooks.Open pth & fn & xxlm
On Error GoTo ErrorHandlerTempFailed
            Set wbTemp = spXLapp.Workbooks(fn & xxlm)
        End If
        With wbTemp
        'Break Links in XLSM first (converts formulas to values)
            If AlsoBreakLinks = True Then
            'code copied from xlUtils (v2.07)
                Dim lnk As Variant, i As Integer
                lnk = .LinkSources(xlLinkTypeExcelLinks)
On Error GoTo NothingToBreak
                For i = 1 To UBound(lnk)
                    .BreakLink lnk(i), xlLinkTypeExcelLinks
                Next i
spXLapp.EnableEvents = False  'prevents any macros running during this step
                .Save
spXLapp.EnableEvents = AllowEvents
NothingToBreak:
On Error GoTo ErrorHandlerTempFailed
            End If
        'save XLSX copy to temp (keeps macros temporarily until closed)
            If spPublishXLSX = True Or spUploadZipXLSX = True Then .SaveAs pth & fn & xxlx, FileFormat:=51
        'save XLS copy to temp (with macros)
            If spPublishXLS = True Or spUploadZipXLS = True Then .SaveAs pth & fn & x2k3 & xxls, FileFormat:=56
            .Close False
        End With
        Set wbTemp = Nothing
        spXLapp.Quit
        Set spXLapp = Nothing
    End If
On Error GoTo 0


'copy XLS to shared path  'v3.01
On Error GoTo ErrorHandlerXLSSharedPathCopyFailed
If AlsoCopyXLSToSharedPath <> "" Then
    If Right(AlsoCopyXLSToSharedPath, 1) <> cBsl Then AlsoCopyXLSToSharedPath = AlsoCopyXLSToSharedPath & cBsl
    FileCopy pth & fn & x2k3 & xxls, AlsoCopyXLSToSharedPath & fn & xxls  'NB: copied without " (2003)" in filename
End If
On Error GoTo 0


'upload (and CheckIn) XLSM to SharePoint
On Error GoTo ErrorHandlerXLSMFailed
    If spPublishXLSM = True Then
        FileCopy pth & fn & xxlm, UploadPath & fn & xxlm
        SP_Open_and_CheckIn UploadPath & fn & xxlm
    End If
On Error GoTo 0
   
'upload (and CheckIn) XLSX to SharePoint
On Error GoTo ErrorHandlerXLSXFailed
    If spPublishXLSX = True Then
        FileCopy pth & fn & xxlx, UploadPath & fn & xxlx
        SP_Open_and_CheckIn UploadPath & fn & xxlx
    End If
On Error GoTo 0
   
'upload (and CheckIn) XLS to SharePoint
On Error GoTo ErrorHandlerXLSFailed
    If spPublishXLS = True Then
        FileCopy pth & fn & x2k3 & xxls, UploadPath & fn & x2k3 & xxls
        SP_Open_and_CheckIn UploadPath & fn & x2k3 & xxls
    End If
On Error GoTo 0
   

'save zip[s] to SharePoint
    If spUploadZipXLSM = True Then If Zip7Sub(pth & fn & xxlm, UploadPath & fn & xzip, True, True) <> 0 Then GoTo ErrorHandlerZipXLSMFailed
    If spUploadZipXLSX = True Then If Zip7Sub(pth & fn & xxlx, UploadPath & fn & xzip, True, True) <> 0 Then GoTo ErrorHandlerZipXLSXFailed
    If spUploadZipXLS = True Then If Zip7Sub(pth & fn & x2k3 & xxls, UploadPath & fn & x2k3 & xzip, True, True) <> 0 Then GoTo ErrorHandlerZipXLSFailed
   

'remove temp files & folder
'NB: only removes temp folder, leaves \\UserDocs\BPM Tools\ folder in place
    If Dir(pth & fn & xxlm) <> "" Then Kill pth & fn & xxlm
    If Dir(pth & fn & xxlx) <> "" Then Kill pth & fn & xxlx
    If Dir(pth & fn & x2k3 & xxls) <> "" Then Kill pth & fn & x2k3 & xxls
    If Dir(pth) = "" Then RmDir pth  'only if pth empty
   
   
'upload (and CheckIn) CSV to SharePoint (works for single sheet only)
On Error GoTo ErrorHandlerCSVFailed
    If Not spUploadWorksheetAsCSV Is Nothing Then
Application.DisplayAlerts = False
        spUploadWorksheetAsCSV.Copy
        Set wbTemp = ActiveWorkbook
        With wbTemp
            .SaveAs UploadPath & fn & xcsv, FileFormat:=6
            .Close False
        End With
        Set wbTemp = Nothing
        SP_Open_and_CheckIn UploadPath & fn & xcsv
    End If
On Error GoTo 0


'upload (and CheckIn?) copy to secondary location
On Error GoTo ErrorHandlerSaveCopyAsFailed
    If AlsoSaveCopyToSecondaryPath <> "" Then
        .SaveCopyAs AlsoSaveCopyToSecondaryPath & fn & xxlm
    'force check in if location is SharePoint
        If InStr(AlsoSaveCopyToSecondaryPath, SPdom) > 0 Then SP_Open_and_CheckIn AlsoSaveCopyToSecondaryPath & fn & xxlm
    End If
On Error GoTo 0
   


End With  'WBToUpload


'clear error message if you got this far - no news is good news
SP_Upload_from_2007 = ""

With Application
    .DisplayAlerts = True
    .StatusBar = False
If QuitWhenDone = True And IsShiftKeyDown = False Then
    Dim wb As Workbook, w As Byte  'v4.05
    For Each wb In Workbooks
        If UCase(wb.Name) <> "PERSONAL.XLSB" Then w = w + 1
    Next wb
    If w = 1 Then Application.Quit Else ThisWorkbook.Close False
End If
    .EnableEvents = blnAEE
End With
Exit Function


ErrorHandler:
    SP_Upload_from_2007 = "SP_Upload_from_2007: failed"  '!! very blunt
    GoTo CleanUp

ErrorHandlerNothingToDo:
    SP_Upload_from_2007 = "SP_Upload_from_2007: nothing to do!  Must specify at least one output option"
    GoTo CleanUp

ErrorHandlerTempFailed:
    SP_Upload_from_2007 = "SP_Upload_from_2007: couldn't save to temp folder"
    GoTo CleanUp

ErrorHandlerSaveCopyAsFailed:
    SP_Upload_from_2007 = "SP_Upload_from_2007: SaveCopyAs failed"
    GoTo CleanUp

ErrorHandlerCSVFailed:
    SP_Upload_from_2007 = "SP_Upload_from_2007: CSV failed"
    GoTo CleanUp

ErrorHandlerXLSFailed:
    SP_Upload_from_2007 = "SP_Upload_from_2007: XLS failed"
    GoTo CleanUp

ErrorHandlerXLSXFailed:
    SP_Upload_from_2007 = "SP_Upload_from_2007: XLSX failed"
    GoTo CleanUp

ErrorHandlerXLSMFailed:
    SP_Upload_from_2007 = "SP_Upload_from_2007: XLSM failed"
    GoTo CleanUp

ErrorHandlerZipXLSFailed:
    SP_Upload_from_2007 = "SP_Upload_from_2007: zipped XLS failed"
    GoTo CleanUp

ErrorHandlerZipXLSXFailed:
    SP_Upload_from_2007 = "SP_Upload_from_2007: zipped XLSX failed"
    GoTo CleanUp

ErrorHandlerZipXLSMFailed:
    SP_Upload_from_2007 = "SP_Upload_from_2007: zipped XLSM failed"
    GoTo CleanUp

ErrorHandlerXLSSharedPathCopyFailed:
    SP_Upload_from_2007 = "SP_Upload_from_2007: XLS copy to shared drive path failed"
    GoTo CleanUp

CleanUp:
If Not wbTemp Is Nothing Then wbTemp.Close False
If Not spXLapp Is Nothing Then spXLapp.Quit
End Function

Function SP_Upload_from_2003(ByVal spulPath As String, ByVal spulFileName As String _
    , Optional spulSheetName As String) As Boolean
'v2.04 2013-07-25 08:59
'WARNING: if SharePoint is set to require CheckOut, this will NOT publish the file
'only works with UNC (can be converted to use URL)
'spulSheetName<>"" will copy that sheet to new WB and upload
'spulSheetName="" will hide "admin" sheet and upload entire workbook

With Application
    If Val(.Version) >= v7 Then
        MsgBox "Error: only for use with Excel 2003.  Use SP_Upload_XLS_and_XLSM", vbCritical, "SP_Upload_XLS_and_XLSM"
        Exit Function
    End If
    .DisplayAlerts = False
End With

Dim wb As Workbook, i As Integer, ipthfn As String, lnk As Variant
ipthfn = cBsl & Replace(spulPath & cBsl, cBsl & cBsl, cBsl) & spulFileName

With ThisWorkbook
On Error Resume Next
    .Sheets(spulSheetName).Copy  'creates new WB only if spulSheetName specified
On Error GoTo 0
End With

Set wb = ActiveWorkbook  'either ThisWorkbook or single-sheet file
With wb
    If wb.Name = ThisWorkbook.Name And spulSheetName <> "" Then
        MsgBox spulSheetName & " did not copy to new WB.", vbCritical, "error in SP_UPload"
        End
    ElseIf spulSheetName <> "" Then
    'break links in new one-sheet file
On Error Resume Next
        Set lnk = wb.LinkSources(xlLinkTypeExcelLinks)
        For i = 1 To UBound(lnk)
            wb.BreakLink lnk(i), xlLinkTypeExcelLinks
        Next i
    'save WB to SharePoint
On Error GoTo SaveFailed
        .SaveCopyAs ipthfn  'will overwrite existing
On Error GoTo 0
    'now close extra WB, not required
        .Close False
    'save copy of Master to SharePoint (i.e. alongside single sheet report)
        With ThisWorkbook
On Error Resume Next
            .Sheets(wsA).Visible = False
On Error GoTo SaveFailed
        'overwrite existing, this works for xxls and xxlm (xxlm = xxls & "m")
        '!! doesn't work for .xlb
            .SaveCopyAs Replace(ipthfn, xxls, " (master)" & xxls)
On Error Resume Next
            .Sheets(wsA).Visible = True
On Error GoTo 0
        End With
    Else
    'save copy of Master to SharePoint as WB
        With ThisWorkbook  'WB is ThisWorkbook
On Error Resume Next
            .Sheets(wsA).Visible = False
On Error GoTo SaveFailed
            .SaveCopyAs ipthfn   'will overwrite existing
On Error Resume Next
            .Sheets(wsA).Visible = True
On Error GoTo 0
        End With
    End If
End With

SP_Upload_from_2003 = True
Exit Function

SaveFailed:
SP_Upload_from_2003 = False
If InStr(Application.Name, "Excel") > 0 Then Application.StatusBar = "ERROR in SP_Upload_from_2003: save failed"
End  'leaves this workbook AND unsaved report open in Excel (theoretically)
End Function

Function SP_Open_and_CheckIn(ByVal PathAndFileName As String, Optional ByVal AllowEvents As Boolean = False) As Byte
'**** BETA VERSION ****
'v4.07b 2013-09-26 16:00
'forces CheckIn
'!! untested with folders that force CheckOut

'ErrorCodes as Byte:
'  0 = no need to CheckIn / successfully CheckedIn
'  1 = couldn't Open
'  2 = couldn't CheckOut
'  3 = couldn't CheckIn
'  8 = pathfile doesn't exist / pathfile access error

PathAndFileName = Replace(PathAndFileName, "/", "\")
PathAndFileName = Replace(PathAndFileName, "http:", "")

'try to CheckOut file and then CheckInWithVersion
Application.DisplayAlerts = False
    Set spXLapp = modAppsOffice.XLlaunch '(True, False)
    With spXLapp
        '.DisplayAlerts = False  'bypasses any warnings, not advisable for testing
        .EnableEvents = AllowEvents
       
    If Dir(PathAndFileName) = "" Then
        SP_Open_and_CheckIn = 8  'file not found
    Else
   
        If .Workbooks.CanCheckOut(PathAndFileName) = True Then GoTo FinishOff  'SP_Open_and_CheckIn = 0
       
    '[CheckOut then] CheckInWithVersion
    '!! untested, try to just CheckInWithVersion first
        If .Workbooks(PathAndFileName).CanCheckIn = True Then .Workbooks(PathAndFileName).CheckInWithVersion False, "Published " & Now(), True
   
    On Error GoTo OpenFailed
        .Workbooks.Open FileName:=PathAndFileName
    On Error GoTo 0
   
    On Error GoTo CheckOutFailed
        .Workbooks.CheckOut PathAndFileName
    On Error GoTo 0
   
    On Error GoTo CheckInFailed
        .Workbooks(PathAndFileName).CheckInWithVersion False, "Published " & Now(), True
    On Error GoTo 0
   
    End If
    End With
'implied success!
GoTo FinishOff

OpenFailed:
    SP_Open_and_CheckIn = 1
    GoTo FinishOff

CheckOutFailed:
    SP_Open_and_CheckIn = 2
    GoTo FinishOff

CheckInFailed:
    SP_Open_and_CheckIn = 3
    GoTo FinishOff

FinishOff:
    spXLapp.Quit
    Set spXLapp = Nothing

End Function
Function SPsetsl() As String
'v1.05 2013-03-04 16:04

If Left(fn_SPpth, 5) = cURL Then SPsetsl = cFsl Else SPsetsl = cBsl

End Function

Function SP_Check_Special(ByVal str As String) As Boolean
'v2.05 2013-07-25 18:57
'checks string for invalid special characters (True=valid or False=invalid)
'~ " # % & * < > ? { | } .. \ or : and /

SP_Check_Special = True  'unless it fails

Const p As String = "."
If Right(str, 1) = p Then SP_Check_Special = False

Const bmax As Byte = 14
Dim sc() As String
ReDim sc(bmax) As String
sc(0) = "~"
sc(1) = Chr(34)  ' Chr(34) = "
sc(2) = "#"
sc(3) = "%"
sc(4) = "&"
sc(5) = "*"
sc(6) = ".."
sc(7) = "<"
sc(8) = ">"
sc(9) = "?"
sc(10) = "{"
sc(11) = "|"
sc(12) = "}"
If SPsetsl = cFsl Then
    sc(bmax - 1) = ""
    sc(bmax) = cBsl
Else:
    sc(bmax - 1) = ":"
    sc(bmax) = cFsl
End If
'sc(15) = cbsl  'disabled to allow checking of pth names
Dim b As Byte
For b = 0 To bmax
    If sc(b) <> "" And InStr(str, sc(b)) > 0 Then SP_Check_Special = False
Next b

End Function

Function fn_SPpth(Optional ByVal TestURLPath As String) As String
'v1.15 2013-05-22 11:18
'determines SPpth (set top of module) as either UNC or URL for this user
'URL uploads won't work if UNC is working
'use this function instead of SPpth in code
'test with Left,1 = cBsl
'also forces SharePoint connection (refresh user and password)

If TestURLPath = "" Then TestURLPath = SPpth

'Const cPrd As String = "."
Dim v As String

'test for UNC first, more reliable
    fn_SPpth = Replace(Replace(TestURLPath, cURL, ""), cFsl, cBsl)
On Error Resume Next
    v = Dir(fn_SPpth, vbDirectory)
On Error GoTo 0
    If v = cPrd Then
    'UNC works, use UNC for SPpth (already set)
        'fn_SPpth = Replace(Replace(SPpth, cURL, ""), cFsl, cBsl)
    Else
    'UNC doesn't work, must use URL for SPpth (change it back)
        fn_SPpth = TestURLPath
    End If

End Function

Function SPDelFile(ByVal pthfn_to_delete As String) As Boolean
'v2.00 2013-07-10 13:01
'deletes file
'UNC reports True if deleted, False if not
'URL always reports True even if delete failed

Dim isUNC As Boolean

Bludgeon:
'try to force deletion by all available methods
On Error Resume Next
Select Case xlSharePoint.SPsetsl  'v2.00
    Case cBsl  'UNC - just delete it
        isUNC = True
        Kill Replace(Replace(pthfn_to_delete, cURL, ""), cFsl, cBsl)
    Case cFsl  'URL - force deletion via URL and UNC
        isUNC = False
        Kill pthfn_to_delete
        Kill Replace(Replace(pthfn_to_delete, cURL, ""), cFsl, cBsl)
End Select
On Error GoTo 0

'check UNC address for deletion
If isUNC = True Then
'UNC - check file exists
    If Dir(pthfn_to_delete) = "" Then
    'success!
        SPDelFile = True
    Else
    'failure!
        SPDelFile = False
    End If
Else
'if URL - always assume successful delete and pray it worked
    SPDelFile = True
End If

End Function

Function SP_pth_sl(ByVal PathToAddSlash As String, Optional DoURL As Boolean) As String
'v3.00b 2013-07-29 22:40
'adds a slash to end of path as required
'DoURL tries to force URL but will be overridden if URL is 'detected'

'commented v1.15, consts are set public
'Const cFsl As String = "/"  'URL
'Const cBsl As String = "\"  'UNC
'Const cURL As String = "http://"

'!! can only specify URL if DoURL=True
If DoURL = True And Left(PathToAddSlash, Len(cURL)) <> cURL Then _
    MsgBox "a URL must be specified if DoURL=True", vbCritical, "error in zSP_pth_sl"

'if Path includes http then force DoURL (NB: fn_SPpth will determine UNC or URL)
If DoURL = False And Left(PathToAddSlash, Len(cURL)) = cURL Then _
    DoURL = True

If DoURL = True And Right(PathToAddSlash, 1) <> cFsl Then
    SP_pth_sl = PathToAddSlash & cFsl
    Exit Function
ElseIf Right(PathToAddSlash, 1) <> cBsl Then
    SP_pth_sl = PathToAddSlash & cBsl
    Exit Function
Else
    SP_pth_sl = PathToAddSlash
End If

End Function

Function SP_fn_val(sFileName As String, Optional sReplaceInvalidWith As String = "_") As String
'v3.00b 2013-07-29 22:40
'Purpose   :    Removes invalid characters from a filename
'Inputs    :    sFileName               The file name to clean the invalid characters from.
'               [sReplaceInvalidWith]   The text to replace any invalid characters with.
'Outputs   :    Returns a valid filename.
'Author    :    Andrew Baker
'Date      :    25/03/2001
'Notes     :    http://www.vbusers.com/code/codeget.asp?ThreadID=578&PostID=1

Const csInvalidChars As String = ":\/?*<>|"""
Dim lThisChar As Long
SP_fn_val = sFileName
'Loop over each invalid character, removing any instances found
For lThisChar = 1 To Len(csInvalidChars)
    SP_fn_val = Replace$(SP_fn_val, Mid(csInvalidChars, lThisChar, 1), sReplaceInvalidWith)
Next

End Function

Function v_MM() As String
'v1.00 2012-11-29 14:37
'converts to 2 digit month

v_MM = Month(Now())
If Len(v_MM) = 1 Then v_MM = "0" & v_MM

End Function

Function v_DD() As String
'v1.00 2012-11-29 14:37
'converts to 2 digit date

v_DD = Day(Now())
If Len(v_DD) = 1 Then v_DD = "0" & v_DD

End Function

Function SPUseCheckOut(docCheckOut As String, Optional TestFirst As Boolean _
    , Optional ForceUNC As Boolean) As Workbook
'v2.00 2013-07-10 13:01
'Source:
'http://social.msdn.microsoft.com/Forums/hu-HU/isvvba/thread/25609303-dc29-4cf4-a526-977bf6129e78

'Sub test_SPUseCheckOut()
'Dim wb As Workbook
'Workbooks.Open src
'Set wb = ActiveWorkbook
'Dim docCheckOut As String
'docCheckOut = wb.FullName
'Call xlSharePoint.SPUseCheckOut(docCheckOut)
' **********************
' * now work with file *
' *   when finished:   *
' **********************
'wb.CheckInWithVersion True  'also closes wb
'End Sub

Dim wb As Workbook
Dim UPathName As String, UCheckOut As String
For Each wb In Workbooks
    If ForceUNC = True Then
    'represent both paths as UNC address
        UPathName = UCase(Replace(Replace(wb.Name, cURL, ""), cFsl, cBsl))
        UCheckOut = UCase(Replace(Replace(wb.Name, cURL, ""), cFsl, cBsl))
    Else
    'represent both paths as URL address
        UPathName = UCase(Replace(Replace(wb.Name, cBsl & cBsl, cURL & cFsl & cFsl), cBsl, cFsl))
        UCheckOut = UCase(Replace(Replace(wb.Name, cBsl & cBsl, cURL & cFsl & cFsl), cBsl, cFsl))
    End If
    If UPathName = UCheckOut Then
    'already open
    'determine if workbook can be checked in
    'only works if already Checked Out to you
        If wb.CanCheckIn = True Then wb.CheckInWithVersion True  'close & save then reopen later
        Exit For
    End If
Next wb

If TestFirst = True Then
' Determine if workbook can be checked out first
    If Workbooks.CanCheckOut(docCheckOut) = True Then
        Workbooks.CheckOut docCheckOut
        Set SPUseCheckOut = Workbooks(docCheckOut)
    Else
        'MsgBox "Unable to check out " & docCheckOut & " at this time."
    End If
Else
' just try to check it out anyway
    Application.Wait Now() + TimeSerial(0, 0, 2)  'prevents time delay errors after uploading / CheckIn
    Workbooks.CheckOut docCheckOut
    Set SPUseCheckOut = Workbooks(docCheckOut)
End If

End Function

Sub SP_OfferToCheckInAllWorkbooks()
'v5.02 2013-12-18 10:24 - added underscore to macro name
'v1.15 2013-05-22 11:23
'check if ThisWorkbook opened from SharePoint then offer to close all

Dim pp As String, tt As String

'check if ThisWorkbook opened from SharePoint

tt = ThisWorkbook.FullName
If InStr(tt, SPdom) > 0 Then

'offer to close all
   
    tt = ThisWorkbook.Name
    pp = "Yes to close, save and CheckIn THIS workbook only," & vbLf _
        & "No to close, save and CheckIn ALL open workbooks (use with caution)"
    If MsgBox(pp, vbExclamation Or vbYesNo, tt) = vbNo Then
        Dim wb As Workbook
        For Each wb In Workbooks
        On Error Resume Next
            If wb.Name <> tt Then
                wb.CheckIn
                wb.Close True
            End If
        On Error GoTo 0
        Next wb
    End If
End If

End Sub

Function SP_Force_Connection(Optional ByVal UNCPathAndOrFilename As String = "defaultUNCpath") As Boolean
'v5.05 2014-01-10 14:27 - bugfix for zips (remove "" from ends)
'v5.03 2014-01-08 10:24 - now accepts filenames at end of path
'v5.02 2013-12-18 10:28 - added option for default UNCpath
'v5.00 2013-12-02 16:17
'!! NB: not totally suitable for end user processes, may close ALL instances of Explorer (file browser)
'   1. launches UNC in Explorer window
'   2. tries to close Explorer window
'   3. if 2 unsuccessful, kills all open instances of Explorer then relaunches Taskbar (!! messy)

If UNCPathAndOrFilename = "defaultUNCpath" Then UNCPathAndOrFilename = fn_SPpth(SPpth)

'v5.03 remove file name from UNCpath and extract last folder name for Windows Explorer title bar
Dim p As String, f As String, b As Integer, s() As Integer, c As Byte
p = Replace(UNCPathAndOrFilename, Chr(34), "")
b = InStr(p, "\")
While b > 0
    c = c + 1  'count slashes
    ReDim Preserve s(1 To c) As Integer  'add another slash character count
    s(c) = b
    b = InStr(s(c) + 1, p, "\")
Wend
If c > 0 Then
    p = Left(p, s(c))   'full 'root' path without last filename (or folder name) so "C:\Folder\Filename.txt" > "C:\Folder\"
    If c = 1 Then f = p Else f = Mid(p, s(c - 1) + 1, s(c) - 1 - s(c - 1))  'folder name (in Explorer title bar) so "C:\Folder\Filename.txt" > "Folder"  NB: "C:\" > "C:\"
End If

SP_Force_Connection = True

'easy option first, see if UNC already connected
On Error Resume Next
Dim testfn As String
testfn = Dir(p, vbDirectory)
If testfn = "." Then Exit Function
On Error GoTo 0

'open UNC in Explorer, try to close specific Explorer window
ShellAndWait "explorer " & p, 10000, vbHide, AbandonWait
If SP_CloseExplorerWindow(f) = False Then  'v5.00
'use brute force, close all Explorer windows, reopen Taskbar
    ShellAndWait "TaskKill /F /IM ""explorer.exe""", 1000, vbHide, AbandonWait
    Shell "C:\Windows\explorer.exe"
End If

'test UNC connection
On Error Resume Next
testfn = Dir(p, vbDirectory)
If testfn <> "." Then SP_Force_Connection = False
On Error GoTo 0

End Function

Function SP_CloseExplorerWindow(ByVal sCurrentFolderName As String) As Boolean
'v5.05 2014-01-10 14:27 - bugfix
'v5.04 2014-01-09 17:11 - bugfix
'v5.00 2013-12-02 16:12
'Function returns "True" if successful, otherwise "False"
'Amended from Source:
' http://gallery.technet.microsoft.com/scriptcenter/3879dd1b-09a1-4a9f-95ca-529351a7e2ac

If sCurrentFolderName = "" Then Exit Function

Dim bTest, wndw
bTest = False
With CreateObject("shell.application")
    For Each wndw In .Windows
        If wndw.Document.Folder = sCurrentFolderName Then
            On Error Resume Next
            wndw.Quit
            bTest = Err.Number = 0
            On Error GoTo 0
        End If
    Next
End With ' shell.application
SP_CloseExplorerWindow = CStr(bTest)

End Function




'=================================================================================
'  TYPICAL CODE FOR THISWORKBOOK MODULE:
'=================================================================================

'Option Explicit
'
'Private Const MasterUserID As String = "bpmgb"      'admin user ID, all other userIDs won't process
''Private Const MasterUserIDList As String = "Sales Admin"      'admin user list
'Private Const MasterIdent As String = " (master)"   'identifier that this is the master file, not a published file
'Private Const ipth As String = "\\ishare.dhl.com\sites\DGFUK\GBOFR\OFR Management\"      'SharePoint location
'Private Const xpth As String = ""   'XLS to shared drive, set "" if not required
'Private Const spth As String = "\\ishare.dhl.com\sites\DGFUK\Sales\TLM\Logis Reports\"   'SaveCopyAs to secondary location, set "" if not required
'Private Const ofn As String = "(Logis Ocean) LCL Customers by Trade Lane"     'output filename must not include MasterIdent
'Private Const cVBAfilename As String = "VBA UPDATE WEEKLY REPORTS.XLSM"    'this wb open means automatic updates in process
'Private bStopLoop As Boolean
'Public bAutoVBA As Boolean
'
'Private Sub Workbook_BeforeClose(Cancel As Boolean)
'
'If bStopLoop = True Then Exit Sub
'If InStr(ThisWorkbook.Name, MasterIdent) = 0 Then Exit Sub
'
''choose one
'If CU_userID(MasterUserID) = False Then Exit Sub
''If CU_Controlled(MasterUserIDList) = False Then Exit Sub
'
'bStopLoop = True
'
'bAutoVBA = chkAutoVBA
'
'Dim tt As String, pp As String, mbxr As VbMsgBoxResult
'tt = "doPublish"
'pp = "Save and publish files to SharePoint?"
'If bAutoVBA Then mbxr = vbYes Else mbxr = MsgBox(pp, vbYesNo Or vbQuestion, tt)
'If mbxr = vbYes Then
'    Application.StatusBar = "Publishing files to SharePoint..."
'    tt = "doPublish failed"
'    pp = doPublish(True)  'this will save then quit when done
'    If bAutoVBA = False And pp <> "" Then MsgBox pp, vbCritical, tt
'    Application.StatusBar = False
'Else
'    pp = "Just save?"
'    If bAutoVBA Then mbxr = vbYes Else mbxr = MsgBox(pp, vbYesNo, "")
'    If mbxr = vbYes Then ThisWorkbook.Save
'    Dim wb As Workbook, w As Byte
'    For Each wb In Workbooks
'        If UCase(wb.Name) <> "PERSONAL.XLSB" Then w = w + 1
'    Next wb
'    If w = 1 Then Application.Quit Else ThisWorkbook.Close False
'End If
'
'End Sub
'
'Private Function chkAutoVBA() As Boolean
'
'Dim wb As Workbook
'If InStr(Application.Caption, "Read-Only") > 0 Then
'    chkAutoVBA = True
'Else
'    For Each wb In Workbooks
'        If UCase(wb.Name) = UCase(cVBAfilename) Then
'            chkAutoVBA = True
'            Exit Function
'        End If
'    Next wb
'End If
'
'End Function
'
'Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
'
'If bStopLoop = True Then Exit Sub
'If InStr(ThisWorkbook.Name, MasterIdent) = 0 Then Exit Sub
'
''choose one
'If CU_userID(MasterUserID) = False Then Exit Sub
''If CU_Controlled(MasterUserIDList) = False Then Exit Sub
'
'bAutoVBA = chkAutoVBA
'
'bStopLoop = True
'
'Dim tt As String, pp As String, mbxr As VbMsgBoxResult
'tt = "doPublish"
'pp = "Save and publish files to SharePoint?"
'If bAutoVBA Then mbxr = vbYes Else mbxr = MsgBox(pp, vbYesNo Or vbQuestion, tt)
'If mbxr = vbYes Then
'Application.StatusBar = "Publishing files to SharePoint..."
'    tt = "doPublish failed"
'    pp = doPublish(False)
'    If pp <> "" And bAutoVBA = False Then MsgBox pp, vbCritical, tt
'Application.StatusBar = False
'End If
'
'bStopLoop = False
'
'End Sub
'
'Function doPublish(ByVal QuitWhenDone As Boolean) As String
'
''choose one
'If CU_userID(MasterUserID) = False Then Exit Function
''If CU_Controlled(MasterUserIDList) = False Then Exit Function
'
''If ThisWorkbook.Sheets.Count > 1 Then xlUtils.xlU_Export_Single_Sheets xpth, False, True, True
'
''NB: outputs are optional, but must specify at least one
'doPublish = xlSharePoint.SP_Upload_from_2007(WBToUpload:=ThisWorkbook _
'    , UploadPath:=ipth _
'    , OutputFilename:=ofn _
'    , spUploadZipXLSM:=True _
'    , AllowEvents:=False, QuitWhenDone:=QuitWhenDone _
'    , AlsoCopyXLSToSharedPath:=xpth _
'    , AlsoSaveCopyToSecondaryPath:=spth _
'    , AlsoBreakLinks:=True)
'
'End Function
'
'Private Sub Workbook_Open()
'
'If CU_userID(MasterUserID) = False Then Exit Sub
'
'bAutoVBA = chkAutoVBA
'
''run invisible to fully automate
'If Application.Visible = False Then
'    Application.Visible = True
'    ThisWorkbook.RefreshAll
'    bStopLoop = True
'    ThisWorkbook.Save
'    doPublish True
'End If
'
'End Sub