I have some files in a folder that seems to be formatted in a strange way when accessing them in VBA.
The code below is a simple test. It scans the Excel-files in a folder and simply opens and closing them again (just to test the problem). It works fine even if the filename contains Swedish characters such as å,ä,ö.
Sub ScanFiles()
Dim ExcelFile As String
Dim WB As Workbook
ExcelFile = Dir("c:\Somepath\*.xlsx")
Do While ExcelFile <> ""
Set WB = Workbooks.Open(ExcelFile)
WB.Close False
Loop
ExcelFile = Dir()
End Sub
This works fine... until... this happens:
One filename is listed as "Sömething.xlsx". Even if I take a detailed look in the file properties. It is really "Sömething.xlsx"!
But the variable ExcelFile stores "So¨mething.xlsx". The Workbooks.Open command fails since the file with that name can't be found
It seems that the specific file has been saved in an Apple environment.
Why is this happening and how do I manage it?
Related
The solution provided here by Transistor1 works perfect except for the below issue I am facing.
The output file is including quotes (") at the beginning and ending of the HTML code and its also adding an extra quote if the quote is already present.
For Example, This Code: <div style="background-color:rgba(0,0,0,0.2);padding:60px;">
Becomes Like This: <div style=""background-color:rgba(0,0,0,0.2);padding:60px;"">
I don't want any extra quotes to get added, just want the text as it is.
Please help me resolve this.
It must be the Write method in FileSystemObject that's doing it. VBA has built in file writing ability, so I'm not sure I understand the benefit of using FSO. Here's how I would do it in VBA and it handles quotes as expected.
Public Sub ExportFile()
Dim sFile As String, lFile As Long
Dim rCell As Range
Dim sFldr As String
sFldr = Environ$("userprofile") & "\My Documents\"
For Each rCell In Sheet1.UsedRange.Columns(1).Cells
sFile = sFldr & rCell.Value & ".html"
lFile = FreeFile
Open sFile For Output As lFile
Print #lFile, rCell.Offset(0, 1).Value
Close lFile
Next rCell
End Sub
To convert your Excel data to HTML, perform the following steps. These instructions apply to all "ribboned" versions of Excel 2016, 2013, 2010 and 2007:
On the workbook, go to the File tab and click Save As.
If you want to export some portion of data only, e.g. a range of cells, pivot table or graph, select it first.
In the Save As dialog, choose one of the following:
Web Page (.htm; .html). This will save your workbook or the selection to a web page and create a supporting folder that will store all of the page's supporting files such as images, bullets and background textures.
Single File Web Page (.mht; .mhl). This will save your workbook or the selection to a single file with supporting files embedded into the web page.
I have the following VBA-code
Dim directory As String, fileName As String
directory = "C:\User\Work\scorix\test_excel\"
fileName = Dir(directory & "*.xl??")
now I would like to change the code so that I would be able to read the path from a given Excel-Workbook cell and then build the fileName.
If I try
directory = Worksheets("Summary").Range("B2").Value
fileName = Dir(directory & "*.xl??")
it dose not work. It means at the end of day directory is empty and therefore fileName is empty.
In the next step I tried
With ThisWorkbook
directory = Sheets("Summary").Range("B2").Value
End With
it works! (But, why?, probably I did not understand the definition of With)
However, in the next step
fileName = Dir(directory & "*.xl??")
filename is still empty. I tried everything with ActiveSheet however, without success!
seems to me those errors occur rather arbitrary, which in my experience can happen when working with several worksheets simultaniously. Maybe replacing
directory = Worksheets("Summary").Range("B2").Value
with
directory = ThisWorkbook.Worksheets("Summary").Range("B2").Value
or alternatively (what is what i prefer to working with a range)
directory = ThisWorkbook.Worksheets("Summary").Cells(2, 2).Value
or alternatively
With ThisWorkbook
' notice the dot in Front of "worksheets"
directory = .Worksheets("Summary").Range("B2").Value
End With
fixes things.
Another situational approach might be to name your Sheet-objects in the VBA-Editor (edit the (Name) property in the property window).
Hope that helps.
P.S.
Since you use the Dir()-Function, I trust you know that in order to get the 2nd+ File, you have to call it repeatedly (maybe in a loop) without supplying a directory.
dir returns the first file in the path\pattern
to recurse you need to do DIR("") pass an empty string
directory = Worksheets("Summary").Range("B2").Value
fileName = Dir(directory & "*.xl??")
there is nothing wrong with this code u might be writing the name of the worksheet wrong maybe?
With ThisWorkbook
directory = Sheets("Summary").Range("B2").Value
End With
Don't forget about using "." before "sheets" when you are using with statements
fileName = Dir(directory & "*.xl??")
The main reason this code didn't work is propably because there are more than one files that ends with "*.xl??" in that folder
I've made a POS system for a friend. The POS system is a excel workbook that runs in a desktop computer with a touchscreen. For every sale it accesses a file in the server called products.xlms were I update products quantities.
The problem arises when a second terminal tries to access (to write) the same file at the same time in the server, because if the first computer is alreday using the file, the second computer will open it in Read only mode.
I thought I made a walkaround usign the following function that I found here at stackoverflow:
Function IsWorkBookOpen(filename As String)
Dim ff As Long, ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open filename For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
End Function
This function opens the file to check if is being used, and closes returning an Error. Depending the Error number we are able to know if its open or not.
Then I call the function whenever I want to open the file, and it will wait a second and try again if the file is open already:
Check:
Ret = IsWorkBookOpen("PATH\products.xlsx")
If Ret = True Then
Application.Wait Now() + TimeSerial(0, 0, 1)
GoTo Check
End If
Workbooks.Open("PATH\products.xlsx")
However, It doesn't work for me like this because interactions are pretty quick and the best way to show it is to explain the problematic scenario:
Terminal 1 checks if file is open: Opens file, closes, no error then variable = False. Then opens the file (2nd time) to work with.
If terminal 2 opens the file just when terminal 1 closed it for the first time, BUM, I have a problem beacuse it will think is unused (and thats true! It was actually unused in that fraction of time), and proceed to open it again (In Read only Mode cause is actually being used).
Hope is clear, I will try to clarify if not.
Any suggestion, workaround?
Thanks
I should note that I agree with Mark Butler; you're definitely making this hard on yourself by using Excel as the "database". However, what's done is done, so here's what I would try...
Sub YourSub()
Dim WB As Workbook
Dim YourFile As String
'Note the ~$ in front of the file name
YourFile = "C:\Users\USERNAME\Documents\~$Book1.xlsx"
Do While IsFileOpen(YourFile)
Loop
'File should be available to you now
Set WB = Workbooks.Open(Replace(YourFile, "~$", ""), ReadOnly:=False, Notify:=False)
End Sub
Function IsFileOpen(fPath As String) As Boolean
Dim FSO As Object 'FileSystemObject
Set FSO = CreateObject("Scripting.FileSystemObject")
If FSO.FileExists(fPath) Then
IsFileOpen = True
End If
End Function
The logic behind this code is that when another user has an Excel file open, Excel creates a 'lock file' with the ~$ prefix. This code checks for whether that lock file exists and if it doesn't it opens the file. This will be far more efficient than the workaround you posted which has to open the entire file each time it needs to check whether the file is in use (not a big deal now, but when you have thousands of rows of data it becomes a much bigger deal).
However, big caveat here, sometimes lock files won't be deleted after the file is closed. In a situation like that, your application(s) would enter an infinite loop since the lock file would perpetually exist. One way to avoid this would be to add some sort of counter so that once the loop hit the count maximum you set (e.g. 100000) it would open the file anyway and check for read only that way.
Another option would be to create a text file with your code immediately before you open the Excel file. Then when you're finished with Excel you delete the text file (basically emulating the 'lock file' I mentioned earlier). This would still be efficient and wouldn't rely on an Excel lock file. To do this try this code:
Sub YourSub()
Dim WB As Workbook
Dim CheckFile As String
Dim YourFile As String
CheckFile = "C:\Users\USERNAME\Documents\OpenCheck.txt"
Do While IsFileOpen(CheckFile)
Loop
'File should be available to you now
YourFile = "C:\Users\USERNAME\Documents\YourFile.xlsx"
Set WB = Workbooks.Open(YourFile, ReadOnly:=False, Notify:=False)
'And then when you're done with the excel file
WB.Close SaveChanges:=True
Kill CheckFile
End Sub
Function IsFileOpen(fPath As String) As Boolean
Dim FSO As Object ' FileSystemObject
Dim TS As Object ' TextStream
Set FSO = CreateObject("Scripting.FileSystemObject")
If FSO.FileExists(fPath) Then
IsFileOpen = True
Else
On Error GoTo AlreadyCreated
Set TS = FSO.CreateTextFile(Filename:=fPath, overwrite:=False)
TS.Close
End If
ExitFunc:
On Error GoTo 0
Exit Function
AlreadyCreated:
IsFileOpen = True
Resume ExitFunc
End Function
Obviously the text file would have to be saved to your server. I don't have any means right now to test this approach, but as far as I know it should work well.
Suggestion: Don't use Excel for multi-user scenarios. In fact, don't use Excel for anything except spreadsheets. It's a fantastic spreadsheet application and not much good when forced to do anything much else; database applications, POS systems etc. are not what it was designed for. Square pegs, round holes and all that.
If you are keen to stick with MS Office, why not use Access for this?
Create a front-end app which runs on each of the touchscreen terminals, and connect it to a back-end database which is stored centrally. This will save you so much headache in the future.
The problem is, that Worksbooks.Open opens the workbook too. You open the workbook in the IsWorkbookOpen Function and again to write the actual values.
You have to put the two things together.
Try to open it via Workbooks.Open ReadOnly:=False, Notify:=False, that will throw an error like your function, which you can check. Like:
Dim wkb As Workbook
On Error Resume Next
Do
'Clear existing (old) Error-Code
Err.Clear
'Try to open
Open "Path/test.xlsx" For Input Lock Read Write As #ff
Set wkb = ActiveWorkbook
If Not Err.Number = 0 Then
'Workbook is opened from another client, put Wait-code here
End If
'If Workbook is open on this client, Error-code is 0 and the loop exits
Loop Until Err.Number = 0
'Write the Values, use wkb
On Error GoTo Errorhandler 'Its always good to catch Errors in an Errorhandler
'Write the Values, use wkb
I can't test the Error-Numbers, so you have to check that for yourself.
I found a workaround.
Instead of checking first and then opening, I will open the file and then check if it is ReadOnly mode, with the code below:
Dim wkb As Workbook
Check:
'disable alerts to skip the excel message "file been used, Open Read Only?"
Application.DisplayAlerts = False
Set wkb = Workbooks.Open((Config.Range("O2").Value) & "\clientdb.xlsx", Notify:=False, ReadOnly:=False)
Application.DisplayAlerts = True
If wkb.ReadOnly Then
wkb.Close
'Wait code here
Application.Wait Now() + TimeSerial(0, 0, 1)
GoTo Check
End If
If is ReadOnly it means is open somewhere, then it will wait 1 second and try again, until it opens the file as write enabled.
The user Reen the Winter, who posted an answer has some credit because he help me think.
Thanks you all for the responses.
I have a macro setup to automatically open/save a file that I am opening from the web. The web format is a #csv.gz format. I have code that currently just saves the file in the default location (which I have changed to c:\files). I want to write a macro that will keep the filename of the file, but change the extention to just file.xlsm. Is there a way to do this with VBA/excel? The reason while I need to change the extension is because it currently does not work with my formulas. The default save code I have just saves the file as a #csv.txt.
Is this possible?
Integrate this code into your own:
Sub SaveIt()
Dim wkb As Workbook
Set wkb = ActiveWorkbook 'change to your workbook reference
wkb.SaveAs Replace(wkb.Name, ".txt", ""), 52 'change ".txt" to ".csv" if need be
End Sub
See Excel File Type Enum for more information the 52.
I need help with a coding requirement that I've not previously experienced. I just browsed a similar issue raised here a couple of years ago - VBA to Copy files using complete path and file names listed in Excel Object.
My issue is similar but somewhat simpler than the OP.
I have a number of folders that each contain about 100 small .csv files; for each folder I need to copy the path for each file to an open worksheet. Each folder of .csv files has its own associated workbook.
As one example, the open workbook is F:\SM\M400AD.xlsm and the active worksheet is CSV_List. The folder containing the .csv files is F:\SM\M400AD.
Doing it manually, my sequence is then:
Open folder F:\SM\M400AD
Select all
Copy path
Paste to Range("B11") of worksheet CSV_List
When I do it manually, as described above, I get a list that looks like:
"F:\SM\M400AD\AC1.csv"
"F:\SM\M400AD\AC2.csv"
"F:\SM\M400AD\AE.csv"
"F:\SM\M400AD\AF.csv"
"F:\SM\M400AD\AG.csv"
"F:\SM\M400AD\AH1.csv"
"F:\SM\M400AD\AH2.csv"
"F:\SM\M400AD\AJ.csv"
and on down the page until I have a list of 100 paths. This single column list is then pasted into worksheet CSV_List, starting at Range("B11").
I need to automate this and would be grateful if a VBA guru could kindly code this for me.
Such of question has been asked before, for example:
Loop through files in a folder using VBA?
List files in folder and subfolder with path to .txt file
The difference is you want to "automate" it, which means you want to execute code on workbook Open event.
How to achieve that?
Open F:\SM\M400AD.xlsm file.
Go to Code pane (ALT+F11)
Insert new module and copy below code
Option Explicit
Sub EnumCsVFilesInCurrentFolder()
Dim sPath As String, sFileName As String
Dim i As Integer
sPath = ThisWorkbook.Path & "\"
i = 11
Do
If Len(sFileName) = 0 Then GoTo SkipNext
If LCase(Right(sFileName, 4)) = ".csv" Then
'replcae 1 with proper sheet name!
ThisWorkbook.Worksheets(1).Range("B" & i) = sPath & sFileName
i = i + 1
End If
SkipNext:
sFileName = Dir(sPath)
Loop While sFileName <> ""
End Sub
Now, go to ThisWorkbook module and insert below procedure:
Private Sub Workbook_Open()
EnumCsVFilesInCurrentFolder
End Sub
Save and close workbook
The workbook is ready to use. Whenever you open it, EnumCsVFilesInCurrentFolder macro will be executed.
Note: you have to change above code to restrict the number of records.