I've looked up the following code from another question on this site and have tried to apply it to my own, to no avail - I am trying to email out a pdf of my report to each user with only their specific information contained therein. If there are 15 users, then there will be 15 different emails sent out containing only their info. any help is very much appreciated.
Option Compare Database
Sub Mac1()
Dim rsAccountNumber As DAO.Recordset
Set rsAccountNumber = CurrentDb.OpenRecordset( _
"SELECT DISTINCT AccountNumber FROM UnAffirmed_Report_for_En Query")
With rsAccountNumber
Do Until .EOF
DoCmd.OpenReport "Unaffirmed Report", _
acViewPreview, _
WhereCondition:="AccountNumber = " & !AccountNumber, _
WindowMode:=acHidden
DoCmd.OutputTo acOutputReport, _
"Unaffirmed Report", _
acFormatPDF, _
"C:\users\rv\folder_name" & !AccountNumber & ".pdf"
DoCmd.Close acReport, "Unaffirmed Report", acSaveNo
.MoveNext
Loop
.Close
End With
End Sub
when I do though, I receive Run-time error '3075':
Syntax error (missing operator) in query expression 'AccountNumber = 1RV80014'
the account # is valid, but not sure why I am getting stuck here or how to fix it - super newbe to this.
Your account number is a String 1RV80014 and your Where condition:="AccountNumber = " & !AccountNumber states an Integer. So change it to a String Where condition:= "AccountNumber = '" & !AccountNumber & "'"
Related
I'm looking for a VBA macro that scrapes outlook for specific, most recent, e-mails that are received MTWTF, saves the .xlsm and .xlsx attachments in a local folder, then another macro to look in to these recently saved files and paste specific tabs in to an excel workbook.
I receive three daily e-mails with static subjects with only the date changing daily. One of the e-mail attachments has a password on it.
Steps-
Search Outlook for the most recent "ABC E-mail subject" and save down attachment in local folder
1.1) Search Outlook for the most recent "DEF E-mail subject" and save down attachment in local folder
1.2) Search Outlook for the most recent "XYZ E-mail subject", enter attachment password and save down attachment in local folder
Ignore older versions of the same e-mail
Look in to the saved down ABC, DEF, and XYZ (again entering the password if needed) files, copy 2+ tabs from each file in to an Excel template
Thank you!!
**What's been tried:
**
On the following code, this object can not be found:
fol = ns.Folders(1).Folders("Dell")
I've created the folder in a shared outlook e-mail/group. I've referenced MicroSoft 16 object library in Tools > References.
Option Explicit
Sub SaveOutlookAttachments()
'This early-binding version requires a reference to the Outlook and Scripting Runtime object libraries
Dim ol As Outlook.Application
Dim ns As Outlook.Namespace
Dim fol As Outlook.Folder
Dim i As Object
Dim mi As Outlook.MailItem
Dim at As Outlook.Attachment
Dim fso As Scripting.FileSystemObject
Dim dir As Scripting.Folder
Dim dirName As String
Set fso = New Scripting.FileSystemObject
Set ol = New Outlook.Application
Set ns = ol.GetNamespace("MAPI")
Set fol = ns.Folders(1).Folders("Dell")
For Each i In fol.Items
If i.Class = olMail Then
Set mi = i
If mi.Attachments.Count > 0 Then
'Debug.Print mi.SenderName, mi.ReceivedTime, mi.Attachments.Count
dirName = _
"C:\Outlook Files\" & _
Format(mi.ReceivedTime, "yyyy-mm-dd hh-nn-ss ") & _
Left(Replace(mi.Subject, ":", ""), 10)
If fso.FolderExists(dirName) Then
Set dir = fso.GetFolder(dirName)
Else
Set dir = fso.CreateFolder(dirName)
End If
For Each at In mi.Attachments
'Debug.Print vbTab, at.DisplayName, at.Size
at.SaveAsFile dir.Path & "\" & at.Filename
Next at
End If
End If
Next i
End Sub
Sub SaveOutlookAttachmentsLateBinding()
'This late-binding version allows you to remove the references to the Outlook and Scripting Runtime object libraries
Dim ol As Object 'Outlook.Application
Dim ns As Object 'Outlook.Namespace
Dim fol As Object 'Outlook.Folder
Dim i As Object
Dim mi As Object 'Outlook.MailItem
Dim at As Object 'Outlook.Attachment
Dim fso As Object 'Scripting.FileSystemObject
Dim dir As Object 'Scripting.Folder
Dim dirName As String
Set fso = CreateObject(Class:="Scripting.FileSystemObject")
Set ol = CreateObject(Class:="Outlook.Application")
Set ns = ol.GetNamespace("MAPI")
Set fol = ns.Folders(1).Folders("Dell")
For Each i In fol.Items
If i.Class = 43 Then
Set mi = i
If mi.Attachments.Count > 0 Then
'Debug.Print mi.SenderName, mi.ReceivedTime, mi.Attachments.Count
dirName = _
"C:\Outlook Files\" & _
Format(mi.ReceivedTime, "yyyy-mm-dd hh-nn-ss ") & _
Left(Replace(mi.Subject, ":", ""), 10)
If fso.FolderExists(dirName) Then
Set dir = fso.GetFolder(dirName)
Else
Set dir = fso.CreateFolder(dirName)
End If
For Each at In mi.Attachments
'Debug.Print vbTab, at.DisplayName, at.Size
at.SaveAsFile dir.Path & "\" & at.Filename
Next at
End If
End If
Next i
End Sub
Instead of getting folders by index from the Namespace class:
Set fol = ns.Folders(1).Folders("Dell")
Use the GetDefaultFolder method of the Namespace class which returns a Folder object that represents the default folder of the requested type for the current profile; for example, obtains the default Inbox folder for the user who is currently logged on.
Then instead of iterating over all items in the folder and checking whether each item has attached files:
For Each i In fol.Items
If i.Class = 43 Then
Set mi = i
If mi.Attachments.Count > 0 Then
You need to use the Find/FindNext or Restrict methods of the Items class. They allow getting only items that correspond to the search criteria, so you can iterate over items that has attachments (and if required belong to a specified date range). There is no need to check each item in the folder separately in the loop. Read more about these methods in the articles that I wrote for the technical blog:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
In cases when you need to check multiple folders you may choose the AdvancedSearch method of the Application class. The key benefits of using the AdvancedSearch method in Outlook are:
The search is performed in another thread. You don’t need to run another thread manually since the AdvancedSearch method runs it automatically in the background.
Possibility to search for any item types: mail, appointment, calendar, notes etc. in any location, i.e. beyond the scope of a certain folder. The Restrict and Find/FindNext methods can be applied to a particular Items collection (see the Items property of the Folder class in Outlook).
Full support for DASL queries (custom properties can be used for searching too). To improve the search performance, Instant Search keywords can be used if Instant Search is enabled for the store (see the IsInstantSearchEnabled property of the Store class).
You can stop the search process at any moment using the Stop method of the Search class.
See Advanced search in Outlook programmatically: C#, VB.NET for more information.
In case if you need to search items for a specific date range, the Filtering Items Using a Date-time Comparison page describes formats of filtering strings. For example:
Dim datStartUTC As Date
Dim datEndUTC As Date
datStartUTC = oPA.LocalTimeToUTC(Date)
datEndUTC = oPA.LocalTimeToUTC(DateAdd("d", 1, Date))
'This filter uses urn:schemas:httpmail namespace
strFilter = AddQuotes("urn:schemas:httpmail:datereceived") _
& " > '" & datStartUTC & "' AND " _
& AddQuotes("urn:schemas:httpmail:datereceived") _
& " < '" & datEndUTC & "'"
'This call succeeds with #SQL prefix
Set colRestrict = colItems.Restrict("#SQL=" & strFilter)
'Get count of restricted items
Debug.Print (colRestrict.Count)
And a small function function to add quotes in VBA:
Public Function AddQuotes(ByVal SchemaName As String) As String
On Error Resume Next
AddQuotes = Chr(34) & SchemaName & Chr(34)
End Function
Good morning,
I have a range set to retrieve values from cells on a protected sheet, in a closed workbook, in a remote backup unit. I tried everything to try stop it from returning Nothing but I just can't.
See an example below.
Dim MyArray As Variant
Dim MyRange As Range
Set MyRange = ExecuteExcel4Macro("'" & "\\WDSENTINEL\Documents\HALTON PROJECTS\YEAR " & UserForm2.ComboBox1.Value & "\" & M + N & "\" & "[" & "Master.xlsm" & "]" & "Shipping" & "'!" & Range("D12").Address(True, True, -4150))
ReDim Preserve MyArray(N)
MyArray(N) = MyRange.Value2
Then, I realized that ALL previous ranges I set for other purposes, that have been working smoothly for the last couple of weeks also started to return Nothing.
Is there maybe a configuration/setting issue on Excel that was set to return nothing on those ranges?
I have a humongous Access form with hundreds of controls, all with some events.
One of them is throwing an error crying about "Validation or After Update event". No control name, nothing more to go by.
So I figured I'd list all the forms with all their validation rules and all their events (not all of them are "[Event procedure]", some are custom function calls).
Is there any way to get a list of a control's events?
My code so far:
Sub ListAllControlsAndTheirEvents(FormToCheck As Form)
Dim ctrlCurrent As Control
For Each ctrlCurrent In FormToCheck.Controls
Debug.Print "Name: " & ctrlCurrent.Name
On Error Resume Next
Debug.Print "Validation: " & ctrlCurrent.ValidationRule 'Not all controls have a ValidationRule property.
On Error GoTo 0
For Each eventCurrent In ctrlCurrent.events 'this thing doesn't exist :(
Debug.Print "Event: " & eventCurrent.Name & "; value: " & eventCurrent.whatever
Next
Next
End Sub
So, is there a way to implement this part?
For Each eventCurrent In ctrlCurrent.events 'this thing doesn't exist :(
Debug.Print "Event: " & eventCurrent.Name & "; value: " & eventCurrent.whatever
Next
Digging through the code module won't give me the custom function event calls, just the [Event Procedure] codes.
You could use typename to determine the type of control, then set so something like if typename(x) ="CommanButton" then set cb=x, then loop the properties, for property names beginning with On, then if there is an [Event Procedure] you know there's an event, also you can then narrow down your search to x_Click
The code below should identify and list all events (whether blank, [Event Procedure], or a custom function) for each control on the form. I tested it on Access 2010 by creating a blank form and adding several controls. I made sure to assign some standard events and custom functions to a few controls in order to validate.
Public Sub ListAllControlsAndTheirEvents(FormToCheck As Access.Form)
Dim ctrlProp As Object
Dim ctrlCurrent As Access.Control
For Each ctrlCurrent In FormToCheck.Controls
Debug.Print "Name: " & ctrlCurrent.Name
If PropertyExists(ctrlCurrent, ctrlProp, "ValidationRule") Then
Debug.Print "Validation: " & ctrlProp.Value
End If
For Each ctrlProp In ctrlCurrent.Properties
' Category 4 type 8 identifies an event property
If ctrlProp.Category = 4 And ctrlProp.Type = 8 Then
Debug.Print _
"Event: " & ctrlProp.Name & "; " & _
"Value: " & ctrlProp.Value
End If
Next
Next
End Sub
Private Function PropertyExists( _
ByRef ctrlCurrent As Access.Control, _
ByRef ctrlProp As Object, _
ByRef PropName As String _
) As Boolean
On Error Resume Next
Set ctrlProp = ctrlCurrent.Properties(PropName)
PropertyExists = Err.Number = 0
End Function
I know that this question has been marked as answered like 4 years ago.
I have been to this question in my search for the same thing in Excel.
This question was the source behind my search for an answer to a similar question.
I have found an answer to this question using TLI.
The answer I provided here is meant to help other people reach for a solution quickly and painlessly.
There is NO intention of gaining merits/points/reputation but just to help others get an answer I've only reached after spending so much time and energy.
I am no expert in Access VBA nor am I in Excel VBA but I think TypeLib Info can be used in Access too.
Requirement: Reference to TypeLib Information library at C:\Windows\SysWow64\TLBINF32.DLL
Sub printControlEventNames(ByVal o As Object)
Dim t As TLI.TLIApplication
Set t = New TLI.TLIApplication
Dim ti As TLI.TypeInfo
Set ti = t.ClassInfoFromObject(o)
Dim mi As TLI.MemberInfo
For Each mi In ti.DefaultEventInterface.Members
Debug.Print mi.Name
Next
End Sub
My question and my own solution can be found here.
I'm developing a data retrieval application in Access 2010 in which the user chooses which table, columns, and rows to view by selecting listbox entries. The VBA code generates a SQL statement from these choices and then creates an ADBDB.Recordset object from this.
How can I display the recordset records in Access? None of the grid controls work in Access 2010 and the subform just isn't designed for this purpose. Can someone recommend another strategy?
You could save the SELECT statement as a named query, then open the query as a datasheet. It's not really a form, but somewhat form-like.
Call DatasheetFromSql(strSql)
Public Sub DatasheetFromSql(ByVal pSql As String)
Const cstrQuery As String = "qryDiscardMe"
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strMsg As String
On Error GoTo ErrorHandler
Set db = CurrentDb
db.QueryDefs.Delete cstrQuery
Set qdf = db.CreateQueryDef(cstrQuery, pSql)
DoCmd.OpenQuery cstrQuery, , acReadOnly
ExitHere:
On Error GoTo 0
Set qdf = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
Select Case Err.Number
Case 3265 ' Item not found in this collection. '
Resume Next
Case Else
strMsg = "Error " & Err.Number & " (" & Err.description _
& ") in procedure DatasheetFromSql"
MsgBox strMsg
GoTo ExitHere
End Select
End Sub
I opened the query read-only. If you want to allow users to edit the data returned by their custom queries, I would not recommend this approach. Instead I would invest the effort in the approach HK1 offered because it can support better control of the user data changes.
With the query opened as a datasheet, you can use Screen.ActiveDatasheet to inspect its properties. At least some of the methods are also available to you. For example you could resize/reposition the datasheet like this:
Screen.ActiveDatasheet.Move Left:=0, Top:=0, Width:=(4 * 1440), Height:=(3 * 1440)
The units are twips (1440 twips / inch), so that would make the width 4 in., height 3 in., and move it to the upper left corner of the Access window.
Here's what I think you'll have to do to get this kind of functionality.
You'll first need to create enough of the correct controls on a form to handle every possible scenario. You'll then need to set the form to be a datasheet form so that it appears as a grid.
Now set the controlsource on your controls to correspond with one of the fields in the recordset. On every control that is not in use needs to have it's ColumnHidden property set to true. You'll also have to change the caption of the associated label to show the appropriate column name for each control that will be visible.
Now, bind that form to your ADO recordset object.
Me.Recordset = rst
'or
Me.Subform1.Form.Recordset = rst
Is this a perfect solution? Most certainly not. Access doesn't have anything that compares to the DataGridView in .Net or even to the Grid controls that are used in VB6. In my opinion, you're really pushing the limits of Access to try to get this sort of functionality. It's like swimming upstream. You'll find that everything you do is going to be fairly difficult and some things just aren't going to be possible.
for an ADP Project where you can't have local MS Access query definitions, you can create a datasheet form with multiple textboxs named txt1, txt2,.... txt30 and labels name lbl1 ... lb30 and this code will set the form.recordsource and set the textbox.controlsource and the label.caption to the appropriate fields from an ADO recordset object. This form will allow you to view your ADO Recordset similar to the Docmd.OpenQuery method.
You must pass the ADO recordset's SQL statement to the form using the OpenArgs property of the form. the code below shows the VBA code to call\open the form (which shows the ADO Recordset like a query) and pass your sql string. The vba code on the form's load event will set all the control properties, resize the columns that have data and hide the columns that do not have a corresponding field from the ado recordset:
'stevekirchner 09/29/2012 Replace Access parameterized query with SQL Server in-line function
'DoCmd.OpenQuery "qry_SearchMaster_CaseTitles", , acReadOnly
strsql = "Select * from dbo.UDF__qry_SearchMaster_CaseTitles ('%" & Me.tbxSearchTerm.Value & "%') "
Call Display_ADO_Recordset_from_Datasheet_Form(strsql, "frm_Display_ADO_Recordset_Result1")
'create a non-form module and put the code for the sub Display_ADO_Recordset_from_Datasheet_Form
'and function fIsLoaded in it (this will allow you make several forms to view ADO recordset and
'call the code from one place\module):
Sub Display_ADO_Recordset_from_Datasheet_Form(sSQL As String, sFormName As String)
On Error GoTo Error_Handler
If fIsLoaded(sFormName) Then
DoCmd.Close acForm, sFormName
End If
DoCmd.OpenForm sFormName, acFormDS, , , acFormReadOnly, , OpenArgs:=sSQL
Exit_Sub:
Exit Sub
Error_Handler:
MsgBox Err.Description & " Error No: " & CStr(Err.Number)
Resume Exit_Sub
End Sub
Function fIsLoaded(ByVal strFormname As String) As Boolean
On Error GoTo Error_Handler
'Returns False if form is not open or True if Open
If SysCmd(acSysCmdGetObjectState, acForm, strFormname) <> 0 Then
If Forms(strFormname).CurrentView <> 0 Then
fIsLoaded = True
End If
End If
Exit_Function:
Exit Function
Error_Handler:
MsgBox Err.Description & " Error No: " & CStr(Err.Number)
fIsLoaded = False
Resume Exit_Function
End Function
'Create a datasheet view form (named frm_Display_ADO_Recordset_Result1) with 30 textboxes and 30
'30 labels named txt1 - txt30 and lbl1 - lbl30 and put this code in the form's module:
Option Compare Database
Private Sub Form_Load()
On Error GoTo Error_Handler
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim rsClone As ADODB.Recordset
Dim strsql As String
Set conn = CurrentProject.Connection
Set rs = New ADODB.Recordset
strsql = Me.OpenArgs
rs.Open strsql, conn, adOpenStatic, adLockOptimistic
Set rsClone = rs.Clone
Call Update_Form_Controls("your text goes here", strsql, rsClone)
Exit_Sub:
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
Exit Sub
Error_Handler:
MsgBox Err.Description & "; Error Number : " & Err.Number, vbOKOnly
Resume Exit_Sub
End Sub
Sub Update_Form_Controls(Header_Label As String, SQL As String, CloneRS As Recordset)
Dim rsCount As Integer
Dim i As Integer
On Error GoTo Error_Handler
Me.Form.Caption = Replace(SQL, "Select * From ", "Display: ")
rsCount = CloneRS.RecordCount
If rsCount <= 0 Then
MsgBox "The Query did not return any data to view", vbOKOnly
DoCmd.Close
Else
Me.Form.SetFocus
Me.RecordSource = SQL
i = 1
Do Until i = 31
Me("lbl" & i).Caption = ""
Me("txt" & i).ControlSource = ""
Me("txt" & i).ColumnHidden = True
i = i + 1
Loop
i = 1
With CloneRS
For Each Field In .Fields
On Error Resume Next
Me("lbl" & i).Caption = .Fields(i - 1).Name
Me("txt" & i).ControlSource = .Fields(i - 1).Name
Me("lbl" & i).Visible = True
Me("txt" & i).ColumnHidden = False
Me("txt" & i).SizeToFit
i = i + 1
'Debug.Print Field.Name
On Error GoTo 0
Next Field
End With
End If
Exit_Sub:
Me.Requery
Exit Sub
Error_Handler:
MsgBox Err.Description & "; Error Number : " & Err.Number, vbOKOnly
Resume Exit_Sub
End Sub
I don't use ADO but simply VBA, in which case above approach is wrong: one doesn't need any means for displaying an existing recordset in a form, but should on the contrary define an adequate recordset within the form !
Instead of creating MyDataBase.OpenRecordset ("SELECT … [SQL query] ;"), just set the form's RecordSource to the very same query definition:
Forms![MyDisplayForm].RecordSource = "SELECT … [SQL query] ;"
Forms![MyDisplayForm].Requery
For me this works perfectly (Windows 7 pro ×64 / MS Office pro ×64)
I'm having problems finding a specific section in word. It was recommended I try looking through the VB Object Browser in Word for help. I know there are at least 5 heading "sets" (I.E. if you look in the Document Map, I see numbered 1,2,3,4,5...). I don't know how to navigate to that fifth heading, initially I thought it was sections, but when I viewed sections I realized that almost all of it is in one section, but in case anyone is looking for information on how to do sections, the below seems to work, since I already went through the trouble of writing it.
my($document) = $Word->Documents->Open($input) || die("Unable to open document ", Win32::OLE->LastError());
my $section = $document->{Sections}->Item(1); # put section number you're looking for in here
$section_five_paragraphs = $section->{Range}->Paragraphs();
$enumerate = new Win32::OLE::Enum($section_five_paragraphs);
while (defined($paragraph = $enumerate->Next()))
{
print $paragraph->{Range}->{Text} . "\n";
}
So does anyone know how to get to this 5th heading area, or can point me to something that might help?
Tell me if I didn't follow you correctly but you're trying to find the 5th Heading 1 in the a certain section? If that's the case, although Word clearly defines sections (which you note as $document->{Sections}->Item(1)), it does not clearly define Headings in specific or styles in general. For that you'll have to go through all the styles looking for those of interest. The following VBA code (and I apologize for not writing perl) does just that and looks only in a specific section.
Sub FindHeading1()
On Error GoTo MyErrorHandler
Dim currentDocument As Document
Set currentDocument = ActiveDocument
Dim findRange As Range
Set findRange = currentDocument.Sections(2).Range 'which section you want
Dim endRange As Long
endRange = findRange.end
findRange.Find.ClearFormatting
findRange.Find.Style = ActiveDocument.Styles("Heading 1")
Dim headingCountFound As Long
Do While findRange.Find.Execute(FindText:="")
If findRange.End > endRange Then Exit Sub
findRange.Select
headingCountFound = headingCountFound + 1
If headingCountFound = 3 Then 'which occurance you want
MsgBox "Found."
Exit Do
End If
DoEvents
Loop
Exit Sub
MyErrorHandler:
MsgBox "FindHeading1" & vbCrLf & vbCrLf & "Err = " & Err.Number & vbCrLf & "Description: " & Err.Description
End Sub