Excel VBA module to test if hyperlink string refers to valid file - excel

I am trying to figure out a way to test if a hyperlink string in excel points to a valid file on our network. But my limited VBA knowledge seems to be getting the better of me.
Example:
The hypothetical function "HyperTest" returns TRUE or FALSE depending on Hyperlink validity.
Cell A1 Contains "c:\123.txt"
Cell B1 Contains "=IF(HyperTest(A1),"Yay, the file is real!","Looks like somebody deleted your file.")
I assume the module code should resemble something like:
Public Function HyperTest(hyperpath As String) As Boolean
If [insert test here] Then
return TRUE
Else
return FALSE
End If
End Function

You can use the Dir() function. If the path doesn't exist, the function doesn't return anything. And since the default value of a boolean is False, you don't need an Else statement.
Public Function HyperTest(hyperpath As String) As Boolean
If Dir(hyperpath) <> vbNullString Then HyperTest = True
End Function

you can use this small UDF to get your OP
Function HyperTest(c As Range)
If Dir(c) <> "" Then
HyperTest = "File exists."
Else
HyperTest = "File doesn't exist."
End If
End Function
and call this function in any cell

Related

VBA Inputbox to Prevent Decimals

I am trying to develop a tool that will help standardize a description catalog of products. I want to have an input box prompt a user to enter a size. I want to encourage size entries like "5-1/2" and prevent users from entering "5.5". Ideally, if the size was entered with a decimal and not a dash with a fraction, I want a message box to pop up saying they can not do that. It would then need to re-show the input box.
Here is what I have -
Private Sub CS_Other_Click()
Unload Me
Sheets("Fill In").Activate
Worksheets("Fill In").Range("C2").NumberFormat = "#"
Dim other_casing_size As Variant
other_casing_size = InputBox("Fill in the casing size. Syntax MUST be in the form of X-X/X", "New Casing Size")
Range("C2") = other_casing_size
I just dont know the code to prevent an entry with decimals. Even better, if i knew how to code an exact syntax to include or exclude anything I wanted that would be perfect.
Thanks
A while loop, which checks the input string for a dot or comma would work quite ok, I guess:
Sub TestMe()
Dim inputString As String
Dim inputNumeric As Boolean
inputString = InputBox("Please, enter a number!")
inputNumeric = isNumeric(Evaluate(inputString))
Do While InStr(1, inputString, ".") Or _
InStr(1, inputString, ",") Or _
Not inputNumeric
If Not CBool(inputNumeric) Then
MsgBox "You tried to cancel or entered empty value!"
Exit Do
End If
MsgBox "Please, do not write dot or comma!"
inputString = InputBox("Please, enter a number!")
inputNumeric = isNumeric(Evaluate(inputString))
Loop
End Sub
The isNumeric() checks the input for being able to be converted to numeric. Thus 5-1/2 should be ok.
Concerning cancellation or entering empty value from the InputBox() - it really depends on the business logic of the "app", but in the case above - there is a msgbox and it exits the loop.
Write a separate function responsible for that prompt, and use it e.g. like this:
Dim casingSize As String
If GetCasingSize(casingSize) Then
ActiveSheet.Range("C2").Value = casingSize
End If
The function needs to return a Boolean for this to work - it returns True if the input is valid, False if there's no valid input to work with (e.g. prompt was cancelled). What makes this work, is passing the result as a ByRef argument, like this:
Public Function GetCasingSize(ByRef outResult As String) As Boolean
Do
Dim raw As Variant
raw = InputBox("Casing size?")
If VarType(raw) = vbBoolean Then
'handle cancelled prompt:
Exit Do
End If
If ValidateFractional(raw) Then
'handle valid input:
outResult = CStr(raw)
GetCasingSize = True
Exit Do
End If
'handle invalid input:
If MsgBox("The value '" & raw & "' is not valid. Try again?", vbYesNo) = vbNo Then
Exit Do
End If
Loop
End Function
Note the ValidateFractional function is its own concern - a separate, Private function would work, but I'd recommend making it Public, and unit-testing it to make sure it works as intended given a wide variety of edge-case inputs - and having it in a separate function means the logic in GetCasingSize doesn't need to change if the validation needs to be fine-tuned; for example this naive implementation uses the Like operator and would work for 5-1/4, but not for e.g. 15-5/8:
Public Function ValidateFractional(ByVal value As String) As Boolean
ValidateFractional = value Like "#[-]#/#"
End Function
Using Regular Expressions for this would probably be a good idea.

"If strMyString.Contains, Then strMyString.Contains, Else Boolean" Procedure

I need help with a-
"If strMyString.Contains, Then strMyString.Contains, Else Boolean" Procedure
I am trying to find the name "lordgun" within a very big data Log. For all the times it's listed, I want to list it to the right of my data.
Here is the my whole attempt thus far-
Option Explicit
Public Function linecontains()lordgun As Boolean
Dim strMyString As String
Dim TargetString1 As String
Dim TargetString2 As String
'Returns True if cell contains substring, False otherwise
If strMyString.Contains("lordgun.org")
Else strMyString.Contains("") Then
End If
End Function
One way is to use the instr function.
This function returns an int. If it returns a value > 0 then the string was found.
e.g:
If Instr(1, MyString, SearchFor, 0) >0 Then
Else
End if

excel VBA return result of function

Simple Layup for anyone. I've written a function isMac() which looks at the current operating System, if the first 3 letters are Mac, it is supposed to return True. I then want to assign the return value of the function to a variable in another function. I'm currently running a Mac, so when I run the following code, the MsgBox says True, while the Debug.Print returns False
Function isMac() As Boolean
If Left(Application.OperatingSystem, 3) = "Mac" Then
result = True
Else
result = False
End If
MsgBox result
End Function
Sub test()
Debug.Print isMac()
End Sub
How do I correctly return a Boolean from my IF statement so that I can utilize it in another function?
Try assigning the result to the function.
Function isMac() As Boolean
isMac = CBool(LCase(Left(Application.OperatingSystem, 3)) = "mac")
End Function
Sub test()
Debug.Print isMac()
End Sub
Another approach would be Compiler Directives and Compiler Constants. In a module code sheet's declarations area as,
#If Mac Then
Public Const bMAC as Boolean = True
#Else
Public Const bMAC as Boolean = False
#End If
Use bMAC anywhere in your code to determine whether the OS is a Mac or not.

VBA Excel: not statement & formula

Is there a way of combining a not statement with a function in the following way:
I want it to work like this example
MsgBox not False
which returns True.
But if I add a function instead of the boolean statement
MsgBox not myFunction()
then it returns a mismatch error.
myFunction() is supposed to return True or False
Any suggestions?
You will need to give us more code, as the following works:
Public Sub test()
MsgBox Not myfunction()
End Sub
Function myfunction() As Boolean
myfunction = False
End Function
Simply
MsgBox Not Application.Evaluate(myFunction)
what is myFunction defined as? if its not :
Function myFunction(...) As Boolean
'...
End Function
that would be why, 'not' operator is reserved for Boolean (true/false)
if your function is like this and only recieves 1 and 0:
Function myFunction(...) As Integer
add this to the call :
MsgBox not CBool(myFunction(...))
or if you are actually trying to compare text:
Function myFunction(...) As String
then you will need:
EDIT: not string.compare, (StrComp(Str1, Str2, vbTextCompare) = 0)
hope that helps :)

Test if range exists in VBA

I have a dynamically defined named range in my excel ss that grabs data out of a table based on a start date and an end date like this
=OFFSET(Time!$A$1,IFERROR(MATCH(Date_Range_Start,AllDates,0)-1,MATCH(Date_Range_Start,AllDates)),1,MATCH(Date_Range_End,AllDates)-IFERROR(MATCH(Date_Range_Start,AllDates,0)-1,MATCH(Date_Range_Start,AllDates)),4)
But if the date range has no data in the table, the range doesn't exists (or something, idk). How can I write code in VBA to test if this range exists or not?
I have tried something like
If Not Range("DateRangeData") Is Nothing Then
but I get "Runtime error 1004, method 'Range' of object '_Global' failed."
Here is a function I knocked up to return whether a named range exists. It might help you out.
Function RangeExists(R As String) As Boolean
Dim Test As Range
On Error Resume Next
Set Test = ActiveSheet.Range(R)
RangeExists = Err.Number = 0
End Function
You can replicate the match in your VBA to count before using the range how many rows you would have, or you can use error handling:
On Error Resume Next
Debug.Print range("DateRangeData").Rows.Count
If Err = 1004 Then
MsgBox "Range Empty"
Exit Sub
Else
MsgBox "Range full"
End If
Err.Clear
On Error GoTo 0
This is another approach. It has the advantage to take the container and the name you want to test. That means you can test either Sheets Names or Workbook Names for example.
Like this:
If NamedRangeExists(ActiveSheet.Names, "Date") Then
...
Else
...
End If
or
If NamedRangeExists(ActiveWorkbook.Names, "Date") Then
...
Else
...
End If
Public Function NamedRangeExists(ByRef Container As Object, item As String) As Boolean
Dim obj As Object
Dim value As Variant
On Error GoTo NamedRangeExistsError:
value = Container(item)
If Not InStr(1, CStr(value), "#REF!") > 0 Then
NamedRangeExists = True
End If
Exit Function
Exit Function
NamedRangeExistsError:
NamedRangeExists = False
End Function
Depending on the application you're doing, it's good to consider using a Dictionary. They're especially useful when you wanna check whether something exists.
Take this example:
Dim dictNames as Scripting.Dictionary
Sub CheckRangeWithDictionary()
Dim nm As Name
'Initially, check whether names dictionary has already been created
If Not dictNames Is Nothing Then
'if so, dictNames is set to nothing
Set dictNames = Nothing
End If
'Set to new dictionary and set compare mode to text
Set dictNames = New Scripting.Dictionary
dictNames.CompareMode = TextCompare
'For each Named Range
For Each nm In ThisWorkbook.Names
'Check if it refers to an existing cell (bad references point to "#REF!" errors)
If Not (Strings.Right(nm.RefersTo, 5) = "#REF!") Then
'Only in that case, create a Dictionary entry
'The key will be the name of the range and the item will be the address, worksheet included
dictNames(nm.Name) = nm.RefersTo
End If
Next
'You now have a dictionary of valid named ranges that can be checked
End Sub
Within your main procedure, all you need to do is do an existence check before using the range
Sub CopyRange_MyRange()
CheckRangeWithDictionary
If dictNames.exists("MyRange") then
Sheets(1).Range("MyRange").Copy
end if
End Sub
While loading the dictionary may look a little longer, it's extremely fast to process and search. It also becomes much simpler to check whether any named range referring to a valid address exists, without using error handlers in this simple application.
Please note that when using names at sheet level rather than workbook level, it is necessary to use more elaborate keys to guarantee uniqueness. From the way the dictionary was created, if a key is repeated, the item value is overwritten. That can be avoided by using the same Exists method as a check in the key creation statement. If you need a good reference on how to use dictionaries, use this one.
Good luck!
This is an old post, but none of the rated answers has a dynamic solution to test if a name exists in a workbook or worksheet. This function below will accomplish that:
Function pg_Any_Name(thename As String) As Boolean
Dim n As Name, t As String
For Each n In ThisWorkbook.Names
t = Mid(n.Name, InStr(1, n.Name, "!", vbTextCompare) + 1, 999)
If UCase(thename) = UCase(t) Then
pg_Any_Name = True
Exit Function
End If
Next n
End Function
Worth noting that this would not have worked for this specific question because OP had a dynamic defined range. This question would have been more accurately titled Test if Name is a Valid Range because the name always existed as a formula, the issue was if it was a valid RANGE. To address this question with a solution that checks both workbook and sheets... this function would work:
Function PG_Range_Name(thename As String) As Boolean
Dim n As Name, t As String
For Each n In ThisWorkbook.Names
t = Mid(n.Name, InStr(1, n.Name, "!", vbTextCompare) + 1, 999)
If UCase(thename) = UCase(t) Then
On Error Resume Next
PG_Range_Name = n.RefersToRange.Columns.Count > 0
Exit Function
End If
Next n
End Function

Resources