Erişim ihlali istisnalarını yakalamak mı?


89

Misal

int *ptr;
*ptr = 1000;

Microsoft'a özgü herhangi bir kullanmadan standart C ++ kullanarak bellek erişim ihlali istisnasını yakalayabilir miyim?

Yanıtlar:


43

Hayır! C ++, kötü bir şey yaptığınızda, bir performans düşüşüne neden olacak bir istisna atmaz. Erişim ihlalleri veya sıfır hatayla bölme gibi şeyler, yakalayabileceğiniz dil düzeyinde şeyler yerine daha çok "makine" istisnaları gibidir.


Bunun HW istisnaları olduğunu biliyorum, ancak bunu ele alan Microsoft'a özgü anahtar kelimeler var (__ deneyin __hariç)?
Ahmed Said

2
@Ahmed: evet, ama onları kullanırsanız 'imkansız' şeyler olabilir. Örneğin , AV kod satırından sonraki bazı ifadeler zaten yürütülmüş olabilir veya AV'den önceki ifadeler yürütülmemiş olabilir.
Aaron

VC ++ 'da normal try ... catch bloğu kullanarak bu tür istisnaların nasıl işleneceğini aşağıdaki cevabıma bakın.
Volodymyr Frytskyy

@Aaron, "imkansız şeyler oluyor" kısmından bahseder misin? derleyici ve / veya CPU yeniden sıralama talimatları yüzünden mi?
Weipeng L

Temeldeki işletim sistemi genellikle bu tür sorunları yakalamak için mekanizmalar sağlar ve istisna CPU mimarisi tarafından oluşturulduğundan, bunlar hiçbir maliyete neden olmaz. Bu, hata ayıklayıcıların kod yürütmeyi yavaşlatmadan hata ayıklamanıza izin vermek için istisnayı yakalayabilmesiyle kanıtlanır.
Dino Dini

108

Oku ve ağla!

Bunu anladım. İşleyiciden atmazsanız, işleyici devam edecek ve istisna da devam edecektir.

Sihir, kendi istisnanızı attığınızda ve bununla başa çıktığınızda gerçekleşir.

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <tchar.h>

void SignalHandler(int signal)
{
    printf("Signal %d",signal);
    throw "!Access Violation!";
}

int main()
{
    typedef void (*SignalHandlerPointer)(int);

    SignalHandlerPointer previousHandler;
    previousHandler = signal(SIGSEGV , SignalHandler);
    try{
        *(int *) 0 = 0;// Baaaaaaad thing that should never be caught. You should write good code in the first place.
    }
    catch(char *e)
    {
        printf("Exception Caught: %s\n",e);
    }
    printf("Now we continue, unhindered, like the abomination never happened. (I am an EVIL genius)\n");
    printf("But please kids, DONT TRY THIS AT HOME ;)\n");

}

Güzel bir ipucu, özellikle __try / __ hariç, AV'yi de yakalayamayacağı için.
Fabio Ceconello

16
Bu gcc'de ÇALIŞMAZ, ancak VC ++ 'da çalışır, sadece "Hata Ayıklama" yapısında çalışır. Hala ilginç bir çözüm için oy kullanıyorum. Sinyal işleyici çağrılacak, ancak istisna atılmayacak.
Natalie Adams

2
Bu portabley çalışmıyor. Bir sinyal işleyici çağrıldığında, yığın çerçevesi ve yazmaç kesme, normal bir işlev yığını çerçevesi ile aynı değildir (bazı sistemlerde aynı yığını bile kullanmayabilir). Yapabileceğiniz en iyi şey, sinyal işleyicinin etkinleştirildiğini gösteren bir bayrak belirlemektir. Sonra kodunuzda o bayrağı test edin ve atın.
Martin York

2
Bunun tanımlanmamış davranışlara yol açma şansı yüksektir. Bunun POSIX üzerinde çalışması için, sigaltstackkurulu herhangi bir alternatif sinyal yığını ( ) bulunmamalıdır (C ++ istisna çözme uygulaması buna izin vermedikçe) ve çözme mekanizmasının kendisini işleyen her çalışma zamanı işlevi sinyal açısından güvenli olmalıdır.
minmaxavg

1
Varsayılan işleyiciyi sinyale döndürmek istiyorsanız (bu durumda SIGSEGV), sadece aşağıdakileri kullanın:signal(SIGSEGV, SIG_DFL);
kocica

67

Try -> catch (...) bloğu kullanarak Visual Studio'da her türlü istisnayı (sıfıra bölme, erişim ihlali vb.) Yakalamanın çok kolay bir yolu vardır . Küçük bir proje ayarlarının ince ayarlanması yeterlidir. Proje ayarlarında / EHa seçeneğini etkinleştirmeniz yeterlidir. Bkz Proje Özellikleri -> C / C ++ -> Kod Üretimi -> Değiştir "Evet SEH İstisnalar ile" C ++ İstisnalar etkinleştirin . Bu kadar!

Ayrıntılara buradan bakın: http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx


Visual Studio .NET 2003'te böyle bir ayar değeri yoktur, yalnızca "Hayır" ve "Evet (/ EHsc)" vardır. Bu ayarı etkinleştirmek için Visual Studio'nun hangi minimum sürümüne ihtiyacınız olduğunu açıklayabilir misiniz?
izogfif

Bağlantı, "Visual Studio 2005" i belirtmek için görünür
Drew Delano

2
ya gcc veya MinGW ile?
user1024

10

En azından benim için, signal(SIGSEGV ...)başka bir cevapta bahsedilen yaklaşım Visual C ++ 2015 ile Win32'de işe yaramadı . Ne yaptım iş için beni kullanmak oldu _set_se_translator()bulundu eh.h. Şu şekilde çalışır:

Adım 1 ) etkinleştirmek emin olun SEH İstisnalar (/ EHA) ile Evet de C ++ İstisnalar etkinleştirme Proje Özellikleri / C ++ / Kod Üretimi / tarafından cevapta belirtildiği gibi Volodymyr Frytskyy .

Adım 2 ) _set_se_translator()Yeni istisna çeviricisi için bir işlev göstericisini (veya lambda) ileterek çağırın . Çevirmen olarak adlandırılır çünkü temelde sadece düşük seviyeli istisnayı alır ve onu yakalanması daha kolay bir şey olarak yeniden fırlatır, örneğin std::exception:

#include <string>
#include <eh.h>

// Be sure to enable "Yes with SEH Exceptions (/EHa)" in C++ / Code Generation;
_set_se_translator([](unsigned int u, EXCEPTION_POINTERS *pExp) {
    std::string error = "SE Exception: ";
    switch (u) {
    case 0xC0000005:
        error += "Access Violation";
        break;
    default:
        char result[11];
        sprintf_s(result, 11, "0x%08X", u);
        error += result;
    };
    throw std::exception(error.c_str());
});

Adım 3 ) Normalde yaptığınız gibi istisnayı yakalayın:

try{
    MakeAnException();
}
catch(std::exception ex){
    HandleIt();
};

1
Bu site, _set_se_translator () yöntemleriyle ilgili basit bir çift örnek içeriyor ve benim için çalışıyor, msdn.microsoft.com/en-us/library/5z4bw5h5.aspx
Pabitra Dash

8

Bu tür bir durum uygulamaya bağlıdır ve sonuç olarak tuzağa düşmek için satıcıya özel bir mekanizma gerektirecektir. Microsoft ile bu SEH'yi içerecek ve * nix bir sinyal içerecektir

Genel olarak, bir Erişim İhlali istisnasını yakalamak çok kötü bir fikirdir. Bir AV istisnasından kurtarmanın neredeyse hiçbir yolu yoktur ve bunu yapmaya çalışmak, programınızda hataları bulmanızı zorlaştıracaktır.


1
Öyleyse tavsiyeniz, AV istisnasının sebebinin ne olduğunu bilmek, değil mi?
Ahmed Said

4
Kesinlikle. AV'ler kodunuzdaki bir hatayı temsil eder ve istisnayı yakalamak sorunu gizleyecektir.
JaredPar

1
Açıklığa kavuşturmak için, C ++ standardı tanımlanmamış, belirtilmemiş ve tanımlanmış uygulama arasında bir ayrım yapar. Tanımlanan uygulama, uygulamanın ne olduğunu belirtmesi gerektiği anlamına gelir. Sorudaki kod tanımsızdır, bu da her şeyin olabileceği ve her seferinde farklı olabileceği anlamına gelir.
KeithB

15
Erişim İhlalini yakalamak kötü bir fikir değildir - Kullanıcı deneyimi için iyidir. Bununla birlikte, bu durumda yaptığım tek anlamlı şey - Hata Raporlama GUI'si ile başka bir işlem oluşturmak ve geçerli bir işlem dökümü oluşturmaya çalışmak. Bir süreci başlatmak her zaman başarılı bir işlemdir. Ardından, kendi kendini öldürmek için TerminateProcess () yapıyorum.
Петър Петров

12
Bir istisnayı yakalamak ve sessizce görmezden gelmek kötü bir fikirdir. Mümkün olduğunda bir istisnayı yakalamak ve teşhis amacıyla uygulamanın durumu hakkında bilgi kaydetmek çok iyi bir fikirdir. Bir keresinde arka uç grafik kitaplığı için bazı hata ayıklama gerektiren bir kullanıcı arayüzü yazmıştım. Her çöktüğünde insanlar bana geldiler çünkü kullanıcı arayüzünü yazdığımı biliyorlardı. Kullanıcıya kütüphanenin çöktüğünü söyleyen bir uyarı veren arka uçun etrafına bir sinyal tuzağı koydum. İnsanlar kütüphanenin yazarına gitmeye başladı.
Kent

8

Belirtildiği gibi, bunu Windows platformunda yapmanın Microsoft / derleyici satıcısı olmayan bir yolu yoktur. Ancak, bu tür istisnaları, hata raporlama için normal try {} catch (ex istisna) {} yönteminde ve daha çok uygulamanızın zarif bir çıkışında (JaredPar'ın dediği gibi, uygulama artık büyük olasılıkla belada) yakalamakta fayda vardır. . _Se_translator_function, aa try işleyicisinde aşağıdaki istisnaları yakalamamıza izin veren basit bir sınıf sarmalayıcısında kullanırız:

DECLARE_EXCEPTION_CLASS(datatype_misalignment)
DECLARE_EXCEPTION_CLASS(breakpoint)
DECLARE_EXCEPTION_CLASS(single_step)
DECLARE_EXCEPTION_CLASS(array_bounds_exceeded)
DECLARE_EXCEPTION_CLASS(flt_denormal_operand)
DECLARE_EXCEPTION_CLASS(flt_divide_by_zero)
DECLARE_EXCEPTION_CLASS(flt_inexact_result)
DECLARE_EXCEPTION_CLASS(flt_invalid_operation)
DECLARE_EXCEPTION_CLASS(flt_overflow)
DECLARE_EXCEPTION_CLASS(flt_stack_check)
DECLARE_EXCEPTION_CLASS(flt_underflow)
DECLARE_EXCEPTION_CLASS(int_divide_by_zero)
DECLARE_EXCEPTION_CLASS(int_overflow)
DECLARE_EXCEPTION_CLASS(priv_instruction)
DECLARE_EXCEPTION_CLASS(in_page_error)
DECLARE_EXCEPTION_CLASS(illegal_instruction)
DECLARE_EXCEPTION_CLASS(noncontinuable_exception)
DECLARE_EXCEPTION_CLASS(stack_overflow)
DECLARE_EXCEPTION_CLASS(invalid_disposition)
DECLARE_EXCEPTION_CLASS(guard_page)
DECLARE_EXCEPTION_CLASS(invalid_handle)
DECLARE_EXCEPTION_CLASS(microsoft_cpp)

Orijinal sınıf bu çok faydalı makaleden geldi:

http://www.codeproject.com/KB/cpp/exception.aspx


8
Bir Microsoft derleyicisi kullanmanın yasa dışı bir talimat veya erişim ihlali olarak değerlendirildiğini görüyorum. İlginç.
David Thornley

3

İstisna işleme mekanizması değil, ancak C tarafından sağlanan signal () mekanizmasını kullanabilirsiniz.

> man signal

     11    SIGSEGV      create core image    segmentation violation

Bir NULL işaretçisine yazmak muhtemelen bir SIGSEGV sinyaline neden olacak


@maidamai signal(), posix standardının bir parçasıdır. Windows, posix standardını uygular (Linux ve unix gibi)
Martin York

-1

Bunun gibi bir ihlal, kodda ciddi bir yanlışlık olduğu anlamına gelir ve bu güvenilmezdir. Bir programın, kullanıcının verilerini, önceki verilerin üzerine yazmayacağını umarak, kullanıcının verilerinin zaten bozuk olmadığı umuduyla kaydetmeye çalışmak isteyebileceğini görebiliyorum, ancak tanım gereği standart bir yöntem yoktur. tanımlanmamış davranışlarla başa çıkma.


7
Erişim ihlalinin giderilmesi mümkün olabilir. EIP atlama voilasyonundan kurtulmak, tehlikeli olmadığınız ve montaj seviyesi talimat işaretlerini tutmadığınız sürece asla mümkün değildir. Ancak, Erişim ihlalini yakalamak, GUI özelliği hata bildirimi için başka bir işlem oluşturmak açısından iyidir.
Петър Петров
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.