Können Sie eine native Ausnahme im C # -Code abfangen?


75

Können Sie in C # -Code eine native Ausnahme abfangen, die tief in einer nicht verwalteten Bibliothek ausgelöst wurde? Wenn ja, müssen Sie etwas anders machen, um es zu fangen, oder versucht ein Standard ... fangen, es zu bekommen?

Antworten:


35

Sie können Win32Exception verwenden und die NativeErrorCode-Eigenschaft verwenden, um sie entsprechend zu behandeln.

// http://support.microsoft.com/kb/186550
const int ERROR_FILE_NOT_FOUND = 2;
const int ERROR_ACCESS_DENIED = 5;
const int ERROR_NO_APP_ASSOCIATED = 1155; 

void OpenFile(string filePath)
{
    Process process = new Process();

    try
    {
        // Calls native application registered for the file type
        // This may throw native exception
        process.StartInfo.FileName = filePath;
        process.StartInfo.Verb = "Open";
        process.StartInfo.CreateNoWindow = true;
        process.Start();
    }
    catch (Win32Exception e)
    {
        if (e.NativeErrorCode == ERROR_FILE_NOT_FOUND || 
            e.NativeErrorCode == ERROR_ACCESS_DENIED ||
            e.NativeErrorCode == ERROR_NO_APP_ASSOCIATED)
        {
            MessageBox.Show(this, e.Message, "Error", 
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Exclamation);
        }
    }
}

7
Ich glaube, dies ist nicht automatisch, sondern wird nur ausgelöst, wenn Sie eine P / Invoke-Signatur verwenden, die dies angibt. Und es wird basierend auf einem Win32-Fehlercode ausgelöst, nicht auf einer Win32-Ausnahme.
Curt Hagenlocher

15

Catch without () fängt nicht CLS-konforme Ausnahmen ab, einschließlich nativer Ausnahmen.

try
{

}
catch
{

}

Weitere Informationen finden Sie in der folgenden FxCop-Regel: http://msdn.microsoft.com/en-gb/bb264489.aspx


11
Ja, das ist wahr. Ich habe das gerade selbst entdeckt, nachdem ich einige besonders nervige Stunden damit verbracht hatte, Protokolldateien und Code zu vergleichen und dachte, ich hätte es verloren. Die offensichtliche Folge der Frage ist jedoch, ob Sie den Typ der nativen Ausnahme aus dem leeren catch-Block heraus bestimmen können. Können Sie irgendetwas Nützliches protokollieren, um die Quelle der Ausnahme zu identifizieren?
Tyson

11

Die Interop-Ebene zwischen C # und nativem Code konvertiert die Ausnahme in eine verwaltete Form, sodass sie von Ihrem C # -Code abgefangen werden kann. Ab .NET 2.0 catch (Exception)sollte alles andere als ein nicht behebbarer Fehler abgefangen werden.


3
In .NET 1.x ist es möglich, dass eine Ausnahme ausgelöst wird, die nicht von der Exception-Klasse abgeleitet ist, aber diese Fähigkeit ist standardmäßig in 2.0
Curt Hagenlocher

1
Ja wirklich? Für was war das?
Camilo Martin

4
In C ++ können Sie jedes Objekt werfen. Bei Interop-Problemen ist es daher hilfreich, ein Objekt abfangen zu können, das nicht von Exception abgeleitet ist. Ein häufiges Szenario ist, dass das C ++ - Programm nur einen String auslöst (die Fehlermeldung).
MattDavey

5

Irgendwo mit einem .NET-Reflektor habe ich den folgenden Code gesehen:

try {
  ...
} catch(Exception e) {
  ...
} catch {
  ...
}

Hmm, C # erlaubt es nicht, eine Ausnahme auszulösen, die nicht von der System.Exception-Klasse abgeleitet ist. Und soweit ich weiß, wird jede vom Interop-Marshaller vorsichtige Ausnahme von der Ausnahmeklasse umschlossen, die die System.Exception erbt.

Meine Frage ist also, ob es möglich ist, eine Ausnahme abzufangen, die keine System.Exception ist.


1
Es ist möglich, IL auszugeben oder auf andere Weise zu erstellen, die ein beliebiges Objekt auslöst. Der C # -Compiler lässt Sie dies nicht zu, aber andere Compiler können oder, wie ich bereits sagte, die IL direkt ausgeben. Eine catch-Anweisung ohne Typ fängt beliebige Objekte sowie Objekte ab, die Exception erben.
technophile

5

Dies hängt davon ab, um welche Art von nativer Ausnahme es sich handelt. Wenn Sie sich auf eine SEH-Ausnahme beziehen, führt die CLR eines von zwei Dingen aus.

  1. Im Falle eines bekannten SEH-Fehlercodes wird dieser der entsprechenden .Net-Ausnahme zugeordnet (dh OutOfMemoryException).
  2. Im Falle eines nicht zuordnbaren (E_FAIL) oder unbekannten Codes wird nur eine SEHException- Instanz ausgelöst .

Beide werden mit einem einfachen "catch (Exception)" - Block abgefangen.

Die andere Art von nativer Ausnahme, die die native / verwaltete Grenze überschreiten kann, sind C ++ - Ausnahmen. Ich bin nicht sicher, wie sie zugeordnet / behandelt werden. Ich vermute, da Windows C ++ - Ausnahmen zusätzlich zu SEH implementiert, werden sie auf die gleiche Weise zugeordnet.


Windows implements C++ exceptions on top of SEH- Dies scheint nur für VC zu gelten?
Wolf

3

Fast, aber nicht ganz. Sie werden die Ausnahme mit abfangen

try 
{
  ...
}
catch (Exception e)
{
  ...
}

Sie werden jedoch immer noch potenzielle Probleme haben. Laut MSDN müssten Sie Folgendes abfangen, um sicherzustellen, dass Ausnahmezerstörer aufgerufen werden:

try
{
  ...
}
catch
{
  ...
}

Dies ist die einzige Möglichkeit, um sicherzustellen, dass ein Ausnahmezerstörer aufgerufen wird (obwohl ich nicht sicher bin, warum). Damit haben Sie jedoch den Kompromiss zwischen Brute Force und einem möglichen Speicherverlust.

Wenn Sie den Ansatz (Ausnahme e) verwenden, sollten Sie übrigens die verschiedenen Arten von Ausnahmen kennen, auf die Sie möglicherweise stoßen. Mit RuntimeWrappedException wird jeder verwaltete Nicht-Ausnahmetyp zugeordnet (für Sprachen, die eine Zeichenfolge auslösen können), und andere werden zugeordnet, z. B. OutOfMemoryException und AccessViolationException. COM Interop HRESULTS oder andere Ausnahmen als E___FAIL werden COMException zugeordnet, und schließlich haben Sie am Ende SEHException für E_FAIL oder eine andere nicht zugeordnete Ausnahme.

Was solltest du also tun? Die beste Wahl ist, keine Ausnahmen von Ihrem nicht verwalteten Code zu werfen! Hah. Wirklich, wenn Sie eine Wahl haben, stellen Sie Barrieren auf und scheitern Sie, wodurch die Wahl schlechter wird, die Möglichkeit eines Speicherverlusts während der Ausnahmebehandlung besteht oder Sie nicht wissen, um welchen Typ es sich bei Ihrer Ausnahme handelt.


-1

Ein Standardversuch sollte den Trick machen, den ich glaube.

Ich stoße auf ein ähnliches Problem mit einer System.data-Ausnahme, die eine nicht erfasste sqlClient-Ausnahme auslöst und einen try..catch in meinen Code einfügt


-2

Wenn Sie eine verwenden

try
{

}
catch(Exception ex)
{

}

Je nachdem, wie Sie die externen Bibliotheken aufrufen, werden ALLE Ausnahmen abgefangen. Möglicherweise wird eine com-bezogene Ausnahme angezeigt, die den Fehler kapselt, den Fehler jedoch abfängt.


Sie können jedoch keine StackOverflow- oder OutOfMemoryException abfangen, egal was passiert, richtig?
Kern

Das sind Endfehler, die die Anwendung anhalten, also können Sie nicht mit ihnen arbeiten.
Mitchel Sellers

7
Dies ist eigentlich nicht ganz richtig, dies fängt alle CLS-kompatiblen Ausnahmen ab. C ++ / CLI und MC ++ sind beide Sprachen, die nicht CLS-konforme Ausnahmen auslösen können.
Peter Oehlert

1
Ich stimme Peter zu, ich arbeite mit nicht verwalteten DLLs und kann keine Ausnahmen feststellen.
Tilendor

20
Diese Frage hat 17 positive Stimmen und 7 Favoriten, wenn ich diesen Kommentar schreibe. Dies ist also eindeutig ein Problem, das den Leuten passiert, und ich würde denken, die Leute würden einen einfachen Fangblock versuchen, bevor sie auf SO zurückgreifen. Ich habe persönlich gesehen, wie Ausnahmen durch das von Ihnen vorgeschlagene Konstrukt explodieren, wenn sie aus nicht verwalteten Abhängigkeiten stammen. Ich denke nicht, dass diese Antwort richtig ist.
JohnFx
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.