PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C#: Anwendung beendet sich einfach, Exception-Handling bringt nichts?


aths
2005-11-01, 02:02:19
Microsoft Visual C# 2005, Express Beta.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class DISPLAY_DEVICE
{
public int cb = 0;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName = new String(' ', 32);
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString = new String(' ', 128);
public int StateFlags = 0;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID = new String(' ', 128);
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey = new String(' ', 128);

} // from http://pinvoke.net/default.aspx/Structures.DISPLAY

public class Displays
{
[DllImport("user32.dll")] static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);
// from http://pinvoke.net/default.aspx/user32.EnumDisplayDevices

public Displays()
{
try
{
DISPLAY_DEVICE d = new DISPLAY_DEVICE();
d.cb = Marshal.SizeOf(d);

for (uint id = 0; EnumDisplayDevices(null, id, ref d, 0); id++)
{
// Hier werden die Member von d dann ausgwertet
}

}
catch (Exception ex)
{
MessageBox.Show("Exception: " + ex.ToString(), "Bemerkung", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);
}
finally
{
MessageBox.Show("Exception! ", "Bemerkung", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);

}
} // proc

} // classSofern ich EnumDisplayDevices nicht auskommentiere, beendet sich das Programm beim Instanzieren von "Displays" sofort. Das Exception-Handling bringt gar nix, die Messageboxen sind nicht zu sehen. Wie debuggt man sowas?

Im Output-Fenster vom Debug steht für den Abbruch nur:

The program '[2120] test.vshost.exe: Managed' has exited with code -1073741819 (0xc0000005).

muhkuh_rs
2005-11-01, 14:24:35
Vielleicht erst mal den Code der Fehler haben kann aus dem Konstruktor nehmen. Dann um alles noch einen exception handler bauen.

Ich kenne mich mit C# nicht aus aber könnte vielleicht ein Problem mit Unicode/Ansi Strings vorliegen? In der user32.dll gibt es ja EnumDisplayDevicesA und EnumDisplayDevicesW. Vielleicht holt er versehentlich EnumDisplayDevicesW?

aths
2005-11-01, 15:12:17
Um den gesamten Code im Constructor ist ja das Exception-Handling. Ich muss "for (uint id = 0; EnumDisplayDevices(null, id, ref d, 0); id++)" auskommentieren, dann sehe ich die Messagebox bei "finally".

Wenn ich schreibe "[DllImport("user32.dll")] static extern bool EnumDisplayDevices" wird er kaum EnumDisplayDevicesW importieren.

Der Aufruf von EnumDisplayDevices führt zum Abbruch der Anwendung, doch bekomme keine Exception geworfen, jedenfalls keine die "try" abfängt.

muhkuh_rs
2005-11-01, 15:24:01
Nein, ich meine ich würde zum Testen erst mal grundsätzlich problematischen Code aus Konstruktoren von Objekten entfernen und in eigene Methoden packen. Es kann ja schon viel beim Instanziieren eines Objektes hinter den Kulissen schief gehen.

Z.B. weißt ich nicht, wann eigentlich die Funktion aus der DLL geladen wird. Geschieht das beim Starten des Programms? Bei jeder Instanziierung von Displays? Bei der ersten Instanziierung von Displays? Passiert es vielleicht nicht, wenn der Compiler merkt, dass Du die Funktion nie aufrufst (auskommentierst)?

Unicode/Ansi:
Es gibt kein EnumDisplayDevices. Es gibt nur EnumDisplayDevicesA und
EnumDisplayDevicesW in der dll. Bei C++ includierst du die entsprechenden Header und je nachdem ob ein _UNICODE #define gesetzt ist oder nicht, wird EnumDisplayDevices als EnumDisplayDevicesA oder EnumDisplayDevicesW definiert. Keine Ahnung wie C# spitz kriegen will, welche Funktion genommen werden soll aber es ist auf jeden Fall wert mal darüber nachzudenken.

Demirug
2005-11-01, 15:28:33
Das kann so nicht gehen.

DISPLAY_DEVICE darf keine Klasse sondern muss eine Struktur sein. Den Aufgrund der Definition werden Klassen immer als Referenz übergeben. Stellt man dem dann noch ein "ref" vor übergibt man eine Referenz auf die Referenz. Das ist dann aber kein Zeiger mehr sondern ein Zeiger auf den Zeiger.

Aus diesem Grund muss es so aussehen.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DISPLAY_DEVICE
{
public int cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
public int StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;

}

Es gibt auch eine Lösung wie man es mit einer Klasse korrekt macht aber es macht keinen Sinn Win32 Strukturen in C# als Klasse zu definieren.

aths
2005-11-01, 15:48:45
Danke, das funktioniert. Wird mit der Verwendung von Marshal eigentlich unsicherer Code erzeugt, oder läuft das Programm weiterhin komplett managed ab?

Demirug
2005-11-01, 15:57:06
Danke, das funktioniert. Wird mit der Verwendung von Marshal eigentlich unsicherer Code erzeugt, oder läuft das Programm weiterhin komplett managed ab?

Sobald man Interop benutzt kann es nicht mehr vollständig Managed sein. Das ganze ist allerdings ein Grenzfall weil der Marshal Code ja durch den Jiter erzeugt wird und daher als sicher gilt. Die Win32 Funktion ist dann allerdings unsicher.

aths
2005-11-01, 16:33:43
MS bietet keine sichere Alternative für EnumDisplayDevices? Wie ernst muss man das nehmen, unsichere Funktionen zu verwenden? Verstößt man damit gegen ein C#-Paradigma oder ist das üblich?

Demirug
2005-11-01, 16:40:58
MS bietet keine sichere Alternative für EnumDisplayDevices? Wie ernst muss man das nehmen, unsichere Funktionen zu verwenden? Verstößt man damit gegen ein C#-Paradigma oder ist das üblich?

EnumDisplayDevices ist viel zu dicht am Systemkern als das es in die Managed Welt gehört. Man könnte die Daten wohl auch mit Managed DirectX herausfinden aber das ist letzten Endes mehr Aufwand wenn man es nur dafür braucht.

Üblich ist relative. Man sollte versuchen ohne Interop auszukommen weil es auch die Portierbarkeit einschränkt. Aber wenn man nun mal anders nicht an die Daten kommt kann man es auch benutzten. Dafür wurde es ja eingebaut.