VB6 - Is it possible to create a full path directory? - vb6

I want to to create a full path directory, like "C:\temp1\temp2\temp2" without having to make multiple "MakeDir", for each directory.
Is this possible?
Is there any reference that I can add to my project that has this kind of function?
Thanks

You can use these functions to make the task a little easier:
Const PATH_SEPARATOR As String = "\"
'"' Creates a directory and its parent directories '''
Public Sub MakeDirectoryStructure(strDir As String)
Dim sTemp As String
If Right$(strDir, 1) = PATH_SEPARATOR Then
sTemp = Left$(strDir, Len(strDir) - 1)
Else
sTemp = strDir
End If
If Dir(strDir, vbDirectory) <> "" Then
' Already exists.'
Else
'We have to create it'
On Error Resume Next
MkDir strDir
If Err > 0 Then
' Create parent subdirectory first.'
Err.Clear
'New path'
sTemp = ExtractPath(strDir)
'Recurse'
MakeDirectoryStructure sTemp
End If
MkDir strDir
End If
End Sub
Public Function ExtractPath(strPath As String) As String
ExtractPath = MiscExtractPathName(strPath, True)
End Function
Private Function MiscExtractPathName(strPath As String, ByVal bFlag) As String
'The string is treated as if it contains '
'a path and file name. '
''''''''''''''''''''''''''''''­''''''''''''''''''''''''''''''
' If bFlag = TRUE: '
' Function extracts the path from '
' the input string and returns it. '
' If bFlag = FALSE: '
' Function extracts the File name from '
' the input string and returns it. '
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim lPos As Long
Dim lOldPos As Long
'Shorten the path one level'
lPos = 1
lOldPos = 1
Do
lPos = InStr(lPos, strPath, PATH_SEPARATOR)
If lPos > 0 Then
lOldPos = lPos
lPos = lPos + 1
Else
If lOldPos = 1 And Not bFlag Then
lOldPos = 0
End If
Exit Do
End If
Loop
If bFlag Then
MiscExtractPathName = Left$(strPath, lOldPos - 1)
Else
MiscExtractPathName = Mid$(strPath, lOldPos + 1)
End If
End Function ' MiscExtractPathName'
I'm not sure where I got this code.

Asked and answered before:
equivalent-of-directory-createdirectory-in-vb6

Private Declare Function MakeSureDirectoryPathExists Lib
"imagehlp.dll" (ByVal lpPath As String) As Long
Dim mF As String
mF = FolderPath
If Right(mF, 1) <> "\" Then
mF = mF & "\"
MakeSureDirectoryPathExists mF
End If

'//Create nested folders in one call
Public Function MkDirs(ByVal PathIn As String) _
As Boolean
Dim nPos As Long
MkDirs = True 'assume success
If Right$(PathIn, 1) <> "\" Then PathIn = PathIn + "\" nPos = InStr(1, PathIn, "\")
Do While nPos > 0
If Dir$(Left$(PathIn, nPos), vbDirectory) = "" Then
On Error GoTo Failed
MkDir Left$(PathIn, nPos)
On Error GoTo 0
End If
nPos = InStr(nPos + 1, PathIn, "\")
Loop
Exit Function
Failed:
MkDirs = False
End Function

Related

how to rename image file name while uploading on web folder

i m using asp classic. i want to rename image file while i upload image on web folder created by me. please help me out of this issue.
If there is a file in targeted folder with same name (like lokesh.jpg) what i am uploading, than new file should b automatically renamed(like lokesh(1).jpg) instead of overwriting
my code is as below:
upload.asp
<%
Class FileUploader
Public Files
Private mcolFormElem
Private Sub Class_Initialize()
Set Files = Server.CreateObject("Scripting.Dictionary")
Set mcolFormElem = Server.CreateObject("Scripting.Dictionary")
End Sub
Private Sub Class_Terminate()
If IsObject(Files) Then
Files.RemoveAll()
Set Files = Nothing
End If
If IsObject(mcolFormElem) Then
mcolFormElem.RemoveAll()
Set mcolFormElem = Nothing
End If
End Sub
Public Property Get Form(sIndex)
Form = ""
If mcolFormElem.Exists(LCase(sIndex)) Then Form = mcolFormElem.Item(LCase(sIndex))
End Property
Public Default Sub Upload()
Dim biData, sInputName
Dim nPosBegin, nPosEnd, nPos, vDataBounds, nDataBoundPos
Dim nPosFile, nPosBound
biData = Request.BinaryRead(Request.TotalBytes)
nPosBegin = 1
nPosEnd = InstrB(nPosBegin, biData, CByteString(Chr(13)))
If (nPosEnd-nPosBegin) <= 0 Then Exit Sub
vDataBounds = MidB(biData, nPosBegin, nPosEnd-nPosBegin)
nDataBoundPos = InstrB(1, biData, vDataBounds)
Do Until nDataBoundPos = InstrB(biData, vDataBounds & CByteString("--"))
nPos = InstrB(nDataBoundPos, biData, CByteString("Content-Disposition"))
nPos = InstrB(nPos, biData, CByteString("name="))
nPosBegin = nPos + 6
nPosEnd = InstrB(nPosBegin, biData, CByteString(Chr(34)))
sInputName = CWideString(MidB(biData, nPosBegin, nPosEnd-nPosBegin))
nPosFile = InstrB(nDataBoundPos, biData, CByteString("filename="))
nPosBound = InstrB(nPosEnd, biData, vDataBounds)
If nPosFile <> 0 And nPosFile < nPosBound Then
Dim oUploadFile, sFileName
Set oUploadFile = New UploadedFile
nPosBegin = nPosFile + 10
nPosEnd = InstrB(nPosBegin, biData, CByteString(Chr(34)))
sFileName = CWideString(MidB(biData, nPosBegin, nPosEnd-nPosBegin))
oUploadFile.FileName = Right(sFileName, Len(sFileName)-InStrRev(sFileName, "\"))
Dim oFileExtension
If sFileName <> "" then
oFileExtension = (Right(sFileName, Len(sFileName)-InStrRev(sFileName, ".")))
If oFileExtension <> "jpg" AND oFileExtension <> "jpeg" AND oFileExtension <> "gif" AND oFileExtension <> "pdf" then
response.write("<h1>Post New File</h1><p><font color=#ff0000>An error has occurred while processing your request.<br><br>We are sorry, Extensions other than JPG, JPEG, Gif, PDF are not allowed to upload<p><b>Click <a href='javascript:history.go(-1);'>here</a> to go back and address the error.</b></font>")
response.end
Exit Sub
End if
end If
nPos = InstrB(nPosEnd, biData, CByteString("Content-Type:"))
nPosBegin = nPos + 14
nPosEnd = InstrB(nPosBegin, biData, CByteString(Chr(13)))
oUploadFile.ContentType = CWideString(MidB(biData, nPosBegin, nPosEnd-nPosBegin))
nPosBegin = nPosEnd+4
nPosEnd = InstrB(nPosBegin, biData, vDataBounds) - 2
oUploadFile.FileData = MidB(biData, nPosBegin, nPosEnd-nPosBegin)
If sfileName <> "" then
If oUploadFile.FileSize > 10000000 Then
response.write("<h1>Post New Image</h1><p><font color=#ff0000>An error has occurred while processing your request.<br><br>We are sorry, Upload file containing 10000000(10mb) bytes only.<p><b>Click <a href='javascript:window:history.go(-1);'>here</a> to go back and address the error.</b></font>")
response.end
Exit Sub
End if
End if
If oUploadFile.FileSize > 0 Then Files.Add LCase(sInputName), oUploadFile
Else
nPos = InstrB(nPos, biData, CByteString(Chr(13)))
nPosBegin = nPos + 4
nPosEnd = InstrB(nPosBegin, biData, vDataBounds) - 2
If Not mcolFormElem.Exists(LCase(sInputName)) Then mcolFormElem.Add LCase(sInputName), CWideString(MidB(biData, nPosBegin, nPosEnd-nPosBegin))
End If
nDataBoundPos = InstrB(nDataBoundPos + LenB(vDataBounds), biData, vDataBounds)
Loop
End Sub
'String to byte string conversion
Private Function CByteString(sString)
Dim nIndex
For nIndex = 1 to Len(sString)
CByteString = CByteString & ChrB(AscB(Mid(sString,nIndex,1)))
Next
End Function
'Byte string to string conversion
Private Function CWideString(bsString)
Dim nIndex
CWideString =""
For nIndex = 1 to LenB(bsString)
CWideString = CWideString & Chr(AscB(MidB(bsString,nIndex,1)))
Next
End Function
End Class
Class UploadedFile
Public ContentType
Public FileName
Public FileData
Public Property Get FileSize()
FileSize = LenB(FileData)
End Property
Public Sub SaveToDisk(sPath)
Dim oFS, oFile
Dim nIndex
If sPath = "" Or FileName = "" Then Exit Sub
If Mid(sPath, Len(sPath)) <> "\" Then sPath = sPath & "\"
Set oFS = Server.CreateObject("Scripting.FileSystemObject")
If Not oFS.FolderExists(sPath) Then Exit Sub
Set oFile = oFS.CreateTextFile(sPath & FileName, True)
For nIndex = 1 to LenB(FileData)
oFile.Write Chr(AscB(MidB(FileData,nIndex,1)))
Next
oFile.Close
End Sub
Public Sub SaveToDatabase(ByRef oField)
If LenB(FileData) = 0 Then Exit Sub
If IsObject(oField) Then
oField.AppendChunk FileData
End If
End Sub
End Class
%>
submit.asp
<!-- #include file="upload.asp" -->
<%
response.buffer = true
Dim Uploader, File, i, j
Set Uploader = New FileUploader
Uploader.Upload()
Dim brandnm, filename
brandnm = Uploader.form("brandname")
Dim objRSa, objCmda, stra
Set objCmda = server.CreateObject("adodb.connection")
Set Objrsa = Server.CreateObject("ADODB.Recordset")
objCmda.open MM_connDUdirectory_STRING
stra = "SELECT * FROM brand"
Objrsa.Open stra,objCmda,1,2
if Uploader.Files.count <> 0 then
File = Uploader.Files.Items()
File(0).SavetoDisk Server.MapPath("upload/brands") 'Folder path where image will save
filename = File(0).Filename
else
filename = ""
End if
Objrsa.addnew
Objrsa.fields("brand_name") = brandnm
Objrsa.fields("brand_createddt") = now()
if filename <>"" then Objrsa.fields("brand_picpath") = filename
For Each File In Uploader.Files.Items
Objrsa("brand_ctype") = File.ContentType
next
Objrsa.Update
Objrsa.Close
Set Objrsa = Nothing
set objCmda = Nothing
%>
Please help me out of this issue.
If you want to rename it to follow a known pattern as in your example ("filename(number).ext"), you must to use a pseudo-code like this:
let counter = 1
let original = file(0).Filename
let current = file(0).Filename
while(current file exists)
current = original-without-extension + (counter) + original-extension
counter = counter + 1
end
However, I think that would be better to store the user provided filename into your database and choose a random-like filename to store the actual file into the filesystem.
let current = userLogin + (currentTime as yyyyMMddHHmmss) + ".uploaded"
By using a bogus file extension you make your application way more secure, as your file will not be interpretable/executable -- imagine a malicious user uploading an .ASP file and executing it.
If this break the image MIME type, you should consider creating another .ASP page read the database to discover the appropriate MIME type based on the user provided file extension, write that content-type and the binary file content.
TL;DR: don't use the user provided file name, create a new one. This will avoid server hacking.

SHGetPathFromIDList does not return pathnames with leading periods

I have some Excel x64 VBA code that gets MP3 files, along with track#, size, length, etc., and puts them in some worksheets. The basic code came from John Walkenbach's page and can be found here: http://spreadsheetpage.com/index.php/file/mp3_file_lister/. I have modified it to run in 64-bit Excel by adding the PtrSafe keyword in the function declarations and changing some data types from Long to either LongLong or LongPtr (and maybe a few others). The code works wonderfully with one not too minor exception, it will not return any files in folders that contain leading periods. For example, I have an album by .38 Special ripped using WMP. The folder is: D:\Users\username\Music\Music.38 Special\Rock & Roll Strategy... This path does not appear in the list generated. I also have: D:\Users\username\Music\Music\Norah Jones...Featuring Nora Jones... and this folder is missing, too (the trailing ellipses represent the list of songs) . I have contacted John Walkenbach via email, and he has no idea why this is happening either.
Here is the code as I've modified it:
Option Explicit
Dim Sht1Row As Integer
Dim Sht2Row As Integer
' By John Walkenbach
' Maybe be distributed freely, but not sold
'API declarations
Declare PtrSafe Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" (ByVal pidl As LongPtr, ByVal pszPath As String) As LongPtr
Declare PtrSafe Function SHBrowseForFolder Lib "shell32.dll" Alias "SHBrowseForFolderA" (lpBrowseInfo As BROWSEINFO) As LongPtr
Public Type BROWSEINFO
hOwner As LongPtr
pidlRoot As LongPtr
pszDisplayName As String
lpszTitle As String
ulFlags As LongPtr
lpfn As LongPtr
lParam As LongPtr
iImage As LongPtr
End Type
Sub GetAllFiles()
Dim Msg As String
Dim Directory As String
Dim lastRow1C As Integer
Dim lastRow2C As Integer
Dim lastRow1D As Integer
Dim lastRow2D As Integer
Msg = "Select the directory that contains the MP3 files. All subdirectories will be included."
Directory = GetDirectory(Msg)
If Directory = "" Then Exit Sub
If Right(Directory, 1) "\" Then Directory = Directory & "\"
With Sheet1
lastRow1C = .Cells(.Rows.Count, "C").End(xlUp).Row
If lastRow1C lastRow2D Then
.Range("D" & lastRow2D, "F" & lastRow2D).Select
Selection.AutoFill Destination:=Range("D" & lastRow2D, "F" & lastRow2C)
End If
.Range("E2:E" & lastRow2C).Copy
.Range("A2:A" & lastRow2C).PasteSpecial xlPasteValues
Columns("A:J").Sort key1:=Range("G2"), order1:=xlAscending, key2:=Range("H2"), order2:=xlAscending, Header:=xlYes
Range("A1").Select
End With
With Sheet1
Worksheets("Music_Library_Full").Activate
lastRow1C = .Cells(.Rows.Count, "C").End(xlUp).Row
lastRow1D = .Cells(.Rows.Count, "D").End(xlUp).Row
If lastRow1C > lastRow1D Then
.Range("D" & lastRow1D, "F" & lastRow1D).Select
Selection.AutoFill Destination:=Range("D" & lastRow1D, "F" & lastRow1C)
End If
.Range("E2:E" & lastRow1C).Copy
.Range("A2:A" & lastRow1C).PasteSpecial xlPasteValues
Columns("A:J").Sort key1:=Range("G2"), order1:=xlAscending, key2:=Range("H2"), order2:=xlAscending, Header:=xlYes
Range("A1").Select
End With
Application.ScreenUpdating = True
End Sub
Function GetDirectory(Optional Msg) As String
Dim bInfo As BROWSEINFO
Dim path As String
Dim r As String
Dim x As String
Dim pos As Integer
' Root folder = Desktop
bInfo.pidlRoot = 0&
' Title in the dialog
If IsMissing(Msg) Then
bInfo.lpszTitle = "Select a folder."
Else
bInfo.lpszTitle = Msg
End If
' Type of directory to return
bInfo.ulFlags = &H1
' Display the dialog
x = SHBrowseForFolder(bInfo)
' Parse the result
path = Space$(512)
r = SHGetPathFromIDList(ByVal x, ByVal path)
If r Then
pos = InStr(path, Chr$(0))
GetDirectory = Left(path, pos - 1)
Else
GetDirectory = ""
End If
End Function
Public Sub RecursiveDir(ByVal currdir As String)
Dim Dirs() As Variant
Dim NumDirs As Long
Dim FileName As String
Dim PathAndName As String
Dim i As Long
Dim PathName As String
Dim TrackNum As Variant
Dim Genre As String
Dim Duration As Variant
Dim FileSize As Variant
' Make sure path ends in backslash
If Right(currdir, 1) "\" Then currdir = currdir & "\"
' Put column headings on active sheet
Worksheets("Music_Library_Full").Activate
Cells(1, 1) = "Artist & Filename Lookup"
Cells(1, 2) = "Filename Lookup"
Cells(1, 3) = "Full Pathname"
Cells(1, 4) = "Artist"
Cells(1, 5) = "Artist & Filename"
Cells(1, 6) = "Filename"
Cells(1, 7) = "Path"
Cells(1, 8) = "Track#"
Cells(1, 9) = "Duration"
Cells(1, 10) = "Size"
Range("1:1").Font.Bold = True
Range("1:1").Font.Italic = True
Range("1:1").Font.Name = "Consolas"
Worksheets("Best_Greatest").Activate
Cells(1, 1) = "Artist & Filename Lookup"
Cells(1, 2) = "Filename Lookup"
Cells(1, 3) = "Full Pathname"
Cells(1, 4) = "Artist"
Cells(1, 5) = "Artist & Filename"
Cells(1, 6) = "Filename"
Cells(1, 7) = "Path"
Cells(1, 8) = "Track#"
Cells(1, 9) = "Duration"
Cells(1, 10) = "Size"
Range("1:1").Font.Bold = True
Range("1:1").Font.Italic = True
Range("1:1").Font.Name = "Consolas"
' Get files
FileName = Dir(currdir & "*.*", vbDirectory)
Do While Len(FileName) 0
If Left$(FileName, 1) "." Then 'Current dir
PathAndName = currdir & FileName
If (GetAttr(PathAndName) And vbDirectory) = vbDirectory Then
'store found directories
ReDim Preserve Dirs(0 To NumDirs) As Variant
Dirs(NumDirs) = PathAndName
NumDirs = NumDirs + 1
Else
If UCase(Right(FileName, 3)) = "MP3" Then
PathName = currdir 'path
FileName = FileName 'filename
TrackNum = FileInfo(currdir, FileName, 26) 'track
Duration = FileInfo(currdir, FileName, 27) 'duration
FileSize = Application.Round(FileLen(currdir & FileName) / 1024, 0) 'size
'Application.StatusBar = Row
If InStr(1, LCase(PathName), LCase("Best of"), vbTextCompare) Or InStr(1, LCase(PathName), LCase("Greatest"), vbTextCompare) Then
'Sht2Row = WorksheetFunction.CountA(Range("C:C")) + 1
Worksheets("Best_Greatest").Activate
Cells(Sht2Row, 2) = FileName
Cells(Sht2Row, 3) = PathName & FileName
Cells(Sht2Row, 7) = PathName
Cells(Sht2Row, 8) = TrackNum
Cells(Sht2Row, 9) = Duration
Cells(Sht2Row, 10) = FileSize
Sht2Row = Sht2Row + 1
Else
'Sht1Row = WorksheetFunction.CountA(Range("C:C")) + 1
Worksheets("Music_Library_Full").Activate
Cells(Sht1Row, 2) = FileName
Cells(Sht1Row, 3) = PathName & FileName
Cells(Sht1Row, 7) = PathName
Cells(Sht1Row, 8) = TrackNum
Cells(Sht1Row, 9) = Duration
Cells(Sht1Row, 10) = FileSize
Sht1Row = Sht1Row + 1
End If
End If
End If
End If
FileName = Dir()
Loop
' Process the found directories, recursively
For i = 0 To NumDirs - 1
RecursiveDir Dirs(i)
Next i
End Sub
Function FileInfo(path, FileName, item) As Variant
Dim objShell As IShellDispatch4
Dim objFolder As Folder3
Dim objFolderItem As FolderItem2
Set objShell = CreateObject("Shell.Application")
Set objFolder = objShell.Namespace(path)
Set objFolderItem = objFolder.ParseName(FileName)
FileInfo = objFolder.GetDetailsOf(objFolderItem, item)
Set objShell = Nothing
Set objFolder = Nothing
Set objFolderItem = Nothing
End Function
If anyone has any idea how to modify this so that pathnames containing a leading period as any part of the path can be returned, I'd be very glad to see it. I'd just rename those particular paths by removing the leading periods, but I'm afraid WMP will just one day put everything back the way it was (has happened before). Also, if you pick the actual folder in the BrowseForFolder API, that folder with the leading periods actually makes it into the sheet, but of course, only that folder.
Thanks
Look at this line in your code sample:
If Left$(FileName, 1) "." Then 'Current dir
Since the current directory is defined as a single '.' character, and this code only checks the initial character, it drops out before recursively examining it. Change the condition to check the length of the string as well as the initial character, e.g.
If (Left$(FileName, 1) = "." And FileName.Length = 1) Then 'Current dir
N.B. This code has not been tested; I hope it works for your use.
I was able to fix this by separating the test for root and subdirectory into separate IF statements, i.e.:
If filename <> "." Then
If filename <> ".." Then
*Code here*
End If
End If
Might be clunky, but it works.
The original If statement was:
If filename <> "." or filename <> ".." Then
This never worked. But then it occurred to me that maybe I needed to use a NAND statement. NAND = Not And. So I tried this:
if Not filename = "." And Not Filename = ".." then
This actually worked and seems to execute more rapidly than the earlier solution.
Option Explicit
' By John Walkenbach
' Maybe be distributed freely, but not sold
Sub GetAllFiles()
Dim Msg As String
Dim Directory
Msg = "Select the directory that contains the MP3 files. All subdirectories will be included."
Set Directory = Application.FileDialog(msoFileDialogFolderPicker)
With Directory
.Title = Msg
.AllowMultiSelect = False
.Show
If .SelectedItems.Count > 0 Then
Directory = .SelectedItems.item(1)
Else
Exit Sub
End If
End With
If Right(Directory, 1) <> "\" Then Directory = Directory & "\"
Worksheets("Sheet1").Activate
Cells.Clear
' Put column headings on active sheet
Cells(1, 1) = "Path"
Cells(1, 2) = "Filename"
Cells(1, 3) = "FullPath"
Cells(1, 4) = "Artist"
Cells(1, 5) = "Album"
Cells(1, 6) = "Title"
Cells(1, 7) = "Track#"
Cells(1, 8) = "Genre"
Cells(1, 9) = "Duration"
Cells(1, 10) = "Year"
Cells(1, 12) = "Size"
Range("1:1").Font.Bold = True
Call RecursiveDir(Directory)
End Sub
Public Sub RecursiveDir(ByVal currdir As String)
Dim Dirs() As Variant
Dim NumDirs As Long
Dim filename As String
Dim PathAndName As String
Dim i As Variant
Dim Row As Variant
' Make sure path ends in backslash
If Right(currdir, 1) <> "\" Then currdir = currdir & "\"
Application.ScreenUpdating = False
' Get files
filename = Dir(currdir & "*.*", vbDirectory)
Do While Len(filename) <> 0
DoEvents
If Not filename = "." And Not filename = ".." Then 'Current dir
PathAndName = currdir & filename
If (GetAttr(PathAndName) And vbDirectory) = vbDirectory Then
'store found directories
ReDim Preserve Dirs(0 To NumDirs) As Variant
Dirs(NumDirs) = PathAndName
NumDirs = NumDirs + 1
Else
If UCase(Right(filename, 3)) = "MP3" Then
Row = WorksheetFunction.CountA(Range("A:A")) + 1
Cells(Row, 1) = currdir 'path
Cells(Row, 2) = filename 'filename
Cells(Row, 3) = PathAndName
Cells(Row, 4) = FileInfo(currdir, filename, 20) 'artist
Cells(Row, 5) = FileInfo(currdir, filename, 14) 'album
Cells(Row, 6) = FileInfo(currdir, filename, 21) 'title
Cells(Row, 7) = FileInfo(currdir, filename, 26) 'track
Cells(Row, 8) = FileInfo(currdir, filename, 16) 'genre
Cells(Row, 9) = FileInfo(currdir, filename, 27) 'duration
Cells(Row, 10) = FileInfo(currdir, filename, 15) 'Year
Cells(Row, 11) = FileInfo(currdir, filename, 5)
Cells(Row, 12) = Application.Round(FileLen(currdir & filename) / 1024, 0) 'size
Application.StatusBar = Row
End If
End If
End If
filename = Dir()
Loop
' Process the found directories, recursively
For i = 0 To NumDirs - 1
RecursiveDir Dirs(i)
Next i
Application.StatusBar = False
End Sub
Function FileInfo(path, filename, item) As Variant
Dim objShell As IShellDispatch4
Dim objFolder As Folder3
Dim objFolderItem As FolderItem2
Set objShell = CreateObject("Shell.Application")
Set objFolder = objShell.Namespace(path)
Set objFolderItem = objFolder.ParseName(filename)
FileInfo = objFolder.GetDetailsOf(objFolderItem, item)
Set objShell = Nothing
Set objFolder = Nothing
Set objFolderItem = Nothing
End Function
Also, other fileinfo items: 27 = duration, 28 = bit rate, 26 = track number.

VBA: Microsoft Word process does not exit after combining many Word files into one

I'm trying to merge many Word files into one. I am doing this inside a VBA routine in MS Excel. The Word files are all in a folder named "files" and I want to create a new file "combinedfile.docx" in a folder one-level above that. The problem I'm facing is regarding how the Word process behaves after merging the files (whether or not it exits after the execution of the VBA function). On some machines, this process works fine (except that it has page 2 and the last page as blank), while on some others, the merged document contains a blank page and the Process Manager shows the Word process started by the VBA function as still running.
I am not used to VBA programming and as you can see in the code below, I don't know the right way to close an open document and exit a open Word process. If anyone could look at what I've done and suggest a way to solve this problem, it would be very helpful.
I am also interested to know if this is the right way to merge several Word files. If there's a better way, please let me know.
'the flow:
' start a word process to create a blank file "combinedfile.docx"
' loop over all documents in "files" folder and do the following:
' open the file, insert it at the end of combinedfile.docx, then insert pagebreak
' close the file and exit the word process
filesdir = ActiveWorkbook.Path + "\" + "files\"
thisdir = ActiveWorkbook.Path + "\"
singlefile = thisdir + "combinedfile.docx"
'if it already exists, delete
If FileExists(singlefile) Then
SetAttr singlefile, vbNormal
Kill singlefile
End If
Dim wordapp As Word.Application
Dim singledoc As Word.Document
Set wordapp = New Word.Application
Set singledoc = wordapp.Documents.Add
wordapp.Visible = True
singledoc.SaveAs Filename:=singlefile
singledoc.Close 'i do both this and the line below (is it necessary?)
Set singledoc = Nothing
wordapp.Quit
Set wordapp = Nothing
JoinFiles filesdir + "*.docx", singlefile
Sub JoinFiles(alldocs As String, singledoc As String)
Dim wordapp As Word.Application
Dim doc As Word.Document
Set wordapp = New Word.Application
Set doc = wordapp.Documents.Open(Filename:=singledoc)
Dim filesdir As String
filesdir = ActiveWorkbook.Path + "\" + "files\"
docpath = Dir(alldocs, vbNormal)
While docpath ""
doc.Bookmarks("\EndOfDoc").Range.InsertFile (filesdir + docpath)
doc.Bookmarks("\EndOfDoc").Range.InsertBreak Type:=wdPageBreak
docpath = Dir
Wend
doc.Save
doc.Close
Set doc = Nothing
wordapp.Quit
Set wordapp = Nothing
End Sub
I propose to optimize your code in following ways:
open the WordApp only once and move files into it without closing/reopening
no need to kill combineddoc upfront, it will be simply overwritten by the new file
no need for a Word.Document object, all can be done in the Word.Application object
so the code gets a lot simpler:
Sub Merge()
Dim WordApp As Word.Application
Dim FilesDir As String, ThisDir As String, SingleFile As String, DocPath As String
Dim FNArray() As String, Idx As Long, Jdx As Long ' NEW 11-Apr-2013
FilesDir = ActiveWorkbook.Path + "\" + "files\"
ThisDir = ActiveWorkbook.Path + "\"
SingleFile = ThisDir + "combinedfile.docx"
Set WordApp = New Word.Application
' NEW 11-Apr-2013 START
' read in into array
Idx = 0
ReDim FNArray(Idx)
FNArray(Idx) = Dir(FilesDir & "*.docx")
Do While FNArray(Idx) <> ""
Idx = Idx + 1
ReDim Preserve FNArray(Idx)
FNArray(Idx) = Dir()
Loop
ReDim Preserve FNArray(Idx - 1) ' to get rid of last blank element
BubbleSort FNArray
' NEW 11-Apr-2013 END
With WordApp
.Documents.Add
.Visible = True
' REMOVED 11-Apr-2013 DocPath = Dir(FilesDir & "*.docx")
' REMOVED 11-Apr-2013 Do While DocPath <> ""
' REMOVED 11-Apr-2013 .Selection.InsertFile FilesDir & DocPath
' REMOVED 11-Apr-2013 .Selection.TypeBackspace
' REMOVED 11-Apr-2013 .Selection.InsertBreak wdPageBreak
' REMOVED 11-Apr-2013 DocPath = Dir
' REMOVED 11-Apr-2013 Loop
' NEW 11-Apr-2013 START
For Jdx = 0 To Idx - 1
.Selection.InsertFile FilesDir & FNArray(Jdx)
.Selection.TypeBackspace
.Selection.InsertBreak wdPageBreak
Next Jdx
' NEW 11-Apr-2013 END
.Selection.TypeBackspace
.Selection.TypeBackspace
.Selection.Document.SaveAs SingleFile
.Quit
End With
Set WordApp = Nothing
End Sub
' NEW 11-Apr-2013 START
Sub BubbleSort(Arr)
Dim strTemp As String
Dim Idx As Long, Jdx As Long
Dim VMin As Long, VMax As Long
VMin = LBound(Arr)
VMax = UBound(Arr)
For Idx = VMin To VMax - 1
For Jdx = Idx + 1 To VMax
If Arr(Idx) > Arr(Jdx) Then
strTemp = Arr(Idx)
Arr(Idx) = Arr(Jdx)
Arr(Jdx) = strTemp
End If
Next Jdx
Next Idx
End Sub
' NEW 11-Apr-2013 END
EDIT 11-Apr-2013
removed original comments in code
added array and bubblesort logic to guarantee files are retrieved in alphabetical order

copy certain file patterns to another folder

Good afternoon, I wish to have a script that will look for all files with the name pattern LCP-??? and/or FSA-??? in c:\streetweeper and copy them to D:\java\temp\sz-files.
This script will run on startup.
I found a vbscript which has very similar functionality yet it uses a text file to read the files which need to be copied. can anyone help me transform this script to function as described above?
thanks for your time, the script is below:
Option Explicit
'The source path for the copy operation.
Const strSourceFolder = "c:\streetweeper"
'The target path for the copy operation.
Const strTargetFolder = "D:\java\temp\sz-files"
'The list of files to copy. Should be a text file with one file on each row. No paths - just file name.
Const strFileList = "C:\filelist.txt"
'Should files be overwriten if they already exist? TRUE or FALSE.
Const blnOverwrite = FALSE
Dim objFSO
Set objFSO = CreateObject("Scripting.FileSystemObject")
Const ForReading = 1
Dim objFileList
Set objFileList = objFSO.OpenTextFile(strFileList, ForReading, False)
Dim strFileToCopy, strSourceFilePath, strTargetFilePath
On Error Resume Next
Do Until objFileList.AtEndOfStream
'Read next line from file list and build filepaths
strFileToCopy = objFileList.Readline
strSourceFilePath = objFSO.BuildPath(strSourceFolder, strFileToCopy)
strTargetFilePath = objFSO.BuildPath(strTargetFolder, strFileToCopy)
'Copy file to specified target folder.
Err.Clear
objFSO.CopyFile strSourceFilePath, strTargetFilePath, blnOverwrite
If Err.Number = 0 Then
'File copied successfully
Else
'Error copying file
Wscript.Echo "Error " & Err.Number & " (" & Err.Description & "). Copying " & strFileToCopy
End If
Loop
in copystuff.cmd
REM The following statement will have no effect if the directory does exist.
mkdir D:\java\temp\sz-files
XCOPY /Y /E c:\streetweeper\LCP-*.* D:\java\temp\sz-files
IF ERRORLEVEL 0 GOTO COPYNEXT
GOTO END
:COPYNEXT
XCOPY /Y /E c:\streetweeper\FSA-*.* D:\java\temp\sz-files
IF ERRORLEVEL 0 GOTO DELETEFILES
GOTO End
:DELETEFILES
DEL /Q LCP-*.*
DEL /Q FSA-*.*
:End
OR
REM The following statement will have no effect if the directory does exist.
mkdir D:\java\temp\sz-files
MOVE /Y C:\StreetSweeper\LCP-*.* D:\Java\Temp\sz-files
MOVE /Y C:\StreetSweeper\FSA-*.* D:\Java\Temp\sz-files
Stuff in the startup folder of your start menu and you're good to go!
This is a VBScript file that copies file patterns using regular expressions. The Directory functionality is written by Christian d'Heureuse
I did not do argument parsing in full detail. So the script will only work, if source and destiation patterns contain an *.
The call should look like:
cscript myscript.vbs "C:\temp\filesToCopy_*.txt" "D:\temp\newName_*.txd"
' ___ _ _ ___
' | _ \__ _| |_| |_ ___ _ _ _ _ / __|___ _ __ _ _
' | _/ _` | _| _/ -_) '_| ' \ (__/ _ \ '_ \ || |
' |_| \__,_|\__|\__\___|_| |_||_\___\___/ .__/\_, |
' |_| |__/
' Copy files using patterns
' any given String is split into three parts {part1}{*}{part3}
' the replacement String is used in the same pattern to replace the three parts
' (c) m.wallner-novak
'
' vbCopyFile
' Usage cscript vbCopyFile.vbs {Source} {Destination}
'
Option Explicit
Main
'''
' Main Procedure
'
Sub Main
dim SourcePattern
dim DestPattern
dim sFile
if Wscript.Arguments.count = 2 then
SourcePattern = WScript.arguments(0)
DestPattern = WScript.arguments(1)
Dim a
a = ListDir(SourcePattern)
If UBound(a) = -1 then
WScript.Echo "No files found with specified source path:"
WScript.Echo SourcePattern
Exit Sub
End If
Dim FileName
dim regEx
Set regEx = new regexp 'Create the RegExp object
dim sPattern
sPattern = SourcePattern
sPattern = replace(sPattern,"\","\\")
sPattern = replace(sPattern,".","\.")
sPattern = replace(sPattern,"*",")(.*)(")
sPattern = "(" & sPattern & ")"
dim part1
dim part3
dim pos1
pos1 = instr(DestPattern,"*")
if pos1>0 then
part1 = left(DestPattern,pos1-1)
part3 = mid(DestPattern,pos1+1,999)
end if
regEx.Pattern = sPattern
regEx.IgnoreCase = True
Dim Fso
Set Fso = WScript.CreateObject("Scripting.FileSystemObject")
on error resume next
For Each FileName In a
WScript.Echo "copying """ & FileName & """ to """ & regEx.Replace(FileName, part1 & "$2" & part3) & """"
Fso.CopyFile FileName, regEx.Replace(FileName, part1 & "$2" & part3)
if err.number <> 0 then
Wscript.echo "ERROR #" & err.number & vbcrlf & err.Description
exit sub
end if
Next
else
WScript.echo "Wrong number of arguments"
WScript.echo Wscript.ScriptName & " SourcePattern DestinationPattern"
WScript.echo "SourcePattern and DestinationPattern are {part1}{*}{part3}"
WScript.echo "Example: Hello*.exe Hello_World*_prefix.exe"
WScript.echo " will copy Hello123.exe to Hello_World123_prefix.exe"
end if
end sub
'''
' Test program for the ListDir function.
' Lists file names using wildcards.
'
' Author: Christian d'Heureuse (www.source-code.biz)
' License: GNU/LGPL (http://www.gnu.org/licenses/lgpl.html)
'
' Changes:
' 2006-01-19 Extended to handle the special case of filter masks
' ending with a ".". Thanks to Dave Casey for the hint.
Sub Main2
Dim Path
Select Case WScript.Arguments.Count
Case 0: Path = "*.*" ' list current directory
Case 1: Path = WScript.Arguments(0)
Case Else: WScript.Echo "Invalid number of arguments.": Exit Sub
End Select
Dim a: a = ListDir(Path)
If UBound(a) = -1 then
WScript.Echo "No files found."
Exit Sub
End If
Dim FileName
For Each FileName In a
WScript.Echo FileName
Next
End Sub
' Returns an array with the file names that match Path.
' The Path string may contain the wildcard characters "*"
' and "?" in the file name component. The same rules apply
' as with the MSDOS DIR command.
' If Path is a directory, the contents of this directory is listed.
' If Path is empty, the current directory is listed.
' Author: Christian d'Heureuse (www.source-code.biz)
Public Function ListDir (ByVal Path)
Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
If Path = "" then Path = "*.*"
Dim Parent, Filter
if fso.FolderExists(Path) then ' Path is a directory
Parent = Path
Filter = "*"
Else
Parent = fso.GetParentFolderName(Path)
If Parent = "" Then If Right(Path,1) = ":" Then Parent = Path: Else Parent = "."
Filter = fso.GetFileName(Path)
If Filter = "" Then Filter = "*"
End If
ReDim a(10)
Dim n: n = 0
Dim Folder: Set Folder = fso.GetFolder(Parent)
Dim Files: Set Files = Folder.Files
Dim File
For Each File In Files
If CompareFileName(File.Name,Filter) Then
If n > UBound(a) Then ReDim Preserve a(n*2)
a(n) = File.Path
n = n + 1
End If
Next
ReDim Preserve a(n-1)
ListDir = a
End Function
Private Function CompareFileName (ByVal Name, ByVal Filter) ' (recursive)
CompareFileName = False
Dim np, fp: np = 1: fp = 1
Do
If fp > Len(Filter) Then CompareFileName = np > len(name): Exit Function
If Mid(Filter,fp) = ".*" Then ' special case: ".*" at end of filter
If np > Len(Name) Then CompareFileName = True: Exit Function
End If
If Mid(Filter,fp) = "." Then ' special case: "." at end of filter
CompareFileName = np > Len(Name): Exit Function
End If
Dim fc: fc = Mid(Filter,fp,1): fp = fp + 1
Select Case fc
Case "*"
CompareFileName = CompareFileName2(name,np,filter,fp)
Exit Function
Case "?"
If np <= Len(Name) And Mid(Name,np,1) <> "." Then np = np + 1
Case Else
If np > Len(Name) Then Exit Function
Dim nc: nc = Mid(Name,np,1): np = np + 1
If Strcomp(fc,nc,vbTextCompare)<>0 Then Exit Function
End Select
Loop
End Function
Private Function CompareFileName2 (ByVal Name, ByVal np0, ByVal Filter, ByVal fp0)
Dim fp: fp = fp0
Dim fc2
Do ' skip over "*" and "?" characters in filter
If fp > Len(Filter) Then CompareFileName2 = True: Exit Function
fc2 = Mid(Filter,fp,1): fp = fp + 1
If fc2 <> "*" And fc2 <> "?" Then Exit Do
Loop
If fc2 = "." Then
If Mid(Filter,fp) = "*" Then ' special case: ".*" at end of filter
CompareFileName2 = True: Exit Function
End If
If fp > Len(Filter) Then ' special case: "." at end of filter
CompareFileName2 = InStr(np0,Name,".") = 0: Exit Function
End If
End If
Dim np
For np = np0 To Len(Name)
Dim nc: nc = Mid(Name,np,1)
If StrComp(fc2,nc,vbTextCompare)=0 Then
If CompareFileName(Mid(Name,np+1),Mid(Filter,fp)) Then
CompareFileName2 = True: Exit Function
End If
End If
Next
CompareFileName2 = False
End Function

VB6: easy way to get folder name from filepath

If I have the full path of a file:
eg. c:\files\file.txt
What would be the easiest way to get the folder of this file: eg. c:\files\ ?
Use FileSystemObject.GetParentFolderName(strFullFilePath) e.g.
Dim strFullFilePath As String
strFullFilePath = "c:\files\file.txt"
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
MsgBox fso.GetParentFolderName(strFullFilePath)
Note this returns c:\file rather than c:\file\
You can use InStrRev for searching for the \, and Left$ for extracting the path bit:
filename = "c:\files\file.txt"
posn = InStrRev(filename, "\")
If posn > 0 Then
pathstr = Left$(filename, posn)
Else
pathstr = ""
End If
I'd make a function out of it for ease of use:
Function pathOfFile(fileName As String) As String
Dim posn As Integer
posn = InStrRev(fileName, "\")
If posn > 0 Then
pathOfFile = Left$(filename, posn)
Else
pathOfFile = ""
End If
End Function
' GetFilenameWithoutExtension: Return filename without extension from complete path
Public Function GetFilenameWithoutExtension(path As String) As String
Dim pos As Integer
Dim filename As String
pos = InStrRev(path, "\")
If pos > 0 Then
filename = Mid$(path, pos + 1, Len(path))
GetFilenameWithoutExtension = Left(filename, Len(filename) - Len(Mid$(filename, InStrRev(filename, "."), Len(filename))))
Else
GetFilenameWithoutExtension = ""
End If
End Function
' GetFilenameWithExtension: Return filename with extension from complete path
Public Function GetFilenameWithExtension(path As String) As String
Dim pos As Integer
pos = InStrRev(path, "\")
If pos > 0 Then
GetFilenameWithExtension = Mid$(path, pos + 1, Len(path))
Else
GetFilenameWithExtension = ""
End If
End Function
' GetDirectoryFromPathFilename: Return directory path contain filename
Public Function GetDirectoryFromPathFilename(path As String) As String
Dim pos As Integer
pos = InStrRev(path, "\")
If pos > 0 Then
GetDirectoryFromPathFilename = Left$(path, pos)
Else
GetDirectoryFromPathFilename = ""
End If
End Function

Resources