Autor Beitrag
Nils:D
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 131

Debian, Win XP
Delphi 7 Arch.
BeitragVerfasst: Mi 25.06.08 14:16 
Hi,

ich möchte alle Datenträger in einer Liste ausgeben. Das auslesen der Laufwerkbuchstaben ist eine kurze Kleinigkeit, das Auslesen der Datenträgernamen ein viel zu langwieriger Akt. Folgenden Code benutze ich, das Bestimmen der Datenträgerkapazität und des freien Speicherplatzes fehlt noch --> wird später noch langsamer. Habt ihr eine Idee, wie man so etwas gut lösen könnte ? Im schlimmsten Falle würde ich die Datenträger bei Programmstart auslesen, in ein Array packen und immer wieder wenn nötig auslesen, drückt der Benutzer auf F5 bzw. auf Aktualisieren, wird alles neu eingelesen.
ausblenden volle Höhe Delphi-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:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
procedure ListLogicalDrives(var LV : TListView; ReadyOnly : Boolean = True);
  function DriveIsReady(const Drive : String) : Boolean;
  var wfd        : TWin32FindData;
      hFindData  : THandle;
  begin
    SetErrorMode(SEM_FAILCRITICALERRORS);
    hFindData := FindFirstFile(Pointer(Drive+'*.*'), wfd);
    if hFindData <> INVALID_HANDLE_VALUE then
      Result := True
    else
      Result := False;
    FindClose(hFindData);
    SetErrorMode(0);
  end;

  function GetVolumeLabel(const Drive: string): string;
  var RootDrive                   : String;
      Buffer                      : Array[0..MAX_PATH+1of Char;
      FileSysFlags, MaxCompLength : DWORD;
  begin
    Result := '';
    FillChar(Buffer, SizeOf(Buffer), #0);
    if Length(Drive) = 1 then
      RootDrive := Drive + ':\'
    else
      RootDrive := Drive;
    if GetVolumeInformation(PChar(RootDrive), Buffer, SizeOf(Buffer), nil, MaxCompLength, FileSysFlags, nil0then
      Result := String(Buffer);
  end;
var FoundDrives, CurrentDrive : PChar;
    Len                       : DWord;
begin
  LV.Clear;

  GetMem(FoundDrives, 255);
  Len := GetLogicalDriveStrings(255, FoundDrives);
  if Len > 0 then
  begin
    try
      CurrentDrive := FoundDrives;
      while CurrentDrive[0] <> #0 do
      begin
        if ReadyOnly then
        begin
          //if DriveIsReady(String(CurrentDrive)) then
         // begin
            with LV.Items.Add do
            begin
              Caption := GetVolumeLabel(CurrentDrive);
              SubItems.Add('Datenträger');
              SubItems.Add(CurrentDrive);
              //SubItems.Add(Speicherplatz/kapazität);
            end;
         // end;
        end else
        begin
          with LV.Items.Add do
          begin
            Caption := GetVolumeLabel(CurrentDrive);
            SubItems.Add('Datenträger');
            SubItems.Add(CurrentDrive);
            //SubItems.Add(Speicherplatz/kapazität);
          end;
        end;
        CurrentDrive := PChar(@CurrentDrive[Succ(lstrlen(CurrentDrive))]);
      end;
    finally
      FreeMem(FoundDrives, Len);
    end;
  end;
end;


Wie kann man Datenträgerveränderungen (DVD in DVD-Laufwerk, externe Festplatte wird angemacht usw.) registrieren ? Ich fand bisher die Funktionen FindFirstChangeNotification/FindNextChangeNotification und ReadDirectoryChangesW. Da steht so schön dabei, sie würden das System extrem verlangsamen. Change Journals kann man vergessen, da die dazu gehörenden Funktionen alle nur bei NTFS funktionieren. Muss ich FindFirstChangeNotification und FindNextChangeNotification benutzen ? Ich weiß nicht, wie ich den Funktionen überhaupt den Mount-Ordner bzw. Arbeitsplatz übergeben soll. Bei Linux wäre das ganz einfach /mnt, aber bei Windows weiß ich es nicht.
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 489
Erhaltene Danke: 14

Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
BeitragVerfasst: Mi 25.06.08 14:31 
Für das Erkennen, ob eine DVD eingelegt oder ein USB-Stick angeschlossen wurde, wirst du vermutlich einen Hook auf eine WinAPI Funktion benötigen.
Webo
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 577
Erhaltene Danke: 14

Win 7, Debian
C# (Visual Studio 2013), PHP, C, C++ (Eclipse, KDevelop)
BeitragVerfasst: Mi 25.06.08 14:35 
Hier poste ich dir nun die Lösung zum erkennen eines CD/DVD-Wechsels :

(nicht meine Arbeit, habe sie mal früher von einem Freund zugeschickt bekommen !)



Wenn eine CD ins Laufwerk einlegt oder eine CD herausgenommen wird, wird die Botschaft WM_DeviceChange an alle Anwendungen geschickt.
Über den Parameter WParam kann bestimmt werden welche Aktion durchgeführt wurde. Die Botschaft wird nur verschickt, wenn die 'Automatische Benachrichtigung bei Wechsel' aktiv ist.

In dem Botschaftsrecord sind weitere Informationen zum Datenträger-Wechsel enthalten. So lässt sich über die Variable "lpdbv.dbcv_unitmask" ermitteln, auf welches Laufwerk sich der Wechsel bezieht.

ausblenden volle Höhe Delphi-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:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
private
 procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;

function GetFirstDrive(UnitMask: DWORD): Char;
var
I: Char;
begin
Result:=#0;
for I:='A' to 'Z' do
begin
  if (UnitMask and 1) = 1 then
  begin
    Result:=I;
    break;
  end;
  UnitMask:=UnitMask Shr 1;
end;
end;

procedure TForm1.WMDeviceChange(var Msg: TMessage);
var
lpdb: PDevBroadcastHdr;
lpdbv: PDevBroadcastVolume;
begin
lpdb:=PDevBroadcastHdr(Msg.lParam);
case Msg.wParam of
  //Datenträger eingelegt
  DBT_DeviceArrival:
    //Prüfen, ob es sich um eine DVD oder CD handelt.
    if lpdb^.dbch_devicetype = DBT_DevTyp_Volume then
    begin
      lpdbv:=PDevBroadcastVolume(Msg.lParam);
      if lpdbv.dbcv_flags = DBTF_Media then
        Label1.Caption:=Format('Datenträger in Laufwerk %s eingelegt.',
                               [GetFirstDrive(lpdbv.dbcv_unitmask)]);
    end;
  //Datenträger entfernt
  DBT_DeviceRemoveComplete:
    //Prüfen, ob es sich um eine DVD oder CD handelt.
    if lpdb^.dbch_devicetype = DBT_DevTyp_Volume then
    begin
      lpdbv:=PDevBroadcastVolume(Msg.lParam);
      if lpdbv.dbcv_flags = DBTF_Media then
        Label1.Caption:=Format('Datenträger aus Laufwerk %s entfernt.',
                               [GetFirstDrive(lpdbv.dbcv_unitmask)]);
    end;

end//case
end;

//Die Deklarationen der entsprechenden Typen finden Sie in der DBT.h. Eine Pascal-Übersetzung der Unit sind in der JEDI //JCL enthalten:

const DBT_DEVICEARRIVAL         = $8000;
const DBT_DEVICEREMOVECOMPLETE  = $8004;
const DBTF_MEDIA                = $0001;
const DBT_DEVTYP_VOLUME         = $0002;

type
PDevBroadcastHdr = ^TDevBroadcastHdr;
{$EXTERNALSYM DEV_BROADCAST_HDR}
DEV_BROADCAST_HDR = packed record
  dbch_size: DWORD;
  dbch_devicetype: DWORD;
  dbch_reserved: DWORD;
end;
TDevBroadcastHdr = DEV_BROADCAST_HDR;

PDevBroadcastVolume = ^TDevBroadcastVolume;
{$EXTERNALSYM DEV_BROADCAST_VOLUME}
DEV_BROADCAST_VOLUME = packed record
  dbcv_size: DWORD;
  dbcv_devicetype: DWORD;
  dbcv_reserved: DWORD;
  dbcv_unitmask: DWORD;
  dbcv_flags: Word;
end;


TDevBroadcastVolume = DEV_BROADCAST_VOLUME;

Die Dokumentation der Botschaft findest du in der PSDK unter dem Stichwort "WM_DEVICECHANGE". Der oben stehende Quelltext basiert auf dem Beispiel, welches in der MSDN zu finden ist: msdn.microsoft.com/l...p?url=/library/en-us /devio/base/detecting_media_insertion_or_removal.asp


---Moderiert von user profile iconNarses: Beiträge zusammengefasst---


Wo ich grade schon dabei bin Quellcode zu posten (Wieder nicht von mir, aber dafür gut)

Hier eine leichte kleine Funktion, mit der man die Laufwerke (Festpaltten, optische Laufwerke usw.) auslesen kann. Die Funktion gibt die Buchstaben des Laufwerks + den Typ (Festplatte etc) aus.


Benutz ich sehr häufig.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
procedure GetDrives(const AItems: TStrings);
const
  DriveTypeTexts: array[DRIVE_UNKNOWN..DRIVE_RAMDISK] of String =
   ('Unbekannt''Kein Wurzelverzeichnis''Diskette''Festplatte''Netzlaufwerk''CDROM''RAMDisk');
var
  Drive: Char;
  DriveType: Integer;
  DriveMask: Integer;
  Flag: Integer;
begin
  DriveMask:=GetLogicalDrives;
  flag:=1;
  for Drive := 'A' to 'Z' do
  begin
    if (flag and DriveMask)<>0 then
    begin
      DriveType := GetDriveType(PChar(Format('%S:\',[Drive]) ) ) ;
      AItems.Add(Format('%s: %s', [Drive, DriveTypeTexts[DriveType]]));
    end;
    flag:=flag shl 1;
  end;
end;

Die Funktion GetLogicalDrives gibt ein Bitmuster zurück, welches die verfügbaren Lauwerke angibt. Ist ein Bit auf 1 gesetzt, ist das Laufwerk vorhanden und kann erkannt werden.

Es wird zwischen Festplatten, Wechsellaufwerken, CD-ROM/DVD-Laufwerken, Netzlaufwerken und RamDisks unterschieden. Ein Beispielaufruf sieht so aus:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
procedure TForm1.Button2Click(Sender: TObject);
begin
 GetDrives(Listbox1.Items)
end;


Hoffe ich konnte die hiermit helfen.
Nils:D Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 131

Debian, Win XP
Delphi 7 Arch.
BeitragVerfasst: Mi 25.06.08 19:07 
Tut mir leid, dass ich mich erst jetzt wieder melde, aber ich habe eben fieberhaft den ganzen Tag am Projekt gearbeitet und da kam eben einiges noch regelrecht dazwischen. Habe aber vorhin schon geschaut und zu der Registrierung von ein- oder rausnehmen einer CD/DVD eine Frage: Das läuft nicht zwangsweise bei jedem Windows, da man die Einstellung wirklich erst aktivieren muss ? Das wäre natürlich nicht so gut, denn wenn mein Programm sich von der Registry vernhält, ist das wesentlich schöner. (Es gibt Registryschutzprogramme welche sofort Alarm melden, sobald nur irgendein Eintrag geändert werden soll. Ich programmiere selbstverständlich keinen Virus und ich kann davon ausgehen, dass der Benutzer die Änderung - solange ausführlich gesagt wird wozu diese dient - auch akzeptiert wird, aber schön ist so etas wirklich nicht, zumal man nicht überall so einfach an die Registry rankommt wegen fehlenden Benutzerrechten.) Gibt es da noch eine bessere Lösung ? Der zweite Beitrag war sehr hilfreich und ich habe ihn mit meinem alten Code so kombiniert, dass es noch ein Stück weit schneller wurde. Wie steht es eigentlich mit der Registration von "USB-Veränderungen" ? Wenn ich meine externe Platte - welche über USB läuft - anschalte, sollte mir das mein Programm melden.
Webo
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 577
Erhaltene Danke: 14

Win 7, Debian
C# (Visual Studio 2013), PHP, C, C++ (Eclipse, KDevelop)
BeitragVerfasst: Do 26.06.08 11:07 
Schön, dass dir der zweite Post geholfen hatt. Bin grad leider im Kofferpackstress und komme voraussichtlich frühestens heute Abend dazu, deine Fragen genau zu beantworten/dir weiter zu helfen.

Bis dann


Grüße

Webeo