Autor Beitrag
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2090
Erhaltene Danke: 382

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Fr 30.06.17 17:28 
Hallo Forum

Allgemeines:

frDriveNET ist eine .NET Assembly, die folgende Klassen inne hat:
- frDrive (frDrive.cs) : C# Hauptklasse
- frFunc (frDriveAPI.cs) : statische C# Klasse, die WinAPI Funktionen beinhaltet.
- frConst (frDriveAPI.cs) : statische C# Klasse, die WinAPI Konstanten beinhaltet.

Der Code liegt offen vor! Die Projektmappe beinhaltet außerdem eine Testanwendung (frDriveTest) sowie ein NuGet package, um die Nutzung zu vereinfachen.
Das Projekt ist eine Übersetzung aus dem bestehenden Projekt frDrive (für Delphi und Lazarus) und soll dem Anwender dabei helfen:
- Laufwerke zu ermitteln und aufzulisten
- (externe) Laufwerke zu trennen / auszuwerfen
- gültige Laufwerke zu erkennen
- Laufwerk-Informationen auszulesen
- die Laufwerk-Anzeige zu bearbeiten

Voraussetzung:

Für Windows (32 und 64bit) WinForm- und WPF Anwendungen.
Minimum:
- Windows XP / Windows Server 2003
- Visual Studio 2008 / .NET 3.5 / C# 3.0
- Adminrechte für Anwendungen, die mit der Klasse erstellt werden

Funktionsweise:

- Laufwerke trennen:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
/* Variante 1 */
  IntPtr h = frDrive1.OpenVolume('D');  // ermittelt ein Laufwerk 
  frDrive1.EjectVolume(h);              // trennt das Laufwerk
  System.Threading.Thread.Sleep(5000);
  frDrive1.CloseVolume(h);              // nimmt das Laufwerk wieder auf und gibt das Handle frei

/* Variante 2 */
  frDrive1.DriveLetter = "CDEF";        // ermittelt ein oder mehrere Laufwerke
  frDrive1.Eject = true;                // trennt die Laufwerke
  System.Threading.Thread.Sleep(5000);
  frDrive1.Eject = false;               // nimmt die Laufwerke wieder auf und gibt alle Handle frei

/* Variante 3 */
  DriveTypes dt = DriveTypes.Fixed | DriveTypes.CDRom;
  frDrive1.DriveType = dt;              // ermittelt ein oder mehrere Laufwerk-Typen
  frDrive1.Eject = true;                // trennt die Laufwerke
  System.Threading.Thread.Sleep(5000);
  frDrive1.Eject = false;               // nimmt die Laufwerke wieder auf und gibt alle Handle frei

/* Abfrage */
  frDrive1.DriveLetter = "CDEF"
  // oder 
  frDrive1.DriveType = DriveTypes.Fixed | DriveTypes.CDRom; 
 
  frDrive1.Eject = true
  frDrive1.Eject = false
  for (int i = 0; i < frDrive1.Count; i++) 
  { 
      char laufwerk = frDrive1.DriveLetter[i]; 
      if (frDrive1.Ejected(i)) 
          MessageBox.Show(laufwerk + " wurde entfernt"); 
      else 
          MessageBox.Show(laufwerk + " wurde nicht entfernt"); 
  }


- Laufwerke auflisten:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
/* Alle Laufwerke auflisten */  
  DriveTypes allDT = DriveTypes.Removable | DriveTypes.Fixed | DriveTypes.Remote | 
                     DriveTypes.CDRom | DriveTypes.RAMDisk; 
  listBox1.Items.AddRange(frDrive1.GetDrives(allDT)); 
  // oder 
  listBox1.Items.AddRange(frDrive1.GetDrives(DriveTypes.All)); 
  // oder 
  listBox1.Items.AddRange(frDrive1.GetDrives(frDrive.DRIVE_ALL)); 
  // oder 
  listBox1.Items.AddRange(frDrive1.GetDrives(255)); 
 
/* Bestimmte Laufwerke auflisten */ 
  DriveTypes someDT = DriveTypes.Fixed | DriveTypes.CDRom; 
  listBox1.Items.AddRange(frDrive1.GetDrives(someDT)); 
  // oder 
  listBox1.Items.AddRange(frDrive1.GetDrives(frDrive.DRIVE_CDROM)); 
  // oder 
  listBox1.Items.AddRange(frDrive1.GetDrives(5));

Natürlich können die Laufwerkbuchstaben auch in der Eigenschaft .DriveLetter festgelegt und wieder abgefragt werden.
Unabhängig von der Zuweisung, werden gültige Laufwerkbuchstaben als (aneinandergereihter) Text ausgegeben.
Ungültige Laufwerkbuchstaben, sowie auch Duplikate, werden ausgefiltert.

- Laufwerk überprüfen:

ausblenden C#-Quelltext
1:
2:
3:
// Gibt "True" zurück, wenn das Laufwerk existiert / der Laufwerkbuchstabe belegt ist.
  if (frDrive1.IsValidDrive('C'))
      // Laufwerk gültig


- Laufwerk-Anzeige bearbeiten:

ausblenden C#-Quelltext
1:
frDrive1.RenameVolume('C'"Windows");					


- Laufwerk Informationen auslesen:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
/* Ein oder mehrere Laufwerke */
  frDrive1.DriveLetter = "CDEF"
  // oder 
  frDrive1.DriveType = DriveTypes.Fixed | DriveTypes.CDRom; 
 
  for (int i = 0; i < frDrive1.Count; i++) 
  { 
      listBox1.Items.Add(frDrive1.Info(i).DisplayName);     // Anzeige-Name wie unter Arbeitsplatz ersichtlich 
      listBox1.Items.Add(frDrive1.Info(i).TypeName);        // Typen-Name wie z.B. "Lokaler Datenträger" 
      listBox1.Items.Add(frDrive1.Info(i).FileSystemName);  // Dateisystem wie z.B. "NTFS" 
      listBox1.Items.Add(frDrive1.Info(i).VolumeSerial);    // Volume-Nummer (8 stelliger HEX Wert) 
      listBox1.Items.Add(frDrive1.Info(i).HDSerial);        // Serien-Nummer einer Festplatte 
      listBox1.Items.Add(frDrive1.Info(i).Model);           // Model-Beschreibung eines Laufwerkes 
      listBox1.Items.Add(frDrive1.Info(i).PDNumber);        // Nummer eines physikalischen Laufwerkes 
      listBox1.Items.Add(frDrive1.Info(i).FullSize.ToString());  // Gesamte Laufwerk-Größe in Bytes 
      listBox1.Items.Add(frDrive1.Info(i).FreeSize.ToString());  // Freier Speicherplatz in Bytes 
      pictureBox1.Image = frDrive1.Info(i).Icon16.ToBitmap();    // 16x16 px großes Laufwerk-Icon 
      pictureBox1.Image = frDrive1.Info(i).Icon32.ToBitmap();    // 32x32 px großes Laufwerk-Icon      
  } 

/* Ein bestimmtes Laufwerk */
  char c = 'C';

  listBox1.Items.Add(frDrive1.GetDisplayName(c));     // Anzeige-Name wie unter Arbeitsplatz ersichtlich
  listBox1.Items.Add(frDrive1.GetTypeName(c));        // Typen-Name wie z.B. "Lokaler Datenträger"
  listBox1.Items.Add(frDrive1.GetFileSystemName(c));  // Dateisystem wie z.B. "NTFS"
  listBox1.Items.Add(frDrive1.GetVolumeSerial(c));    // Volume-Nummer (8 stelliger HEX Wert)
  listBox1.Items.Add(frDrive1.GetHDSerial(c));        // Serien-Nummer einer Festplatte
  listBox1.Items.Add(frDrive1.GetModel(c));           // Model-Beschreibung eines Laufwerkes
  listBox1.Items.Add(frDrive1.GetPDNumber(c));        // Nummer eines physikalischen Laufwerkes
  listBox1.Items.Add(frDrive1.GetFullSize(c).ToString());  // Gesamte Laufwerk-Größe in Bytes
  listBox1.Items.Add(frDrive1.GetFreeSize(c).ToString());  // Freier Speicherplatz in Bytes
  pictureBox1.Image = frDrive1.GetIcon16(c).ToBitmap();    // 16x16 px großes Laufwerk-Icon
  pictureBox1.Image = frDrive1.GetIcon32(c).ToBitmap();    // 32x32 px großes Laufwerk-Icon


- Konstruktoren und Ereignisse:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
/* Konstruktoren */
  // Variante 1
  frDrive frDrive1 = new frDrive();  // Setzt im Anschluss alle verfügbaren Laufwerkbuchstaben in die Eigenschaft ".DriveLetter"
  // Variante 2
  frDrive frDrive1 = new frDrive(frDrive.DRIVE_CDROM); // Setzt im Anschluss CD/DVD Laufwerke in die Eigenschaft ".DriveLetter" 
  // Variante 3
  frDrive frDrive1 = new frDrive(DriveTypes.Fixed | DriveTypes.CDRom); // Setzt im Anschluss Festplatten-, CD/DVD Laufwerke in die Eigenschaft ".DriveLetter"  

/* Ereignisse */
  DeviceConnected // Tritt ein, sobald ein (externes) Laufwerk angeschlossen wird.
  DeviceDisconnected // Tritt ein, sobald ein (externes) Laufwerk entfernt wird.



Sollten Mängel auftreten oder Verbesserungsvorschläge vorliegen, so lasst es mich wissen.
Danke für's Lesen und die anschließende Nutzung. ;)
Einloggen, um Attachments anzusehen!
_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)


Zuletzt bearbeitet von Frühlingsrolle am Fr 30.06.17 18:08, insgesamt 2-mal bearbeitet
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2090
Erhaltene Danke: 382

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Fr 30.06.17 17:35 
Platzhalter ...

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4337
Erhaltene Danke: 873


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Fr 30.06.17 18:41 
Zitat:
oder Verbesserungsvorschläge vorliegen, so lasst es mich wissen.

Den Syntax wenn möglich ;) Es wäre schön wenn du IntPtr's verstecken könntest. Ein IntPtr sagt ja nicht wofür er steht darum liegt es nahe die immer mit einer passenden contextabhängigen Klasse zu kapseln an der man dann auch nur die entsprechenden auf diesen IntPtr möglichen Methoden kapselt.

Z.B. anstatt

ausblenden C#-Quelltext
1:
2:
3:
4:
  IntPtr h = frDrive1.OpenVolume('D');  // ermittelt ein Laufwerk 
  frDrive1.EjectVolume(h);              // trennt das Laufwerk
  System.Threading.Thread.Sleep(5000);
  frDrive1.CloseVolume(h);              // nimmt das Laufwerk wieder auf und gibt das Handle frei

eher
ausblenden C#-Quelltext
1:
2:
3:
4:
  Volume d = frDrive1.OpenVolume('D');
  d.Eject();
  System.Threading.Thread.Sleep(5000);
  d.Close();

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2090
Erhaltene Danke: 382

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Fr 30.06.17 19:07 
Werde ich mit den nächsten Erweiterungen so übernehmen. Nur statt Volume, hätte ich gesagt, HDrive oder HVolume ?! Man soll es als Handle identifizieren können.
Dankeschön! Freu mich auf weitere Verbesserungsvorschläge sowie Ideen für Erweiterungen. SMART Werte kommem demnächst.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4337
Erhaltene Danke: 873


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Fr 30.06.17 19:35 
Zitat:
Man soll es als Handle identifizieren können.

Warum? Ich vermute mal das selbst bei professionellen .Net Entwicklern höchsten ein Viertel weiß was ein Handle ist bzw. mehr weiß als den Namen Handle. Auch in Winforms würdest du ja nicht eine HForm anstatt Form erwarten. Und eine Form ist letztlich auch nicht viel mehr als eine Kapsel für ein Window Handle.

Wo ich gerade sehe das ein Volume auch eine Close kennt spinne ich mal die Analogie zu Form weiter. Am besten du implementierst du in diesen Klassen auch IDisposable damit sich der arme Entwickler Fragen muss ob er nun Close oder Dispose oder beides am Ende aufrufen muss ;) Nein, empfehlen würde ich Close wegzulassen und nur Dispose anzubieten. Bzw. das Handle freigeben von Close zu trennen und nur in Dispose zu machen.

Edit: Vergiss den letzten Satz. Ich habe kurzzeitig Close missverstanden. Es ist nicht die gegenteilige Operation zu Eject :roll:
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2090
Erhaltene Danke: 382

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Fr 30.06.17 20:23 
Ich könnte es mit IDisposable erweitern, sehe aber im Moment nicht die Notwendigkeit dafür.

Wie du bemerkt hast, bereitet OpenVolume() ein Handle eines einzelnen Laufwerkbuchstaben vor, während CloseVolume() dieses freigibt.
Die Eigenschaft .Eject nimmt diese in eine Liste auf, und trennt/wirft die Laufwerke über die Methode EjectVolume aus.

Nun ja, das Projekt ist vollgespickt mit kurzen und klaren Kommentaren. Englisch setze ich dabei voraus.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 3884
Erhaltene Danke: 789

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Sa 01.07.17 09:01 
Ich bin auch der Meinung von Ralf, daß die API objektorientierter sein könnte. Bisher ist es einfach eine große Klasse (und als Anwender weiß man nicht auf Anhieb, welche Methoden wie in Korrelation stehen).
Ich fände hier sogar eine Schnittstelle IVolume am schönsten:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
interface IVolume
{
  // Handle
  // Eject
  // Close
  // ...
}

IVolume OpenVolume(char cDrive);

So wäre dann auch der Code auf 2 kleinere Klassen aufgeteilt (frDrive, (I)Volume).

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2090
Erhaltene Danke: 382

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Sa 01.07.17 09:45 
Danke für die Idee, nur weiss ich nicht, wie du dir das vorgestellt hast. Das wirst du wohl nicht damit gemeint haben, oder:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
public class frDrive : IVolume
{
    public IntPtr OpenVolume(char cDrive) { /* Code */ }
    public bool CloseVolume(IntPtr hDrive) { /* Code */ }
    public bool EjectVolume(IntPtr hDrive) { /* Code */ }
}

interface IVolume
{
    public IntPtr OpenVolume(char cDrive);
    public bool CloseVolume(IntPtr hDrive);
    public bool EjectVolume(IntPtr hDrive);
}

Ich wüsste nicht, wie ich es aufteilen könnte, denn den meisten Code aus der "großen" Klasse machen die .GET-Methoden aus.
Was übrigt bleibt, sind die 3 genannten Methoden, 1 struct und 4 Eigenschaften.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 3884
Erhaltene Danke: 789

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Sa 01.07.17 10:32 
Ich meine alle Methoden, die bisher den IntPtr hDrive als Parameter entgegennehmen, also auch die privaten Methoden LockVolume, UnlockVolume, d.h. diese kämen dann auch in die Implementationsklasse Volume (und dies würde dann schon den Code kleiner und übersichtlicher machen).

Und bei den anderen Methoden, welche bisher char cDrive als Parameter haben, kannst du dich ja an DriveInfo orientieren. So würde sich dann deine Komponente besser in die .NET-Infrastruktur anlehnen (umgekehrt kannst du dir ja auch für die Delphi-Komponente überlegen, ob dies nicht der bessere Designansatz wäre).

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2090
Erhaltene Danke: 382

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Sa 01.07.17 10:40 
Verstanden. Das ist eine schöne Idee, danke!

Nachtrag

Ich hoffe das stimmt nun soweit: Methoden mit dem Parameter IntPtr hDrive wurden in eine eigene Klasse verschoben, sowie ein Alias für IntPtr erstellt:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
// frVolume.CS
using Volume = System.IntPtr;

namespace frDriveNET
{
    public class frVolume
    {
        // ... Methoden(Volume hDrive) inkl. "OpenVolume(char cDrive)"
    }
}

// frDrive.CS

namespace frDriveNET
{
    public class frDrive : frVolume
    {
        // ... Methoden(char cDrive)
    }

    public enum DriveTypes : Byte { ... }

    public struct DriveInfo { ... }

    internal class WndControl : NativeWindow { ... }
}

War das so gedacht? Außerdem, sollen das Enum, das Struct und die interne Klasse ebenso in eine eigene .CS ausgelagert werden?

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)