DeviceIoControl() - Problem bei übersetzen VB.NET Datei

zer0

Erfahrenes Mitglied
Hallo,

ich will die S.M.A.R.T. Daten der Festplatten auslesen. Dazu habe ich eine VB.NET Klasse gefunden Hier!.

Diese habe ich nun nach C# übersetzt und ab geändert so wie es halt nötig war. Mein problem, beim aufruf der DLL-Funktion DeviceIoControl() wird das Programm einfach beendet. Der Output im VisualStudio:
The program '[3352] SmartView.vshost.exe: Managed (v2.0.50727)' has exited with code -1073740791 (0xc0000409).

Ich hänge mal meine von VB nach C# übersetzte Smart-Klasse dran. Aber wie gesagt manche Sachen musste ich ändern.

C#:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Management;
using System.Linq;
using System.Text;
using System.Collections;
using System.Windows.Forms;

namespace SmartView
{
    #region ReadSmartResult Enum
    public enum ReadSmartResult
    {
        DRIVE_DOES_NOT_EXIST = 0,
        ACCESS_DENIED = 1,
        NO_SMART_AVAILABLE = 2,
        SUCCEEDED = 3
    }
    #endregion

    #region Attributes Enum
    public enum Attributes
    {
        // Invalid attribute identifier
        SMART_ATTRIB_Invalid = 0,
        // Frequency of errors while reading raw data
        SMART_ATTRIB_RAW_READ_ERROR_RATE = 1,
        // Average efficiency of a hard disk
        SMART_ATTRIB_THROUGHPUT_PERFORMANCE = 2,
        // needed to spin up
        SMART_ATTRIB_SPIN_UP_TIME = 3,
        // Number of spindle start/stop cycles
        SMART_ATTRIB_START_STOP_COUNT = 4,
        // Quantity of remapped sectors
        SMART_ATTRIB_REALLOCATION_SECTOR_COUNT = 5,
        // Reserve of channel while reading
        SMART_ATTRIB_Read_Channel_Margin = 6,
        // Frequency of errors while positioning
        SMART_ATTRIB_SEEK_ERROR_RATE = 7,
        // Average efficiency of operations while positioning
        SMART_ATTRIB_Seek_TimerPerformance = 8,
        // Number of hours elapsed in the power-on state
        SMART_ATTRIB_POWER_ON_HOURS_COUNT = 9,
        // Number of retry attempts to spin up
        SMART_ATTRIB_SPIN_RETRY_COUNT = 10,
        // count Number of attempts to calibrate the device
        SMART_ATTRIB_RECALIBRATION_RETRIES = 11,
        // Number of power-on events
        SMART_ATTRIB_DEVICE_POWER_CYCLE_COUNT = 12,
        // Frequency of ‘program’ errors while reading from a disk
        SMART_ATTRIB_SOFT_READ_ERROR_RATE = 13,
        // Fequency of mistakes as a result of impact loads
        SMART_ATTRIB_AIRFLOW_TEMPERATURE = 190,
        // Fequency of mistakes as a result of impact loads
        SMART_ATTRIB_G_Sense_Error_Rate = 191,
        // Number of power-off or emergency retract cycles
        SMART_ATTRIB_Power_Off_Retract_Count = 192,
        // Number of cycles into landing zone position
        SMART_ATTRIB_LOAD_UNLOAD_CYCLE_COUNT = 193,
        // Temperature of a hard disk assembly
        SMART_ATTRIB_HDA_TEMPERATURE = 194,
        // Number of ECC on-the-fly errors
        SMART_ATTRIB_Hardware_ECC_Recovered = 195,
        // Number of remapping operations
        SMART_ATTRIB_REALLOCATION_COUNT = 196,
        // Number of unstable sectors (waiting for remapping)
        SMART_ATTRIB_CURRENT_PENDING_SECTOR_COUNT = 197,
        // Number of uncorrected errors
        SMART_ATTRIB_UNCORRECTABLE_SECTOR_COUNT = 198,
        // Number of CRC errors during UDMA mode
        SMART_ATTRIB_ULTRA_DMA_CRC_ERROR_COUNT = 199,
        // Number of errors while writing to disk (or) multi-zone 
        // error rate (or) flying height
        SMART_ATTRIB_WRITE_ERROR_RATE = 200,
        // Number of off-track errors
        SMART_ATTRIB_Soft_Read_Error_Count = 201,
        // Number of Data Address Mark (DAM) errors (or) vendor-specific
        SMART_ATTRIB_Data_Address_Mark_Errors = 202,
        // Number of ECC errors
        SMART_ATTRIB_Run_Out_Cancel = 203,
        // Number of errors corrected by software ECC
        SMART_ATTRIB_Soft_ECC_Correction = 204,
        // Number of thermal asperity errors
        SMART_ATTRIB_Thermal_Asperity_Rate = 205,
        // Height of heads above the disk surface
        SMART_ATTRIB_Flying_Height = 206,
        // Amount of high current used to spin up the drive
        SMART_ATTRIB_Spin_High_Current = 207,
        // Number of buzz routines to spin up the drive
        SMART_ATTRIB_Spin_Buzz = 208,
        // Drive’s seek performance during offline operations
        SMART_ATTRIB_Offline_Seek_Performance = 209,
        // Shift of disk is possible as a result of strong shock loading 
        // in the store, as a result of falling (or) temperature
        SMART_ATTRIB_Disk_Shift = 220,
        // Number of errors as a result of impact loads as detected 
        // by a shock sensor
        SMART_ATTRIB_G_SENSE_ERROR_Count = 221,
        // Number of hours in general operational state
        SMART_ATTRIB_Loaded_Hours = 222,
        // Loading on drive caused by numerous recurrences of operations, 
        // like reading, recording, positioning of heads, etc.
        SMART_ATTRIB_Load_Unload_Retry_Count = 223,
        // Load on drive caused by friction in mechanical parts of the store
        SMART_ATTRIB_Load_Friction = 224,
        // Total number of load cycles
        SMART_ATTRIB_Load_Unload_Cycle_Count1 = 225,
        // General time for loading in a drive
        SMART_ATTRIB_Load_In_Time = 226,
        // Quantity efforts of the rotating moment of a drive
        SMART_ATTRIB_Torque_Amplification_Count = 227,
        // Number of power-off retract events.
        SMART_ATTRIB_POWER_OFF_RETRACT_COUNT1 = 228,
        // Amplitude of heads trembling (GMR-head) in running mode
        SMART_ATTRIB_GMR_Head_Amplitude = 230,
        // Temperature of a drive
        SMART_ATTRIB_Drive_Temperature = 231,
        // Time while head is positioning
        SMART_ATTRIB_Head_Flying_Hours = 240,
        // Number of errors while reading from a disk
        SMART_ATTRIB_Read_Error_Retry_Rate = 250
    }
    #endregion

    public class Smart
    {
        public string[] Drives;
        public string[] Names;
        public STORAGE_PREDICT_FAILURE SmartData;

        #region Konstanten
        private const int IOCTL_STORAGE_PREDICT_FAILURE = 0x2D1100; // uint könnte probleme geben
        private const uint STATUS_INVALID_DEVICE_REQUEST = 0xC0000010;

        private const short FILE_SHARE_NONE = 0;
        private const short OPEN_EXISTING = 3;
        private const short INVALID_HANDLE_VALUE = -1;
        private const uint GENERIC_READ = 0x80000000; // fehler beim casten?
        private const int GENERIC_WRITE = 0x40000000;
        private const int FILE_SHARE_READ = 1;
        private const int FILE_SHARE_WRITE = 2;
        #endregion

        #region DLL Deklaration
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr CreateFile(string lpFileName,
                                                uint dwDesiredAccess,
                                                int dwShareMode,
                                                IntPtr lpSecurityAttributes,
                                                int dwCreationDisposition,
                                                int dwFlagsAndAttributes,
                                                IntPtr hTemplateFile);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(IntPtr hObject);

        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int DeviceIoControl(IntPtr hDevice,
                                                  int dwioControlCode,
                                                  IntPtr lpInBuffer,
                                                  int nInBufferSize,
                                                  out STORAGE_PREDICT_FAILURE lpOutBuffer,
                                                  int nOutBufferSize,
                                                  ref int lpBytesReturned,
                                                  IntPtr lpOverlapped);
        #endregion

        #region Funktionen
        // Festplatten (und auch USB-Laufwerke) per WMI ermitteln
        public int GetPhysicalDrives()
        {
            ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
            Hashtable ht = new Hashtable();
            int i = 0;

            ManagementObjectCollection moc = searcher.Get();
            Drives = new string[moc.Count];
            Names = new string[moc.Count];

            try
            {
                foreach (ManagementObject wmi_HD in moc)
                {
                    ht.Add(wmi_HD.Properties["deviceid"].Value, wmi_HD.Properties["caption"].Value);

                    // Hier kommt die Exception
                    Drives[i] = wmi_HD.Properties["deviceid"].Value.ToString();
                    Names[i] = wmi_HD.Properties["caption"].Value.ToString();
                    i++;
                }

                return i;
            }
            catch (Exception ex)
            {
                return 0;
            }
            finally
            {
                ht = null;
            }
        }

        // Smart-Werte für angegebenes Laufwerk lesen
        // stehen danach in SmartData zur Verfügung
        public ReadSmartResult ReadSmart(string Drive)
        {
            IntPtr Device;
            bool Result;
            int ReturnedBytes = 0;

            this.SmartData = new STORAGE_PREDICT_FAILURE();

            IntPtr zeroPtr = new IntPtr(0);

            // Laufwerk öffnen
            Device = CreateFile(Drive,
                                GENERIC_READ,
                                FILE_SHARE_READ,
                                IntPtr.Zero,
                                OPEN_EXISTING,
                                0,
                                IntPtr.Zero);

            if (Device.ToInt32() == INVALID_HANDLE_VALUE)
            {
                if (Marshal.GetLastWin32Error() == 2)
                {
                    //Zugriff verweigert, wahrscheinlich fehlen Adminrechte
                    return ReadSmartResult.ACCESS_DENIED;
                }
                else
                {
                    // das angegebene Laufwerk existiert nicht
                    return ReadSmartResult.DRIVE_DOES_NOT_EXIST;
                }
            }

            // S.M.A.R.T. auslesen
            Result = Convert.ToBoolean(DeviceIoControl(Device,
                                                       IOCTL_STORAGE_PREDICT_FAILURE,
                                                       IntPtr.Zero,
                                                       0,
                                                       out SmartData,
                                                       Marshal.SizeOf(SmartData),
                                                       ref ReturnedBytes,
                                                       IntPtr.Zero));

            CloseHandle(Device);

            if (Result)
            {
                // erfolgreich
                return ReadSmartResult.SUCCEEDED;
            }
            else
            {
                // es konnten keine S.M.A.R.T. Eigenschaften gelesen werden
                return ReadSmartResult.NO_SMART_AVAILABLE;
            }
        }

        // Attributsnamen (falls vohanden) ausgeben
        public string GetAttribute(Attributes Index)
        {
            string outString;

            outString = Index.ToString();
            // Wenn outString noch numerisch ist, dann ist kein 
            // Beschreibungstext in der Definition angegeben, 
            // Ersatztext wird ausgegeben
            if (IsNumeric(outString))
                outString = "<Herstellerspezifisch>";

            return outString;
        }

        // Smart-Wert auswerten, 12Byte beginnend ab Offset
        // Offset = X * 12 + 2
        public void GetSmartValue(int Offset,
                                  ref int Attribut,
                                  ref long Value,
                                  ref long Worst,
                                  ref long Data)
        {

            // Der S.M.A.R.T.-Wert setzt sich aus 12Byte zusammen
            // 1. Byte - Attributesnamen
            Attribut = SmartData.VendorSpecific[Offset];
            // 2./3. Byte - Herstellerspezifisch
            // interessant ist hier das 1. Bit, falls der Wert dieses 
            // Smartwertes unter dem Grenzwert liegt, gibt dieses bit an, 
            // ob die Festplatte innerhalb der nächsten 24h ausfallen wird!
            // 4. Byte - Wert
            Value = SmartData.VendorSpecific[Offset + 3];
            // 5.-12. Byte - RAW-Value (herstellerspezifisch)
            Worst = SmartData.VendorSpecific[Offset + 4];
            string str = "";

            for (int i = 11; i <= 5; i--)
            {
                str += Convert.ToString((SmartData.VendorSpecific[Offset + i]), 16);
            }

            Data = Convert.ToInt64(str, 16);
        }
        #endregion

        // Werte aus SmartData in Listview eintragen
        public void AddSmartToListView(ListView lv, int Index)
        {
            int Attribut = 0;
            long Value = 0, Worst = 0, Data = 0;

            for (int i = 0; i <= 29; i++)
            {
                GetSmartValue(i * 12 + 2, ref Attribut, ref Value, ref Worst, ref Data);
                // 0 ist kein gültiger Wert, d.h. dieser Speicherblock enthält
                // kein Attribut
                if (!Attribut.Equals(Attributes.SMART_ATTRIB_Invalid))
                {
                    // ins Listview eintragen
                    ListViewItem lvi = new ListViewItem();

                    //Attributes at = new Attributes();
                    //string att = Enum.GetName(typeof(Attributes), Attribut);

                    Attributes at = (Attributes)Enum.Parse(typeof(Attributes), Convert.ToString(Attribut));

                    //string attributeName = Enum.GetName(typeof(Attributes), Convert.ToString(1));
                    //Attributes attr = (Attributes) Enum.Parse(typeof(Attributes), attributeName);

                    lvi.Text = Attribut + " - " + GetAttribute(at);
                    lvi.SubItems.Add(Value.ToString());
                    lvi.SubItems.Add(Worst.ToString());
                    lvi.SubItems.Add(Data.ToString());

                    for (int j = 0; j <= lv.Groups.Count - 1; i++)
                    {
                        if (lv.Groups[j].Name == "LW" + Index)
                            lvi.Group = lv.Groups[j];
                    }

                    lv.Items.Add(lvi);
                }
            }
        }

        #region Hilfsklasse zur Zwischenspeicherung
        [StructLayout(LayoutKind.Sequential)]
        public class STORAGE_PREDICT_FAILURE
        {

            public int PredictFailure;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]

            public byte[] VendorSpecific;

            public STORAGE_PREDICT_FAILURE()
            {
                VendorSpecific = new byte[511];
            }
        }
        #endregion

        #region IsNumeric Methode
        // IsNumeric Function
        static bool IsNumeric(object Expression)
        {
            // Variable to collect the Return value of the TryParse method.
            bool isNum;

            // Define variable to collect out parameter of the TryParse method. If the conversion fails, the out parameter is zero.
            double retNum;

            // The TryParse method converts a string in a specified style and culture-specific format to its double-precision floating point number equivalent.
            // The TryParse method does not generate an exception if the conversion fails. If the conversion passes, True is returned. If it does not, False is returned.
            isNum = Double.TryParse(Convert.ToString(Expression), System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
            return isNum;
        }
        #endregion
    }
}

Und hier noch mein Aufruf der Klasse:
C#:
private void button1_Click(object sender, EventArgs e)
        { 
            Smart smart = new Smart();
            listView1.Items.Clear();
            listView1.Groups.Clear();
 
            // Laufwerke ermitteln
            int j = smart.GetPhysicalDrives() - 1;
 
            // S.M.A.R.T. Eigenschaften auslesen und ins Listview eintragen
            for(int i = 0; i <= j; i++) {
                switch(smart.ReadSmart(smart.Drives[i])) {
                    case ReadSmartResult.SUCCEEDED:
                        // S.M.A.R.T.-Werte wurden ausgelesen und werden 
                        // ins Listview eingetragen
                        listView1.Groups.Add("LW" + i, smart.Names[i] + "  (" + smart.Drives[i] + ")");
                        smart.AddSmartToListView(listView1, i);
                        break;
                    case ReadSmartResult.NO_SMART_AVAILABLE:
                        // S.M.A.R.T. ist für dieses Laufwerk nicht verfügbar
                        listView1.Groups.Add("LW" + i, smart.Names[i] + "  (" + smart.Drives[i] + ")");
                        ListViewItem lvi = new ListViewItem();
                        lvi.Text = "S.M.A.R.T. not available";
                        lvi.Group = listView1.Groups[i];
                        listView1.Items.Add(lvi);
                        break;
                    case ReadSmartResult.ACCESS_DENIED:
                        // Zugriff uafs Laufwerk verweigert, wahrscheinlich fehlen 
                        // Adminrechte (v.a. unter Vista)
                        listView1.Groups.Add("LW" + i, smart.Names[i] + "  (" + smart.Drives[i] + ")");
                        ListViewItem lvi2 = new ListViewItem();
                        lvi2.Text = "Zugriff verweigert, Administratorrechte benötigt!";
                        lvi2.Group = listView1.Groups[i];
                        listView1.Items.Add(lvi2);
                        break;
                    case ReadSmartResult.DRIVE_DOES_NOT_EXIST:
                        // Das angegebene Laufwerk existiert nicht
                        // nichts ausgeben
                        break;
                }
            }
        }

Hoffentlich kann mir jemand helfen! :)
 
Was musstest du denn ändern?
Ich würde mal probieren ein bisschen mit den DLLImport-Attributen herumzuspielen, vielleicht CharSet auf Ansi setzen.
Oder es passt irgendwas nicht mit der Übergabe by value/by reference. Ist aber nur geraten.
 
Hallo,

nachdem ich erst zunächst gelöst habe indem ich die VB Datei einfach benutz habe muss ich es nun aber nach C# portieren.

Ich weiß jedoch wo der Fehler ist aber nicht warum.

Dieser VB.NET Code klappt:
Visual Basic:
<DllImport("kernel32.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Auto)> _
    Friend Function DeviceIoControl(ByVal hDevice As IntPtr,
                                    ByVal dwioControlCode As Integer,
                                    ByVal lpInBuffer As IntPtr,
                                    ByVal nInBufferSize As Integer,
                                    <Out()> ByVal lpOutBuffer As StoragePredictFailure,
                                    ByVal nOutBufferSize As Integer,
                                    ByRef lpBytesReturned As Integer,
                                    ByVal lpOverlapped As IntPtr) As Boolean

Übersetz nach C# mit dem dazugehörigen Aufruf geht es nicht.

C#:
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool DeviceIoControl(IntPtr hDevice,
                                                  int dwioControlCode,
                                                  IntPtr lpInBuffer,
                                                  int nInBufferSize,
                                                  out STORAGE_PREDICT_FAILURE lpOutBuffer,
                                                  int nOutBufferSize,
                                                  out int lpBytesReturned,
                                                  IntPtr lpOverlapped);
// ...
// Aufruf
bool Result = DeviceIoControl(Device,
                                                       IOCTL_STORAGE_PREDICT_FAILURE,
                                                       IntPtr.Zero,
                                                       0,
                                                       out SmartData,
                                                       Marshal.SizeOf(SmartData),
                                                       out ReturnedBytes,
                                                       IntPtr.Zero);

Ich kann mir das nicht erklären, das Programm wird einfach beendet, das heißt ich kann auch nicht den letzten Win32Error abrufen.

Das einzigste was ich sagen kann ist ...has exited with code -1073740791 (0xc0000409)., aber dazu findet man nichts im Internet.
 
Hier hat jemand geschrieben, es würde bei ihm funktionieren.
Beim viertletzten Parameter gibt es einen Unterschied:
C#:
[Out] IntPtr lpOutBuffer
statt
C#:
out STORAGE_PREDICT_FAILURE lpOutBuffer
Also ein Pointer statt einer Struktur. Und man beachte die eckigen Klammern! Diese sind eine verkürzte Schreibweise für das System.Runtime.InteropServices.OutAttribute.
Ich blicke durch das OutAttribut aber ehrlich gesagt nicht durch (aber: 'out' = '[Out] ref').
Ein weiterer Unterschied ist beim vorletzten Parameter:
C#:
ref uint lpBytesReturned
statt
C#:
out int lpBytesReturned
Und auch sonst sind hier alle Integers als UInt übergeben.
Viel Spaß beim Experimentieren! Bitte lasse uns deine funktionierende Lösung dann wissen!
 
Zuletzt bearbeitet:
Zurück