Olaylara Genel Bakış

Birçoğunuzun bildiği veya tahmin ettiği üzere Microsoft Office ürünlerinin hepsinin birbirleriyle konuşabilmesi diye birşey sözkonusu. Bunu günlük hayatta Excel'deki bir grafiği veya başka bir nesneyi Word'e veya PowerPointe kopyalarken görebiliyoruz. Bu ilişki kod tarafında da kurulabiliyor. Bunun için de hangi uygulamadaysanız, iletişim kurmak istediğiniz diğer uygulamadan bir Application nesnesi yaratma yoluyla ilerlersiniz. Bunun için iki yöntem var. Ya ilgili uygulamanın kütüphanesini(library) VBE'de Tools>Reference menüsünden eklemek ve bundan bir nese yaratmak, ki buna Early Binding denir, veya Object türünde bir değişken yaratıp Late Binding yöntemiyle ilgili atamayı yapmak. Bu şekilde ilgili uygulamanın nesne modeline erişim sağlamış oluruz. Bu arada, bu konuyla ilgili örnek dosyayı indirmek isterseniz buraya tıklayın.

Biz burada ağırlıklı olarak Early Binding metodunu göreceğiz, bir iki yerde ise Late Binding örneği yapacağız. Tabiki iki tür yöntemin de kendine göre avantajları var, bunları ilgili linklerde bulabilirsiniz ama ben intellisense özelliğinden faydalanmak istediğim için genelde Early Bindingi tercih ediyorum. Kütüphaneyi henüz eklememişken aşağıdaki gibi bir kod yazıp intellisensin bana ne gösterdiğine(daha doğrusu ne göstermediğine) bakalım.

post-thumb

Şimdi referansımızı ekleyelim.

post-thumb

Siz tabi kendinizde hangi Office versiyonu varsa onu eklersiniz. Ben şuan Office 2016 ile çalışıyorum, eğer sizde 2016'dan farklı bir Outlook versiyonu varsa ve bu sayfadaki örnek dosyaya bakıyorsanız sizde MISSING olarak görünür, bunu kaldırıp doğru versiyonu ekediğinizden emin olun. Eğer sıfırdan oluşturuyorsanız zaten başka bir library dosyası göreceksiniz.
Ve intellisense'i tekrar kontrol edelim. Gördüğünüz gibi şimdi Outlook geliyor.

post-thumb

Bu arada Outlook nesne modeliyle ilgili yaygın bir notasyon vardır. Outlook'un kendisini atayacağımız değişken adına genelde oApp/outApp, mail değişkenine ise oMail/outMail adı verilir. Ben de bu geleneği sürdüreceğim.

<

Bu kısımda önce basit bir mail gönderimi yapacağız, sonra da bir liste üzerinden çoklu mail gönderimi yapacağız. Aşağıda ayrı bir kısım olarak oluşturduğum Otomatik Mail Gönderim formunda ise kompleks bir mail gönderim uygulaması yazacağız.

Nesneleri Yaratmak

Mail göndermede ilk adım bir Outlook nesnesini bir de mail nesnesini temsil eden değişkenleri yaratmaktır. Bunu aşağıdaki kodla yapıyoruz.

                    
                        Dim oApp As Outlook.Application 'Outlook'un kendisi
                        Dim oMail As Outlook.MailItem 'mail nesnesi
                            
                            

Daha sonra bu değişkenlere değerlerini atıyoruz. Akabinde mailin temel özelliklerini (konusu, alıcısı, gövde metni) tanımlıyoruz, ancak maili göndermiyoruz, sadece gösteriyoruz (Display metodu). Maili göndermeden sadece gösterme eylemini uygulamak, test yapma konusunda bize büyük kolaylıklar sağlar. Bu metodu sık sık kullanmanızı tavsiye ederim.

                    
                            Sub mailGöster()

                            Dim oApp As Outlook.Application
                            Dim oMail As Outlook.MailItem

                            Set oApp = New Outlook.Application
                            Set oMail = oApp.CreateItem(olMailItem)

                            With oMail
                              .Subject = "bu bir deneme mailidir"
                              .To = "volkan.yurtseven@hotmail.com"
                              .Body = "deneme"
                              .Display
                            End With

                            End Sub
                                
                                

Outlook nesnesini taşıyacak değişkenle değerini atama işini iki ayrı satırda yaptık ancak daha önceki bölümlerden gördüğünüz üzere bunu tek satırda da yapabilirdik:

post-thumb
                    
                        Dim oApp As New Outlook.Application
                            
                            

Mail nesnesini yaratma işini ise Application nesnesinin CreateItem metodu ile yaptık, parametre olarak da intellisenseden açılan olMailItem değerini verdik. Bunların numerik değerleri de var, ancak Early Binding'in nimetlerinden faydalanalım diyorum. Yine de sıklıkla farklı kaynaklarda görebilirsiniz ve belki de kullanmak istersiniz diye sadece mailitem'ın nümerik değerini bilmeniz de faydalı olacaktır; o da 0'dır.

Late Binding ile Gönderim

Late Binding ile gönderim şekli ise şöyle olurdu:

                    
                                Sub lateBindingMail()

                                Dim oApp As Object
                                Dim oMail As Object

                                Set oApp = CreateObject("Outlook.Application")
                                Set oMail = oApp.CreateItem(olMailItem)

                                With oMail
                                  .Subject = "bu bir deneme mailidir"
                                  .To = "volkan.yurtseven@hotmail.com"
                                  .Body = "deneme"
                                  .Display
                                End With

                                End Sub
                                    
                                    

Outlook nesnesini Late Binding ile elde etmenin bir yolu da GetObject metodunu kullanmaktır.

O anda çalışan bir Outlook nesnesi varsa, yenisini yaratmamak adına bu bellekteki Outlook nesnesine başvurulur. Eğer yoksa, hata alınır, hata olmaması adına öncesine On Error Resume Next denir, hata kodu kontrol edilir, hata varsa CreateObjectle nesne yaratılır.

                    
                        On Error Resume Next
                        Set oApp = GetObject(, "Outlook.Application")

                        If Err.Number <> 0 Then '0 demek hata yok demek
                          Set oApp = CreateObject("Outlook.Application")
                        End If
                            
                            
Tek Mail Göndermek

Yukarıda gördüğümüz üzere, mail nesnesini yarattıktan sonra olmazsa olmazımız mailin alıcısını tespit etmektir. Bunu genelde To özelliği ile yaparız. Başka bir alternatif daha var, buna sonra değineceğiz.

Aşağıdaki örnekte, bir sayfada çeşitli Name'ler oluşturdum, ve mail gönderme işini de bir butona atadım. Maili gönderme işini Send metodu ile yapıyoruz.

post-thumb
                    
                                    Sub Button1_Click()

                                    Dim oApp As Outlook.Application
                                    Dim oMail As Outlook.MailItem

                                    Set oApp = New Outlook.Application
                                    Set oMail = oApp.CreateItem(olMailItem)

                                    With oMail
                                      .Subject = Range("mailSubj")
                                      .To = Range("mailTo")
                                      .Body = Range("mailBody")
                                      .Send
                                    End With

                                    End Sub
                                        
                                        
Çoklu (Batch) Mail Göndermek

Öncelikle şunu belirtmek isterim ki, çok sayıda alıcıya mail göndermekle kastımız, alıcıların birbirlerini görmeden mail göndermektir ve bunun birden çok yolu vardır. Bunlara hızlıca bir bakalım.

  • Alıcıları bcc'ye koyarak klasik yolla mail göndermek: Bu yolla parametrik bilgi geçilemez. Mesela kişiye özel hitap edilemez.
  • Word Mailmerge: Kişiye özel mesajlar vermek için Word'ün MailMerge özelliğini kullanabilirsiniz. Kişiye ismiyle hatta bay/bayan/doktor gibi çeşitli hitap şekilleriyle hitap edebileceğiniz gibi, onlara çeşitli rakamsal veya metinsel bilgileri kendilerine özel olarak iletebilirsiniz. Bununla ilgili olarak internette bol miktarda makale ve video bulunmakta. Hiç makro bilgisi gerektirmediği için de herkesin kullanabileceği bir özellik olmaktadır.
  • VBA: Mail Merge'le yapılamayan kişiye özel ek, kişiye özel cc/bcc belirleme, koşullu konu başlığı veya gövde metni belirleme gibi daha kompleks işleri VBA ile yapabilirsiniz.
  • Diğer Programatik Yaklaşımlar: Tüm diğer programlama dilleriyle bunu yapabilirsiniz, veya bu konuda hazır yazılmış programları veya benim Excelent isimli Add-in'im gibi paketleri kullanabilirsiniz.

Biz doğal olarak burada sadece VBA yöntemine değiniyor olacağız.

Bunun için aşağıdaki gibi bir listemiz olsun.

post-thumb

Mesaj kısmını, gördüğünüz gibi parametrik oluşturdum. Formülün sonundaki CHAR(13) ifadesi Enter işareti anlamına gelir, yani takip eden metin bir alt satırdan devam eder. (Bazı kaynaklarda CHAR(10) da görebilirsiniz, Excel'de ikisi de aynı sonucu üretir)

Sırayla tüm alıcılara mail atılması gerektiği için döngüsel bir kod yazmamız gerekiyor. Şimdi bu kodumuzu oluşturup bir düğmeye atayalım.

                    
                            Sub çoklumail_Button1_Click()
                            Dim oApp As Outlook.Application
                            Dim oMail As Outlook.MailItem
                            Dim alıcılar As Range, a As Range

                            Set oApp = New Outlook.Application
                            Set alıcılar = Range(Range("A2"), Range("A2").End(xlDown))

                            For Each a In alıcılar
                                Set oMail = oApp.CreateItem(olMailItem)
                                With oMail
                                    .Subject = "Doğum günü"
                                    .To = a.Value
                                    .Body = a.Offset(0, 3).Value & "Doğum gününüz kutlar, ailenizle birlikte mutlu yıllar dilerim"
                                    .Body = .Body & vbCrLf & "Gönderenin adı soyadı"
                                    .Send
                                End With
                                Set oMail = Nothing
                            Next a

                            Set oApp = Nothing
                            End Sub
                                
                                

Şimdi farkettiyseniz, bu sefer mail nesnesini For Each döngüsü içine aldık. Zira her gönderim sonunda ilgili nesneyi Nothing ataması yaparak yok etmemiz gerekiyor. Yok ettiğimiz nesneyi ise tekrar kullanamayacağımız için tekrar yaratıyoruz.

Aslında yok edilmesi gereken bir nesne daha var, o da oApp nesnesi, onu da programın en sonunda yok ediyoruz.

Evet sonuçta Sent Items'a baktığımızda aşağıdaki gibi kişiye özel maillerin gittiğini topluca görebilirsiniz.

post-thumb

Giden maillerden birinin içeriği de aşağıdaki gibidir.

post-thumb

Merhaba satırından sonra bir boşluk daha olsun isterseniz formülün içine iki tane CHAR(13) yazmanız gerkeir.

Maile Canlılık Katmak
HTMLBody

Gönderdiğiniz mailde çeşitli yerleri renkli, koyu, farklı yazı tipinde vs. göstermek isterseniz mail nesnesinin Body özelliğini değil HTMLBody özelliğini kullanmanız gerekir.

Bunun için biraz HTML bilmenizde fayda var. Aşağıdaki örnekte renklendirme ve kalınlaştırma örneğini görebilirsiniz.

                    
                                    Sub Button2_Click()
                                    Dim oApp As Outlook.Application
                                    Dim oMail As Outlook.MailItem

                                    Set oApp = New Outlook.Application
                                    Set oMail = oApp.CreateItem(olMailItem)

                                    imza = "

Adım Soyadım

" With oMail .Subject = Range("mailSubj") .To = Range("mailTo") .HTMLBody = Range("mailBody") .HTMLBody = .HTMLBody & imza .Send End With End Sub

Burada, H3, B, font gibi ifadelere Tag denir. Size, face, color gibi ifadelere ise özellik denir. Bunlardan

, özel bir başlık türüdür. Ben de mesela bu sayfayı hazırlarken az yukarıdaki "Maile Canlılık Katmak" başlığında H3 tag'ini kullanıyorum. , ilgili metni kalın yapar. Bu etiketler

ve etiketleriyle kapatılmak zorundadırlar.

Diğer kullanmak istediğiniz Tag'ler için bir HTML eğitim sayfasına bakmanızı tavsiye ederim.

Aşağıda giden mail Görünmektedir.

post-thumb
Resim Kullanmak

Mail gövdesine resim dahil etmek için yine HTMLBody özelliğini kullanıyoruz. Bunun için tag'i kullanılır.

Yukarıdaki kodda Send komutundan hemen önceki satıra şunu ekleyelim:

                    
                        .HTMLBody = .HTMLBody & ""
                            
                            

Giden mail de şöyle görünür:

post-thumb

Bu arada farkettiyseniz bu sefer img tag'inin özelliklerini kesme işareti arasına aldım. Bir üstteki örnekte ise iki tane çift tırnak içine almıştım. İki yöntem de özdeştir, tek kesme işareti pratik olmakla birlikte iki tane çift tırnak görürseniz de şaşırmayın.

Recipients Collection ve Recipient Nesnesi

Bazen mail göndermeye çalıştığımız mail adresi artık geçerli olmayabilir. Geçerli olmaktan kastım, ilgili adresin Address Book içinde kayıtlı olmasıdır. Mesela bu bir personel listesiyse, personellerden bazısı kurumdan ayrılmış olabilir. Böyle durumlarda, mail adresinin geçerli olup olmadığını kontrol etmemiz gerekebilir. Bunu da Recipient nesnesinin Resolve metodu ile yapıyoruz. Bu işlem, aslında Ribbon'dan Check Names düğmesine tıklamakla özdeştir.

Tabii ilgili mail adresinin Address Book'ta olması gerekiyor, ki personeller söz konusu olduğunda bu zaten kesindir, ama başka alıcılara mail göndermeye çalışırken Address Book'ta değillerse resolve yapamazsınız.

Aşağıdaki örnekte, önce Recipients nesnesini yaratıyoruz. Bunu MailItem nesnesine Add diyerek yapıyoruz. Parantez içine de bir mail adresi veya bunu temsil eden bir değişken atıyoruz. Hemen arkasından da Type özelliği ile bu alıcının tipini belirtiyoruz.

Sonrasında Resolve diyoruz, Resolve olup olmadığını (kontrol sonucunda adresin geçerli olup olmadığını) ise bir alt satırdaki Resolved property'si ile anlıyoruz. Eğer geçerliyse maili gönderiyoruz.

                    
                        Sub resolveörnek()

                        Dim oApp As New Outlook.Application
                        Dim olMailtem As Outlook.MailItem
                        Dim alıcı As Outlook.Recipient

                        Set olMailtem = oApp.CreateItem(olMailItem)
                        Set alıcı = olMailtem.Recipients.Add(geçerlimail_veya_outlookkullanıcısı)
                        alıcı.Type = olTo

                        alıcı.Resolve 'Check Names tuşuna basmışız gibi

                        If alıcı.Resolved Then 'Address Book'ta varsa
                          olMailtem.Subject = "deneme"
                          olMailtem.Send
                        End If

                        Set olMailtem = Nothing
                        Set oApp = Nothing
                        End Sub
                            
                            

Birçok kaynakta MailItem'ın To/Bc/Bcc özelliklerini kullanmak yerine Recipient'ın kullanımı önerilir. Genel olarak doğru bir öneri olmakla birlikte, listenizdekilerin kesin geçerli mailler/siciller olduğunu biliyorsanız Recipient çok da elzem değildir. Zira Recipient'ın kullanımı bazen karmaşık olabilmektedir. Mesela diğer üçünde tek seferde birden çok alıcı eklenebilirken, Recipient'ta her alıcı döngüsel olarak tek tek eklenmelidir.

Recipients Collection ve ResolveAll Metodu

Bu kontrol işlemini çoklu alıcılı versiyonu Recipients collection'ı ile ve bunun ResolveAll metodu ile yapmaktayız. ResolveAll da tıpkı Resolve gibi sonuç olarak True/False döndürür. Eğer alıcılardan birisi bile Resolve olmazsa False döner. Hangi alıcılar Resolve olmamış diye bakmak istersek For Next içinde tek tek Recipient'ları dolaşmamız gerekir. Aşağıda bununla ilgili bir örnek bulunmaktadır.

                    
                                        Sub CheckRecipients()
                                         Dim oapp As New Outlook.Application
                                         Dim omail As Outlook.MailItem
                                         Dim myRecipients As Outlook.Recipients
                                         Dim myRecipient As Outlook.Recipient

                                         Set omail = oapp.CreateItem(olMailItem)
                                         Set myRecipients = omail.Recipients
                                         myRecipients.Add ("volkan.yurtseven@hotmail.com")
                                         myRecipients.Add ("Dan Wilson asdas") 'bu mail, Address book'umda yok
                                         myRecipients.Add ("mvolkanyurtseven@gmail.com")

                                         'tüm mailler için Check Names tuşuna basılıp, hepsi de var mı diye bakıyoruz
                                         If Not myRecipients.ResolveAll Then 'hepsi birden aynı anda mevcut değilse
                                            For Each myRecipient In myRecipients
                                                If Not myRecipient.Resolved Then
                                                    myRecipients.Remove (myRecipient.Index) 'Address Book'ta bulunmayanları collection'dan çıkarıyoruz
                                                End If
                                            Next
                                         End If

                                         omail.Display 'sadece diğer ikisi görünür

                                        End Sub
                                            
                                            
Attachment (Ek) Gönderimi

Mail gönderimiyle ilgili en geniş kaynağa sahip sitelerden biri, belki de en popüleri, kendisini birçok kişinin referans olarak verdiği Ron de Bruin ustanın sitesidir. Burada bir dolu mail gönderim şekli var. O anki aktif sayfayı mail olarak göndermek, o an açık olan dosyayı göndermek, hatta bunun da iki versiyonu var: varolan bir dosya ve henüz yeni yaratılmış ancak hiç kaydedilmemiş bir dosyayı göndermek gibi.

Açıkçası şimdiye kadarki otomasyon süreçlerimde bu yöntemlerin birçoğuna başvurmadım. O yüzden kullanılma ihtimali çok düşük olan yöntemleri buraya koyarak vaktinizi almak istemem. İhtiyaç duyanlar, İngilizcesi varsa ustanın sitesinden bakabilir, İngilizceniz olmasa bile bu sitede öğrendiğiniz teknikler ve genel kodlama bakış açınızla bu işleri çok rahat yapabilirsiniz.

Mail otomasyonu sırasında en çok ihtiyaç duyduğum şey, parametrik bilgileri alıp toplu mail gönderimi yapmak oldu. Bu parametrik bilgilerden biri de kişiye özgü ek kullanmaktı. Ana kod bloklarını yukarıda gördünüz, ek eklemek için de yapmanız gereken aşağıdaki kodu Send metodundan önce bir yere dahil etmek olacaktır.

                    
                        oMailItem.Attachments.Add "Dosya adı"
                            
                            

Birden fazla ek koyacaksanız, bunları alt alta yazabilirsiniz, veya bir klasördeki tüm dosyaları göndermek istiyorsanız önce I/O teknikleriyle klasördeki dosyaları elde edip bir collection'a atar, sonra da for döngüsü içinde bunları maile eklersiniz.

                    
                        'Çoklu ek
                        oMailItem.Attachments.Add "Dosya adı1"
                        oMailItem.Attachments.Add "Dosya adı2"

                        'döngüsel ek, öncesinde I/O işlemi yapılıp collection'a alındığı varsayıldı
                        For Each f in dosyalar
                           oMailItem.Attachments.Add f
                        Next f
                            
                            

Zaten biraz aşağıda göreceğiniz gibi mail otomasyonu ile userform bilginizi bir araya getirdiğinizde gayet şık bir Toplu Mail Gönderim uygulamasını yazabilirsiniz.

Excel Range'i Body İçinde Göndermek

Bazen belli bir hücre kümesini (Range) ek olarak değil de mail bodysi içinde göndermek isteriz. Bunun için yine Ron de Bruin ustanın yazdığı ve hemen hemen tüm blog ve forumlarda referans verilen meşhur RangeToHtml fonksiyonunu kullanabiliriz. Fonksiyona hiç dokunmadan olduğu gibi kullanabilirsiniz. Fonksiyonu çağırırken ona ilgili Range'i parametre olarak vermeniz yeterli. Tabii dönen değeri de mailitem nesnesinin HTMLBody özelliğine atarız. Önce fonksiyona bakalım.

                    
                    Function RangetoHTML(rng As Range)
                    ' Changed by Ron de Bruin 28-Oct-2006
                    ' Working in Office 2000-2016
                        Dim fso As Object
                        Dim ts As Object
                        Dim TempFile As String
                        Dim TempWB As Workbook

                        TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"

                        'Copy the range and create a new workbook to paste the data in
                        rng.Copy
                        Set TempWB = Workbooks.Add(1)
                        With TempWB.Sheets(1)
                            .Cells(1).PasteSpecial Paste:=8
                            .Cells(1).PasteSpecial xlPasteValues, , False, False
                            .Cells(1).PasteSpecial xlPasteFormats, , False, False
                            .Cells(1).Select
                            Application.CutCopyMode = False
                            On Error Resume Next
                            .DrawingObjects.Visible = True
                            .DrawingObjects.Delete
                            On Error GoTo 0
                        End With

                        'Publish the sheet to a htm file
                        With TempWB.PublishObjects.Add( _
                             SourceType:=xlSourceRange, _
                             Filename:=TempFile, _
                             Sheet:=TempWB.Sheets(1).Name, _
                             Source:=TempWB.Sheets(1).UsedRange.Address, _
                             HtmlType:=xlHtmlStatic)
                            .Publish (True)
                        End With

                        'Read all data from the htm file into RangetoHTML
                        Set fso = CreateObject("Scripting.FileSystemObject")
                        Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
                        RangetoHTML = ts.readall
                        ts.Close
                        RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
                                              "align=left x:publishsource=")

                        'Close TempWB
                        TempWB.Close savechanges:=False

                        'Delete the htm file we used in this function
                        Kill TempFile

                        Set ts = Nothing
                        Set fso = Nothing
                        Set TempWB = Nothing
                    End Function
                        
                        
Excel Range'i Body İçinde Göndermek

Bazen belli bir hücre kümesini (Range) ek olarak değil de mail bodysi içinde göndermek isteriz. Bunun için yine Ron de Bruin ustanın yazdığı ve hemen hemen tüm blog ve forumlarda referans verilen meşhur RangeToHtml fonksiyonunu kullanabiliriz. Fonksiyona hiç dokunmadan olduğu gibi kullanabilirsiniz. Fonksiyonu çağırırken ona ilgili Range'i parametre olarak vermeniz yeterli. Tabii dönen değeri de mailitem nesnesinin HTMLBody özelliğine atarız. Önce fonksiyona bakalım.

                    
                        Function RangetoHTML(rng As Range)
                        ' Changed by Ron de Bruin 28-Oct-2006
                        ' Working in Office 2000-2016
                            Dim fso As Object
                            Dim ts As Object
                            Dim TempFile As String
                            Dim TempWB As Workbook

                            TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"

                            'Copy the range and create a new workbook to paste the data in
                            rng.Copy
                            Set TempWB = Workbooks.Add(1)
                            With TempWB.Sheets(1)
                                .Cells(1).PasteSpecial Paste:=8
                                .Cells(1).PasteSpecial xlPasteValues, , False, False
                                .Cells(1).PasteSpecial xlPasteFormats, , False, False
                                .Cells(1).Select
                                Application.CutCopyMode = False
                                On Error Resume Next
                                .DrawingObjects.Visible = True
                                .DrawingObjects.Delete
                                On Error GoTo 0
                            End With

                            'Publish the sheet to a htm file
                            With TempWB.PublishObjects.Add( _
                                 SourceType:=xlSourceRange, _
                                 Filename:=TempFile, _
                                 Sheet:=TempWB.Sheets(1).Name, _
                                 Source:=TempWB.Sheets(1).UsedRange.Address, _
                                 HtmlType:=xlHtmlStatic)
                                .Publish (True)
                            End With

                            'Read all data from the htm file into RangetoHTML
                            Set fso = CreateObject("Scripting.FileSystemObject")
                            Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
                            RangetoHTML = ts.readall
                            ts.Close
                            RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
                                                  "align=left x:publishsource=")

                            'Close TempWB
                            TempWB.Close savechanges:=False

                            'Delete the htm file we used in this function
                            Kill TempFile

                            Set ts = Nothing
                            Set fso = Nothing
                            Set TempWB = Nothing
                        End Function
                            
                            
Mail Gönderim Makrosu

Bu fonksiyonu kullanarak mail gönderim makrosu aşağıdaki gibi olabilir:

                    
                        Sub Mail_Selection_Range_Outlook_Body()
                            Dim rng As Range
                            Dim OutApp As Object
                            Dim OutMail As Object

                            Range("A1").CurrentRegion.Select
                            Set rng = Selection.SpecialCells(xlCellTypeVisible)
                            Set OutApp = CreateObject("Outlook.Application")
                            Set OutMail = OutApp.CreateItem(0)

                            With OutMail
                                .To = "ron@debruin.nl"
                                .CC = ""
                                .BCC = ""
                                .Subject = "This is the Subject line"
                                .HTMLBody = RangetoHTML(rng) 'fonksiyonu burada çağırıyoruz
                                .Send   'or use .Display
                            End With

                            Set OutMail = Nothing
                            Set OutApp = Nothing
                        End Sub
                            
                            
Gerçek Bir Örnek

Şimdi bir de kendi dünyamızdan gerçek bir örnek yapalım. Tabii bu, hakiki örneğin yine de basitleşmiş hali olacak. Zira hakiki örnekte, rapor schedule edilmiş, kendiliğinden çalışmış, mail gönderimi de kendiliğinden olmaktadır. Biz ise bu örnekte önceki aşamalar zaten olmuş farz edeceğiz ve gönderimi manuel yapacağız. (Full otomasyon detayı için Application.OnTime ve Veritabanı programlama konularına bakınız.)

Diyelim ki çekilen rapor aşağıdaki gibi. Bu 3 şubeye kendi müşterilerine ait listeyi göndereceğiz.

post-thumb

Öncelikle şubeler isimli bir collection'a dinamik bir şekilde listedeki şube kodlarını atarım. Bunun için Collectionlar sayfasındaki Coldavarmı fonksiyonumdan yararlandım.

Sonra da bu collection içinde tek tek hareket edip filtreleme yapıyor, filtre sonucunda tüm alanı seçip visible olanları mail body'sine yapıştırıyorum.

                    
                        Sub mevduatçıkışları()
                            Dim rng As Range
                            Dim OutApp As Object
                            Dim OutMail As Object
                            Dim şubeler As New Collection

                            Set OutApp = CreateObject("Outlook.Application")

                            For Each r In Range([a2], [a2].End(xlDown))
                                If ColdaVarmı(şubeler, r.Value) = False Then
                                    şubeler.Add r.Value
                                End If
                            Next r

                            For Each şb In şubeler
                                'Selection.AutoFilter
                                ActiveSheet.Range("A1").CurrentRegion.AutoFilter Field:=1, Criteria1:=şb

                                Range("A1").CurrentRegion.Select
                                Set rng = Selection.SpecialCells(xlCellTypeVisible)

                                Set OutMail = OutApp.CreateItem(0)

                                With OutMail
                                    .To = "volkan.yurtseven@hotmail.com" 'normalde burası parametrik olacak
                                    '.To = "sube." & r.Value & "@kurumadı.local" parametrikken böyle yapılır
                                    .Subject = "Mevduat Çıkışı yaşayan müşterileriniz"
                                    .HTMLBody = RangetoHTML(rng)
                                    '.Send   'ya da .Display
                                    .Display
                                End With

                                Set OutMail = Nothing
                            Next şb

                            Set OutApp = Nothing
                        End Sub
                        ---------
                        Function ColdaVarmı(col As Collection, kontrol As Variant) As Boolean
                        On Error Resume Next
                        ColdaVarmı = False
                        Dim x As Variant
                        For Each x In col
                          If x = kontrol Then
                            ColdaVarmı = True
                            Exit Function
                          End If
                        Next
                        End Function
                            
                            

Örnek bir mail görüntüsü aşağıdaki gibi olacaktır. Tabii kod içinde commentli olarak belirttiğim gibi aslında bu mailin 2 nolu şubeye gitmesi gerekiyor. Ayrıca body'ye konacak alanın formatlaması durumunda aynı şekilde gelecektir ve yine ayrıca isterseniz yapıştırılan bu Range'in öncesine veya sonrasına ilave mesaj ekleyebilirsiniz.

post-thumb
Excel Range'i Body İçinde Göndermek

Bazen belli bir hücre kümesini (Range) ek olarak değil de mail bodysi içinde göndermek isteriz. Bunun için yine Ron de Bruin ustanın yazdığı ve hemen hemen tüm blog ve forumlarda referans verilen meşhur RangeToHtml fonksiyonunu kullanabiliriz. Fonksiyona hiç dokunmadan olduğu gibi kullanabilirsiniz. Fonksiyonu çağırırken ona ilgili Range'i parametre olarak vermeniz yeterli. Tabii dönen değeri de mailitem nesnesinin HTMLBody özelliğine atarız. Önce fonksiyona bakalım.

                    
                                Function RangetoHTML(rng As Range)
                                ' Changed by Ron de Bruin 28-Oct-2006
                                ' Working in Office 2000-2016
                                    Dim fso As Object
                                    Dim ts As Object
                                    Dim TempFile As String
                                    Dim TempWB As Workbook

                                    TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"

                                    'Copy the range and create a new workbook to paste the data in
                                    rng.Copy
                                    Set TempWB = Workbooks.Add(1)
                                    With TempWB.Sheets(1)
                                        .Cells(1).PasteSpecial Paste:=8
                                        .Cells(1).PasteSpecial xlPasteValues, , False, False
                                        .Cells(1).PasteSpecial xlPasteFormats, , False, False
                                        .Cells(1).Select
                                        Application.CutCopyMode = False
                                        On Error Resume Next
                                        .DrawingObjects.Visible = True
                                        .DrawingObjects.Delete
                                        On Error GoTo 0
                                    End With

                                    'Publish the sheet to a htm file
                                    With TempWB.PublishObjects.Add( _
                                         SourceType:=xlSourceRange, _
                                         Filename:=TempFile, _
                                         Sheet:=TempWB.Sheets(1).Name, _
                                         Source:=TempWB.Sheets(1).UsedRange.Address, _
                                         HtmlType:=xlHtmlStatic)
                                        .Publish (True)
                                    End With

                                    'Read all data from the htm file into RangetoHTML
                                    Set fso = CreateObject("Scripting.FileSystemObject")
                                    Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
                                    RangetoHTML = ts.readall
                                    ts.Close
                                    RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
                                                          "align=left x:publishsource=")

                                    'Close TempWB
                                    TempWB.Close savechanges:=False

                                    'Delete the htm file we used in this function
                                    Kill TempFile

                                    Set ts = Nothing
                                    Set fso = Nothing
                                    Set TempWB = Nothing
                                End Function
                                    
                                    
Mail Gönderim Makrosu

Bu fonksiyonu kullanarak mail gönderim makrosu aşağıdaki gibi olabilir:

                    
                        Sub Mail_Selection_Range_Outlook_Body()
                            Dim rng As Range
                            Dim OutApp As Object
                            Dim OutMail As Object

                            Range("A1").CurrentRegion.Select
                            Set rng = Selection.SpecialCells(xlCellTypeVisible)
                            Set OutApp = CreateObject("Outlook.Application")
                            Set OutMail = OutApp.CreateItem(0)

                            With OutMail
                                .To = "ron@debruin.nl"
                                .CC = ""
                                .BCC = ""
                                .Subject = "This is the Subject line"
                                .HTMLBody = RangetoHTML(rng) 'fonksiyonu burada çağırıyoruz
                                .Send   'or use .Display
                            End With

                            Set OutMail = Nothing
                            Set OutApp = Nothing
                        End Sub
                            
                            
Gerçek Bir Örnek

Şimdi bir de kendi dünyamızdan gerçek bir örnek yapalım. Tabii bu, hakiki örneğin yine de basitleşmiş hali olacak. Zira hakiki örnekte, rapor schedule edilmiş, kendiliğinden çalışmış, mail gönderimi de kendiliğinden olmaktadır. Biz ise bu örnekte önceki aşamalar zaten olmuş farz edeceğiz ve gönderimi manuel yapacağız. (Full otomasyon detayı için Application.OnTime ve Veritabanı programlama konularına bakınız.)

Diyelim ki çekilen rapor aşağıdaki gibi. Bu 3 şubeye kendi müşterilerine ait listeyi göndereceğiz.

Öncelikle şubeler isimli bir collection'a dinamik bir şekilde listedeki şube kodlarını atarım. Bunun için Collectionlar sayfasındaki Coldavarmı fonksiyonumdan yararlandım.

Sonra da bu collection içinde tek tek hareket edip filtreleme yapıyor, filtre sonucunda tüm alanı seçip visible olanları mail body'sine yapıştırıyorum.

                    
                                Sub mevduatçıkışları()
                                    Dim rng As Range
                                    Dim OutApp As Object
                                    Dim OutMail As Object
                                    Dim şubeler As New Collection

                                    Set OutApp = CreateObject("Outlook.Application")

                                    For Each r In Range([a2], [a2].End(xlDown))
                                        If ColdaVarmı(şubeler, r.Value) = False Then
                                            şubeler.Add r.Value
                                        End If
                                    Next r

                                    For Each şb In şubeler
                                        'Selection.AutoFilter
                                        ActiveSheet.Range("A1").CurrentRegion.AutoFilter Field:=1, Criteria1:=şb

                                        Range("A1").CurrentRegion.Select
                                        Set rng = Selection.SpecialCells(xlCellTypeVisible)

                                        Set OutMail = OutApp.CreateItem(0)

                                        With OutMail
                                            .To = "volkan.yurtseven@hotmail.com" 'normalde burası parametrik olacak
                                            '.To = "sube." & r.Value & "@kurumadı.local" parametrikken böyle yapılır
                                            .Subject = "Mevduat Çıkışı yaşayan müşterileriniz"
                                            .HTMLBody = RangetoHTML(rng)
                                            '.Send   'ya da .Display
                                            .Display
                                        End With

                                        Set OutMail = Nothing
                                    Next şb

                                    Set OutApp = Nothing
                                End Sub
                                ---------
                                Function ColdaVarmı(col As Collection, kontrol As Variant) As Boolean
                                On Error Resume Next
                                ColdaVarmı = False
                                Dim x As Variant
                                For Each x In col
                                  If x = kontrol Then
                                    ColdaVarmı = True
                                    Exit Function
                                  End If
                                Next
                                End Function
                                    
                                    

Örnek bir mail görüntüsü aşağıdaki gibi olacaktır. Tabii kod içinde commentli olarak belirttiğim gibi aslında bu mailin 2 nolu şubeye gitmesi gerekiyor. Ayrıca body'ye konacak alanın formatlaması durumunda aynı şekilde gelecektir ve yine ayrıca isterseniz yapıştırılan bu Range'in öncesine veya sonrasına ilave mesaj ekleyebilirsiniz.

Excel Range'i Ek Olarak Göndermek

Eğer göndermek istediğiniz kısım body'ye konmayacak kadar büyükse bu Range'i ek olarak göndermek isteyebilirsiniz. Bunun için kodumuz şöyle olabilir. (Bu kod da Ron de Bruin'in sayfasından alınarak sadeleştirilmiştir)

Burada geçici bir dosya oluşturup, ilgili range'i bu yeni dosyaya yapıştırıp kaydediyoruz, sonra bunu attachment yapıyoruz, mail gönderildikten sonra da geçici dosyayı siliyoruz.

                    
                            Sub selection_ek_olarak_mail()

                            Dim Source As Range
                            Dim Dest As Workbook
                            Dim wb As Workbook
                            Dim TempFilePath As String
                            Dim TempFileName As String
                            Dim FileExtStr As String
                            Dim FileFormatNum As Long
                            Dim OutApp As Object
                            Dim OutMail As Object

                            Dim şubeler As New Collection

                            Set OutApp = CreateObject("Outlook.Application")

                            For Each r In Range([a2], [a2].End(xlDown))
                              If ColdaVarmı(şubeler, r.Value) = False Then
                                şubeler.Add r.Value
                              End If
                            Next r

                            For Each şb In şubeler
                              ActiveSheet.Range("A1").CurrentRegion.AutoFilter Field:=1, Criteria1:=şb
                              Range("A1").CurrentRegion.Select
                              Set Source = Selection.SpecialCells(xlCellTypeVisible)
                              Set wb = ActiveWorkbook
                              Set Dest = Workbooks.Add(xlWBATWorksheet)

                              Source.Copy
                              With Dest.Sheets(1)
                                .Cells(1).PasteSpecial Paste:=8
                                .Cells(1).PasteSpecial Paste:=xlPasteValues
                                .Cells(1).PasteSpecial Paste:=xlPasteFormats
                                .Cells(1).Select
                                Application.CutCopyMode = False
                              End With

                              TempFilePath = Environ$("temp") & "\"
                              TempFileName = şb & "-dosyadı"

                              If Val(Application.Version) < 12 Then
                                'You use Excel 97-2003
                                FileExtStr = ".xls": FileFormatNum = -4143
                              Else
                                'You use Excel 2007-2016
                                FileExtStr = ".xlsx": FileFormatNum = 51
                              End If

                              Set OutApp = CreateObject("Outlook.Application")
                              Set OutMail = OutApp.CreateItem(0)

                              With Dest
                                .SaveAs TempFilePath & TempFileName & FileExtStr, FileFormat:=FileFormatNum
                              On Error Resume Next
                                With OutMail
                                  .To = "volkan.yurtseven@hotmail.com"
                                  .Subject = "Mevduat çıkışı yaşayan müşterileriniz"
                                  .Body = "Değerli şube çalışanlarımız ......."
                                  .Attachments.Add Dest.FullName
                                  '.Send
                                  .Display
                                End With
                                .Close savechanges:=False
                              End With

                              Kill TempFilePath & TempFileName & FileExtStr

                              Set OutMail = Nothing
                            Next şb

                            Set OutApp = Nothing

                            End Sub
                                
                                
post-thumb
Outlook'un yerleşik (built-in) klasörlerine erişim

Namespace nesnesini elde ettikten sonra artık bunun metodlarını kullanabiliriz.

Yerel klasörlere erişmek için GetDefaultFolder metodunu kullanırız.

"GetDefaultFolder(" yazdıktan sonra intellisense çıkar ve ihtiyacımız olan folder tipini seçeriz. Burada 20 çeşit default folder bulunur. (Inbox, SentItems, DeletedItems v.s)

                    
                                    Sub defaultfolderişleri()

                                    Dim oApp As New Outlook.Application
                                    Dim ns As Outlook.Namespace
                                    Dim myInbox As Outlook.Folder

                                    Set ns = oApp.GetNamespace("MAPI")
                                    Set myInbox = ns.GetDefaultFolder(olFolderInbox) 'Inbox seçildi

                                    MsgBox myInbox.Items.Count 'Inboxtaki mail adedi

                                    End Sub
                                
                                
Kullanıcı tanımlı klasörlere erişim

Birçoğumuz, Outlook'ta mailleri kategorilendirmek için özel klasörler oluşturur ve bunlar için de kural tanımlar. İşte bu özel klasörlere ulaşmak için de Folders metodunu kullanırız. Ancak öncesinde hangi yerleşik klasörde olduğunu seçmemiz gerekiyor.

Mesela aşağıdaki kod ile Inbox'ın altındaki "Önemli Mailler" klasörüne erişiyoruz.

VBA Kodu
                    
                                Dim oApp As New Outlook.Application
                                Dim ns As Outlook.Namespace
                                Dim myInbox As Outlook.Folder, önemliMailler As Outlook.Folder

                                Set ns = oApp.GetNamespace("MAPI")
                                Set myInbox = ns.GetDefaultFolder(olFolderInbox)
                                Set önemliMailler = myInbox.Folders("Önemli Mailler")
                            
                            

Aradığımız klasör kök (root) dizindeyse o zaman önce root foldera erişir, sonra alt klasöre erişiriz.

VBA Kodu
                    
                                    Dim oApp As New Outlook.Application
                                    Dim ns As Outlook.Namespace
                                    Dim myroot As Outlook.Folder, önemliMailler As Outlook.Folder

                                    Set ns = oApp.GetNamespace("MAPI")
                                    Set myroot = ns.Folders("volkan.yurtseven@hotmail.com")
                                    Set önemliMailler = myroot.Folders("Önemli Mailler")
                                
                                

Aşağıdaki kod ile root dizindeki tüm alt dizinlere erişmiş oluruz.

VBA Kodu
                    
                                Sub altklasörler()
                                Dim oApp As New Outlook.Application
                                Dim ns As Outlook.Namespace
                                Dim myroot As Outlook.Folder

                                Set ns = oApp.GetNamespace("MAPI")
                                Set myroot = ns.Folders("volkan.yurtseven@hotmail.com")

                                For Each mysubfolder In myroot.Folders
                                  Debug.Print mysubfolder.Name & ":" & mysubfolder.Items.Count
                                Next mysubfolder

                                End Sub
                            
                            

Bir klasöre erişmenin bir yolu da ona EntryID'si ile doğrudan ulaşmaktır. Bu kodu öğrenmek için Outlook'ta bir folder üzerinde dururken, Outlook'un VBA editörünü açın (Alt+F11 ile). Sonra Ctrl+G ile immediate windowunu açıp oraya şunu yazın ve entera basın.

VBA Kodu
                    
                                ?activeexplorer.CurrentFolder.EntryID
                            
                            

Size şuna benzer bir kod verecektir:

000000003E7EE53625476B448D0FE831F453D7B701005069785A8AC0604C87420FC7F4CA7EE90000000001540000

Bu kodu Excel'de GetFolderFromID metoduna parametre olarak verebilirsiniz.

VBA Kodu
                    
                            Set yeniklasör = ns.GetFolderFromID("000000003E7EE53625476B448D0FE831F453D7B701005069785A8AC0604C87420FC7F4CA7EE90000000001540000")
                        
                        

Bir klasördeki mailler içinde dolaşma

Bir kez hedef klasörü belirledikten sonra döngüsel bir yapı ile mailler içinde dolaşabiliriz.

VBA Kodu
                    
                            Sub folderdakimaillerdegezinme()
                            Dim oApp As New Outlook.Application
                            Dim ns As Outlook.Namespace
                            Dim myroot As Outlook.Folder, myfolder As Outlook.Folder
                            Dim msg As Outlook.MailItem

                            Set ns = oApp.GetNamespace("MAPI")
                            Set myroot = ns.Folders("volkan.yurtseven@hotmail.com")
                            Set myfolder = myroot.Folders("önemli mailler")

                            For Each msg In myfolder.Items
                                Debug.Print msg.Subject
                            Next msg
                            End Sub
                        
                        
Beklediğiniz mail gelmiş mi kontrolü

Diyelim ki, günlük schedule raporlarınızın çalışmaya başlaması için kurumunuzun Veri ambarındaki (DWH) tabloların yüklenmesi gerekiyor. Ve bu bilgi de size günlük olarak maille geliyor. Bu maillerin subjecti de "DWH yüklemesi tamamlanmıştır" olsun. Ortalama olarak sabah 7-8 civarında yüklemenin tamamlandığını varsayalım.

Böyle bir durumda ana schedule işlemini 06:00'ye kurduğunuzu düşünelim. Ana prosedürdeki işlerden biri de tabii ki "DWH yüklemesi tamam mı" kontrolü olacaktır. Bu tamam değilse, 10 dakika sonraya kendini tekrar schedule etsin ve kalan kodları çalıştırmasın istiyoruz.

Aşağıdaki örnekte ana schedule kodu yok, böyle bir kod örneğine buradan ulaşabilirsiniz. Biz sadece DWH kontrolü yapan koda bakıyoruz, ki bu kodun içinde 10 dk sonra kendisini tekrar çalıştıran recursive bir kod da eklenmiş durumda.

Bu örnekte ilgili maili bulmak için Find metodunu kullanıyoruz. Eğer aranan maili bulamazsa ilgili değişkenin değeri Nothing olacaktır ve bu yüzden hata almaması için Nothing mi? kontrolü yapıyoruz, bulamadıysa 10 dk sonraya tekrar schedule ediyoruz. Bulursa ama bulduğu bugünden eski bir mailse, yine 10 dk sonraya schedule ediyoruz, ta ki bugün gelen maili bulana kadar.

VBA Kodu
                    
                            Sub dwh_kontrol()
                            Dim oApp As New Outlook.Application
                            Dim ns As Outlook.Namespace
                            Dim myInbox As Outlook.Folder
                            Dim eItems As Outlook.Items
                            Dim eItem As Outlook.MailItem

                            Set ns = oApp.GetNamespace("MAPI")
                            Set myInbox = ns.GetDefaultFolder(olFolderInbox)
                            Set eItems = myInbox.Items
                            Set eItem = eItems.Find("[Subject]=""DWH Yüklemesi tamamlanmıştır""")
                            If eItem Is Nothing Then
                               Application.OnTime Now + TimeSerial(0, 10, 0), procedure:="dwh_kontrol"
                            ElseIf DateValue(eItem.ReceivedTime) < Date Then 'var ama eski mailse
                               Application.OnTime Now + TimeSerial(0, 10, 0), procedure:="dwh_kontrol"
                            Else 'bugün geldiyse
                               'buraya yapılacak işler konur
                            End If
                            End Sub
                        
                        

Find yerine doğrudan tüm mailleri tarama yolunu da seçebilirdiniz. Ancak bu durumda tüm mailleri taraması gerekebildiği için kod performansı kötü etkilenebilir. Böyle olmaması adına bir rule tanımlayıp DWH yükleme maillerini belli bir klasöre alabilirsiniz, böylece göreceli daha küçük bir klasörde tarama yapmış olursunuz. Ama çok özel bir sebebiniz yoksa bence bunlara hiç gerek yok, Find metodu oldukça kullanışlıdır.

Olur da bir nedenle aynı konuyla ilgili başka mail var mı diye bakmak isterseniz, döngü içinde FindNext metodunu kullanabilirsiniz. Bu durumda klasördeki tüm maillerde dolaşmanız gerekebilir.

VBA Kodu
                    
                                Sub dwh_kontrol()
                                Dim oApp As New Outlook.Application
                                Dim ns As Outlook.Namespace
                                Dim myInbox As Outlook.Folder
                                Dim eItems As Outlook.Items
                                Dim eItem As Outlook.MailItem

                                Set ns = oApp.GetNamespace("MAPI")
                                Set myInbox = ns.GetDefaultFolder(olFolderInbox)
                                Set eItems = myInbox.Items
                                Set eItem = eItems.Find("[Subject]=""DWH yüklemesi tamamlanmıştır""")
                                'çeşitli işler

                                For i = 1 To eItems.Count
                                  If TypeName(eItem) = "Nothing" Then
                                    MsgBox "Başka mail yok!"       
                                    Exit Sub
                                  End If
                                  'Diğer işlemler
                                Set eItem = eItems.FindNext
                                Next i
                                End Sub
                            
                            
Restrict metodu

Find ve FindNext'e bir alternatif Restrict metodudur. Find ve FindNext ile ilgili kritere uyan maillerde teker teker ilerlenirken, Restrict ile bunlar filtrelenir ve yeni bir collection elde edilmiş olur.

                        
                                    Dim oApp As New Outlook.Application
                                    Dim ns As Outlook.Namespace
                                    Dim myInbox As Outlook.Folder
                                    Dim eItems As Outlook.Items

                                    Set ns = oApp.GetNamespace("MAPI")
                                    Set myInbox = ns.GetDefaultFolder(olFolderInbox)
                                    Set eItems = myInbox.Items

                                    Kriter ="[Subject]=""DWH yüklemesi tamamlanmıştır"""
                                    Set Kriterliler = eItems.Restrict(Kriter)

                                    If Kriterliler.Count < 1 Then
                                      MsgBox "Hiçbir DWH yükleme maili bulunamadı"
                                      Exit Sub
                                    End If

                                    For Each eItem In Kriterliler
                                      'yapılacak işler
                                    Next
                                
                                

Birden fazla kriter söz konusu ise ilgili metodu ardışık bir şekilde uygulayabilirsiniz.

                        
                                    Sub restricli_çokkriter()

                                    Dim oApp As New Outlook.Application
                                    Dim ns As Outlook.Namespace
                                    Dim myInbox As Outlook.Folder
                                    Dim eItems As Outlook.Items
                                    Dim kriterliler As Outlook.Items

                                    Set ns = oApp.GetNamespace("MAPI")
                                    Set myInbox = ns.GetDefaultFolder(olFolderInbox)
                                    Set eItems = myInbox.Items

                                    Kriter = "[Subject]=""DWH yüklemesi tamamlanmıştır"""
                                    Kriter2 = "[ReceivedTime]>'" & Format("01.06.2018", "dd.mm.yyyy") & "'"
                                    Set kriterliler = eItems.Restrict(Kriter).Restrict(Kriter2)

                                    If kriterliler.Count < 1 Then
                                      MsgBox "Aranan kritere uygun mail bulunamadı"
                                      Exit Sub
                                    End If

                                    MsgBox kriterliler.Count

                                    End Sub
                                
                                

İpucu: Arama yapılacak klasörde çok mail varsa Restrict'i, görece daha az mail varsa Find ve FindNext'i kullanın. (Eğer tabi aranan kritere uygun tüm maillerle ilgileniyorsanız. Yoksa yukarıdaki DWH yükleme örneğindeki gibi sadece ilk maille ilgileniyorsanız Find yeterlidir.)

Bir maildeki ekleri bir klasöre kaydetme

Kurum olarak çok sağlam bir veri ambarımız var ama bazen dataya erken ulaşmak önem arz edebiliyor. O yüzden iştirakler gibi dışardan gelen datayı veri ambarına eklenmesini beklemeden işlemek isteyebiliyorsunuz. Bunun için de az yukarıda bahsettiğim "beklediğiniz mail gelmiş mi" kontrolünü yaptıktan sonra ekleri istediğim klasöre alıyorum, akabinde raporumu oluşturuyorum.

Bu koda ustanın şu sayfasından ulaşabilirsiniz. (Ron de Bruin Reis hakikaten efsane)

Örnek kodlar
                        
                                    'Bu kod, MyFolder altındaki tüm mailleri "C:\mailek" klasörüne taşır
                                    SaveEmailAttachmentsToFolder "MyFolder", "", "C:\mailek"

                                    'Bu kod, MyFolder altındaki tüm xlsx uzantılı mailleri "C:\mailek" klasörüne taşır
                                    SaveEmailAttachmentsToFolder "MyFolder", "xlsx", "C:\mailek"
                                
                                
Bir maildeki ekleri bir klasöre kaydetme

Kurum olarak çok sağlam bir veri ambarımız var ama bazen dataya erken ulaşmak önem arz edebiliyor. O yüzden iştirakler gibi dışardan gelen datayı veri ambarına eklenmesini beklemeden işlemek isteyebiliyorsunuz. Bunun için de az yukarıda bahsettiğim "beklediğiniz mail gelmiş mi" kontrolünü yaptıktan sonra ekleri istediğim klasöre alıyorum, akabinde raporumu oluşturuyorum.

VBA Kodu
                        
                                Sub SaveEmailAttachmentsToFolder(OutlookFolderInInbox As String, _
                                                                 ExtString 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

                                    On Error GoTo ThisMacro_err

                                    Set ns = GetNamespace("MAPI")
                                    Set Inbox = ns.GetDefaultFolder(olFolderInbox)
                                    Set SubFolder = Inbox.Folders(OutlookFolderInInbox)

                                    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 = ""
                                    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
                                    End If

                                    If Right(DestFolder, 1) <> "\" Then
                                        DestFolder = DestFolder & "\"
                                    End If

                                   ' Check each message for attachments and extensions
                                    For Each Item In SubFolder.Items
                                        For Each Atmt In Item.Attachments
                                            If LCase(Right(Atmt.FileName, Len(ExtString))) = LCase(ExtString) Then
                                                FileName = DestFolder & Item.SenderName & " " & Atmt.FileName
                                                Atmt.SaveAsFile FileName
                                                I = I + 1
                                            End If
                                        Next Atmt
                                    Next Item

                                ' Show this message when Finished
                                    If I > 0 Then
                                        MsgBox "You can find the files here : " _
                                             & DestFolder, vbInformation, "Finished!"
                                    Else
                                        MsgBox "No attached files in your mail.", vbInformation, "Finished!"
                                    End If

                                ' Clear memory
                                ThisMacro_exit:
                                    Set SubFolder = Nothing
                                    Set Inbox = Nothing
                                    Set ns = Nothing
                                    Set fs = Nothing
                                    Set wsh = 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
                            
                            
Toplu mail silme, arşive kaldırma

Otomasyon süreci sonunda eğer ki bilgisayarınızdan çok sayıda mail gidiyorsa (Bende bir ara farklı konu başlıklarında olmak üzere günde 5 bine yakın mail gidiyordu), bir süre sonra mail hesabınız şişmeye başlar, hatta admin'lerin belirlediği kapasiteye göre her gün bu kapasiteyi aşabilirsiniz. Bunun için yine otomatize edilmiş bir temizlik makrosuna ihtiyacınız olacaktır.

Şimdi öncelikle basit bir döngüsel mail silme nasıl oluyor ona bakalım. Silme işlemi Delete metodu ile yapılıyor.

VBA Kodu
                        
                                    Sub mailsilme()

                                    Dim oApp As New Outlook.Application
                                    Dim ns As Outlook.Namespace
                                    Dim myroot As Outlook.Folder, delFolder As Outlook.Folder
                                    Dim silinecekler As Outlook.Items

                                    Set ns = oApp.GetNamespace("MAPI")
                                    Set myroot = ns.Folders("volkan.yurtseven@hotmail.com")
                                    Set delFolder = myroot.Folders("klasörler")
                                    Set silinecekler = delFolder.Items

                                    For n = silinecekler.Count To 1 Step -1
                                       silinecekler(n).Delete
                                    Next n

                                    End Sub
                                
                                

Dikkat döngülerini anlatırken yaptığım bir uyarı vardı: Akılda bulundurulması gereken önemli bir husus, ForEach kullanıldığında read-only bir özellik gösterir. Yani bu yöntemle dizi elemanlarını değiştiremezsiniz. Elamanları değiştirmek istiyorsanız basit For döngüsü kullanmanız lazım. O yüzden silme işlemlerinde asla ForEach kullanmayın, normal For kullanın.

Otomasyon süreçlerinde toplu mail silme

Konu başında belirttiğim gibi, eğer sizin hesabınızdan günde binlerce mail gidiyorsa SentItems'ınızı arada bir temizlemek yararlı olacaktır.

Bunun için ilk önerim şudur, ki ben de böyle yapardım, otomasyon sürecinizdeki maillerin subject kısmında son karakteri "*" veya benzer bir özel karakter koyun. Ör: "Mevduat çıkışlarınız hakkında*". Böylece silinecek mailleri özel bir işaretle etiketlemiş olursunuz ve böylece bunları tek bir kontrolle silebilirsiniz. Aksi halde her gönderim subjecti için ayrı ayrı kontrol yapmak zorunda kalırsınız, bu da hem kod yazımını uzatır hem de silme işleminin uzun sürmesine neden olabilir.

Bu makroyu da yine ana schedule kodunuz içinde gece çalışacak şekilde kurabilirsiniz, zira silinecek mail çoksa silme işlemi gün içinde sizi bloke edebilir.

Şimdi SentItems içindeki mailler arasında subject'inde "*" geçenleri bulmalıyız. Restrict metodu ile bunu yaparız ancak normalde yukarıda restrict metodunda tam eşleşmeli bir kriter vermiştik. Şimdiki kriterimiz ise tam eşleşmeli değil, içinde "*" geçenleri arıyoruz. Bunun için aşağıdaki özel yöntemi kullanıyoruz. Bu özel yöntemdeki 0x0037001f ifadesi sadece Subject alanına filtre uygulamış olur, diğer alanlar için buraya bakmanız gerekmektedir.

VBA Kodu
                        
                                    Sub mailsilme_kriterli()

                                    Dim oApp As New Outlook.Application
                                    Dim ns As Outlook.Namespace
                                    Dim delFolder As Outlook.Folder
                                    Dim silinecekler As Outlook.Items

                                    Set ns = oApp.GetNamespace("MAPI")
                                    Set delFolder = ns.GetDefaultFolder(olFolderSentMail)

                                    kriter = "@SQL=""http://schemas.microsoft.com/mapi/proptag/0x0037001f"" like '%" & "*" & "%'"

                                    Set silinecekler = delFolder.Items.Restrict(kriter)
                                    MsgBox silinecekler.Count

                                    For n = silinecekler.Count To 1 Step -1
                                       silinecekler(n).Delete
                                    Next n

                                    End Sub
                                
                                

Bir nedenle maillerinizin ortasında * işareti kullanıyorsanız, bu yöntem onların da silinmesine neden olabilir. O yüzden başka bir işaret kullanabilirsiniz. Veya SentItems içindeki tüm mailler içinde dolaşıp If Right(subject,1) = "*" diye de kontrol edebilirsiniz.

Arşive kaldırma

Mailleri silmek yerine bir süre elinizde kanıt/kayıt olması babında arşiv klasörlerine kaldırmak isteyebilirsiniz. Zira bir gün birisi size gelip de şu alıcılara şöyle şöyle mail atmışsın (aslı astarı olmadığı halde) diyebilir. Arşivlemek için Move metodunu kullanabilirsiniz. Aslında arşivlemekten kastımız arşiv olarak belirlediğiniz bir klasöre taşımaktır, yoksa arşive özel bir durum yoktur.

Bunun için kendinize bir hedef klasör belirlersiniz, sonra ilgili maili bu klasöre taşırsınız.

VBA Kodu
                        
                                    'Önceki tanımlamalar
                                    Set hedefklasör = myInbox.Folders("Gönderilen Arşivi")
                                    oMailitem.Move hedefklasör
                                
                                

Bu yöntemi sadece arşive kaldırma için değil herhangi bir klasöre taşımak için de kullanabilirsiniz. Aşağıdaki örnekte SentItems'da subjecti "*" karakterini içeren tüm mailleri arşiv klasörüne alıyoruz. Bu sefer Restrict metodu ile değil Find-FindNext metodu ile ilerledik.

VBA Kodu
                        
                                    Sub arşive_kaldırma()
                                     Dim oApp As New Outlook.Application
                                     Dim ns As Outlook.Namespace
                                     Dim myInbox As Outlook.Folder
                                     Dim hedefklasör As Outlook.Folder
                                     Dim myItems As Outlook.Items
                                     Dim myItem As Object

                                     Set ns = oApp.GetNamespace("MAPI")
                                     Set myInbox = ns.GetDefaultFolder(olFolderSentMail)
                                     Set myItems = myInbox.Items

                                     Set hedefklasör = ns.Folders("volkan.yurtseven@hotmail.com").Folders("Gönderilen arşivi")
                                     kriter = "@SQL=""http://schemas.microsoft.com/mapi/proptag/0x0037001f"" like '%" & "*" & "%'"

                                     Set myItem = myItems.Find(kriter)
                                     Do While TypeName(myItem) <> "Nothing"
                                        myItem.Move hedefklasör
                                        Set myItem = myItems.FindNext
                                     Loop

                                    End Sub
                                
                                

Calendar'a, Contact'lara ve Task'lere erişme de VBA ile pek tabi yapılabilir ama bu sitenin kapsamı gözönüne bulundurulduğunda çok fazla alakalı olduğunu söyleyemem, ve şahsen ben de ufak tefek kodlar yazma dışında pek haşır neşir olmadım.

Mesela bu linkte, Outlook contact listenizdeki herkesin bilgilerini Excel'e alabileceğiniz bir örnek var.

Outlook Programlamadan Faydalanma

Bunun dışında küçük işleriniz için bile Outlook programlamadan faydalanabilirsiniz. Mesela daha bu hafta işyerinde, iş yükümü planlayan bir kod yazdım. Yeni işim gereği çok fazla toplantım oluyor, o yüzden yapmayı planladığım işlerin tahmini iş yüküne ek olarak toplantı saatlerini de toplam iş yükü listesine almam gerekiyor, ki kendime aşırı fazla yükleme yapmayayım.

Bunun için de Recipients nesnesinin Freebusy metoduna erişerek o haftanın toplantı saatlerini çekip onu bir dönüştürme işlemine tabi tutuyor, sonra da Excel'e diğer işlerin yanına çekiyorum.

post-thumb
                    
                            Sub toplantı_süresi_bul()
                             Dim oApp As New Outlook.Application
                             Dim ns As Outlook.Namespace
                             Dim rcp As Outlook.Recipient
                             Dim dt As Date
                             Dim karakterler() As String

                             Set ns = oApp.GetNamespace("MAPI")
                             Set rcp = ns.CreateRecipient(12345) 'buraya outlookta sizi tanımlayan sicil numaranız v.s gelecek

                             For Each dts In Range("tarihler") 'tarihler diye bir NamedRange var
                                dt = DateValue(dts)
                                topldurumu = Mid(rcp.FreeBusy(dt, 30, True), 19, 37)     
                                karakterler = Split(StrConv(topldurumu, vbUnicode), Chr$(0))
                                ReDim Preserve karakterler(UBound(karakterler) - 1)
                                For Each krk In karakterler
                                    toplam = toplam + Sgn(Val(krk))
                                Next krk
                                süre = toplam / 2
                                dts.Offset(0, 1) = süre 'ilgili tarih hücreinin 1 sağındaki hücreye o günkü toplam toplantı süresini yazdırıyorum
                                toplam = 0 'resetliyorum
                             Next dts

                             Set oApp = Nothing
                            End Sub
                        
                        
Ödev Önerisi

Siz de kendinize şöyle bir ödev verebilirsiniz: Contact List'teki tüm alıcılara ismiyle hitap ettiğiniz bir yılbaşı kutlama otomasyonu.

Çalıştığım kurumda genelde bölme makrosuyla kullanılan ve en çok kullanılan kodlarımdan biri de mail gönderim formudur. Bu form ek olmadan da kullanılabilmekle birlikte, ek de göndermeyi düşünüyorsanız ekleri oluşturmak için bu bölme makrosuna ihtiyaç duyulmaktadır. Tabi isteyen öyle bir kod yazar ki, ayrı bir bölme koduna ihtiyaç olmaz; aynı kod içinde önce bölüp hemen arkasından da mail gönderir. Ben ise farklı bir yöntem belirledim, önce bölme makrousu ile tüm dosyaları oluşturuyorum, sonra da mail atma işlemini yapıyorum. Yani özetle iki ana işi birbirinden ayırdım. Zira böldüğümüz şube dosyalarını zipleyip bölgeye de topluca atma durumu olabilir, o yüzden benim yöntemi benimsemenizi tavsiye ederim. Daha yukarıda belirttiğim gibi, bunun daha farklı ve gelişmiş bir versiyonu Excelent içinde yer almaktadır. İstediğiniz, kod yazmadan böyle bir çözüme ulaşmaksa Excelent'ı kullanabilirsiniz. Şimdi kod yazmak isteyenlerle devam edelim. Amacımız aşağıdaki gibi bir listedeki alıcılara tek tek kendilerine özel olacak şekilde mail göndermek.

post-thumb

Öncelikle aşağıdaki gibi bir form oluşturacağız.(Buraya koyacağım form, işyerinde kullandığıma göre biraz daha basit olacak, siz bunu daha işlevsel hale getirebilirsiniz.)
Gerekli açıklamaları hem kod içinde hem de aşağıdaki çeşitli yerlerde bulacaksınız.

post-thumb

Bu formu açmak için sayfaya bir buton koydum ancak tabi ideal dünyada bunun bir Add-in'den açılması gerekir. Bunu yapmayı da şurada göreceğiz

Formu oluşturma

Bu formun başlangıç ayarlarını öyle bir yapcaz ki, aşağıdaki gibi görünecek.(Siyah yazılar grileşti ve bazı kontroller gizlendi)

post-thumb

Başlangıç için Properties ayarları ve Formun Initiliaze eventi aşağıdaki gibidir. Bu arada belirtmem gerekir ki gerekli tüm kontrollere anlaşılır bir isim verdim. Mesajı dinamik değişmeyen label'lara veya sadece bir kez kullanılan Multipage gibi kontrollere özel isim vermedim.


Properties ayarları
  • txtBody'nin ve parametrik mail sayfasındaki 4 kutunun EnterKeyBehaviour özelliğini True yapın. Böylece Enter tuşuna bastıkça bir alt satıra geçersiniz, aksi halde textbox içinden çıkmış olursunuz.
  • Yine aynı kontrollerin Multiline özelliğine de True atayın.
  • optMetinTip'i seçili halde başlatmak için Value=True yapın.
  • optFromKendimden'in de Value değerini True yapın.
  • chkDeneme'nin de Value değerini True yapın.
Initialize ayarları

Burada tek seferde yapılan görünmezlik, renk ayarı ve combobox değer atamalarını yapıyoruz.

<
                    
                                    Private Sub UserForm_Initialize()
                                    Dim ctrl As Control

                                    MultiPage1.Value = 0 'her zaman ilk sayfa açık gelsin

                                    'ek'teki tüm kontrolleri görünmez yapıyoruz, checkbox hariç
                                    For Each ctrl In Me.frmEk.Controls
                                        If Not TypeOf ctrl Is MSForms.CheckBox Then
                                            ctrl.Visible = False
                                        End If
                                    Next ctrl

                                    'şimdi tüm textboxların metnini gri yapalım
                                    For Each cf In Me.Controls
                                        If TypeName(cf) = "Frame" Then
                                        For Each ctrl In cf.Controls
                                            If TypeName(ctrl) = "TextBox" Then
                                                ctrl.ForeColor = &H808080
                                            End If
                                        Next ctrl
                                        End If
                                    Next cf
                                    Me.txtSubject.ForeColor = &H808080 'bu frame içinde değil, ayrıca yapıyoruz

                                    'şimdi de comboboxların değerlerini yüklüyoruz
                                    Me.cbFromBölümler.List = Array("satış@kurum.com", "pazarlama@kurum.com", "ik@kurum.com")
                                    Me.cbEkUzantı1.List = Array("xlsx", "xlsb", "xls", "pdf", "docx", "doc", "zip", "rar")
                                    Me.cbEkUzantı2.List = Array("xlsx", "xlsb", "xls", "pdf", "docx", "doc", "zip", "rar")
                                    Me.cbEkUzantı3.List = Array("xlsx", "xlsb", "xls", "pdf", "docx", "doc", "zip", "rar")
                                    Me.cbEkUzantı1.Value = "xlsx"
                                    Me.cbEkUzantı2.Value = "xlsx"
                                    Me.cbEkUzantı3.Value = "xlsx"
                                    Me.cbBelirteç.List = Array("Sicil", "Şube", "Bölge")

                                    End Sub
                                
                                
Textbox'lara tıklama olayları

Şimdi de font rengini grileştirdiğimiz textbox'lara tıklandığında hem talimat metnini silen hem de metin rengini siyaha dönüştüren kod parçasını yazalım. Şimdilik bu kontrollere tek tek kod yazıyoruz, ancak yazılan kodu fonksiyonel yapmak için ana bir fonksiyon yazdım, textboxların Enter eventinde bu kodu çağırıyorum. (Bununla birlikte bizi her bir kontrol için tek tek aynı kodu yazmaktan kurtaran bir yöntem var. Bunu Class Modül içine bir custom eventi olan değişken tanımlayarak yapabiliyoruz. Bununla ilgili bilgiyi şurada bulabilirsiniz)

VBA Kodu
                    
                                        '-----EVENT BÖLGE BAŞLANGICI:initializede grileştirilen tüm textboxlar için event ayarlıyoruz
                                        Sub txtEnter(txt As MSForms.TextBox)
                                          If txt.ForeColor = &H808080 Then
                                            txt.Text = ""
                                            txt.ForeColor = vbBlack
                                          End If
                                        End Sub
                                        Private Sub txtGlobalcc_Enter()
                                            Dim txtctrl As MSForms.TextBox
                                            Set txtctrl = Me.txtGlobalcc
                                            Call txtEnter(txtctrl)
                                        End Sub
                                        Private Sub txtGlobalbcc_Enter()
                                            Dim txtctrl As MSForms.TextBox
                                            Set txtctrl = Me.txtGlobalbcc
                                            Call txtEnter(txtctrl)
                                        End Sub
                                        Private Sub txtBağımsızEk1_Enter()
                                            Dim txtctrl As MSForms.TextBox
                                            Set txtctrl = Me.txtBağımsızEk1
                                            Call txtEnter(txtctrl)
                                        End Sub
                                        Private Sub txtBağımsızEk2_Enter()
                                            Dim txtctrl As MSForms.TextBox
                                            Set txtctrl = Me.txtBağımsızEk2
                                            Call txtEnter(txtctrl)
                                        End Sub
                                        Private Sub txtEk1_Enter()
                                            Dim txtctrl As MSForms.TextBox
                                            Set txtctrl = Me.txtEk1
                                            Call txtEnter(txtctrl)
                                        End Sub
                                        Private Sub txtEk2_Enter()
                                            Dim txtctrl As MSForms.TextBox
                                            Set txtctrl = Me.txtEk2
                                            Call txtEnter(txtctrl)
                                        End Sub
                                        Private Sub txtEk3_Enter()
                                            Dim txtctrl As MSForms.TextBox
                                            Set txtctrl = Me.txtEk3
                                            Call txtEnter(txtctrl)
                                        End Sub
                                        Private Sub txtSubject_Enter()
                                            Dim txtctrl As MSForms.TextBox
                                            Set txtctrl = Me.txtSubject
                                            Call txtEnter(txtctrl)
                                        End Sub
                                        '-----EVENT BÖLGE BİTİŞ
                                    
                                    
Framelerin içi

Bölüm/departman adına mail göndermek isterseniz, bu comboboxa tıkladığınızda bununla ilgili seçeneği aktive edelim.

VBA Kodu
                    
                                        Private Sub cbFromBölümler_Enter()
                                            Me.optFromBölüm.Value = True
                                        End Sub
                                    
                                    

Kimden gideceği ile ilgili olarak optionbuttonlarda seçim yapıldığında sadece birini kontrol etmek yeterli, diğeri zaten otomatikman if kontrolünün Else bölümüne düşer.

VBA Kodu
                    
                            Private Sub optFromMe_Change()
                                If Me.optFromBölüm.Value = True Then
                                    Me.cbFromBölümler.Value = "varsayılanbirim@kurum.com"
                                    Me.cbFromBölümler.Enabled = True
                                Else 'kendi adresimden gidecekse
                                    Me.cbFromBölümler.Value = ""
                                    Me.cbFromBölümler.Enabled = False
                                End If
                            End Sub
                        
                        

Şimdi de eklerle ilgili framede, ek checkboxına tıklandığında neler olduğuna bakalım. Burada After_update eventine bakıyoruz. Chekboxın kendi dışındakileri, hep bir önceki(zıttı) değerine atıyoruz. Görünürlerse görünmez, görünmezlerken görünür olsunlar.

VBA Kodu
                    
                                Private Sub chkEk_AfterUpdate()
                                Dim ctrl As Control
                                For Each ctrl In Me.frmEk.Controls
                                    If Not TypeOf ctrl Is MSForms.CheckBox Then
                                        ctrl.Visible = Not ctrl.Visible
                                    End If
                                Next ctrl
                                End Sub
                            
                            

Son olarak, ek çerçevesindeki Klasör seçme düğmesindeki koda bakalım. Burada varsayılan olarak C:\böl klasörü dikkate alınırken (zira bölme makrosu da varsayılan olarak dosyaları bu klasöre atar) bu düğme ile istersek dosyaların bulunduğu klasörü değiştirebiliriz.

VBA Kodu
                    
                                Private Sub btnKlasör_Click()
                                Dim fd As FileDialog
                                Set fd = Application.FileDialog(msoFileDialogFolderPicker)

                                With fd
                                    .Title = "Klasör seçin"
                                    If .Show = True Then
                                        Me.lblKlasör.Caption = .SelectedItems(1)
                                    End If
                                End With

                                End Sub
                            
                            
Ana mail gönderim kodu

Şimdi geldik esas baba kodun olduğu kısma. Burada şimdiye kadar gördüğümüz birçok tekniği kullanacağız. Hata yönetiminden, DoEvents kullanımına, dizilerden döngülere kadar birçok içerik var. Öncelikle performans açısından önem arzeden bir kodlama olacağı için kendimizi değişken tanımlamaya zorlayalım, en tepeye Option Explicit koayalım. Bu örnekte outlook nesnesini Late Binding ile yaratıcam. Çünkü bu uygulamayı sadece kendim kullanmayı düşünmüyorum, başka arkadaşlara da göndermeyi düşünüyorum, o yüzden olur da onların Office versiyonu benden farklı olabilir diye böyle yapıyorum. Bunu yaparken önce Early Bindingle başladım, böylece intellisenseten faydalandım, sonra kodları Late Binding'e çevirdim.
Kodumuz biraz uzunca ve şöyle:

                    
                                Private Sub btnGönder_Click()

                                    Dim oApp As Object, oMail As Object, alıcılar As Object 'Bunu gönderdiğim kişilerdeki office versiyonu farklı olabilir diye
                                    Dim sayac As Integer, giden As Integer, i As Integer, kolonsayı As Integer, s As Integer, k As Integer
                                    Dim toSon As Integer, ccSon As Integer, bccSon As Integer
                                    Dim carpan As Double
                                    Dim progress_char As String, stbar As String, mesaj As String, ekPrefix As String, klasör As String
                                    Dim emailList As Variant
                                    Dim kutu As MSForms.TextBox
                                    Dim toKolon As Integer, ccKolon As Integer, bccKolon As Integer, ilkDeğişkenKolon As Integer
                                    Dim cckombine As String, bcckombine As String

                                    On Error GoTo hata
                                    'On Error GoTo 0 'bir hata çıkarsa nerede çıktığını anlamak içinbunu aktiv yapıp üstteki pasifleştiriyorum

                                    'bazı textboxlarda boş olup olmama kontrolü yapıyoruz, o yüzden ilk baştan ben giriyorum, nolur nolmaz diye
                                    'zaten içleri gri renkteyse temizlik işi yapacak, benhalihazırda girip başka birşey yazdıysam bişey yapmayacak
                                    Call txtGlobalcc_Enter
                                    Call txtGlobalbcc_Enter
                                    Call txtSubject_Enter
                                    Call txtEk1_Enter
                                    Call txtEk2_Enter
                                    Call txtEk3_Enter
                                    Call txtBağımsızEk1_Enter
                                    Call txtBağımsızEk2_Enter

                                    Set oApp = CreateObject("outlook.application")
                                    sayac = 0

                                    'statusbar için ayarlamalar
                                    carpan = 100 / (Cells(2, 1).End(xlDown).Row - 1)
                                    progress_char = Chr(8)
                                    stbar = Application.StatusBar 'o an statusbarda ne yazıyorsa onu alıyoruz.
                                    'Muhtemelen boştur, ancak olur da benim gibi burayı çok sık kullanan biriyseniz
                                    'o sırada başka birşey yazıyor olabilir, kod bitince ilk haline döndrümek isteyebilirsiniz

                                    'kod hızlandırıcıları devreye sokuyorum
                                    Application.DisplayAlerts = False
                                    Application.ScreenUpdating = False
                                    Application.Interactive = False

                                    'Döngüye giriyoruz
                                    [a2].Select
                                    Do While WorksheetFunction.CountA(Range(ActiveCell, ActiveCell.Offset(0, kolonsayı))) <> 0
                                        s = ActiveCell.Row
                                        Set oMail = oApp.CreateItem(0)
                                        Set alıcılar = oMail.Recipients

                                        'body metni
                                        If Me.MultiPage1.Value = 0 Then 'ilk sayfaysa yani tek parça mail gidecekse
                                            mesaj = Me.txtBody.Text
                                        Else
                                            ilkDeğişkenKolon = 6 'formatımızın sabit olduğunu düşünerek hardcoded 6 yazdım
                                            For i = 1 To 4 '4 kutu olduğu için
                                                Set kutu = Me.Controls("txtParametrikBody" & i) 'textboxlara isimleriyle ulaşıyoruz
                                                mesaj = mesaj & kutu.Text & IIf(Cells(s, i + ilkDeğişkenKolon - 1) = "Paragraf", vbCrLf & vbCrLf, Cells(s, i + ilkDeğişkenKolon - 1))
                                            Next i
                                        End If

                                        'from
                                        If Me.optFromBölüm Then oMail.sentonbehalfname = Me.cbFromBölümler.Value

                                        'to
                                        toKolon = 2
                                        emailList = Split(Cells(s, toKolon).Value, ";")
                                        toSon = UBound(emailList)
                                        For k = 0 To toSon
                                            alıcılar.Add emailList(k)
                                            alıcılar(k + 1).Type = 1
                                        Next k

                                        'cc
                                        If Not IsEmpty(Cells(2, 4)) Then
                                            ccKolon = 4
                                            cckombine = IIf(Me.txtGlobalcc.Text <> "", Me.txtGlobalcc.Text & ";" & Cells(s, ccKolon), Cells(s, ccKolon))
                                            emailList = Split(cckombine, ";")
                                            ccSon = UBound(emailList)
                                            For k = 0 To ccSon
                                                alıcılar.Add emailList(k)
                                                alıcılar(toSon + 1 + k + 1).Type = 2 'cc
                                            Next k
                                        Else 'sadece globalccyi almak yeterli, onda da doğrudan CC propertysi ile
                                            oMail.CC = Me.txtGlobalcc.Text
                                        End If

                                        'bcc
                                        If Not IsEmpty(Cells(2, 5)) Then
                                            bccKolon = 5
                                            bcckombine = IIf(Me.txtGlobalbcc.Text <> "", Me.txtGlobalbcc.Text & ";" & Cells(s, bccKolon), Cells(s, bccKolon))
                                            emailList = Split(bcckombine, ";")
                                            bccSon = UBound(emailList)
                                            For k = 0 To bccSon
                                                alıcılar.Add (emailList(k))
                                                alıcılar(toSon + ccSon + 2 + k + 1).Type = 3 'bcc
                                            Next k
                                        Else 'sadece globalbccyi almak yeterli, onda da doğrudan bcc propertysi ile
                                            oMail.BCC = Me.txtGlobalbcc.Text 'boş da olsa ekleyelim
                                        End If

                                        'subject
                                        If Me.txtSubject.Value <> "" Then
                                            oMail.Subject = Me.txtSubject.Text
                                        Else
                                            MsgBox "Konu alanı boş olamaz, doldurup tekrar deneyin"
                                            GoTo çıkış
                                        End If

                                        'düz metin mi html mi
                                        If Me.optMetinTipDüz Then
                                            oMail.Body = mesaj
                                        Else
                                            oMail.HTMLBody = mesaj
                                        End If

                                        'ekler
                                        If Me.chkEk.Value Then
                                            klasör = Mid(Me.lblKlasör.Caption & "\", 15)

                                            Select Case Me.cbBelirteç.Value
                                                Case "Sicil"
                                                    If IsEmpty(Cells(2, 2)) Then
                                                        MsgBox "belirteç olarak sicil seçtiniz ancak sicil kolonu boş, doldurup tekrar denyin"
                                                        GoTo çıkış
                                                    End If
                                                    ekPrefix = Trim(CStr(Cells(s, 2)))
                                                Case "Şube"
                                                    If IsEmpty(Cells(2, 1)) Then
                                                        MsgBox "belirteç olarak şube seçtiniz ancak sicil kolonu boş, doldurup tekrar denyin"
                                                        GoTo çıkış
                                                    End If
                                                    ekPrefix = Trim(CStr(Cells(s, 1)))
                                                Case "Bölge"
                                                    If IsEmpty(Cells(2, 3)) Then
                                                        MsgBox "belirteç olarak bölge seçtiniz ancak sicil kolonu boş, doldurup tekrar denyin"
                                                        GoTo çıkış
                                                    End If
                                                    ekPrefix = Trim(CStr(Cells(s, 3)))
                                                Case Else
                                                    MsgBox "Ek için belirteç seçmeyi unutuyorsunuz"
                                                    GoTo çıkış
                                            End Select

                                            'normalde attachment eklemenini son 2 parametresi
                                            If Me.txtEk1.Text <> "" Then oMail.Attachments.Add klasör & ekPrefix & "-" & Me.txtEk1.Text & "." & Me.cbEkUzantı1.Value
                                            If Me.txtEk2.Text <> "" Then oMail.Attachments.Add klasör & ekPrefix & "-" & Me.txtEk2.Text & "." & Me.cbEkUzantı2.Value
                                            If Me.txtEk3.Text <> "" Then oMail.Attachments.Add klasör & ekPrefix & "-" & Me.txtEk3.Text & "." & Me.cbEkUzantı3.Value

                                            If Me.txtBağımsızEk1.Text <> "" Then oMail.Attachments.Add klasör & Me.txtBağımsızEk1.Text
                                            If Me.txtBağımsızEk2.Text <> "" Then oMail.Attachments.Add klasör & Me.txtBağımsızEk2.Text
                                        End If

                                        '***********************************
                                        '******gönderme işlemi başlıyor*****
                                        '***********************************
                                        'deneme maili mi diye kontrol, evetse Display
                                        If Me.chkDeneme Then
                                            oMail.Display
                                            Set oApp = Nothing
                                            GoTo çıkış
                                        Else
                                            'tüm alıcıların geçerli olup olmadığına bakıyoruz, geçerli değilse ilgili satır renklendrilir
                                            If Not alıcılar.ResolveAll Then
                                                sayac = sayac + 1
                                                Call renklendir
                                                GoTo sonraki
                                            End If

                                            oMail.Send
                                            'oMail.Display
                                         End If

                                         ActiveCell.Offset(1, 0).Select
                                         sayac = sayac + 1
                                         giden = giden + 1
                                         mesaj = vbNullString
                                         Set oMail = Nothing

                                        'statusbara progress durumunu yazdıralım
                                        DoEvents
                                        Application.StatusBar = "Tamamlanma oranı: " & String(Int(sayac * carpan), progress_char) & " %" & Int(sayac * carpan)
                                    sonraki:
                                        Set oMail = Nothing
                                    Loop

                                    Set oApp = Nothing
                                    MsgBox giden & " adet mail gönderimi tamamlanmıştır. Gönderimi başarısız olansatırlar sarı-kırmızı boyanmıştır"

                                    çıkış:
                                        Application.StatusBar = stbar
                                        Application.DisplayAlerts = True
                                        Application.ScreenUpdating = True
                                        Application.Interactive = True
                                        Exit Sub

                                    hata:
                                        If Err.Number = 13 Then
                                            Call renklendir
                                            Resume sonraki
                                        End If

                                        Set oApp = Nothing
                                        Application.StatusBar = stbar
                                        Application.DisplayAlerts = True
                                        Application.ScreenUpdating = True
                                        Application.Interactive = True
                                        If Err.Number <> 424 Then
                                            MsgBox Err.Description & vbCrLf & _
                                            "Bi sorun oluştu, Volkanla görüşün"
                                        End If
                                    End Sub
                                    Sub renklendir()
                                        With ActiveCell
                                            .EntireRow.Interior.Color = vbRed
                                            .EntireRow.Font.Color = vbYellow
                                            .Offset(1, 0).Select
                                        End With
                                    End Sub
                            
                            

Form'u açıp aşağıdaki gibi doldurup, deneme maili gönderiyoruz. Burada ek olarak bir tane parametrik ek, bir tane de sabit bağımsız ek koyuyoruz. Gönderim yaptığımız kişilere şube koduna göre bölünmüş dosyaları atacağımız için belirteç olarak Şube seçiyoruz.

post-thumb

Örnek deneme maili aşağıdaki gibi oluyor. Kod içindeki açıklamlardan göreceğinzi üzere, deneme mailini Send metodu ile değil Display metodu ile oluşturuyoruz.

post-thumb

"Deneme" chekcbox'ınbdan işareti kaldırıp gönderirsek hepsi gider ve aşağıdaki mesajla karşılaşırız.

post-thumb

Mail adreslerinden birini bilerek hatalı hale geitirirsem, kodumuz bu satırı atlar ve onu işaretler. Hatalı mailden kastımız, ribbondan Check Names dediğimizde bize sorun çıkaran mail adresleridir.

post-thumb
post-thumb

Tüm mailler giderken statusbarda da ne aşamada olduğumuzu görürüz. 6 mail olduğu için sırayla(az olduğu için çok hızlı) %16,33,49,66,83,100 olarak görürüz.

post-thumb
HTMLBody ile formatlı maili gönderimi

Göndermek istediğimiz mail özel formatlandırılmış olacaksa bu maili önce Wordde hazırlarız, sonra bunu web page olarak(html uznatılı) kaydederiz. Sonra bu dosyayı bir tarayıcıda açarız, ve sayfada herhangi bir yere sağ tıkayıp aşağıdaki gibi sayfa kaynağını görüntüle deriz.

post-thumb

Word, birsürü ıvız zıvır kodu kendiliğinden üretir. Bunların çoğu bize lazım değildir, aşağıdaki gibi Body tag'inden sonraki kısmı alırız ve bunu formumuzdaki kutuya yapıştırıtız.

post-thumb

Sonra bunu @" " tagleri içine alırız.

<html>

                        <body lang=TR style='tab-interval:35.4pt'>

                        <div class=WordSection1>

                        <p class=MsoNormal><b style='mso-bidi-font-weight:normal'>Değerli Bölge
                        Yöneticilerimiz<o:p></o:p></b></p>

                        <p class=MsoNormal><o:p>&nbsp;</o:p></p>

                        <p class=MsoNormal>Falan filana bla bla falan</p>

                        <p class=MsoNormal>Falan filan</p>

                        <p class=MsoNormal>Filana falna</p>

                        <p class=MsoNormal><o:p>&nbsp;</o:p></p>

                        <p class=MsoNormal><b style='mso-bidi-font-weight:normal'><span
                        style='color:red'>Falan Bölümü<br>
                        Filan Departmanı<o:p></o:p></span></b></p>

                        </div>

                        </body>
            </html>
                    

Kafanız çok karışacak gibi olursa hepsini de alabilirsiniz, sıkıntı olmaz.
Şimdi bir de ek ekleyelim ve Gönder diyelim: İşlem tamam.

post-thumb
Parametrik bilgili mail gönderimi

Listemiz aşağıdaki gibi. İlk değişkende personelin yaptığı satış adetleri, onun yanında hedefleri, onun yanında hedefine göre nerede olduğu ve nihai bir mesaj var. Buraya ilaveten kişinin adı, hitap şekli için cinsiyeti v.s de olabilirdi, biz basit olsun diye değişken miktarını 4'te tuttuk.

post-thumb

Formumuzu açalım ve aşağıdaki gibi dolduralım:

post-thumb

2. kutudan itibaren tüm kutulara bir boşlukla başlamak gerekiyor, yoksa kendinden önce gelen değişkene yapışık çıkar. Sonuçta giden mailimiz aşağıdaki gibi görünecek.

post-thumb

Malesef bu kod ile html ve parametriki bir arada çalıştıramayız, bunun için ilave kod yazılması gerekir. Ben açıkçası bununla uğraşmadım ancak isterseniz siz yapabilirsiniz, sizin için de güzel pratik olur. Bu arada unutmayın, eğer cc ve ek gönderimi olmayacaksa parametrik ve formatlı mail gönderimini Word'ün mail merge aracı ile de yapabilirsiniz.