Derler ki Wright kardeşler uçağı uçurmadan önce düşmeyi öğrenmişler. "Nasıl
iyi bir şekilde düşelim ki ölmeyelim" diye düşünmüşler. Uçağın icadına giden yol
böyleymiş.
Biz de makro yazarken çok sık hata yapacağız, ama bu hataları öyle zarif
bir şekilde ele almalıyız ki, bi terslik durumunda yumuşak bir iniş olsun.
İşte bu bölümde program akışı içinde
oluşan hataları nasıl ele alacağımızı ve yumuşak iniş yapmayı öğreneceğiz.
Zira iyi bir makro içinde mutlaka olası hataları ele alan bir bölüm olmalıdır.
On Error kalıbı hata yakalamanın kalbidir ve bu kalıbı takip eden bir dizi ifade vardır. Bunlar,
"hata olması
durumunda şunu yap" şeklinde özetlenebilecek ifadelerdir.
- On Error Resume Next
- On Error GoTo etiket
- On Error GoTo 0
- On Error GoTo -1
Şimdi bunlara tek tek bakalım
On Error Resume Next
Bu kalıp, VBA'e hatayı görmezden gelmesini ve bir sonraki satıra
geçmesini söyler. Yani hatayı düzeltmek yerine yola devam eder. O yüzden çok
dikkat edilmesi gereken bir kalıptır. Birçok durumda bunu kullanmaktan sakınmanız
gerekir, bütün üstatlar da bunu söyler zaten, ancak belli bazı durumlar vardır ki
bu kalıbı kullanmak akıllıca olmaktadır.
Mesela
bankada çalışıyoruz diyelim ve bir otomatik mail makromuz olsun. Bankadaki belli bir roldeki tüm
personele, kişiye özel olacak şekilde mail gönderiyor olalım. Ancak
elimizdeki kişi listesi güncel olmayabilir, yani bu kişilerden bazıları
artık bankadan ayrılmış olabilir. Böyle bir durumda programa şunu
söyleyebiliriz: "Sırayla herkese mail at, maili bulunmayanlar için durma,
bir sonrakine devam et". Bunu demezsek, program, ilk ayrılmış kişide durur.
Bunu ele almanın başka yöntemleri de var tabi; Resolve metodunu kullanmak, On
Error GoTo etiket diyip, ilgili kaydı formatlamak ve bir sonrakine devam
etmek gibi ama konuyu anlatmak adına basit düşünüyoruz.
İkinci bir örnek, her gün belirli bir saate schedule edilmiş ve o günün sonucunu tarih adıyla kaydeden bir rutininiz olsun.
Bir süre sonra
diskinizde şişkinlik olmasın diye her gün t-30 tarihli raporları silmeye yarayan
da başka bir rutininiz olsun. Yani bize sadece son 30 günün raporu yeter
diyoruz. Ama bazı günler raporlarınız oluşmamış olabilir, o yüzden
varolmayan bir t-30 raporunu silmeye çalışabilirsiniz. Böyle günlük schedue
edilmiş 40 raporunuzun ikisi için rapor oluşmadığını düşünelim. Diğer 38ini tehlikeye
atmamak
için yine bu kalıbı kullanabiliriz. Tabi bunu bir alternatifi de dosya mevcut mu
diye kontrol etmek de olabilir ama bu kontrol de kodun süresini uzatacaktır.
Böyle bir durumda bu kalıbı kullanmak daha pratik bir çözüm olacaktır.
Aşağıda ilk örneğimize ait kod bloğunu görüyoruz.
Sub onerrorresumeornek()
'Dim ile değişken deklerasyonları
'1000 diye sabit bir rakam olmaz tabi, bunu son satır gibi bir yöntemle yakalamanız lazım _
örnek basit olsun diye 1000 yazdım
For i=1 to 1000
On Error Resume Next 'sadece döngü içinde hata olursa bir sonraki satıra geçsin
'mail gönderim kodları
Next i
'varsa diğer kodlar
End Sub
On Error GoTo Etiket
Bu kalıp, tüm hata türlerini yakalar ve programı etiketin olduğu yere
yönlendirir. Bu kısımda genelde şunlar yapılır:
- Hata mesajları veririz
- Olası hatayı düzeltebiliyorsak düzeltir ve hata aldığımız yere veya
hemen sonraki satıra yönlendiririz
- Programın başında False atadığımız
bazı özellikleri burada tekrar True'ya döndürürüz
- Sayfa veya Workbook seviyesinde protection'ı geçici kaldırdıysak
tekrar protection uygularız
- Log tutarız
Etiketin hemen öncesinde bir Exit Sub ifadesinin olmasında fayda vardır, yoksa hatasız
ilerleyen kod son olarak
buraya gelir ve gereksiz yere burdaki kodları da çalıştırır, ki bunu
istemeyiz.
Hata mesajında genelde Err.Description ve Err.Number
özelliklerini kullanırız.
Err.Number için bir koşullu yapı kullanılarak, "Hata numarası şuysa şu mesajı, buysa bu
mesajı, diğer durumlar için Err.Description & "Volkanla görüşün" " gibi bir
mesaj verdirebiliriz.
Sub Hataornek()
On Error Goto hata
With Application
.DisplayAlerts=False
.ScreenUpdating=False
End With
'çeşitli kodlar
'program bitişi öncesinde bu ayarları eski haline getriyoruz
With Application
.DisplayAlerts=True
.ScreenUpdating=True
End With
'hata etiketinden hemen önce programdan çıkıyoruz
Exit Sub
hata:
'hata durumunda bu ayarları eski haline getriyoruz
With Application
.DisplayAlerts=True
.ScreenUpdating=True
End With
MsgBox Err.Description & vbcrlf & "Bu bilgi yeterli değilse Vlkanla görüşün lütfen"
End Sub
Dikkat:Bu kalıbı kullanırken akılda bulundurulması gereken önemli bir husus var,
o da bir anda sadece tek bi hata yakalama bloğunun aktif olabileceğidir. Yani
bi hata için On Error GoTo hata1 dediniz diyelim, hata1 bloğunda da bir hata meydana
gelirse oraya da On Error GoTo hata2 yönlendirmesi
yaparsanız bu etkisiz
kalacatır, çünkü ilk hata yakalayıcı hala aktiftir. Bu durumu aşmanın bir yolu var
ve biraz
sonra bunu göreceğiz. Siz şimdi sadece, bunun gerçekten işe yaramadığını görmek için şu
kodu çalıştırmayı deneyin.
Bu kodda, önce 0'a bölme hatası yapıyoruz. Bu bizi hata1 etiketine
yönlendiriyor. Hata1 Error Handlerında da, bu hatayı erişimimiz olmayan bir
dosyaya yazmaya ve kaydını tutmaya çalışıyoruz. Yine hata alınca bu sefer
hata mesajı görüntüleniyor.
Sub cokluhata()
On Error GoTo hata1
x = 0
MsgBox 1 / x
MsgBox "İşlem sonucu başarıyla gösterildi"
Exit Sub
hata1:
On Error GoTo hata2
dosya = "C:\deneme.txt" 'bu dosyayı yönetici izni sağlayarak oluşturdum, yani dosya var,
'ancak izin isteyen bir konum olduğu içn dosyaya yazma sırasında hata alacağım
Open dosya For Append As #1
Write #1, Now, Err.Number, Err.Description, Environ("username")
Close #1
Exit Sub
hata2:
MsgBox Err.Description, , "Hata"
End Sub
On Error GoTo 0
Hiçbir hata yakalama kodu yazmazsak, VBA ilk hatada bize
hata mesajı gösterir
demiştik. İşte On Error GoTo 0 da, VBA'in default modu olan "Hata yakalama
bloğu yok, tüm hatalar için uyarı göster"e
dönüş yapmamızı sağlar. Böyle birşeyi neden isteyelim ki? Birazdan
göreceğiz. Ama şunu anlamışsınızdır ki, bu kalıbı direkt programın başında kullanmak
anlamsızdır, zaten default ayar öyledir.
Bu kalıbı daha çok, varolan bir hata yakalama rutini iptal etmek için kullanırız.
Neden böyle birşeye ihtiyaç duyacağımızı bir düşünün! Sonra devam edin.
Mesela
yukarıda bahsettiğimiz otomatik mail gönderim
makrosunu düşünün. Yaklaşık 1000 kişiye mail gidecek, ama gönderim yapacağınız
kişilerden bazılarına mail gönderilemiyorsa bir sonrakine geçsin demiştik. Bunun için de
On Error Resume Next demiştik. Ancak mail gönderememe dışında başka bir sorun
varsa bunun size veya kullanıcıya gösterilmesini istemiş olabilirsiniz. Ne
tür hatalar çıktığını gördükten sonra bunlara özgü çözümlerinizi de
ayarlayabilesiniz diye On Error GoTo kalıbını kullanabilirsiniz. Sonrasında ya If blokları ile yakalarsınız veya bir etikete
göndererek işlem yaparsınız. Örnek bir kod bloğu şöyle olacaktır:
Sub onerrorgotosfır()
'Dim ile değişken deklerasyonları
'1000 diye sabit bir rakam olmaz tabi, bunu son satır gibi bir yöntemle yakalamanız lazım _
örnek basit olsun diye 1000 yazdım
For i=1 to 1000
On Error Resume Next 'sadece döngü içinde hata olursa bir sonraki satıra geçsin
'mail gönderim kodları
ooMail.Send
'On Error GoTo 0 buraya da konabilir, aşağıya da
Next i
'buradan sonra bir hata çıkarsa hata mesajını görelim
On Error GoTo 0
'diğer kodlar
End Sub
On Error GoTo -1
Yukarda
bir programda sadece bir adet hata yakalama bölümü aktif
olabilir demiştik, dolayısıyla bir Error Handler bloğunda başka bir Error
handlera yönlendirme yapılırsa bu işe yaramıyordu. İşte On Error GoTo -1 ile bu sorunu
aşmış oluyoruz. Bu kalıp, varolan Error Handler'ı resetler(Err
nesnesini yokeder) ve sizin yeni bir Error handler yaratmanıza imkan verir.
(Bunun, aşağıda anlatacğımız Err.Clear ile karıştırılmaması lazım.
Err.Clear
sadece hata bilgilerini yokeder, Err nesnesini değil).
Sözün özü; bu kalıp, başka bir Error Handler bloğu
içinde kullanılır ve varolan Error handler içinden başka bir Error
handlera yönledirmenin tek yolu budur. On Error Resume Next bile burada işe yaramaz.
Şimdi yukarda örneği tekrar alalım ve hata1'den hemen sonra kalıbımızı
yerleştirelim.
Sub cokluhata()
On Error GoTo hata1
x = 0
MsgBox 1 / x
MsgBox "İşlem sonucu başarıyla gösterildi"
Exit Sub
hata1:
On Error GoTo -1 'bu satırın başına yorum işareti koyarak etkisini görün
On Error GoTo hata2
dosya = "C:\deneme.txt" 'bu dosyayı yönetici izni sağlayarak oluşturdum, yani dosya var,
'ancak izin isteyen bir konum olduğu içn dosyaya yazma sırasında hata alacağım
Open dosya For Append As #1
Write #1, Now, Err.Number, Err.Description, Environ("username")
Close #1
Exit Sub
hata2:
MsgBox Err.Description, , "Hata"
End Sub
İkisi bir arada örnek
Inputbox konusunu anlatıkren görmüştük ki, dönüş tipi Range olan bir
Inputbox sorusuna Cancel diyip çıkarsak program hata alıyordu. Bunun
için "On Error Resume Next" diyerek hata mesajını yokediyorduk. Peki ya
sonraki hatalarda devam etmesini istemiyorsak, o zaman yeni bir hata
yakalama bloğu set etmeliyiz. Ör:
Sub hataikili()
Dim a As Range
On Error Resume Next 'geçici olarak tanımlanır
Set a = Application.InputBox("Bir hücre seçin", Type:=8) 'buna esc dersek diye, commenteleyip görelim
On Error GoTo hata 'esas hata yakalama kodumuzu yazıyoruz
If Not a Is Nothing Then
MsgBox "Seçim yapıldı"
'Diğer kodlar buraya
Else
MsgBox "Bir seçim yapılmadan çıkmayı tercih ettiniz"
End If
MsgBox 1 / 0
MsgBox 2 / 4 'buraya ulaşmaz
Exit Sub
hata:
MsgBox Err.Description
End Sub
Kapsamlı bir örnek
Şimdiki örnekte 4 çeşit On Error ifadesini de
kullanıyoruz. Bu açıdan bu örneğin çok faydalı olacağını düşünüyorum. Özeti
şu:Networkte duran ve her gece refresh olması beklenen bir dosya schedule
edilmiş durumda. Schedule saati gelmiş ve dosya açılınca onun içindeki
ThisWorkbook_Open makrosu devreye girecek ve dosyadaki tablolar refresh
olacak. Refresh sonucunda data dönüyorsa(yani
veritabanına ilgili data yüklenmişse) mailing işlemine devam edecek, henüz
data yüklenmediyse dosyayı kaydetmeden çıkacak. Aradaki detaylar kafanızı karıştırmasın, sadece
hata bloklarına odaklanın.
Sub musteri_degisim()
Dim OutApp As Object
Dim OutMail As Object
Dim alicilar As Object
Dim scl As Range
Dim rng As Range
Dim alan As Range
Workbooks.Open Filename:= _
gunlukyol + "\Müşterisi değişen miyler\Müşterisi_Değişen_Miyler.xlsm"
'outlook nesnemizi yaratıyoruz
Set OutApp = CreateObject("Outlook.Application")
'ilk hata yakalama kodumuzu yazıyoruz
On Error GoTo cleanup
'kodlarımız hızlı çalışsın diye ayarlamalarımızı yapıyoruz
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
'ANA kod bloğu burada başyor,
Sheets(1).Select
If IsEmpty(Cells(2, 1)) Then
ActiveWorkbook.Close savechanges:=True
Exit Sub 'hiç kayıt gelmediyse çıksın
End If
For i = 2 To Cells(1, 1).End(xlDown).Row
Cells(i, 2).Select
Set OutMail = OutApp.CreateItem(0)
Set alicilar = OutMail.Recipients.Add(ActiveCell.Value)
If ActiveCell.Value = ActiveCell.Offset(1, 0).Value Then
'yani hem giren hem çıkan varsa
If ActiveCell.Offset(0, 1).Value = "Portföye Giren" Then
'ikinci hata bloğu, hata olma ihtimali var, çünkü mail gönderemye çalışacağım kişi bankadan istifa etmiş olabilir
' ve mail önderimi sırasında hata oluşabilir, o yüzden hata oluşursa devam et diyorum
On Error Resume Next
With OutMail
.SentOnBehalfOfName = "BBSatisPerformans@akbank.com"
.Subject = "Portföyünüze giren ve portföyünüzden çıkan müşteriler hakkında"
.htmlbody = "Değerli Miyimiz," & Chr(14) & Chr(14)
.htmlbody = .htmlbody + "Dün itibarıyle portföyünüze " & ActiveCell.Offset(0, 2).Value & " adet müşteri girişi olmuştur. Bu müşterilerin 2 gün önceki TMV bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(0, 3).Value), 0, ActiveCell.Offset(0, 3).Value)) & " TL, Kredi bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(0, 4).Value), 0, ActiveCell.Offset(0, 4).Value)) & " TL'dir. (Bu listeye, hesap devriyle gelen ve yeni yaratılan mbbler dahil değildir.)" & Chr(14)
.htmlbody = .htmlbody + "Ayrıca portföyünüzden " & ActiveCell.Offset(1, 2).Value & " adet müşteri çıkışı olmuştur. Bu müşterilerin 2 gün önceki TMV bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(1, 3).Value), 0, ActiveCell.Offset(1, 3).Value)) & " TL, Kredi bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(1, 4).Value), 0, ActiveCell.Offset(1, 4).Value)) & " TL'dir." & Chr(14) & Chr(14)
.htmlbody = .htmlbody + "MBB detayına ulaşmak için OPERA'dan 'Müşterisi değişen Miyler' raporuna bakabilirsiniz." & Chr(14) & Chr(14)
.htmlbody = .htmlbody + "Bilginizi rica ederiz." & Chr(14) & Chr(14)
.htmlbody = .htmlbody + "<B>Saygılarımızla, </B>" & Chr(14)
.htmlbody = .htmlbody + "<B><FONT COLOR=""Red"">" & "Bireysel Bankacılık Satış Yönetimi </FONT></B>"
alicilar.Resolve
If Not alicilar.Resolved Then
GoTo sonraki
End If
.Send
End With
Else ' ilk satır portföyden çıkansa
With OutMail
.SentOnBehalfOfName = "BBSatisPerformans@akbank.com"
.Subject = "Portföyünüze giren ve portföyünüzden çıkan müşteriler hakkında"
.htmlbody = "Değerli Miyimiz," & Chr(14) & Chr(14)
.htmlbody = .htmlbody + "Dün itibarıyle portföyünüzden " & ActiveCell.Offset(0, 2).Value & " adet müşteri çıkışı olmuştur. Bu müşterilerin 2 gün önceki TMV bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(0, 3).Value), 0, ActiveCell.Offset(0, 3).Value)) & " TL, Kredi bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(0, 4).Value), 0, ActiveCell.Offset(0, 4).Value)) & " TL'dir." & Chr(14)
.htmlbody = .htmlbody + "Ayrıca portföyünüze " & ActiveCell.Offset(1, 2).Value & " adet müşteri girişi olmuştur. Bu müşterilerin 2 gün önceki TMV bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(1, 3).Value), 0, ActiveCell.Offset(1, 3).Value)) & " TL, Kredi bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(1, 4).Value), 0, ActiveCell.Offset(1, 4).Value)) & " TL'dir. (Bu listeye, hesap devriyle gelen ve yeni yaratılan mbbler dahil değildir.)" & Chr(14) & Chr(14)
.htmlbody = .htmlbody + "MBB detayına ulaşmak için OPERA'dan 'Müşterisi değişen Miyler' raporuna bakabilirsiniz." & Chr(14) & Chr(14)
.htmlbody = .htmlbody + "Bilginizi rica ederiz." & Chr(14) & Chr(14)
.htmlbody = .htmlbody + "<B>Saygılarımızla, </B>" & Chr(14)
.htmlbody = .htmlbody + "<B><FONT COLOR=""Red"">" & "Bireysel Bankacılık Satış Yönetimi </FONT></B>"
alicilar.Resolve
If Not alicilar.Resolved Then
GoTo sonraki
End If
.Send
End With
'istifa eden kişiye mail gönderme hatası dışında bir hata olursa
'bu hatayı greyim ki düzlteyim istiyroum; tabi ekran o an takılı kalır, sornaki schdeul rogramlar aksıya alınır
'ama olsun, hatayı düzeltmek adıan bunu yapmaız lazım
On Error GoTo 0
End If
i = i + 1
Else 'yani tek satır varsa
'yukardaki nedenlerin aynı sebeple resume next
On Error Resume Next
With OutMail
.SentOnBehalfOfName = "BBSatisPerformans@akbank.com"
.Subject = "Portföyünüze giren ve portföyünüzden çıkan müşteriler hakkında"
.htmlbody = "Değerli Miyimiz," & Chr(14) & Chr(14)
If ActiveCell.Offset(0, 1).Value = "Portföye Giren" Then
.htmlbody = .htmlbody + "Dün itibarıyle portföyünüze " & ActiveCell.Offset(0, 2).Value & " adet müşteri girişi olmuştur. Bu müşterilerin 2 gün önceki TMV bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(0, 3).Value), 0, ActiveCell.Offset(0, 3).Value)) & " TL, Kredi bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(0, 4).Value), 0, ActiveCell.Offset(0, 4).Value)) & " TL'dir. (Bu listeye, hesap devriyle gelen ve yeni yaratılan mbbler dahil değildir.)" & Chr(14) & Chr(14)
Else
.htmlbody = .htmlbody + "Dün itibarıyle portföyünüzden " & ActiveCell.Offset(0, 2).Value & " adet müşteri çıkışı olmuştur. Bu müşterilerin 2 gün önceki TMV bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(0, 3).Value), 0, ActiveCell.Offset(0, 3).Value)) & " TL, Kredi bakiyesi " & Int(IIf(IsError(ActiveCell.Offset(0, 4).Value), 0, ActiveCell.Offset(0, 4).Value)) & " TL'dir." & Chr(14) & Chr(14)
End If
.htmlbody = .htmlbody + "MBB detayına ulaşmak için OPERA'dan 'Müşterisi değişen Miyler' raporuna bakabilirsiniz." & Chr(14) & Chr(14)
.htmlbody = .htmlbody + "Bilginizi rica ederiz." & Chr(14) & Chr(14)
.htmlbody = .htmlbody + "<B>Saygılarımızla, </B>" & Chr(14)
.htmlbody = .htmlbody + "<B><FONT COLOR=""Red"">" & "Bireysel Bankacılık Satış Yönetimi </FONT></B>"
alicilar.Resolve
If Not alicilar.Resolved Then
GoTo sonraki
End If
.Send
End With
'yine aynı sbeebple goto 0
On Error GoTo 0
End If
sonraki:
Set OutMail = Nothing
Next i
cleanup:
On Error GoTo -1 'Burdada bir hata çıkarsa öncekini resetliyorum
On Error GoTo hata 'hata çıakrsa "hata" etiketine yönlendiriyoru
Set OutApp = Nothing
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Windows("Müşterisi_Değişen_Miyler.xlsm").Close savechanges:=True
rapor = "Müşteri-Miy değişim"
alici = "13245;32222"
Call Mailat2(rapor, alici)
Exit Sub
hata:
Logcu Now, Err.Description 'Looger isimli loglama proseürümle hata kaydı tutuyorum
End Sub
Error handlerdan çıkma yolları
GoTo -1 bölümünde yaptığım açıklamlar şöyle bir yanlış anlaşılmaya sebep olmamalı.
GoTo -1, bir error handler içinden
başka bir error handler içine göndermenin tek yoludur, error handler içinden
çıkmanın tek yolu değil. Error handler içinden On Error GoTo -1
kalıbına ilave
olarak Resume, Resume Next, Resume etiket veya Exit
Sub diyerek de
çıkabilirsiniz. Bu şekillerde çıkış yapıldığında da Error Nesnesi resetlemiş
olur.
Şimdi bunlara bakalım:
Resume
Resume tek başına kullanıldığında hata olan yerden tekrar devam etmeye
çalışır. Bunun için tabi error handlerda hatayı düzeltmeniz gerekir. Duruma
göre kullanıcıya düzeltmeyle ilgili bilgi vermek de gerekebilir.
Dikkat:Hatayı
düzeltmeden Resume dersek program sonsuz bir kısırdöngüye girer.
Sub resume1()
On Error GoTo hata
x = InputBox("0 ile 100 arasında bir sayı girin") '0 dahil mi belli dğeil
sayı = 1 / x
Debug.Print sayı
Exit Sub
hata:
If x=0 Then
x=1
MsgBox "Girdiğiniz sayı aralık dışı olup en düşük 1 girmeniz gerkeirdi. Sayı otomatikman 1e yuvarlandı""
Resume
Else
MsgBox Err.Description 'mesela bir harf girilise hata versin
End If
End Sub
Başka bir örnek de şu olabilr. Biz biliriz ki nerdeyse tüm Workbooklarda
Sheet1 diye bir dosya vardır, ve kodumuz içinde kullanıcıdan bir dosya
seçmesini istedik diyelim, sonra gidip Sheet1 isimli sayfaya bazı şeyleri
otomatik yazacağız. Eğer Sheet1 diye bir sayfa yoksa hata verecektir. Bunu aşağıdaki gibi ele
alabiliriz.
Sub resume3()
On Error GoTo hata:
'Kullanıcıdan dosya seçtiren kodlar
Worksheets("Sheet1").Activate
'diğer işlemler
hata:
If Err.Number = 9 Then 'sayfa yoksa
Worksheets.Add.Name = "Sheet1"
Resume 'Worksheets("Sheet1").Activate satırına geri döner
End If
End Sub
Aşağıdaki örnekte de Error handler içinde hatalı kaydı düzeltip aynı
kayıtla(sonraki değil) devam etmesini sağlıyorum:
Sub resume4()
On Error GoTo hata
Dim kareal As Variant
kareal = Array(9, 16, -64, 100) 'yanlışlıkla 64ün önüne - yazmışım diyelim
For Each a In kareal
MsgBox a & " sayısının kökü:" & Sqr(a)
Next a
Exit Sub
hata:
a = Abs(a)
Resume
End Sub
Resume etiket
Hata bloğunda gerekli bir mesaj verdikten sonra bir etikete yönlendirme
işlemini Resume Etiket kalıbı ile yaparız. Burada alternatif olarak
GoTo Etiket kalıbı
kullanıldığı görülmekle birlikte bu kullanım beklenmeyen hatalara neden
olabilir. Bu yüzden Error handlerlar içindeyken GoTo yerine Resume kullanmanızı öneriyorum.
Sub resume2()
On Error GoTo hata
yeniden:
x = InputBox("0 ile 100 arasında bir sayı girin") '0 dahil mi belli dğeil
sayı = 1 / x
Debug.Print sayı
Exit Sub
hata:
If x=0 Then
x=1
MsgBox "Girdiğiniz sayı aralık dışı olup en düşük 1 girmeniz gerkeirdi. Sayı otomatikman 1e yuvarlandı""
Resume 'burada da istersek kullanıcıya tekrar seçim hakkı vermek isteyebilri virz
Else
If Err.Number=13 Then 'Type Mismatch hatasıysa
MsgBox "Lütfen geçerli bir sayı giriniz"
Resume yeniden 'GoTo etiket de alternatif olmakla birlikte kullanmayın Else
Else 'başka bir hataysa
MsgBox Err.Description
End If
End If
End Sub
Resume Next
Mesela otomatik mail programınızda sicili olmayan kişiler çıkarsa
program akışı durmasın diye On Error Resume Next demiştik ya,
bunun bir diğer
ve şık alternatifi de şu olabilir. On Error Goto hata dersiniz, hata
bölümünde bir log kaydı oluşturabilir ve/veya mail gitmeyen kişilerin listesi o an
açık olan bir excel dosyasında ise onları renklendirebilirsiniz, böylece
kimlere mail gitmediğini de görmüş olursunuz. Zira belki sorun To'ya
koyduğunuz kişide değil, cc'deki kişiden kaynaklı da olabilir.
Tabi bu
örnekte başka bir hata durumunda istenmeyen sonuçlar elde edebilirsiniz, o
yüzden kodunuzu bu duruma göre ayarlamanız gerekecektir. Burada basitlik olması adına
sadece olmayan bir adrese mail gitme ihtimali olduğunu düşündük.
Sub resumenext1()
'Dim ile değişken deklerasyonları
On Error GoTo hata
'1000 diye sabit bir rakam olmaz tabi, bunu son satır gibi bir yöntemle yakalamanız lazım _
örnek basit olsun diye 1000 yazdım
For i=1 to 1000
'mail gönderim kodları
Next i
'varsa dieğr kodlar
hata:
'Loglama veya formatlama işlemi
Resume Next
End Sub
Aşağıdaki örnekte ise Error Handlerda hatayı yakaldıktan sonra durmasını
istemiyorum ve diğer sayılar için devam etmesini istiyorum, ama devam etmeden
önce de bi mesaj vermesini istiyorum.
Sub resumenext2()
On Error GoTo hata
Dim kareal As Variant
kareal = Array(9, 16, -1, "A", 81, "B", 100)
For Each a In kareal
MsgBox a & " sayısının kökü:" & Sqr(a)
Next a
Exit Sub
hata:
MsgBox a & ":Karekökü alınacak bir sayı değil"
Resume Next
End Sub
Err nesnesi
Bir runtime hatası oluştuğunda Err nesnesi oluşturulur ve bu nesnenin
özellikleri bu hatanın detayıyla doldurulur.
Bu nesnenin üyeleri, Exit'li ifadelerden(Exit Sub gibi) ve
Resume
Next'ten sonra resetlenir, yani 0 ve ""'a döner. Veya manuel resetlemek için
Err.Clear metodu kullanılır.
Err'in default property'si(özelliği) Numberdır. Bir de Description vardır.
Bunları yukardaki
örneklerde bolca kullandık. Genelde bu descriptiondaki bilgi
son kullanıcıya yeterli bilgi vermez, hele bi de İngilizceleri yoksa birşey
anlamazlar, o yüzden hata numarasına göre kendi mesajlarınızı belirtebilirsiniz.
Select Case Err.Number
Case Is = 5
MsgBox "Negatif sayıların karekökü alınamaz"
Case Is = 13
MsgBox "Metinsel ifadelerin karekökü alınamaz"
End Select
Çıkan hatanın Number özelliği yoksa Description öelliğinde "Application-defined or object-defined error" mesajı
gösterilir. Herhangi bir anda hata yoksa Number=0 ve Description=""
değerindedir. O yüzden belirli bir anda hata olup olmadığını If Err.Number=0
ile kontrol edebilirsiniz.
Err.Raise
Err nesnesi oluşturmanın bir yolu da Raise metodunu kullanmaktır.
Bu metodla birlikte kendimize özgü Hata Kaynağı, Hata Numarası ve Hata
açıklaması üretiriz. Hata numarasını 513 ile 65535 arası bir değer
atayabilyoruz ve bunu vbObjectError ile birlikte
kullanıyoruz.
Err.Raise'in bir alternatifi GoTo hata diyerek ilerlemek
olabilir ancak bu şekilde hatayı kayıt altına
alamayız. Özellikle birden fazla yönlendirme olacaksa GoTo etiket yerine
Err.Raise
diyip açıklayıcı bilgilerle de donatmak gerekir.
Sub errraise()
On Error GoTo hata
x = InputBox("1000den küçük bir sayı girin") 'çok net değiliz, belki kullanıcı 0 girecek, belki 1000 dahil sanacak
'bunu direkt select case içinde kullanamadığımız için if blok içinde kullanıyorum
If Not IsNumeric(x) Then
Err.Raise 517 + vbObjectError, "Sayısal olmayan veri", "Kullanıcı sayısal olmayan bir değer girdi"
End If
Select Case x
Case 0
Err.Raise 513 + vbObjectError, "x nedeniyle 0'a bölme hatası", "Kullanıcı x=0 girdi"
Case 1000
Err.Raise 514 + vbObjectError, "x nedeniyle 0'a bölme hatası", "Kullanıcı x=1000 girdi"
Case Is > 1000
Err.Raise 515 + vbObjectError, "aralık dışı değer", "Kullanıcı x>0 girdi"
Case Is < 0
Err.Raise 516 + vbObjectError, "aralık dışı değer", "Kullanıcı x<0 girdi"
Case Else
sayı = (1 / x) * (1 / (1000 - x))
End Select
Debug.Print sayı
Exit Sub
hata:
Debug.Print Err.Source, Err.Number, Err.Description
End Sub
Err.Source
Bu özellik, özellikle Log tutulurken faydalı olabilmektedir, ve
yukarıdaki örnekte görüldüğü gibi, Raise ile manuel yaratılan hata
mesajlarında bizim tarafımızdan belirtildiğinde anlamlıdır, yoksa her zaman
VBAProject gibi birşey ifade etmeyen bir sonuç üretir. (E neden var o zaman?
Çünkü bu özellik VB dilinin kendisinde var ve orada anlamlı ama VBA içinde
anlamsızlaşıyor, eğer ki biz kendimiz belirtmezsek!)
Err.Clear
Bu metod yukarda da gördüğümüz gibi hata nesnesinin üyelerini sıfırlar.
Pratik olarak buna ihtiyaç duyabileceğimiz tek yer On Error Resume Next dendiği
durumlarda, karşılaşılan hata adedini saymak için kullanmak olacaktır. Zira ben
başka bir kullanımını görmedim.
Şöyle ki, döngüsel bir kodumuz var diyelim, döngünün bir yerinde Err.Number<>0
kontrolüne takılır ise hata oluşmuş demektir, bu durumda hatasayısı=hatasayısı+1
diyerek sayacı arttırırız. Ama arada birçok yerde hatasız geçiş olacağı için ama en sonki kod numarası da
hala hafızada olacağı için bir kez hata oluştuğunda döngünün sonraki
turlarında heryerde tekrar hata kontrolüne takılır. O yüzden sayacı
arttırdıktan sonra hata numasını sıfırlamak gerekir.
Aşağıdaki örnekte, yine otomatik mailing yapacağız ve kaç adet mailin
gitmediğini MsgBox ile bildireceğiz. Örneği özellikle basit tuttum, sadece
bu kısma odaklanmanız için. Tabiki yukardaki örneklerle birleştirilerek mail
gitmeyen satırlar için renklendirme de yapılabilir.
On Error Resume Next
son=Range("A1").End(xlDown).Row
For i=1 to son
'mail kodları
If Err.Number<>0 The
hata=hata+1
Err.Clear
End If
Next i
MsgBox hata & " adet mail gönderilememiştir"