Function doesn't get variable value? - vba

I'm writing an automation script that will collect data from 4 emails that arrive during a day and sending a report email at the end of the day, I already figured out how to append text file that will be sent and how to search email for specific string that tell me if everything's alright.
But I have an issue. I put some code into Subs and Functions and it stopped work. itm.Body is not visible in Function CheckSafeSet, and what strange Else don't run if IF isn't ture.
Please help. If you have any suggestions to do it differently, smarter better simpler please tell me
Thanks Michal
Public Sub AppendTextFiles(safeset As String)
Open "C:\AppSupport\testfilew.txt" For Append As #1
Print #1, safeset
Close #1
End Sub
Function CheckSafeSet(safeset As String)
MsgBox ("1")
MsgBox (itm.Body)
If itm.Body Like safeset Then
MsgBox ("2")
Call AppendTextFiles("adfsdfasdfsergedgrrt")
Else:
MsgBox ("FAIL")
End If
End Function
Public Sub process_email(itm As Outlook.MailItem)
'Strings - Savegroups
Dim d1000i As String
Dim d1200i As String
Dim l0001i As String
l0001i = "*Savegroup: VNX_UK_NDMP_00:01*"
Dim l2000i As String
Dim lonparch01 As String
'lonparch01 = "*pnwifsvbbup03.r3-core.r3.aig.net:/root_vdm_1/vol_lonparch01_snap 42927:nsrndmp_save: Successfully done*"
lonparch01 = "*NDMP*"
Dim new_msg As MailItem
If itm.Body Like l0001i Then
MsgBox (itm.Body)
Call CheckSafeSet(lonparch01)
Else:
End If
End Sub

The issue is that you have not passed the itm object to the CheckSafeSet Function. YOu need to add itm As Outlook.MailItem to the signature of the CheckSafeSet Function and then pass the itm object
Public Sub AppendTextFiles(safeset As String)
Open "C:\AppSupport\testfilew.txt" For Append As #1
Print #1, safeset
Close #1
End Sub
Function CheckSafeSet(safeset As String, itm As Outlook.MailItem)
MsgBox ("1")
MsgBox (itm.Body)
If itm.Body Like safeset Then
MsgBox ("2")
Call AppendTextFiles("adfsdfasdfsergedgrrt")
Else
MsgBox ("FAIL")
End If
End Function
Public Sub process_email(itm As Outlook.MailItem)
'Strings - Savegroups
Dim d1000i As String
Dim d1200i As String
Dim l0001i As String
l0001i = "*Savegroup: VNX_UK_NDMP_00:01*"
Dim l2000i As String
Dim lonparch01 As String
'lonparch01 = "*pnwifsvbbup03.r3-core.r3.aig.net:/root_vdm_1/vol_lonparch01_snap 42927:nsrndmp_save: Successfully done*"
lonparch01 = "*NDMP*"
Dim new_msg As MailItem
If itm.Body Like l0001i Then
MsgBox (itm.Body)
Call CheckSafeSet(lonparch01, itm)
Else
End If
End Sub

Related

Access VBA calling function does nothing when run

I have a macro that I'm trying to build in Access which will change the source table on a set of queries. Here is what I've built (based off already written code that I found on a forum):
Function ReplaceSources()
Call SourceQueries
End Function
Sub SourceQueries()
Call UpdateSource("YYYY_Count_of_items_by_floor", Building_Audit_2021, Building_Audit_YYYY)
End Sub
Sub UpdateSource(QueryName, CurrentSourceTable, NewSourceTable)
Dim strQryName, strCTbl, strNTbl, strCsql, strNsql As String
Dim defqry As DAO.QueryDef
strQryName = QueryName
strCTbl = CurrentSourceTable
strNTbl = NewSourceTable
Set defqry = CurrentDb.QueryDefs(strQryName)
strCsql = defqry.SQL
strNsql = Replace(strCsql, strCTbl, strNTbl)
defqry.SQL = strNsql
defqry.Close
End Sub
When I use the RunCode option in the macro builder using function name ReplaceSources(), nothing happens. I get no errors, I can step through the code with no issues, and adding Debug.Print lines throughout the function and subs does nothing. What is preventing this function from doing anything?
ETA:
Maybe it will help if I add the other two parts of code that I'm piecing together with this one. Before the above code, I run:
Function Copy_audit_table()
On Error GoTo Copy_audit_table_Err
Dim strPath As String
strPath = CurrentProject.FullName
DoCmd.TransferDatabase acImport, "Microsoft Access", strPath, acTable, "Building_Audit_2021", "Building_Audit_YYYY", True
DoCmd.CopyObject "", "YYYY_Count_of_items_by_floor", acQuery, "2021_Count_of_items_by_floor"
Copy_audit_table_Exit:
Exit Function
Copy_audit_table_Err:
MsgBox Error$
Resume Copy_audit_table_Exit
End Function
Then after the code in question, I run:
Function Copy_audit_table_rename()
On Error GoTo Copy_audit_table_rename_Err
Dim AuditYear As Variant
AuditYear = InputBox("Enter audit year (YYYY)")
Dim strPath As String
strPath = CurrentProject.FullName
DoCmd.Rename "Building_Audit_" & AuditYear, acTable, "Building_Audit_YYYY"
DoCmd.Rename AuditYear & "_Count_of_items_by_floor", acQuery, "YYYY_Count_of_items_by_floor"
Copy_audit_table_rename_Exit:
Exit Function
Copy_audit_table_rename_Err:
MsgBox Error$
Resume Copy_audit_table_rename_Exit
End Function
I'm not too familiar with VBA, so most of this is code that I found elsewhere that I was able to piece together. I know that I can use the macro builder to run each Function, but I really don't know any other ways. If there are any recommended tutorials that will help me code what I want to do, I'd like to read them.
You must execute the query for something to happen.
And skip the macro and the function. All you need is to call:
Sub UpdateSource(QueryName As String, CurrentSourceTable As String, NewSourceTable As String)
Dim strCsql As String
Dim defqry As DAO.QueryDef
Set defqry = CurrentDb.QueryDefs(QueryName)
strCsql = defqry.SQL
strNsql = Replace(strCsql, CurrentSourceTable, NewSourceTable)
defqry.SQL = strNsql
defqry.Execute
defqry.SQL = strCsql
defqry.Close
End Sub

Email body is empty without .Display

I've read this: VBA Outlook 2010 received mail .Body is empty but it is old and the other question referenced in the answer(s) is not found when I click on it.
Here's my basic code.
Sub AutoReplyTrap(objInMail As MailItem)
Dim objOutMail As Outlook.MailItem
Dim vText As Variant
Dim sText As String
Dim strID As String
Dim sSubject As String
Dim vItem As Variant
Dim vFirstName As Variant
Dim i As Long
Dim j As Integer
Dim strSignature As String
Dim strSigString As String
Dim strFirstName As String
Dim strFirstLetter As String
Dim strEMailAddress As String
Dim blnFirstName As Boolean
Dim blnEMail As Boolean
' change the bodyformat to plain text
objInMail.BodyFormat = Outlook.OlBodyFormat.olFormatPlain
objInMail.Display
blnFirstName = False
blnEMail = False
j = 0
' believe there is a timing issue that Body may not be fully loaded.
' so I'm going to pause and loop through 20 times to see if it gets loaded.
WaitForBody:
sText = objInMail.Body
If sText = "" Then
If j < 20 Then
j = j + 1
Sleep 1000
GoTo WaitForBody
End If
End If
If sText = "" Then
MsgBox ("No body in email!")
Exit Sub
End If
End Sub
I thought it was a timing issue, so I built the loop to test if I have the body, and if not, wait a second and try again up to 20 times.
I have objInMail.Display it works, but if I remove that line it will loop through the 20 attempts.
I could live with the display if I could then "un-display" it, but I wonder if the .close will close everything with the email and I'll lose the body again.
I'd prefer it to work without the objInMail.Display.
Ignoring the cause, this may provide a workaround without .Display.
Option Explicit
Private Sub test_GetInspector()
Dim currSel As Object
Set currSel = ActiveExplorer.Selection(1)
If currSel.Class = olMail Then
AutoReplyTrap_GetInspector currSel
End If
End Sub
Sub AutoReplyTrap_GetInspector(objInMail As mailItem)
' change the bodyformat to plain text
objInMail.BodyFormat = OlBodyFormat.olFormatPlain
' objInMail.GetInspector ' Previously "valid".
' My setup finally caught up and provided the clue.
' Directly replacing .Display with .GetInspector
' Compile error:
' Invalid use of property
' https://learn.microsoft.com/en-us/office/vba/api/outlook.mailitem.getinspector
Dim objInspector As Inspector
Set objInspector = objInMail.GetInspector
' You should find this is necessary
'objInMail.Save
End Sub
Working with Outlook 2010 right now and have an update. The issue is caused by a bug in Outlook 2010/2013 that only gives a blank message body in VBA when:
(1) using IMAP protocol; and,
(2) automatically processing incoming emails.
This holds true even if you just set a Rule from the front end, such as automatically printing specific incoming emails (my task). This prints the email header, not the body.
A workaround that worked for me was to use POP3 protocol instead of IMAP with the same email server.

Speech in PowerPoint (VBA)

I want to be able to make PowerPoint speak, say something.
I tried this code to make PP speak:
Private Sub CommandButton1_Click()
Application.Speech.Speak "Hello World"
End Sub
But the code doesn't work, it doesn't exists. What can I do, which is the right code?
It says:
Compile Error Method or Data member not found.
Sorry for any error on my question.
The reason it's not working is that there's no Application.Speech property/method in the PPT object model. Somewhere or other I've seen code that invokes Excel to do the lifting but here's an answer from John Wilson of PPT Alchemy that seems more direct:
There is a page on our site about talking message boxes
http://www.pptalchemy.co.uk/PowerPoint_speech.html
It could be easily modified to speak the text in a shape in Slide Show mode.
Sub speak(oshp As Shape)
Dim strSpeak As String
Dim SAPIObj As Object
Set SAPIObj = CreateObject("SAPI.SPvoice")
SAPIObj.Rate = -2
If oshp.HasTextFrame Then
If oshp.TextFrame.HasText Then
strSpeak = oshp.TextFrame.TextRange.Text
End If
End If
SAPIObj.speak "<pitch middle='-15'>" & strSpeak
End Sub
If you don't need to pick up the text from a particular shape, a modification along these lines should do it:
Sub SayThisAloud(sText as String)
Dim SAPIObj As Object
Set SAPIObj = CreateObject("SAPI.SPvoice")
SAPIObj.Rate = -2
SAPIObj.speak "<pitch middle='-15'>" & sText
End Sub
REgarding the MSDN page, it'll take some editing/modification to convert it to something a bit more useful. For example, here's an example of how you can get a list of the voices available:
In the declarations section:
Private V As Object
Private T As Object
Then
Sub ListVoices()
On Error GoTo EH
Dim strVoice As String
Dim SAPIObj As Object
Set SAPIObj = CreateObject("SAPI.SPvoice")
'Get each token in the collection returned by GetVoices
For Each T In SAPIObj.GetVoices
strVoice = T.GetDescription 'The token's name
'List1.AddItem strVoice 'Add to listbox
Debug.Print strVoice
Next
Exit Sub
EH:
If Err.Number Then ShowErrMsg
End Sub
Private Sub ShowErrMsg()
' Declare identifiers:
Dim T As String
T = "Desc: " & Err.Description & vbNewLine
T = T & "Err #: " & Err.Number
MsgBox T, vbExclamation, "Run-Time Error"
End
End Sub

VBA: Sub to Write to a Log File

I have a set of macros defined in my workbook, and I'd like to offer the user the option to log events related to those macros in a log file.
I initiate the log by creating the following in ThisWorkbook:
Public writeLog as Boolean
Public logWrite as Object
Public log as Object
Private Sub Worksheet_Open()
Dim prompt as Integer
prompt = MsgBox("Would you like to log events for this session?", vbYesNo, "Log Events?")
If prompt Then
writeLog = True
Set logWrite = CreateObject("Scripting.FileSystemObject")
Set log = logWrite.CreateTextFile("C:/TEST.txt", False)
Else
writeLog = False
End If
End Sub
I then created a procedure that I can use to write an argument to this object, which I've stored in its own module:
Public Sub PrintLog(obj as Object, argument as String)
If writeLog = True Then
obj.WriteLine argument
End If
End Sub
Unfortunately, this doesn't work, and I'm not sure why: even if I don't include obj as an argument to the function (since log and logWrite were created as global variables), I'm not able to Call WriteLog("String here.") or Call WriteLog(log, "String here.") without an error (Compile Error: Argument Not Optional.)
Is it possible to get such a Sub() to work, so that I can call it from anywhere in the workbook (after a button is pressed in a userform, for example) without having to define a new Scripting.FileSystemObject in every module?
I think that you can solve your problem by making some minor changes to your code. I tried the following setup:
logger module:
Option Explicit
Private log As Object
Public Sub initLog()
Dim prompt As VbMsgBoxResult
Dim fso As Object
prompt = MsgBox("Would you like to log events for this session?", vbYesNo, "Log Events?")
If prompt = vbYes Then
Set fso = CreateObject("Scripting.FileSystemObject")
Set log = fso.CreateTextFile("C:/TEST.txt", False)
End If
End Sub
Public Sub PrintLog(argument As String)
If Not log Is Nothing Then
log.WriteLine argument
End If
End Sub
Public Sub yadda()
'test
PrintLog "yadda"
End Sub
ThisWorkbook:
Private Sub Workbook_Open()
initLog
End Sub
This is my no-frills drop in replacement for Debug.Print(), that logs to "Log.txt" at your Workbook path.
To install : Just search and replace "Debug.Print" with "Log", and optionally call LogClear() at the start of your program.
Public Function Log(ByRef a_stringLogThis As String)
' send to TTY
Debug.Print (a_stringLogThis)
' append (not write) to disk
Open ThisWorkbook.path & "\Log.txt" For Append As #1
Print #1, a_stringLogThis
Close #1
End Function
OPTIONAL : And here's a helper you COULD call at the beginning of your to clear out the previous logs.
Public Function LogClear()
Debug.Print ("Erasing the previous logs.")
Open ThisWorkbook.path & "\Log.txt" For Output As #1
Print #1, ""
Close #1
End Function
OPTIONAL : Finally, if can't live without date and time in your logging, use this Log statement instead:
Public Function Log(ByRef a_stringLogThis As String)
' prepare date
l_stringDateTimeNow = Now
l_stringToday = Format(l_stringDateTimeNow, "YYYY-MM-DD hh:mm:ss")
' concatenate date and what the user wants logged
l_stringLogStatement = l_stringToday & " " & a_stringLogThis
' send to TTY
Debug.Print (l_stringLogStatement)
' append (not write) to disk
Open ThisWorkbook.path & "\Log.txt" For Append As #1
Print #1, l_stringLogStatement
Close #1
End Function
I believe you're having issues as writeLog already exists as a boolean. Error should be popping up "Ambiguous name detected"
Try the following,
Public bLog as Boolean
Public logWrite as Object
Public log as Object
Private Sub Worksheet_Open()
Dim prompt as Integer
prompt = MsgBox("Would you like to log events for this session?", vbYesNo, "Log Events?")
If prompt Then
bLog = True
Set logWrite = CreateObject("Scripting.FileSystemObject")
Set log = logWrite.CreateTextFile("C:/TEST.txt", False)
Else
bLog = False
End If
End Sub
Public Sub WriteLog(Optional obj as Object, Optional argument as String)
If bLog = True Then
obj.WriteLine argument
End If
End Sub
Edit: made parameters optional in WriteLog (or PrintLog) for further testing
' Write to a log file using Separator and Array of variant Parameters
' Auto generate the file
' USE EndLog to close
'use:
' PrintLog vbtab, "one", 2, 3
' PrintLog vbtab, "Apple","Windows","Linux","Android","Commodore","Amiga","Spectrum"
' EndLog
' Generate a csv file:
' PrintLog ";", rst!ID, rst!Name
Private FileLog As Object
Private fso As Object
Const DEBUG_LOG_FILE = "C:\log.txt"
Public Sub PrintLog(ByVal Separator As String, ParamArray Arguments() As Variant)
Dim ele As Variant
Dim line As String
If FileLog Is Nothing Then
Set fso = CreateObject("Scripting.FileSystemObject")
Set FileLog = fso.CreateTextFile(DEBUG_LOG_FILE, True, True)
End If
line = CStr(Now()) ' Print Timestamp
For Each ele In Arguments
If line > "" Then line = line & Separator
line = line & CStr(ele)
Next
If line > "" Then FileLog.WriteLine line
End Sub
Public Sub EndLog()
On Error Resume Next
FileLog.Close
Set FileLog = Nothing
Set fso = Nothing
On Error GoTo 0
End Sub

Workbook not found when trying to call function from Excel-add in

I'm trying to call a function from a 3rd party Excel-add in a VBA-sub. The function loads data from a database into specified cells in the Excel workbook.The function I'm calling is huge and unfortunaly I can't post it in its entirety, but here are the first two lines:
Public Function loadFromDatabase(ByVal XLname As String, ByVal sMark As String)
Dim xlWB As Workbook
Then it declares a bunch of variables before running the following tests:
'
' Get the excel book and check if it is run in compatibility mode
'
Set xlWB = getXLBook(XLname)
If xlWB Is Nothing Then
loadFromDatabase = "Workbook '" + XLname + "' not found!"
Exit Function
End If
bExcel8Limits = True
If isExcel2007orLater Then
bExcel8Limits = bCheckCompMode(xlWB)
End If
Here I get this message: "Workbook " not found!" http://imgur.com/HQFAzoC .
The getXLBook function looks like this:
'
' Routine to get a specified Workbook
'
Function getXLBook(sName As String) As Workbook
Dim xlWB As Workbook
On Error Resume Next
Set xlWB = Nothing
Set xlWB = Application.Workbooks(sName)
On Error GoTo 0
Set getXLBook = xlWB
End Function
A hint here may be that I'm able to call the function from a Private Sub place in a worksheet like this...
Private Sub loadFromDB()
Dim res As Variant
res = Application.Run("loadFromDatabase", Me.Parent.Name, "")
If res <> "OK" Then
MsgBox res
End If
End Sub
...but not from a module in the same workbook like this
Sub loadFromDB_test()
Dim res As Variant
res = Application.Run("loadFromDatabase", XLname, sMark)
If res <> "OK" Then
MsgBox res
End If
End Sub
Any suggestions?
Edit: To clarify, it's when running loadFromDB_test the "Workbook not found" message pops up.
Edit 2: An obvious hotfix (that I didnt think of) is to just call the Private Sub in the worksheet from the Sub in the module.
Sub load_test_new()
Application.Run "Sheet1.loadFromDB"
End Sub
From a learning point of view this is clearly not a good solution as it is inefficient coding.
Based on the msgbox you display, you're passing an empty string to the function getXLBook. (within the scope of getXLBook this value is stored as sName, but the cause of the error is before you call this function).
So, somewhere in your code, before this:
Set xlWB = getXLBook(XLname)
You should have a line like this, where the right side of the statement assigns a string representing a full, valid filepath:
XLName = "C:\filename.xlsx"
I suspect that your code does not contain this assignment statement, so that should explain the error.