Link to home
Start Free TrialLog in
Avatar of Robert Berke
Robert BerkeFlag for United States of America

asked on

Batch file to make folders available offline

I will boost this to 500 points for a successful solution.

I have about 20 folders that need to be kept offline.  

doing this manually is a pain because each request waits while the folder contents are copied to the csc cache.

I am looking for an XP command line method so I can put all 20 into a batch file. I thought csccmd would do it, but I think I am wrong (or not smart enough to figure it out).

This will also be useful after I reinitialize cache (which I most do about once a month).

Avatar of William Elliott
William Elliott
Flag of United States of America image

rather than using offline folders why not set a script that states if the folders exists(meaning connected to the network) then synchronize using a simple command such as xcopy or robocopy?
Avatar of Robert Berke

ASKER

I like offline folders a lot.  

I usually don't have to explain why because most laptop owners now understand.

But, for a simple example for future researchers that might stumble across this page:

Lets say I have a shortcut pointing to \\server01\project1\file 55 of 100

making project1 be available offline means that the shortcut will still work when my laptop is home.  In other words, it will look as if I am still connect to the server for \\server01\project1.

Plus, if I only update files 10, 55 and 100,  only those files are downloaded when I logoff.

I know xcopy won't do that.  Perhaps robocopy will, but why should I pay for a function that Windows has been providing for free since windows 2000?



fair enough,... robocopy is free as well fyi,...

i found this, you may like it
http://blogs.technet.com/filecab/pages/cscsyncall-vbs-sync-the-entire-offline-files-cache.aspx


save the following as "CscSyncAll.vbs"



'
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
' ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
' THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
' PARTICULAR PURPOSE.
'
' Copyright (c) Microsoft Corporation. All rights reserved.
'
'
' Usage: CscSyncAll.vbs [/Direction:in|out|inout] [/PinNewFiles:1:0] [/Conflicts:local|remote|latest] [/Machine:value] [/User:value] [/Password:value]
'
'
' Demonstrates how to sync the entire Offline Files cache.
'
'

const cComputerName = "LocalHost"
const cWMINamespace = "root\cimv2"


'
' Process commandline arguments
'
strComputerName = WScript.Arguments.Named("Machine")
If Len(strComputerName) = 0 Then strComputerName = cComputerName

strUserID = WScript.Arguments.Named("User")
If Len(strUserID) = 0 Then strUserID = ""

strPassword = WScript.Arguments.Named("Password")
If Len(strPassword) = 0 Then strPassword = ""

'
' Sync control flags from Win32_OfflineFilesCache.Synchronize
'
const fFillSparse        = &H00000001
const fSyncIn            = &H00000002
const fSyncOut           = &H00000004
const fPinNewFiles       = &H00000008
const fPinLinkTargets    = &H00000010
const fPinForUser        = &H00000020
const fPinForUser_Policy = &H00000040
const fPinForAll         = &H00000080
const fLowPriority       = &H00000200
const fAsyncProgress     = &H00000400
const fInteractive       = &H00000800
const fConsole           = &H00001000
const fSkipSuspendedDirs = &H00002000
const fBackground        = &H00010000
const fCrKeepLocal       = &H10000000
const fCrKeepRemote      = &H20000000
const fCrKeepLatest      = &H30000000

const wbemFlagSendStatus = &H00000080

SyncControlFlags = fSyncIn + _
                   fSyncOut + _
                   fPinNewFiles + _
                   fPinForUser + _
                   fConsole + _
                   fInteractive

strDirection = WScript.Arguments.Named("Direction")
If Len(strDirection) <> 0 Then
    if LCase(strDirection) = "in" Then
        SyncControlFlags = SyncControlFlags - fSyncOut
    Elseif LCase(strDirection) = "out" Then
        SyncControlFlags = SyncControlFlags - fSyncIn
    Elseif LCase(strDirection) = "inout" Then
        '
        ' Do nothing.  Flags already indicate in/out
        '
    Else
        Wscript.Echo "Invalid direction value [" & strDirection & "]"
        Err.Raise 507 ' "an exception occured" error
    End if
End if

strPinNewFiles = WScript.Arguments.Named("PinNewFiles")
If Len(strPinNewFiles) <> 0 And strPinNewFiles = "0" Then
    SyncControlFlags = SyncControlFlags - fPinNewFiles
End if

strConflicts = WScript.Arguments.Named("Conflicts")
If Len(strConflicts) <> 0 Then
    If LCase(strConflicts) = "local" Then
        SyncControlflags = SyncControlFlags + fCrKeepLocal
    Elseif LCase(strConflicts) = "remote" Then
        SyncControlFlags = SyncControlFlags + fCrKeepRemote
    Elseif LCase(strConflicts) = "latest" Then
        SyncControlFlags = SyncControlFlags + fCrKeepLatest
    End if
End if

Set objWMILocator  = WScript.CreateObject("WbemScripting.SWbemLocator")
Set objWMIServices = objWMILocator.ConnectServer(strComputerName, _
                                                 cWMINameSpace, _
                                                 strUserID, _
                                                 strPassword)
Set objCache         = objWMIServices.Get("Win32_OfflineFilesCache=@")
Set objParams        = objCache.Methods_("Synchronize").InParameters.SpawnInstance_
Set objSink          = WScript.CreateObject("WbemScripting.SWbemSink", "SINK_")
Set objItemInstances = objWMIServices.InstancesOf("Win32_OfflineFilesItem")

'
' Set up the API arguments
'
Dim ItemPaths(0)

objParams.Paths = ItemPaths
objParams.Flags = SyncControlFlags

'
' Constants for cache item types
'
const cFile      = 0
const cDirectory = 1
const cShare     = 2
const cServer    = 3

'
' Execute the sync action on each share in the Offline Files cache.
'
For Each objItem in objItemInstances
    If cShare = objItem.ItemType Then
        ItemPaths(0) = objItem.ItemPath
        objParams.Paths = ItemPaths
        objWMIServices.ExecMethodAsync objSink, "Win32_OfflineFilesCache", "Synchronize", objParams, wbemFlagSendStatus
    End If
Next

'
' Loop until we receive an OnCompleted event
'
bDone = False
While Not bDone
    wscript.sleep 1000
Wend


Sub SINK_OnProgress(UpperBound, Current, Message, objContext)

    DIM PART_REASON, PART_RESULT, PART_RESULTMSG, PART_PATH
    DIM REASON_BEGIN, REASON_END, REASON_ITEMBEGIN, REASON_ITEMRESULT
    DIM Reasons(3)

    '
    ' The message is composed as follows:
    '
    '     <reason>:<result>:<result msg>:<path>
    '
    '     <reason>
    '         Integer indicates reason for progress callback.
    '            Values are:
    '                0 - "Begin" overall operation
    '                1 - "End" overall operation
    '                2 - "Begin Item" operation
    '                3 - "Item result"
    '
    '     <result>
    '         Integer result code; HRESULT.
    '
    '     <result msg>
    '         Text describing the result.  In most cases this is translated using
    '         the Win32 function FormatMessage.  In some cases a more descriptive
    '         message is provided that is better aligned with Offline Files.
    '
    '     <path>
    '         UNC path string associated with the progress notifcation.  This is
    '         empty for the "Begin" and "End" notifications.
    '

    '
    ' Define indexes of the various parts in the message.
    '
    PART_REASON    = 0
    PART_RESULT    = 1
    PART_RESULTMSG = 2
    PART_PATH      = 3

    '
    ' The reason codes present in the <reason> part.
    '
    REASON_BEGIN      = 0
    REASON_END        = 1
    REASON_ITEMBEGIN  = 2
    REASON_ITEMRESULT = 3

    '
    ' split the message into the 4 parts and extract those parts.
    '
    Parts = Split(Message, ":", -1, 1)

    Reason = CInt(Parts(PART_REASON))
    Path = Parts(PART_PATH)
    Result = CLng(Parts(PART_RESULT))
    ResultMsg = Parts(PART_RESULTMSG)

    Select Case Reason
        Case REASON_ITEMRESULT
            WScript.Echo Path
            if 0 <> Result then
                Wscript.Echo "  Error: " & Hex(Result) & "  " & ResultMsg
            end if

        Case REASON_END
            if 0 <> Result then
                Wscript.Echo "Error: " & Hex(Result) & "  " & ResultMsg
            end if
    End Select

End Sub

'
' Called when the operation is complete.
'
Sub SINK_OnCompleted(HResult, objLastError, objContext)
    bDone = True
End Sub



oh my god !  

at a quick glance, that does not look like it will solve my problem.  It seems that it will just make sure that every offline file is fully refreshed (windows only refreshes the files that have changed, which may sometimes be a problem.)

I am pretty sure the code still assumes that someone manually set" make folders available offline" for each and every offline folder.

Perhaps buried somewhere in the code there is something that does what I want, but there is no way I am going to tackle that beast.

If no one comes up with a better way in the next few days, I'll give you the points for your efforts.

Bob

if you want i can make it only sync the specific folders,..  it's 2am here, so i'm doubting i'll do it tonight, but i'll look at it tomorrow and throw something for specific folders together for you.
If you are willing to do the work, I would be grateful.  But, while this would solve an annoying issue, it is NOT critical. So don't worry too much if you can't get it done.


The ideal syntax is something

mybatch.bat
    call myvbs   "\\server01\master folder\new york"
    call myvbs   "\\server01\master folder\NJ"
    call myvbs   "\\server01\master folder\indiana"
    call myvbs   "\\server01\master folder\another folder"

And, its not critical, but I hope it will integrate with Windows native mode offline files.  For instance after calling it, I should be able to right click on any of those folders and see a check mark next to "make available online". And, when I logoff and on it should sync automatically without me having to call it.


try this
save as syncme.vbs

i didn't add eror checking or any output, if you want all of that let me know.
basically you just have a file c:\offlinefolders.txt with a list of shares  you want to sync, and when you double click on the synvme.vbs  it will sync them all



'*****************************
'
' usage
' c:\>cscript syncme.vbs
'
'on error resume next  
strreadfile = "c:\offlinefolders.txt" 'the file with the list of shares. one per line

Set fso = CreateObject("Scripting.FileSystemObject")
Set fsoReadshareList = fso.OpenTextFile(strreadfile, 1, TristateFalse)
aryshares = Split(fsoReadshareList.ReadAll, vbCrLf)
fsoReadComputerList.Close

For Each share In aryshares
SynchronizeFolder(share)
Next

Function SynchronizeFolder(folder)
        Dim objShell
        Dim objFolder

        Set objShell = CreateObject("Shell.Application")
        Set objFolder = objShell.NameSpace(folder)

        If (not objFolder is nothing) then
            objFolder.Synchronize
        End if

        Set objFolder = nothing
        Set objShell = nothing
End Function
'*****************************
fsoReadComputerList.close
should be
fsoReadshareList.close

as you can see i copied this line from another script i have :)

this will synchronize, but not 'check' the "make available offline" option.. i'll look into that and respond later
ASKER CERTIFIED SOLUTION
Avatar of William Elliott
William Elliott
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
CSCCMD 1.1 documentation says it will do what I want.  
Unfortuantely, the csccmd 1.0 included with sbs 2003 and rktools does not have the new functionality.

http://support.microsoft.com/default.aspx?scid=kb;en-us;884739 says I can get 1.1  by calling Product Support.  but the link leads to consulting services which seem to start at $60 per incident.

I don't have time to track down csdcmd 1.1 right now -- maybe tonight.

weelio:  Have you tested your script?

When I try it, it does not seem to work.  For debugging I put in <msgbox folder> right before the sync.
 
offlinefiles.txt contains a single line: \\server02\mysharename\myfoldername2\

When I run the vbs the folder name displays. When I click ok, a "synchronization complete" dialog flashs quickly. that says "no offline edits found".

I then use explorer to view offline files, and I only see the ones that I have manually pinned from other folders.

Any ideas ?



found csccmd 1.1  on web

http://www.mcse.ms/message1742431.html  led me to it.

http://www.megaupload.com/?d=5EUO5R6K  is where I downloaded it

I will play with it and post back my success or failure
I renamed my old csccmd and replaced it with the new one.  The following command turn the available offline flag on and off.  The /user switch seems to be required.



csccmd /pin2:\\server02\allread\office\     /user  
csccmd /unpin2:\\server02\allread\office\     /user  

but the following command does not force the files to be downloaded. I'm going home now.

csccmd /FILL:\\server02\allread\office\    
i can only get the "csccmd /FILL:\\server02\allread\office\    " to work if it is connecting to a direct share.,.. not share\subfolder

try this

strcscmd = "C:\Documents and Settings\Administrator\Desktop\csccmdv1.1\csccmd.exe" 'location of the csccmd.exe file
strreadfile = "c:\offlinefolders.txt" 'the file with the list of shares. one per line

on error resume next
Set objShell1 = CreateObject("WScript.Shell")
Set objShell = CreateObject("Shell.Application")
Set fso = CreateObject("Scripting.FileSystemObject")

strtmpfile = FSO.GetSpecialFolder(2) & "~.tmp" ' a place to put a temp file to read the dos results

Set fsoReadshareList = fso.OpenTextFile(strreadfile, 1, TristateFalse)
aryshares = Split(fsoReadshareList.ReadAll, vbCrLf)
fsoReadshareList.Close
If FSO.FileExists(strtmpfile) Then fso.GetFile(strtmpfile).delete

For Each folder In aryshares
        Dim objShell
        Dim objFolder
        Set objFolder2 = objShell.NameSpace(folder)
            
        If (not objFolder2 is nothing) then
                  objShell1.Run   "%comspec% /c " & chr(34) & strcscmd & chr(34) &_
                                          " /pin2:" & folder & " /user >> " & strtmpfile, 0, false
                        wscript.sleep (1500)
                  objShell1.Run   " %comspec% /c echo ----------------------------------------" &_
                                          "---------------------------------------  >> " & strtmpfile, 0, false
                  objShell1.Run   "%comspec% /c " & chr(34) & strcscmd & chr(34) &_
                                          " /fill:" & folder & " >> " & strtmpfile, 0, false
                        wscript.sleep (1500)
                        Set t = fso.OpenTextFile(strtmpfile, 1, TristateFalse)
                        tarra = Split(t.ReadAll, vbCrLf)
                        t.Close
                              For each la in tarra
                                    tresults = tresults & la & vbcrlf
                              next
                        tresults =  tresults &  "------------------------------------------------" &_
                                          "-------------------------------" & vbcrlf
                        Set objFolder = nothing
                        set t = nothing
                  objFolder2.Synchronize
        End if
next
wscript.sleep (1500)
wscript.echo tresults
Set fso = nothing
set fsoReadshareList = nothing
Set objShell = nothing
set objShell1 = nothing
>>i can only get the "csccmd /FILL:\\server02\allread\office\    " to work if it is connecting to a direct share

by george, I think you have hit the nail on the head !.

I think the /fill   \\server02\allread  is exactly what I want.  It should simply force a sync of everything on the server02.

Which means that I can call it at the end of my batch file.

AFTERINITIALIZECACHE.BAT
   csccmd /pin2:pathtofolder1
   ..
   csccmd /pin2:pathtofolder50
   cscmd /fill:\\server02\allread

I will try it when I get back to the office.

I think it will totally meet my purposes -- so your vbs program might not be necessary.  
Typical Microsoft !!!!  It almost does what I want but not quite.
Struction of files:
\\server02
   \myshare
      \myfolder1
      \myfolder2
         \mysubfolder2.1
      \myfolder3

csccmd  \\server02\myshare\myfolder2  /user /userinherit    

will make myfolder2 available offline, BUT NOT mysubfolder 2.1. Fpr some reason it does not apply to the already existing subfolders.

But, it does apply to all newly created subfolders, even those under mysubfolder2.1  !!!

It also means that the /pin2:filename option will be useless to me.    (Too bad, because it does exactly what I want other than the subfolder issue).

I think this means that vbs will be essential in order to recurse through the existing subfolders.

Ugh..

Also, I still can't get /fill to work.  csccmd /fill:\\server02\allread   says successful, but the subfiles are not actually cached. It looks like a no-op.  This is actually not any problem at all, because the automatic sync points at logoff and logon works fine. So does the sync button  provided in system tools.

I will mess with the previously provided vbs code over the weekend if I get a chance.
possibly if you do an unpin, then a pin it will get all the sub subfolders? just a thought..
honestly i can't truly test the code, because my laptop was recently stolen and i've yet to replace it, so i'm writing this stuff blind, based on the way it is described to work.
That's a shame when you lose you laptop.  I hope you had good backup. I would think you could test on any computer that belongs to a domain.

But, don't worry, I have decided to go with a vba solution instead of vbs.  

   #1 reason is I have never been able to debug vbs scripts well.  The mse7.exe IDE is supposed to work, but I have never figured it out.

   #2 reason is that I already have vba code that will recurse through folders. No sense in me debugging your  Vbs code when I have working vba routines that will do what I want. When I get a chance to make them work, I will post here.

Also, unpin followed by pin does not work either.  

Finally, there are even more problems with csccmd.  

For instance, I have somehow accidentally used it to pin a folder that I can no longer be unpinned.  The Make available offline property is checked and grey shaded. It looks like I will have to reinitialize the cache and start over.


Anyway, I am tired of this question, so I am closing it.  Here comes the points and thanks to everyone for their efforts.
This seems to work pretty well when I run it under Excel 2003.  I set up  c:\aaatmp\cscfolders.txt to have
  pin2 \\server02\folder1
  pin2 \\server02\folder2
  ...
  pin2 \\server02\folder50

When I no longer need folder2 to be offline, I would move the line to the top and change it to have
  unpin2 \\server02\folder50

The next day I would try to remember to delete the unpin2 commands, so they don't litter the file.

Sometime soon I hope to improve this program so that I can embed comments in the .txt file.

Bob



Option Explicit
Const NamesOfFoldersToCopy = "e:\aaatmp\cscfolders.txt"

Dim lvl
Dim objFSO, objTS
Declare Function WNetGetUniversalNameA Lib "mpr" _
(ByVal lpLocalPath As String, _
ByVal dwInfoLevel As Long, _
lpBuffer As Any, _
lpBufferSize As Long) As Long

Dim wShell
Sub reinitializeCache()
    Set objFSO = CreateObject("Scripting.FileSystemObject")
   
    Set objTS = objFSO.OpenTextFile(NamesOfFoldersToCopy)

    Set wShell = CreateObject("wscript.shell")
   
    While Not objTS.AtEndOfStream
        Dim thisLine As String
        thisLine = Trim(objTS.ReadLine)
        If Trim(thisLine) = "" Then
            'do nothing
        ElseIf Right(thisLine, 1) <> "\" Then
            MsgBox "All folder paths must end in '\' so we did not process" & _
                vbCrLf & "  " & thisLine
        Else
Dim firstCmd As String, folder As String
            firstCmd = LCase(Trim(Left(thisLine, InStr(thisLine & " ", " "))))
            Select Case firstCmd
                Case "pin2":
                    folder = GetUNCPath(Trim(Mid(thisLine, 6)))
                    If Left(folder, 2) <> "\\" Then
                        MsgBox "We can only pin paths that are on the server so we did not process" & _
                            thisLine
                    Else
                        Call Recurse(folder, firstCmd)
                    End If
                Case "unpin", "unpin2"
                    folder = GetUNCPath(Trim(Mid(thisLine, 7)))
                    If folder <> "\\" Then
                        MsgBox "We can only pin paths that are on the server so we did not process" & _
                             vbCrLf & "  " & thisLine
                    Else
                        Call Recurse(folder, firstCmd)
                    End If
                Case Else
                      MsgBox "command must begin with Pin2, Unpin, or Unpin2 so we did not process" & _
                            vbCrLf & "  " & thisLine
                End Select
       
        End If
    Wend
    MsgBox "Folders have been marked to be available offline." & _
            vbCrLf & vbCrLf & "But, the cache will not be loaded until you logoff or click on the sync button"

End Sub

Sub Recurse(FolderName, PinOrUnpin)
Dim subf As folder, cmd As String

    lvl = lvl + 1
    Dim fld As folder
   On Error Resume Next
   Set fld = objFSO.GetFolder(FolderName)
    If err <> 0 Then
        MsgBox "could not find '" & FolderName & "' so it was not processed"
        GoTo exitsub
    End If
    On Error GoTo 0
   For Each subf In fld.SubFolders
        Call Recurse(subf.path, PinOrUnpin)
   Next subf
    cmd = "CscCmd "
    ' MsgBox lvl & Application.WorksheetFunction.Rept("  ", lvl) & GetUNCPath(fld.path)

    cmd = "csccmd /pin2:" & Chr(34) & GetUNCPath(fld.path) & Chr(34) & " /user"
    If lvl = 1 Then cmd = cmd & " /userinherit"
    ' MsgBox cmd
    wShell.Run cmd
 
    lvl = lvl - 1
exitsub:

End Sub


Private Function GetUNCPath(FolderName)
' from http://www.pcreview.co.uk/forums/thread-974892.php


Dim bytBuffer(1 To 256) As Byte
Dim lReturn As Long
Dim szPath As String

lReturn = WNetGetUniversalNameA(FolderName, 1, bytBuffer(1), 256)
Select Case lReturn
    Case 0:
    szPath = StrConv(bytBuffer, vbUnicode)
    szPath = Left$(szPath, InStr(szPath, vbNullChar) - 1)
    szPath = Right$(szPath, Len(szPath) - 4)
    GetUNCPath = szPath
Case 2250: GetUNCPath = FolderName
Case 1200:
    If Left(FolderName, 2) = "\\" Then
        GetUNCPath = FolderName
    Else
        MsgBox "Error getting UNC path for '" & FolderName & "'", vbCritical
    End If
   
               
Case Else:
    MsgBox "Error getting UNC path for '" & FolderName & "'", vbCritical
End Select
End Function