Delete special character SOH with RegExp - vba

For an Access development I used GetOpenFilename function as explained here
But lpstrTitle is set with special character SOH. To delete SOH, I create a regExp function
Public Function RegParse(sStr As String) '----> sStr is lpstrTitle from getopenfiled
Dim oRegex As New RegExp
sPattern = "^.*?(?=\x01)" '--> Failed on .Test
'sPattern = ^[^\x01]* '--> successful.Test but SOH still there
'sPattern = (^.*)v(.*) '-->Ok but v deleted
.Replace(sStr, "$1")
With oRegex
.IgnoreCase = True
.pattern = sPattern
.Global = False
If .Test(sStr) Then
sStr1 = .Execute(sStr)(0)
End With
End Function
But sStr1 is still with SOH characters with sPattern = ^[^\x01]*
And command sStr1 = .replace(sStr1, “$1”) is impossible because sPattern = "^.*?(?=\x01) failed in .test
Thanks by advance for any help

Just do a simple find and replace with nothing if you want to remove a specific character:
sStr1 = Replace(sStr1, Chr(1), "", Compare := vbBinaryCompare)
vbBinaryCompare makes the find and replace binary, to avoid weirdness with control characters.

My code in a module
Public Declare Function GetOpenFileName Lib "comdlg32.dll" Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long
Public Type OPENFILENAME
lStructSize As Long
hwndOwner As Long
hInstance As Long
lpstrFilter As String
lpstrCustomFilter As String
nMaxCustFilter As Long
nFilterIndex As Long
lpstrFile As String
nMaxFile As Long
lpstrFileTitle As String
nMaxFileTitle As Long
lpstrInitialDir As String
lpstrTitle As String
flags As Long
nFileOffset As Integer
nFileExtension As Integer
lpstrDefExt As String
lCustData As Long
lpfnHook As Long
lpTemplateName As String
End Type
Public typOpenFile As OPENFILENAME
Public Function RegParse(psStr As String, psPattern As String) As String
Dim oRegex As New RegExp
Dim sStr As String, sPattern As String
sStr2 = Replace(psStr, Chr(1), "", Compare:=vbBinaryCompare)
Debug.Print sStr2
sPattern = psPattern
With oRegex
.Global = True 'restricting regex to find only first match.
.IgnoreCase = True 'ignoring cases while regex engine performs the search.
.Pattern = sPattern
If .Test(psStr) Then 'Testing if the pattern matches or not
sStr = .Execute(psStr)(0) 'will provide the String which matches with Regex
sStr1 = .Replace(psStr, "$1") '.Replace function will replace the String with whatever is in the first set of braces - $X
End If
End With
Exit_:
RegParse = sStr
Exit Function
Err_:
sStr = ""
gsMsg = Err.Number & " : " & Err.Description & Chr(13) & "Process aborted"
MsgBox gsMsg, vbCritical, "Error message"
GoTo Exit_
End Function
Public Function mfOpenFileDialog(psPathDir As String, Optional psFileCrit As String) As Boolean
Dim lReturn As Long
Dim strFilter As String
Dim sFileSelected As String
Dim bOk As Boolean
bOk = True
typOpenFile.lStructSize = Len(typOpenFile)
strFilter = "Text File (*" & psFileCrit & "*.csv)" & Chr(0) & "*" & psFileCrit & "*.csv" & Chr(0) '--> Define your filter here
With typOpenFile
.lpstrFilter = strFilter
.nFilterIndex = 1
.lpstrFile = String(257, 0)
.nMaxFile = Len(.lpstrFile) - 1
.lpstrFileTitle = .lpstrFile
.nMaxFileTitle = .nMaxFile
.lpstrInitialDir = psPathDir
.lpstrTitle = "My FileFilter Open"
.flags = 0
End With
If GetOpenFileName(typOpenFile) = 0 Then
MsgBox "No file selected", vbCritical, "Error message"
bOk = False
End If
Exit_:
mfOpenFileDialog = bOk
Exit Function
Err_:
bOk = False
gsMsg = "Function mfOpenFileDialog" & Chr(13) & Err.Number & Chr(13) & Err.Description
MsgBox gsMsg, vbCritical, "Error message"
GoTo Exit_
End Function
I call mfOpenFileDialog function from a click button event
sPathDefault = "c:\Extraction"
sFileCrit = "rapport_"
If mfOpenFileDialog(sPathDefault, sFileCrit) = False Then GoTo Exit_
sPattern = "(^.*?(?=\x01))(\x01*)"
sFileName = RegParse(typOpenFile.lpstrFile, sPattern)
That's all

In fact, the special characters are not chr(1) but chr(0) or x00
I tested with this function
Public Function mfShowChar(psStr As String)
Dim i As Integer
Dim arrChar() As Integer
For i = 1 To Len(psStr)
ReDim Preserve arrChar(i)
ArrChar(i) = Asc(Mid(psStr, i))
Next
End Function
And the both solutions work
sStr = Replace(typOpenFile.lpstrFile, Chr(0), vbNullString)
RegExp but with sPattern = "(^.?(?=\x00))(\x00)"

Related

VBA wordapp.document.open and selection.WholeStory

Thank you in advance to looking and helping.
I'm trying to open a word document, then run some code on the document's contents, and save it. Here's what I have:
wordApp.Documents.Open (strFile)
wordApp.Visible = True
wordApp.Selection.WholeStory
strAll = wordApp.Selection.XML
This seems to get a handle to the contents of the document being opened. I can change search it, etc, and it appears to be changing the contents, however when I attempt to save it:
using this:
wordApp.ActiveDocument.Save NoPrompt:=True
or using this:
wordApp.ActiveDocument.SaveAs FileName:=folderName + "\test.xml", FileFormat:=wdFormatXML
the actual saved file isn't changed. If the actual document isn't being changed, and yet the changes are being made, where would the changes being made?
I can actually open the document, not as part of processing a folder, but opening it manually and run an action that has the same code in it and it makes the changes and prompts me to save when I close it. The ValidateFolder is the sub. It opens all .xml documents in a folder and validates contents, then I need to save any changes. The code for the whole things is:
Private Sub ValidateFolder_Click()
Dim wordApp
Dim folderName As Variant
Dim fileDir As String
Dim strAll As String
Dim strFile As String
Dim arrString() As String, occurInStr() As String, fldVal As String
Dim logResults As String
Dim dispVal As String
Dim i As Integer, v As Integer
Dim lnCount As Integer
Dim charPos As Long
folderName = BrowseForFolder("C:\")
lnCount = 0
If folderName <> "" Then
MsgBox ("check " + folderName)
fileDir = Dir$(folderName + "\*", 16)
Do While fileDir <> ""
If fileDir <> "." And fileDir <> ".." Then
Rem If entry is an xml file, then check the file.
If InStr(1, fileDir, ".xml", 5) > 0 Then
Set wordApp = CreateObject("word.Application")
strFile = folderName + "\" + fileDir
wordApp.Documents.Open strFile
wordApp.Visible = True
wordApp.Selection.WholeStory
strAll = wordApp.Selection.XML
arrString = Strings.Split(strAll, "»")
MsgBox ("Opened: " + strFile)
MsgBox (CStr(strAll))
For i = 0 To UBound(arrString)
'MsgBox (CStr(UBound(arrString)))
fldVal = strRight(arrString(i), "«")
'MsgBox (fldVal)
If fldVal <> "" Then
fldVal = fldVal & "»"
occurInStr = Split(fldVal, "»")
'MsgBox ("Match-" & CStr(i + 1) & ": " & fldVal & " occurances: " & CStr(UBound(occurInStr, 1)) & " error occur: " & CStr(InStrRegEx(fldVal, "«[A-Z_! ,d+0-9]*<.*»")))
If InStrRegEx(fldVal, "«[A-Z_! ,d+0-9]*<.*»") > 0 Then
Dim repVal As String
repVal = leftOfStrRightBack(fldVal, ">")
repVal = strRight(repVal, "<")
Dim newFldVal As String
newFldVal = Replace(fldVal, repVal, "")
Dim myRange As Range
Set myRange = ActiveDocument.Content
myRange.Find.Execute FindText:=newFldVal, Forward:=True
If myRange.Find.found = True Then
myRange.Find.Execute FindText:=newFldVal, ReplaceWith:="FLDSTART" & newFldVal, Format:=True, Replace:=wdReplaceAll
End If
Set myRange = ActiveDocument.Content
myRange.Find.Execute FindText:="FLDSTART", Forward:=True
If myRange.Find.found = True Then
myRange.Find.Execute FindText:="FLDSTART", ReplaceWith:="", Format:=True, Replace:=wdReplaceAll
End If
logResults = "errors"
If logResults = "" Then
logResults = "The Following Fields had errors:" & Chr(10) & " " & newFldVal
MsgBox (logResults = "The Following Fields had errors:" & Chr(10) & " " & newFldVal)
Else
logResults = logResults & Chr(10) & " " & newFldVal
End If
End If
End If
Next
If logResults = "" Then
MsgBox ("No errors")
Else
MsgBox ("errors")
End If
If logResults = "" Then
logResults = "Success!" & Chr(10) & Chr(10) & "There were no detected errors in fields."
Else
logResults = logResults & Chr(10) & Chr(10) & "They have been fixed." & Chr(10) & "Please save this document."
End If
MsgBox (logResults)
Rem Saving and closing the document.
wordApp.ActiveDocument.Save NoPrompt:=True
MsgBox ("Save and Quit now")
'wordApp.ActiveDocument.SaveAs FileName:=folderName + "\test.xml", FileFormat:=wdFormatXML
'wordApp.ActiveDocument.SaveAs (folderName + "\" + fileDir
'MsgBox ("Saved")
Exit Sub 'Stop here so you process only one document for testing.
wordApp.Quit SaveChanges:=wdSaveChanges, OriginalFormat:=wdWordDocument
End If
End If
fileDir = Dir$()
Loop
End If
End Sub
Function BrowseForFolder(Optional OpenAt As Variant) As Variant
Dim ShellApp As Object
'Create a file browser window at the default folder
Set ShellApp = CreateObject("Shell.Application"). _
BrowseForFolder(0, "Please choose a folder", 0, OpenAt)
'Set the folder to that selected. (On error in case cancelled)
On Error Resume Next
BrowseForFolder = ShellApp.self.Path
On Error GoTo 0
'Destroy the Shell Application
Set ShellApp = Nothing
'Check for invalid or non-entries and send to the Invalid error
'handler if found
'Valid selections can begin L: (where L is a letter) or
'\\ (as in \\servername\sharename. All others are invalid
Select Case Mid(BrowseForFolder, 2, 1)
Case Is = ":"
If Left(BrowseForFolder, 1) = ":" Then GoTo Invalid
Case Is = "\"
If Not Left(BrowseForFolder, 1) = "\" Then GoTo Invalid
Case Else
GoTo Invalid
End Select
Exit Function
Invalid:
'If it was determined that the selection was invalid, set to False
BrowseForFolder = False
End Function
Public Function GetFolder() As String
Dim fldr As FileDialog
Dim sItem As String
Set fldr = Application.FileDialog(msoFileDialogFolderPicker)
MsgBox (fldr)
With fldr
.Title = "Select a Folder"
.AllowMultiSelect = False
.InitialFileName = OpenAt 'Application.DefaultFilePath
If .Show <> -1 Then GoTo NextCode
sItem = .SelectedItems(1)
End With
NextCode:
GetFolder = sItem
Set fldr = Nothing
End Function
Public Function leftOfStrRightBack(ByVal searchIn As String, ByVal searchFor As String) As String
Dim charPos As Long
Dim retStr As String
retStr = ""
If InStr(1, searchIn, searchFor, 5) > 0 Then
charPos = Len(searchIn)
While charPos > 0
If CStr(Mid(searchIn, charPos, 1)) = searchFor Then
'MsgBox ("Searched: " & searchIn & " found: " & searchFor & " at pos: " & charPos)
retStr = CStr(Mid(searchIn, 1, charPos))
'MsgBox ("Return: " & retStr)
GoTo BreakOut
End If
charPos = charPos - 1
Wend
BreakOut:
End If
leftOfStrRightBack = CStr(retStr)
End Function
Public Function strRight(ByVal searchIn As String, ByVal searchFor As String) As String
Dim charPos As Long
Dim retStr As String
retStr = ""
charPos = InStr(1, searchIn, searchFor, 5)
If charPos > 0 Then
retStr = CStr(Mid(searchIn, charPos, Len(searchIn)))
End If
'CStr(CStr(Mid(arrString(i), charPos, Len(arrString(i))) & "»"))
strRight = CStr(retStr)
End Function
Public Function InStrRegEx(ByVal searchIn As String, ByVal searchFor As String) As Long
Dim regEx As Object, found As Object
If Len(searchIn) > 0 And Len(searchFor) > 0 Then
Set regEx = CreateObject("VBScript.RegExp")
regEx.Pattern = searchFor
regEx.Global = True
regEx.IgnoreCase = True
Set found = regEx.Execute(searchIn)
If found.Count <> 0 Then InStrRegEx = found(0).FirstIndex + 1
End If
End Function
Public Function getText(ByVal searchIn As String, ByVal searchFor As String) As String
Dim regEx As Object, found As Object
If Len(searchIn) > 0 And Len(searchFor) > 0 Then
Set regEx = CreateObject("VBScript.RegExp")
regEx.Pattern = searchFor
regEx.Global = True
regEx.IgnoreCase = True
Set found = regEx.Execute(searchIn)
If found.Count <> 0 Then getText = CStr(found(0))
End If
End Function

Connecting Excel spreadsheet with Google maps while using VBA

I have found a code on which helps me export both travel time and a distance between 2 locations from Google Maps, while using Excel spreadsheet.
It works fine, but one thing I am missing is an opportunity to add an in-between point. Do you think it is doable to modify the code so I could have 3th cell with in-between point?
Please find code below:
' Usage :
' GetGoogleTravelTime (strFrom, strTo) returns a string containing journey duration : hh:mm
' GetGoogleDistance (strFrom, strTo) returns a string containing journey distance in either miles or km (as defined by strUnits)
' GetGoogleDirections (strFrom, strTo) returns a string containing the directions
'
' where strFrom/To are address search terms recognisable by Google
' i.e. Postcode, address etc.
'
' by Desmond Oshiwambo
Const strUnits = "metric" ' imperial/metric (miles/km)
Function CleanHTML(ByVal strHTML)
'Helper function to clean HTML instructions
Dim strInstrArr1() As String
Dim strInstrArr2() As String
Dim s As Integer
strInstrArr1 = Split(strHTML, "<")
For s = LBound(strInstrArr1) To UBound(strInstrArr1)
strInstrArr2 = Split(strInstrArr1(s), ">")
If UBound(strInstrArr2) > 0 Then
strInstrArr1(s) = strInstrArr2(1)
Else
strInstrArr1(s) = strInstrArr2(0)
End If
Next
CleanHTML = Join(strInstrArr1)
End Function
Public Function formatGoogleTime(ByVal lngSeconds As Double)
'Helper function. Google returns the time in seconds, so this converts it into time format hh:mm
Dim lngMinutes As Long
Dim lngHours As Long
lngMinutes = Fix(lngSeconds / 60)
lngHours = Fix(lngMinutes / 60)
lngMinutes = lngMinutes - (lngHours * 60)
formatGoogleTime = Format(lngHours, "00") & ":" & Format(lngMinutes, "00")
End Function
Function gglDirectionsResponse(ByVal strStartLocation, ByVal strEndLocation, ByRef strTravelTime, ByRef strDistance, ByRef strInstructions, Optional ByRef strError = "") As Boolean
On Error GoTo errorHandler
' Helper function to request and process XML generated by Google Maps.
Dim strURL As String
Dim objXMLHttp As Object
Dim objDOMDocument As Object
Dim nodeRoute As Object
Dim lngDistance As Long
Set objXMLHttp = CreateObject("MSXML2.XMLHTTP")
Set objDOMDocument = CreateObject("MSXML2.DOMDocument.6.0")
strStartLocation = Replace(strStartLocation, " ", "+")
strEndLocation = Replace(strEndLocation, " ", "+")
strURL = "http://maps.googleapis.com/maps/api/directions/xml" & _
"?origin=" & strStartLocation & _
"&destination=" & strEndLocation & _
"&sensor=false" & _
"&units=" & strUnits 'Sensor field is required by google and indicates whether a Geo-sensor is being used by the device making the request
'Send XML request
With objXMLHttp
.Open "GET", strURL, False
.setRequestHeader "Content-Type", "application/x-www-form-URLEncoded"
.Send
objDOMDocument.LoadXML .ResponseText
End With
With objDOMDocument
If .SelectSingleNode("//status").Text = "OK" Then
'Get Distance
lngDistance = .SelectSingleNode("/DirectionsResponse/route/leg/distance/value").Text ' Retrieves distance in meters
Select Case strUnits
Case "imperial": strDistance = Round(lngDistance * 0.00062137, 1) 'Convert meters to miles
Case "metric": strDistance = Round(lngDistance / 1000, 1) 'Convert meters to miles
End Select
'Get Travel Time
strTravelTime = .SelectSingleNode("/DirectionsResponse/route/leg/duration/value").Text 'returns in seconds from google
strTravelTime = formatGoogleTime(strTravelTime) 'converts seconds to hh:mm
'Get Directions
For Each nodeRoute In .SelectSingleNode("//route/leg").ChildNodes
If nodeRoute.BaseName = "step" Then
strInstructions = strInstructions & nodeRoute.SelectSingleNode("html_instructions").Text & " - " & nodeRoute.SelectSingleNode("distance/text").Text & vbCrLf
End If
Next
strInstructions = CleanHTML(strInstructions) 'Removes MetaTag information from HTML result to convert to plain text.
Else
strError = .SelectSingleNode("//status").Text
GoTo errorHandler
End If
End With
gglDirectionsResponse = True
GoTo CleanExit
errorHandler:
If strError = "" Then strError = Err.Description
strDistance = -1
strTravelTime = "00:00"
strInstructions = ""
gglDirectionsResponse = False
CleanExit:
Set objDOMDocument = Nothing
Set objXMLHttp = Nothing
End Function
Function getGoogleTravelTime(ByVal strFrom, ByVal strTo) As String
'Returns the journey time between strFrom and strTo
Dim strTravelTime As String
Dim strDistance As String
Dim strInstructions As String
Dim strError As String
If gglDirectionsResponse(strFrom, strTo, strTravelTime, strDistance, strInstructions, strError) Then
getGoogleTravelTime = strTravelTime
Else
getGoogleTravelTime = strError
End If
End Function
Function getGoogleDistance(ByVal strFrom, ByVal strTo) As String
'Returns the distance between strFrom and strTo
'where strFrom/To are address search terms recognisable by Google
'i.e. Postcode, address etc.
Dim strTravelTime As String
Dim strDistance As String
Dim strError As String
Dim strInstructions As String
If gglDirectionsResponse(strFrom, strTo, strTravelTime, strDistance, strInstructions, strError) Then
getGoogleDistance = strDistance
Else
getGoogleDistance = strError
End If
End Function
Function getGoogleDirections(ByVal strFrom, ByVal strTo) As String
'Returns the directions between strFrom and strTo
'where strFrom/To are address search terms recognisable by Google
'i.e. Postcode, address etc.
Dim strTravelTime As String
Dim strDistance As String
Dim strError As String
Dim strInstructions As String
If gglDirectionsResponse(strFrom, strTo, strTravelTime, strDistance, strInstructions, strError) Then
getGoogleDirections = strInstructions
Else
getGoogleDirections = strError
End If
End Function
Thank you,
Przemek

How to generate and send an email using Mozilla Thunderbird through Excel VBA

I've been looking into trying to use VBA Macro's to send an email through Mozilla Thunderbird with the spreadsheet as an attachment.
///I've searched Google and Stack Overflow itself and none of those solutions seem to be working./// I am not the best at coding or excel itself so I was just wondering if any kind soul could help me out?
Appreciate any help given.
Regards,
Looked at a load more articles and tried following what the comments have said but they didn't help. I have, however, managed to get the email portion of this to work myself. Below is the code I use
Private Declare Function ShellExecute Lib "shell32.dll" _
Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal lpOperation As String, _
ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As
String, _
ByVal nShowCmd As Long) As Long
Sub Send_Email_Using_Keys()
Dim Mail_Object As String
Dim Email_Subject, Email_Send_To, Email_Cc, Email_Bcc, Email_Body As String
Email_Subject = "ACT Form Completed and Confirmed"
Email_Send_To = "kieranfarley#achievementtraining.com"
Email_Cc = "kieranfarley#achievementtraining.com"
Email_Bcc = "kieranfarley#achievementtraining.com"
Email_Body = "ACT Form Completed and Confirmed Please see attached"
Mail_Object = "mailto:" & Email_Send_To & "?subject=" & Email_Subject &
"&body=" & Email_Body & "&cc=" & Email_Cc & "&bcc=" & Email_Bcc
On Error GoTo debugs
ShellExecute 0&, vbNullString, Mail_Object, vbNullString, vbNullString,
vbNormalFocus
Application.Wait (Now + TimeValue("0:00:02"))
Application.SendKeys "%s"
debugs:
If Err.Description <> "" Then MsgBox Err.Description
End Sub
This opened the 'Write' box in thunderbird with all the fields pre-filled out ready to send.
Found some old code. Not recently tested but it worked with attachments for Thunderbird. You probably have to adapt it to your needs:
'***********************************************************************
'* Send mail with Thunderbird
'*
Option Explicit
'***********************
'* HTML formatting
'*
Private Const STARTBODY = "<html><head><style type='text/css'> body { font: 11pt Calibri, Verdana, Geneva, Arial, Helvetica, sans-serif; } </style></head><body> "
Private Const ENDBODY = "</body></htlm>"
'* Test only
Private Const ATTACHMENT1 = "C:\Temp\attachment1.pdf"
Private Const ATTACHMENT2 = "C:\Temp\attachment2.pdf"
'*******************************************************************************************
'* Test code only. Can be run by placing the cursor anywhere within the code and press F5
'* SetX THUNDERBIRD_PATH "C:\Program Files\Mozilla Thunderbird\thunderbird.exe"
'*
Private Sub MailTest()
Dim Rcp As String
Dim CC As String
Dim BCC As String
Dim Result As Boolean
Rcp = "someone#domain.com"
CC = "someoneelse#domain.com"
BCC = "onedude#domain.com"
Result = SendMail(Rcp, CC, BCC, "Test", "Hello World", False, ATTACHMENT1 & ";" & ATTACHMENT2)
End Sub
'****************************************************************************
'* Send e-mail through Thunderbird
'* SetX THUNDERBIRD_PATH "C:\Program Files\Mozilla Thunderbird\thunderbird.exe"
'*
Function SendMail(strTo As String, _
strCC As String, _
strBCC As String, _
strSubject As String, _
strMessageBody As String, _
Optional PlainTextFormat As Boolean = False, _
Optional strAttachments As String = "", _
Optional SignatureFile As String = "") As Boolean
Dim Cmd As String
Dim Arg As String
Dim Result As Integer
Dim objOutlook As Outlook.Application
Dim MAPISession As Outlook.NameSpace
Dim MAPIMailItem As Outlook.MailItem
Dim strTemp As String
Dim MailResult As Boolean
Dim I As Integer
Dim Account As Object
MailResult = False
Cmd = Environ("THUNDERBIRD_PATH") 'E:\Program Files\Mozilla Thunderbird\thunderbird.exe
If Cmd <> "" Then ' Thunderbird installed
Arg = " -compose """
strTo = Replace(strTo, ";", ",")
If strTo <> "" Then Arg = Arg & "to='" & strTo & "',"
strCC = Replace(strCC, ";", ",")
If strCC <> "" Then Arg = Arg & "cc='" & strCC & "',"
strBCC = Replace(strBCC, ";", ",")
If strBCC <> "" Then Arg = Arg & "bcc='" & strBCC & "',"
If strSubject <> "" Then Arg = Arg & "subject=" & strSubject & ","
If PlainTextFormat = True Then
strTemp = "2" 'Plain text
Else
strTemp = "1" 'HTML
strMessageBody = STARTBODY & strMessageBody & ENDBODY 'Add HTML and CSS
End If
Arg = Arg & "format=" & strTemp & "," 'Format specifier HTML or Plain Text
Arg = Arg & "body='" & strMessageBody & "'," 'Add body text
Call AddSignature(SignatureFile, strMessageBody) 'Add signature if any
Arg = Arg & "attachment='"
Call AddAttachments(strAttachments, , Arg) 'Add attachment(s) if any
Arg = Arg & "'""" 'Closing quotes
Shell Cmd & Arg 'Call Thunderbird to send the message
MailResult = True
SendMail = MailResult
End Function
'*******************************************************************
'* Add recipients, CC or BCC recipients to the email message
'* Recipients is a string with one or more email addresses,
'* each separated with a semicolon
'* Returns number of addresses added
'*
Private Function AddRecipients(Recipients As String, MAPIMailItem As Outlook.MailItem, RecType As Integer) As Integer
Dim OLRecipient As Outlook.Recipient
Dim TempArray() As String
Dim Recipient As Variant
Dim Emailaddr As String
Dim Count As Integer
Count = 0
TempArray = Split(Recipients, ";")
For Each Recipient In TempArray
Emailaddr = Trim(Recipient)
If Emailaddr <> "" Then
Set OLRecipient = MAPIMailItem.Recipients.Add(Emailaddr)
OLRecipient.Type = RecType
Set OLRecipient = Nothing
Count = Count + 1
End If
Next Recipient
AddRecipients = Count
End Function
'******************************************************
'* Add possible signature to the email message
'* Returns True if signature added
'*
Private Function AddSignature(SignatureFile As String, ByRef strMessageBody As String) As Boolean
Dim Signature As String
Dim Tempstr As String
Dim Added As Boolean
Added = False
If SignatureFile <> "" Then
Signature = ""
Open SignatureFile For Input As #1 'Open file for reading
Do While Not EOF(1) 'Loop through file
Input #1, Tempstr 'One line
Signature = Signature & Tempstr 'Add it
Loop
Close #1
strMessageBody = strMessageBody & Signature 'Add to message
Added = True
End If
AddSignature = Added
End Function
'******************************************************
'* Add possible attachments to the email message
'* Returns number of attachments added
'*
Private Function AddAttachments(ByRef strAttachments As String) As Integer
Dim TempArray() As String
Dim Attachment As Variant
Dim Tempstr As String
Dim Count As Integer
Count = 0
TempArray = Split(strAttachments, ";")
For Each Attachment In TempArray
Tempstr = CStr(Trim(Attachment))
If Tempstr <> "" Then
If Count > 0 Then Arg = Arg & ","
Arg = Arg & "file:///" & Tempstr
End If
Count = Count + 1
Next Attachment
AddAttachments = Count
End Function
The code below iterates through a range in excel and for each record marked for sending it will send an email using Thunderbird. Additionally, if the path to a file is specified it will attach that file. Be careful with the apostrophes when building the command string. If you get them wrong the non-printing characters will be removed from the message body for some reason.
Public Sub sendEmail(subject As String, msg As String, path As String)
Dim contactRange As Range, cell As Range
Dim count As Integer
Dim thund As String
Dim email As String
Dim recipientName As String
Dim pathToThunderBird
Set contactRange = Range("ContactYesNo")
pathToThunderBird = "C:\Program Files (x86)\Mozilla Thunderbird\thunderbird.exe "
With Worksheets("IT consulting")
For Each cell In contactRange
If cell.Value = "Yes" Then
count = count + 1
recipientName = cell.Offset(0, 2).Value
email = cell.Offset(0, 6).Value
emailMsg = "Hi " & recipientName & vbCrLf & vbCrLf & msg & vbCrLf
'You'll want to change the salutation.
thund = pathToThunderBird & _
"-compose " & """" & _
"to='" & email & "'," & _
",subject='" & subject & "'," & _
",body='" & emailMsg & vbCrLf & vbCrLf & _
"Your Name" & vbCrLf & _
"123.456.7890" & "'" & """"
If path = "" Then 'no attachment
'do nothing
Else 'with attachment
thund = thund & ",attachment=" & path
End If
Call Shell(thund, vbNormalFocus)
'comment this out if you do not want to send automatically
SendKeys "^+{ENTER}", True
End If
Next cell
End With
End Sub

Regex for VBA Excel macro for new folder chars

I have a function which is Boolean, and returns whether is the cell OK for creating a New Folder based on its value or its not (if it posses following chars:<,>,|,\,*,?)
But from some weird reason, it returns always false, either is a cell OK or not.
So, I have a sub which creates a loop for all rows and creates some .txt files and puts it in auto-generated folders.
Here is my code:
Sub CreateTxtSrb()
Dim iRow As Long
Dim iFile As Integer
Dim sPath As String
Dim sFile As String
Dim iEnd As Range
'iEnd = Cells(Rows.Count, "B").End(xlUp).Row
For iRow = 1 To Cells(Rows.Count, "B").End(xlUp).Row
iFile = FreeFile
With Rows(iRow)
If IsValidFolderName(.Range("B2").Value) = False Or IsValidFolderName(.Range("D2").Value) = False Or IsValidFolderName(.Range("F2").Value) = False Then
MsgBox ("Check columns B,D or F, it cannot contains chars: <,>,?,|,\,/,*,. or a space at the end")
Exit Sub
Else
strShort = IIf(InStr(.Range("E2").Value, vbCrLf), Left(.Range("E2").Value, InStr(.Range("E2").Value, vbCrLf) - 2), .Range("E2").Value)
sPath = "E:\" & .Range("B2").Value & "\"
If Len(Dir(sPath, vbDirectory)) = 0 Then MkDir sPath
sFile = .Range("D2").Value & ".txt"
Open sPath & sFile For Output As #iFile
Print #iFile, .Range("E2").Value
Close #iFile
End If
End With
Next iRow
End Sub
Function IsValidFolderName(ByVal sFolderName As String) As Boolean
'http://msdn.microsoft.com/en- us/library/windows/desktop/aa365247(v=vs.85).aspx#file_and_directory_names
'http://msdn.microsoft.com/en-us/library/ie/ms974570.aspx
On Error GoTo Error_Handler
Dim oRegEx As Object
'Check to see if any illegal characters have been used
Set oRegEx = CreateObject("vbscript.regexp")
oRegEx.Pattern = "[<>:""/\\\|\?\*]"
IsValidFolderName = Not oRegEx.test(sFolderName)
'Ensure the folder name does end with a . or a blank space
If Right(sFolderName, 1) = "." Then IsValidFolderName = False
If Right(sFolderName, 1) = " " Then IsValidFolderName = False
Error_Handler_Exit:
On Error Resume Next
Set oRegEx = Nothing
Exit Function
Error_Handler:
MsgBox ("test")
' MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
' "Error Number: " & Err.Number & vbCrLf & vbCrLf & _
' "Error Source: IsInvalidFolderName" & vbCrLf & _
' "Error Description: " & Err.Description, _
' vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
How can I make it return true if need be?
You don't need the external reference you can simply:
hasInvalidChars = sFolderName like "*[<>|\/:*?""]*"
I added " and : which are also illegal.
(In your example you have HTML entities (E.g. <) - these have no meaning in your RegEx string and are interpreted as 4 characters in the class)
That's a mess. Use a separate function
Public Function IsInvalid(ByVal name As String) As Boolean
Dim regex As Object
Set regex = VBA.CreateObject("VBScript.RegExp")
regex.Pattern = "[\\/:\*\?""<>\|]" 'the disallowed characters
IsInvalid = (regex.Execute(name).Count > 0)
End Function
instead, and call it when appropriate.

Displaying MacroOptions

In Excel/VBA it's possible to define some informations relative to a macro or function using the function MacroOptions. Is it possible to access such information once entered via VBA ? Thank you
I've been searching for a while but I found nothing great.
The only workaround I found is to use the code build by Chip Pearson and described on his website.
With this code, you can get some general information about a procedure.
Public Enum ProcScope
ScopePrivate = 1
ScopePublic = 2
ScopeFriend = 3
ScopeDefault = 4
End Enum
Public Enum LineSplits
LineSplitRemove = 0
LineSplitKeep = 1
LineSplitConvert = 2
End Enum
Public Type ProcInfo
ProcName As String
ProcKind As VBIDE.vbext_ProcKind
ProcStartLine As Long
ProcBodyLine As Long
ProcCountLines As Long
ProcScope As ProcScope
ProcDeclaration As String
End Type
Function ProcedureInfo(ProcName As String, ProcKind As VBIDE.vbext_ProcKind, _
CodeMod As VBIDE.CodeModule) As ProcInfo
Dim PInfo As ProcInfo
Dim BodyLine As Long
Dim Declaration As String
Dim FirstLine As String
BodyLine = CodeMod.ProcStartLine(ProcName, ProcKind)
If BodyLine > 0 Then
With CodeMod
PInfo.ProcName = ProcName
PInfo.ProcKind = ProcKind
PInfo.ProcBodyLine = .ProcBodyLine(ProcName, ProcKind)
PInfo.ProcCountLines = .ProcCountLines(ProcName, ProcKind)
PInfo.ProcStartLine = .ProcStartLine(ProcName, ProcKind)
FirstLine = .Lines(PInfo.ProcBodyLine, 1)
If StrComp(Left(FirstLine, Len("Public")), "Public", vbBinaryCompare) = 0 Then
PInfo.ProcScope = ScopePublic
ElseIf StrComp(Left(FirstLine, Len("Private")), "Private", vbBinaryCompare) = 0 Then
PInfo.ProcScope = ScopePrivate
ElseIf StrComp(Left(FirstLine, Len("Friend")), "Friend", vbBinaryCompare) = 0 Then
PInfo.ProcScope = ScopeFriend
Else
PInfo.ProcScope = ScopeDefault
End If
PInfo.ProcDeclaration = GetProcedureDeclaration(CodeMod, ProcName, ProcKind, LineSplitKeep)
End With
End If
ProcedureInfo = PInfo
End Function
Public Function GetProcedureDeclaration(CodeMod As VBIDE.CodeModule, _
ProcName As String, ProcKind As VBIDE.vbext_ProcKind, _
Optional LineSplitBehavior As LineSplits = LineSplitRemove)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' GetProcedureDeclaration
' This return the procedure declaration of ProcName in CodeMod. The LineSplitBehavior
' determines what to do with procedure declaration that span more than one line using
' the "_" line continuation character. If LineSplitBehavior is LineSplitRemove, the
' entire procedure declaration is converted to a single line of text. If
' LineSplitBehavior is LineSplitKeep the "_" characters are retained and the
' declaration is split with vbNewLine into multiple lines. If LineSplitBehavior is
' LineSplitConvert, the "_" characters are removed and replaced with vbNewLine.
' The function returns vbNullString if the procedure could not be found.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim LineNum As Long
Dim S As String
Dim Declaration As String
On Error Resume Next
LineNum = CodeMod.ProcBodyLine(ProcName, ProcKind)
If Err.Number <> 0 Then
Exit Function
End If
S = CodeMod.Lines(LineNum, 1)
Do While Right(S, 1) = "_"
Select Case True
Case LineSplitBehavior = LineSplitConvert
S = Left(S, Len(S) - 1) & vbNewLine
Case LineSplitBehavior = LineSplitKeep
S = S & vbNewLine
Case LineSplitBehavior = LineSplitRemove
S = Left(S, Len(S) - 1) & " "
End Select
Declaration = Declaration & S
LineNum = LineNum + 1
S = CodeMod.Lines(LineNum, 1)
Loop
Declaration = SingleSpace(Declaration & S)
GetProcedureDeclaration = Declaration
End Function
Private Function SingleSpace(ByVal Text As String) As String
Dim Pos As String
Pos = InStr(1, Text, Space(2), vbBinaryCompare)
Do Until Pos = 0
Text = Replace(Text, Space(2), Space(1))
Pos = InStr(1, Text, Space(2), vbBinaryCompare)
Loop
SingleSpace = Text
End Function
You can call the ProcedureInfo function using code like the following:
Sub ShowProcedureInfo()
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim CompName As String
Dim ProcName As String
Dim ProcKind As VBIDE.vbext_ProcKind
Dim PInfo As ProcInfo
CompName = "modVBECode"
ProcName = "ProcedureInfo"
ProcKind = vbext_pk_Proc
Set VBProj = ActiveWorkbook.VBProject
Set VBComp = VBProj.VBComponents(CompName)
Set CodeMod = VBComp.CodeModule
PInfo = ProcedureInfo(ProcName, ProcKind, CodeMod)
Debug.Print "ProcName: " & PInfo.ProcName
Debug.Print "ProcKind: " & CStr(PInfo.ProcKind)
Debug.Print "ProcStartLine: " & CStr(PInfo.ProcStartLine)
Debug.Print "ProcBodyLine: " & CStr(PInfo.ProcBodyLine)
Debug.Print "ProcCountLines: " & CStr(PInfo.ProcCountLines)
Debug.Print "ProcScope: " & CStr(PInfo.ProcScope)
Debug.Print "ProcDeclaration: " & PInfo.ProcDeclaration
End Sub