For some reason this is only printing the first attachment in the e-mail. It doesn't seem like my for loop is working. Any clue? Basically it saves a backup of the attachments, prints the e-mail, prints the .pdf attachment, and then categorizes it as "printed". I need it do it for every .pdf on the e-mail. Not just the first one that is attached.
Private Declare PtrSafe 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
Option Compare Text
Sub PrintAttachments(oMail As Outlook.MailItem)
Dim colAtts As Outlook.Attachments
Dim oAtt As Outlook.Attachment
Dim sFile As String
Dim sDirectory As String
Dim sFileType As String
Dim strSubject As String
strSubject = oMail.Subject
sDirectory = "MYDIRISHERE"
Set colAtts = oMail.Attachments
If colAtts.Count Then
For Each oAtt In colAtts
sFileType = LCase$(Right$(oAtt.FileName, 4))
Select Case sFileType
' Add additional file types below followed by comma
Case ".pdf"
If oMail.Categories <> "Printed" Then
sFile = sDirectory & oAtt.FileName & " " & strSubject & sFileType
oAtt.SaveAsFile sFile
oMail.PrintOut
oMail.Categories = "Printed"
oMail.Save
ShellExecute 0, "print", sFile, vbNullString, vbNullString, 0
Debug.Print "Email " & strSubject & " with attachment " & oAtt.FileName & " from " & oMail.SenderName & " Printed."
End If
Case Else
Debug.Print "Attachment: " & oAtt.FileName & " from " & oMail.SenderName & " is not authorized to print."
End Select
Next oAtt
End If
End Sub
Check the category at the start so "Not Printed" remains in force until all attachments are processed.
Private Declare PtrSafe 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
Option Compare Text
Sub PrintAttachments(oMail As MailItem)
Dim colAtts As Attachments
Dim oAtt As Attachment
Dim sFile As String
Dim sDirectory As String
Dim sFileType As String
Dim strSubject As String
strSubject = oMail.Subject
sDirectory = "MYDIRISHERE"
If oMail.Categories <> "Printed" Then
Set colAtts = oMail.Attachments
If colAtts.Count Then
For Each oAtt In colAtts
sFileType = LCase$(Right$(oAtt.FileName, 4))
Select Case sFileType
' Add additional file types below followed by comma
Case ".pdf"
' This stops the second and subsequent attachments
' from being printed since
' the category is prematurely set to "Printed".
' If oMail.Categories <> "Printed" Then
sFile = sDirectory & oAtt.FileName & " " & strSubject & sFileType
oAtt.SaveAsFile sFile
oMail.PrintOut
oMail.Categories = "Printed"
oMail.Save
ShellExecute 0, "print", sFile, vbNullString, vbNullString, 0
Debug.Print "Email " & strSubject & " with attachment " & oAtt.FileName & " from " & oMail.SenderName & " Printed."
' End If
Case Else
Debug.Print "Attachment: " & oAtt.FileName & " from " & oMail.SenderName & " is not authorized to print."
End Select
Next oAtt
End If
End If
End Sub
Related
I want to print all emails and attachments of an Outlook folder. I want to print excel, word and pfd files.
This works but not in the right order. Emails en printed attachments get mixed-up. So i want to synchronize the printing. The process has to wait until the print job has been send. The problem is probably that ShellExecute command works asynchronically from the VBA.
So how can I let the VBA wait until the ShellExecute has finished. I've read on the MSDN that I have to use the CreateProcess but I don't know how to use a print command on this. It only runs an application.
I also tried to use the Sleep method in VBA to give the printing some time but it doesn't seems to be the right solution or work very good. Please has anyone an advice?
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
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal Milliseconds As LongPtr)
Sub SaveBijlageArgumenten()
SaveEmailAttachmentsToFolder "Postvak IN", "Account...", "xlsx", "xls", "pdf", "doc", "docx", "C:\....."
End Sub
Sub SaveEmailAttachmentsToFolder(OutlookInbox As String, OutlookAccount As String, _
ExtString As String, ExtString2 As String, ExtString6 As String, ExtString3 As String, ExtString4 As String, _
ExtString5 As String, DestFolder As String)
Dim ns As NameSpace
Dim Inbox As MAPIFolder
Dim SubFolder As MAPIFolder
Dim Item As Object
Dim Atmt As Attachment
Dim FileName As String
Dim MyDocPath As String
Dim I As Integer
Dim wsh As Object
Dim fs As Object
Dim xlApp As Object
Dim myBook As Object
' Create Excel Application
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = False 'Visible is False by default, so this isn't necessary
On Error GoTo ThisMacro_err
Set ns = GetNamespace("MAPI")
Set Inbox = ns.Folders(OutlookAccount)
Set SubFolder = Inbox.Folders(OutlookInbox)
I = 0
' Check subfolder for messages and exit of none found
If SubFolder.Items.Count = 0 Then
MsgBox "There are no messages in this folder : " & OutlookFolderInInbox, _
vbInformation, "Nothing Found"
Set SubFolder = Nothing
Set Inbox = Nothing
Set ns = Nothing
Exit Sub
End If
'Create DestFolder if DestFolder = ""
Set fs = CreateObject("Scripting.FileSystemObject")
If DestFolder = "" Then
Set wsh = CreateObject("WScript.Shell")
Set fs = CreateObject("Scripting.FileSystemObject")
MyDocPath = wsh.SpecialFolders.Item("mydocuments")
DestFolder = MyDocPath & "\" & Format(Now, "dd-mmm-yyyy hh-mm-ss")
If Not fs.FolderExists(DestFolder) Then
fs.CreateFolder DestFolder
End If
Else
DestFolder = DestFolder & "\" & Format(Now, "dd-mmm-yyyy hh-mm-ss")
If Not fs.FolderExists(DestFolder) Then
fs.CreateFolder DestFolder
End If
End If
If Right(DestFolder, 1) <> "\" Then
DestFolder = DestFolder & "\"
End If
'On Error Resume Next
' Check each message for attachments and extensions
For Each Item In SubFolder.Items
Item.PrintOut Background:=False
Item.UnRead = False
Sleep 500
For Each Atmt In Item.Attachments
If LCase(Right(Atmt.FileName, Len(ExtString))) = LCase(ExtString) Or _
LCase(Right(Atmt.FileName, Len(ExtString2))) = LCase(ExtString2) Then
FileName = DestFolder & Item.SenderName & " " & Atmt.FileName
Atmt.SaveAsFile FileName
Set myBook = xlApp.Workbooks.Open(FileName, UpdateLinks:=0)
myBook.PrintOut Background:=False
myBook.Close SaveChanges:=False
I = I + 1
ElseIf LCase(Right(Atmt.FileName, Len(ExtString3))) = LCase(ExtString3) Or LCase(Right(Atmt.FileName, Len(ExtString4))) = LCase(ExtString4) _
Or LCase(Right(Atmt.FileName, Len(ExtString5))) = LCase(ExtString5) Then
FileName = DestFolder & Item.SenderName & " " & Atmt.FileName
Atmt.SaveAsFile FileName
ShellExecute 0, "print", FileName, vbNullString, vbNullString, 0
Sleep 3000
I = I + 1
End If
Next Atmt
Next Item
On Error GoTo ThisMacro_err
' Show this message when Finished
If I > 0 Then
MsgBox "De bestanden in de bijlage zijn opgeslagen op onderstaande locatie: " _
& DestFolder, vbInformation, "Klaar!"
Else
MsgBox "Er bevonden zich geen bijlagen bij de emails", vbInformation, "Klaar!"
End If
' Clear memory
ThisMacro_exit:
Set SubFolder = Nothing
Set Inbox = Nothing
Set ns = Nothing
Set fs = Nothing
Set wsh = Nothing
Set xlApp = Nothing
Set myBook = Nothing
Set AcroExchApp = Nothing
Set AcroExchAVDoc = Nothing
Exit Sub
' Error information
ThisMacro_err:
MsgBox "An unexpected error has occurred." _
& vbCrLf & "Please note and report the following information." _
& vbCrLf & "Macro Name: SaveEmailAttachmentsToFolder" _
& vbCrLf & "Error Number: " & Err.Number _
& vbCrLf & "Error Description: " & Err.Description _
, vbCritical, "Error!"
Resume ThisMacro_exit
End Sub
The WshShell object provides the Run method which has the bWaitOnReturn optional parameter. It indicates whether the script should wait for the program to finish executing before continuing to the next statement in your script. If set to true, script execution halts until the program finishes, and Run returns any error code returned by the program. If set to false (the default), the Run method returns immediately after starting the program, automatically returning 0 (not to be interpreted as an error code).
I created a macro for a file and first it was working fine, but today I've been opening and restarting the file and macro hundreds of times and I'm always getting the following error:
Excel VBA Run-time error '13' Type mismatch
I didn't change anything in the macro and don't know why am I getting the error. Furthermore it takes ages to update the macro every time I put it running (the macro has to run about 9000 rows).
ERROR is somewhere "FileData = WHTTP.ResponseBody"
Sub Test2()
Dim A As Long
Dim FileNum As Long
Dim FileData() As Byte
Dim MyFile As String
Dim WHTTP As Object
On Error Resume Next
Set WHTTP = CreateObject("WinHTTP.WinHTTPrequest.5")
If Err.Number <> 0 Then
Set WHTTP = CreateObject("WinHTTP.WinHTTPrequest.5.1")
End If
On Error GoTo 0
If Dir("C:\MyDownloads", vbDirectory) = Empty Then MkDir "C:\MyDownloads"
For A = 1 To 228
MyFile = Cells(A, 1).Text
TempFile = Right(MyFile, InStr(1, StrReverse(MyFile), "/") - 1)
WHTTP.Open "GET", MyFile, False
WHTTP.Send
FileData = WHTTP.ResponseBody
FileNum = FreeFile
Open "C:\MyDownloads\" & TempFile For Binary Access Write As #FileNum
Put #FileNum, 1, FileData
Close #FileNum
Next
Set WHTTP = Nothing
MsgBox "Open the folder [ C:\MyDownloads ] for the downloaded file..."
End Sub
Put On Error Resume Next above the line causing an error (probably this line WHTTP.Send). Put this block of code after your line with an error.
Files/Web Addresses/Registry keys - YOU MUST ASSUME IT MAY NOT WORK and trap errors so you know why (and where it's not working). Usually these are not programming questions.
If err.number <> 0 then
ERRString = ErrString & ""
ERRString = ErrString & "Error getting file"
ERRString = ErrString & "=================="
ERRString = ErrString & ""
ERRString = ErrString & "Error " & err.number & "(0x" & hex(err.number) & ") " & err.description
ERRString = ErrString & "Source " & err.source
ERRString = ErrString & ""
ERRString = ErrString & "HTTP Error " & WHTTP.Status & " " & WHTTP.StatusText
ERRString = ErrString & WHTTP.getAllResponseHeaders
Msgbox ErrString
End If
Just download direct using API call and URL
Option Explicit
#If VBA7 And Win64 Then
Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" _
Alias "URLDownloadToFileA" ( _
ByVal pCaller As LongPtr, _
ByVal szURL As String, _
ByVal szFileName As String, _
ByVal dwReserved As LongPtr, _
ByVal lpfnCB As LongPtr _
) As Long
Private Declare PtrSafe Function DeleteUrlCacheEntry Lib "Wininet.dll" _
Alias "DeleteUrlCacheEntryA" ( _
ByVal lpszUrlName As String _
) As Long
#Else
Private Declare Function URLDownloadToFile Lib "urlmon" _
Alias "URLDownloadToFileA" ( _
ByVal pCaller As Long, _
ByVal szURL As String, _
ByVal szFileName As String, _
ByVal dwReserved As Long, _
ByVal lpfnCB As Long _
) As Long
Private Declare Function DeleteUrlCacheEntry Lib "Wininet.dll" _
Alias "DeleteUrlCacheEntryA" ( _
ByVal lpszUrlName As String _
) As Long
#End If
Public Const BINDF_GETNEWESTVERSION As Long = &H10
Public Const folderName As String = "C:\Users\User\Desktop\Blah.zip" '<== Change to destination
Public Sub downloadIFolder()
Dim ret As Long
ret = URLDownloadToFile(0, "http://www.bseindia.com/BSEDATA/margins/VAR290716.zip", folderName, BINDF_GETNEWESTVERSION, 0)
MsgBox ret
End Sub
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
I'm working on writing a script that will import the csv file outputs from a scanning electron microscope to a master spreadsheet organized by date and sample number. Having never used vba before and with little programming experience before now, this has been quite a challenge. There are a few thousand files organized by sample and image number. Right now what I have is able to read in the csv files and copy them to a single spreadsheet. The csv files look something like this
Atomic number,Element symbol,Element name,Concentration percentage,Certainty
8,O,Oxygen,57.5,0.99
14,Si,Silicon,15.5,0.99
26,Fe,Iron,13.6,0.97
13,Al,Aluminium,8.4,0.98
19,K,Potassium,3.3,0.97
22,Ti,Titanium,0.9,0.89
65,Tb,Terbium,0.7,0.53
When I run the code I have, the above data is copied from each file and pasted into the master spreadsheet. What I would like to do is have it format this data. Here is what I have so far to actually write the data to a spreadsheet.
Option Explicit
#If VBA7 Then
Private Declare PtrSafe Function OpenProcess Lib "kernel32" _
(ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long
Private Declare PtrSafe Function GetExitCodeProcess Lib "kernel32" _
(ByVal hProcess As Long, _
lpExitCode As Long) As Long
#Else
Private Declare Function OpenProcess Lib "kernel32" _
(ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" _
(ByVal hProcess As Long, _
lpExitCode As Long) As Long
#End If
Public Const PROCESS_QUERY_INFORMATION = &H400
Public Const STILL_ACTIVE = &H103
Public Sub ShellAndWait(ByVal PathName As String, Optional WindowState)
Dim hProg As Long
Dim hProcess As Long, ExitCode As Long
If IsMissing(WindowState) Then WindowState = 1
hProg = Shell(PathName, WindowState)
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, hProg)
Do
GetExitCodeProcess hProcess, ExitCode
DoEvents
Loop While ExitCode = STILL_ACTIVE
End Sub
Sub Merge_CSV_Files()
Dim BatFileName As String
Dim TXTFileName As String
Dim XLSFileName As String
Dim FileExtStr As String
Dim FileFormatNum As Long
Dim DefPath As String
Dim Wb As Workbook
Dim oApp As Object
Dim oFolder
Dim foldername
BatFileName = Environ("Temp") & _
"\CollectCSVData" & Format(Now, "dd-mm-yy-h-mm-ss") & ".bat"
TXTFileName = Environ("Temp") & _
"\AllCSV" & Format(Now, "dd-mm-yy-h-mm-ss") & ".txt"
DefPath = Application.DefaultFilePath
If Right(DefPath, 1) <> "\" Then
DefPath = DefPath & "\"
End If
If Val(Application.Version) < 12 Then
FileExtStr = ".xls": FileFormatNum = -4143
Else
FileExtStr = ".xlsx": FileFormatNum = 51
'FileExtStr = ".xls": FileFormatNum = 56
End If
XLSFileName = DefPath & "SEM Master File" & _
Format(Now, "dd-mmm-yyyy h-mm-ss") & FileExtStr
Set oApp = CreateObject("Shell.Application")
Set oFolder = oApp.BrowseForFolder(0, "Select folder with CSV files", 512)
If Not oFolder Is Nothing Then
foldername = oFolder.Self.Path
If Right(foldername, 1) <> "\" Then
foldername = foldername & "\"
End If
Open BatFileName For Output As #1
Print #1, "Copy " & Chr(34) & foldername & "*.csv" _
& Chr(34) & " " & TXTFileName
Close #1
ShellAndWait BatFileName, 0
If Dir(TXTFileName) = "" Then
MsgBox "There are no csv files in this folder"
Kill BatFileName
Exit Sub
End If
Application.ScreenUpdating = False
Workbooks.OpenText Filename:=TXTFileName, Origin:=xlWindows, StartRow _
:=1, DataType:=xlDelimited, TextQualifier:=xlDoubleQuote, _
ConsecutiveDelimiter:=False, Tab:=False, Semicolon:=False, Comma:=True, _
Space:=False, Other:=False, AdjustColumnWidth = True
Set Wb = ActiveWorkbook
Application.DisplayAlerts = False
Wb.SaveAs Filename:=XLSFileName, FileFormat:=FileFormatNum
Application.DisplayAlerts = True
Wb.Close savechanges:=False
MsgBox "You will find the Excel file here: " & vbNewLine & XLSFileName
Kill BatFileName
Kill TXTFileName
Application.ScreenUpdating = True
End If
End Sub
The name of each file is its sample/image number and date. What I need is for this to ignore the first row of data in each csv file (Atomic number,Element symbol, etc.), create a single protected row at the top of the sheet that has those labels, and record the name of each file in a column next to each row of data from that file. With this information recorded I think I will be able to organize the data the way I want.
check out this approach using ADO. Adapted from: https://msdn.microsoft.com/en-us/library/ms974559.aspx
Const adOpenStatic = 3
Const adLockOptimistic = 3
Const adCmdText = &H0001
Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")
strPathtoTextFile = "C:\Databases\"
objConnection.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & strPathtoTextFile & ";" & _
"Extended Properties=""text;HDR=YES;FMT=Delimited"""
objRecordset.Open "SELECT * FROM MyCSV.csv where [Atomic number] <> "Atomic number"", _
objConnection, adOpenStatic, adLockOptimistic, adCmdText
Range("A2").CopyFromRecordset objRecordset
objRecordset.close
objConnection.close
the code below is creating an email, whilst searching through folders and attaching the relevant docs.
I have coded it to check whether the user has sent the email or closed it. I have put it so an input box gets displayed when the user closes the email. What i would like to happen is when the email gets closed, is the InputBox is set to focus, and after a reason why the email was sent have been entered then go back to the email to click on don't save draft.
Or even have the input box display after the email has been closed, after the save without changed dialog box.
Userform Code:
Dim OutApp As Object
Dim itmevt As New CMailItemEvents
Private Sub btnEMSent_Click()
Dim i, j, lastG, lastD As Long
Dim OutMail As Object
Dim sFName As String, colFiles As New Collection
Dim myDir As String, ChDir As String, attName As New Collection, attName2 As String
Dim dte As String
Dim greet As String, cntName As String, SigString As String, Signature As String
lastG = Sheets("File Locations").Cells(Rows.Count, "B").End(xlUp).Row
SigString = "H:\AppData\Roaming\Microsoft\Signatures\"
If Dir(SigString, vbDirectory) <> vbNullString Then
SigString = SigString & Dir$(SigString & "*.htm")
Else:
SigString = ""
End If
Signature = CreateObject("Scripting.FileSystemObject").GetFile(SigString).OpenAsTextStream(1, -2).readall
If Me.cmbMonth.Value = "" Then
Me.lblErrorMsg.Visible = True
Me.lblErrorMsg.Caption = "Payment Month Required!"
Me.cmbMonth.SetFocus
Exit Sub
ElseIf Me.txtbxYear.Value = "" Then
Me.lblErrorMsg.Visible = True
Me.lblErrorMsg.Caption = "Payment Year Required!"
Me.txtbxYear.SetFocus
Exit Sub
ElseIf Me.cmbSubbie.Value = "" Then
Me.lblErrorMsg.Visible = True
Me.lblErrorMsg.Caption = "Sub-Contractor Required!"
Me.cmbSubbie.SetFocus
Exit Sub
End If
For i = 1 To lastG
lookupVal = Sheets("File Locations").Cells(i, "B") ' value to find
If Dir(lookupVal, vbDirectory) = "" Then
Else
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
Set itmevt.itm = OutMail
dte = Me.cmbMonth.Value & " " & Me.txtbxYear.Text
myDir = lookupVal 'Set Dir to search
ChDir = (myDir & "\" & Me.cmbSubbie.Value & "\Remittance\") 'Change to that dir
sFName = Dir(ChDir & "*" & dte & "*") 'Set Search spec
While InStr(sFName, dte)
colFiles.Add (ChDir & sFName)
attName.Add (sFName)
sFName = Dir
Wend
End If
Next i
On Error Resume Next
With OutMail
If Me.txtbxSubNAME.Value <> "" Then
cntName = " " & Me.txtbxSubNAME.Value & ","
Else
cntName = ","
End If
If Time < TimeValue("12:00:00") Then
greet = "Good Morning" & cntName
Else
greet = "Good Afternoon" & cntName
End If
If colFiles.Count > 0 Then
For i = 1 To colFiles.Count
.Attachments.Add colFiles(i)
attName2 = attName(i) & "<br>" & attName2
Next i
End If
.To = Me.txtbxSubEMAIL.Value
.CC = ""
.BCC = ""
.Subject = Me.cmbMonth.Value & "'s Remittances"
.BodyFormat = olFormatHTML
.HTMLbody = "<HTML><BODY></BODY></HTML>" & .HTMLbody & Signature
.Display True
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
In the class module CMailItemEvents:
Option Explicit
Public WithEvents itm As Outlook.MailItem
Private Declare PtrSafe Function MessageBox _
Lib "User64" Alias "MessageBoxA" _
(ByVal hWnd As Long, _
ByVal lpText As String, _
ByVal lpCaption As String, _
ByVal wType As Long) _
As Long
Private Sub itm_Close(Cancel As Boolean)
Dim blnSent As Boolean
Dim lastG As Long
Dim myValue As Variant
lastG = Sheets("Report").Cells(Rows.Count, "A").End(xlUp).Row + 1
On Error Resume Next
blnSent = itm.Sent
If Err.Number = 0 Then
myValue = inputBox("Why was " & usrFrmEMAIL.cmbSubbie & " Remittance E-Mail not sent?", "Remittance Error")
Sheets("Report").Range("A" & lastG).Value = usrFrmEMAIL.cmbSubbie.Value
Sheets("Report").Range("B" & lastG).Value = usrFrmEMAIL.cmbMonth.Text & " " & usrFrmEMAIL.txtbxYear.Text
Sheets("Report").Range("C" & lastG).Value = Now
AppActivate (myValue.ActiveExplorer.CurrentItem)
Sheets("Report").Range("D" & lastG).Value = myValue
Exit Sub
Else
Sheets("Report").Range("A" & lastG).Value = usrFrmEMAIL.cmbSubbie.Value
Sheets("Report").Range("B" & lastG).Value = usrFrmEMAIL.cmbMonth.Text & " " & usrFrmEMAIL.txtbxYear.Text
Sheets("Report").Range("C" & lastG).Value = Now
End If
End Sub
sorry for taking so long to get back, i've been away on holiday.
I managed to figure out how to get this to work.
Class Module:
Option Explicit
Private Declare Function GetWindowThreadProcessId Lib _
"user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
Private Declare Function AttachThreadInput Lib "user32" _
(ByVal idAttach As Long, ByVal idAttachTo As Long, _
ByVal fAttach As Long) As Long
Private Declare Function GetForegroundWindow Lib "user32" () As Long
Private Declare Function SetForegroundWindow Lib "user32" _
(ByVal hWnd As Long) As Long
Private Declare Function IsIconic Lib "user32" _
(ByVal hWnd As Long) As Long
Private Declare Function ShowWindow Lib "user32" _
(ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Declare Function FindWindowA Lib "user32" (ByVal lpClassName _
As Long, ByVal lpWindowName As String) As Long
Const SW_SHOW = 5
Const SW_RESTORE = 9
Public WithEvents itm As Outlook.MailItem
Function ForceForegroundWindow(ByVal hWnd As Long) As Boolean
Dim ThreadID1 As Long
Dim ThreadID2 As Long
Dim nRet As Long
If hWnd = GetForegroundWindow Then
ForceForegroundWindow = True
Else
ThreadID1 = GetWindowThreadProcessId( _
GetForegroundWindow, ByVal 0&)
ThreadID2 = GetWindowThreadProcessId(hWnd, ByVal 0&)
If ThreadID1 <> ThreadID2 Then
AttachThreadInput ThreadID1, ThreadID2, True
nRet = SetForegroundWindow(hWnd)
AttachThreadInput ThreadID1, ThreadID2, False
Else
nRet = SetForegroundWindow(hWnd)
End If
If IsIconic(hWnd) Then
ShowWindow hWnd, SW_RESTORE
Else
ShowWindow hWnd, SW_SHOW
End If
ForceForegroundWindow = CBool(nRet)
End If
End Function
Private Sub itm_Close(Cancel As Boolean)
Dim blnSent As Boolean
Dim lastG As Long, currentrow As Integer
Dim myValue As String
Dim bOK As Boolean
Dim idx As Long
idx = usrFrmEMAIL.cmbSubbie.ListIndex
lastG = Sheets("Report").Cells(Rows.Count, "A").End(xlUp).Row + 1
On Error Resume Next
blnSent = itm.Sent
If Err.Number = 0 Then
itm.Close olDiscard
ForceForegroundWindow FindWindowA(0, Application.Caption)
Do
myValue = inputBox("Why was " & usrFrmEMAIL.cmbSubbie & "'s Remittance E-Mail not sent?", "Remittance Error")
If StrPtr(myValue) = 0 Then
bOK = False
MsgBox "You cannot just press Cancel!" & vbLf & vbLf & "A reason is needed for not sending the email.", vbCritical
ElseIf myValue = "" Then
bOK = False
MsgBox "You didn't enter anything, but pressed OK" & vbLf & vbLf & "A reason is needed for not sending the email.", vbExclamation
ElseIf Len(Application.WorksheetFunction.Substitute(myValue, " ", "")) = 0 Then
bOK = False
MsgBox "You only entered spaces!" & vbLf & vbLf & "A reason is needed for not sending the email.", vbExclamation
Else
bOK = True
Exit Do
End If
Loop Until bOK = True
Sheets("Report").Range("A" & lastG).Value = usrFrmEMAIL.cmbSubbie.Value
Sheets("Report").Range("B" & lastG).Value = usrFrmEMAIL.cmbMonth.Text & " " & usrFrmEMAIL.txtbxYear.Text
Sheets("Report").Range("C" & lastG).Value = Now
Sheets("Report").Range("D" & lastG).Value = myValue
If idx <> usrFrmEMAIL.cmbSubbie.ListCount - 1 Then
usrFrmEMAIL.cmbSubbie.ListIndex = idx + 1
Else
usrFrmEMAIL.cmbSubbie.ListIndex = 0
End If
Else
For currentrow = 6 To lastG
Sheets("Report").Range("A" & lastG).Value = usrFrmEMAIL.cmbSubbie.Value
Sheets("Report").Range("B" & lastG).Value = usrFrmEMAIL.cmbMonth.Text & " " & usrFrmEMAIL.txtbxYear.Text
Sheets("Report").Range("C" & lastG).Value = Now
Sheets("Report").Range("D" & lastG).Value = "Sent"
Next
If idx <> usrFrmEMAIL.cmbSubbie.ListCount - 1 Then
usrFrmEMAIL.cmbSubbie.ListIndex = idx + 1
Else
usrFrmEMAIL.cmbSubbie.ListIndex = 0
End If
End If
End Sub
UserForm Code:
Dim OutApp As Object
Dim itmevt As New CMailItemEvents
Private Sub btnCreateEmail_Click()
Dim i, j, lastG, lastD As Long
Dim OutMail As Object
Dim sFName As String, colFiles As New Collection
Dim myDir As String, ChDir As String, attName As New Collection, attName2 As String
Dim dte As String
Dim greet As String, cntName As String, SigString As String, Signature As String
lastG = Sheets("File Locations").Cells(Rows.Count, "B").End(xlUp).Row
SigString = "H:\AppData\Roaming\Microsoft\Signatures\"
If Dir(SigString, vbDirectory) <> vbNullString Then
SigString = SigString & Dir$(SigString & "*.htm")
Else:
SigString = ""
End If
Signature = CreateObject("Scripting.FileSystemObject").GetFile(SigString).OpenAsTextStream(1, -2).readall
If Me.cmbMonth.Value = "" Then
Me.lblErrorMsg.Visible = True
Me.lblErrorMsg.Caption = "Required!"
Me.cmbMonth.SetFocus
Exit Sub
ElseIf Me.txtbxYear.Value = "" Then
Me.lblErrorMsg.Visible = True
Me.lblErrorMsg.Caption = "Required!"
Me.txtbxYear.SetFocus
Exit Sub
ElseIf Me.cmbSubbie.Value = "" Then
Me.lblErrorMsg.Visible = True
Me.lblErrorMsg.Caption = "Required!"
Me.cmbSubbie.SetFocus
Exit Sub
End If
For i = 1 To lastG
lookupVal = Sheets("File Locations").Cells(i, "B") ' value to find
If Dir(lookupVal, vbDirectory) = "" Then
Else
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
Set itmevt.itm = OutMail
dte = Me.cmbMonth.Value & " " & Me.txtbxYear.Text
myDir = lookupVal 'Set Dir to search
ChDir = (myDir & "\" & Me.cmbSubbie.Value & "\***\") 'Change to that dir
sFName = Dir(ChDir & "*" & dte & "*") 'Set Search spec
While InStr(sFName, dte)
colFiles.Add (ChDir & sFName)
attName.Add (sFName)
sFName = Dir
Wend
End If
Next i
On Error Resume Next
With OutMail
If Me.txtbxSubNAME.Value <> "" Then
cntName = " " & Me.txtbxSubNAME.Value & ","
Else
cntName = ","
End If
If Time < TimeValue("12:00:00") Then
greet = "Good Morning" & cntName
Else
greet = "Good Afternoon" & cntName
End If
If colFiles.Count > 0 Then
For i = 1 To colFiles.Count
.Attachments.Add colFiles(i)
attName2 = attName(i) & "<br>" & attName2
Next i
End If
.To = Me.txtbxSubEMAIL.Value
.CC = ""
.BCC = ""
.Subject = Me.cmbMonth.Value & "'s "
.BodyFormat = olFormatHTML
.HTMLbody = "<HTML><BODY STYLE='font-family:Calibri;font-size:14.5'>" & greet & "<br><br>" & "Thank for your . Please see the attached remittances." & "<br><br>" & "<b>" & attName2 & "</b>" _
& "<br>" & "Please submit if you are required to do so. In this email, could you please copy in:" & "<br><br>" & _
"" & "<br><br>" & _
"<b>" & "Please note: " & "</b>" & "If you need to supply a matching invoice and we do not receive one, your payment's will be held." & "<br><br>" & "Kind regards," & "</BODY></HTML>" & .HTMLbody & Signature
.Display
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub