Verschiedenes

Zugriff auf Komponenten über ihre Namen

Meine Frage: Wie kann ich Edit1 bis Edit27 einen bestimmten Text zuweisen ohne
Edit1.text:='bla';
Edit2.text:='bla';
...
schreiben zu müssen?

Nutze die Funktion "FindComponent":

For i:=1 to 27 do
  (FindComponent('Edit'+IntToStr(i)) as TEdit).text := 'bla';

Mehreren Komponenten dieselbe Ereignisbehandlungs-Routine zuweisen

Ich will z.B. dem OnClick-Ereignis mehrerer Buttons dieselbe Behandlungs-Routine zuweisen. Dabei soll aber eine unterschiedliche Aktion ausgeführt werden, abhängig vom Button, der angeklickt wurde. Wie kann ich den angeklickten Button in der Ereignisbehandlungs-Routine ermitteln?

Als Antwort sei hier die ausführliche Erklärung von Marian Aldenhövel aus der delphi-NG zitiert:

Der Typ der Ereignismethode steht fest. Im Falle von OnClick eine einfache Benachrichtigung:

  TNotifyEvent=procedure(Sender:TObject) of object;
Das bedeutet, ein Handler für dieses Ereignis muss eine Objektmethode sein ('of object') und genau einen Parameter vom Typ TObject haben. Der Name ist egal, Typ, Anzahl und Reihenfolge müssen passen (siehe 'procedural types' in der Sprachreferenz).

Welchen Wert dieser Parameter hat wenn das Ereignis ausgelöst wird, bestimmst nicht Du bei der Zuweisung, sondern wer auch immer das aufruft. Im Falle eines TNotifyEvent zeigt Sender auf die Instanz, die das Ereignis auslöst. Die allermeisten VCL-Ereignisse bringen einen solchen Parameter mit. Innerhalb der VCL sieht die Auslösung eines OnClick-Ereignisses etwa so aus:

  if Assigned(FOnClick) then
    FOnClick(Self);
Also wird Deine Prozedur mit 'Self' als Parameter aufgerufen, der Knopf liefert also eine Referenz auf sich selbst mit. Du kannst dann im Handler Sender benutzen um festzustellen, welcher Deiner Buttons das war. Du kannst den Wert von Sender mit einer von Dir bei der Erzeugung gespeicherten Referenz auf den Knopf vergleichen:

  for i:=0 to 10 do
    if Sender=FButtons[i] then
      ShowMessage('Button #'+IntToStr(i)+' geklickt!);
Oder Du kannst die Beschriftung benutzen:

  if Sender is TButton then
    ShowMessage(''''+TButton(Sender).Caption+''' geclickt.');
Oder Du weist bei der Erzeugung der Eigenschaft Tag einen Wert zu und vergleichst den:

  if Sender is TComponent then
    ShowMessage('Komponente mit Tag='+IntToStr(TComponent(Sender).Tag))+' geklickt.');
Oder Du gibst jedem einen Namen:

  if Sender is TComponent then
    ShowMessage('Komponente mit Name='''+TComponent(Sender).Name))+''' geklickt.');

Warum wird eine zur Laufzeit erzeugte Komponente nicht angezeigt?

Ich möchte dynamisch eine Instanz von der Klasse TEdit zur Laufzeit erzeugen. Leider erscheint auf meinem Formular aber kein Eingabefeld. Was muß man nach dem Aufruf der Create-Methode noch machen?

Du mußt dem neuen TEdit-Objekt einen Besitzer (Parent) zuweisen:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Eingabefeld:=tedit.create(self); // In diese Fall ist self = Form1
  Eingabefeld.Parent := self;      // oder z.B. auch Eingabefeld.Parent := Panel1;
end;
Näheres zu der Eigenschaft Parent findest du in der Online-Hilfe.

Wie kann man während der Abarbeitung einer Schleife einen Tastenklick abfragen?

Haette da mal ne Frage: Wie kann ich innerhalb einer Schleife mit jedem Durchlauf beispielsweise ein OnClick Ereignis abfragen? Ich will die Berechnung die in dieser Schleife durchgefuehrt wird auf Knopfdruck abbrechen, falls die Berechnung zu lange dauert. Nur wird die Queue in der die Ereignisse zwischengelagert werden erst am Ende der Berechnung abgefragt. Gibt es eine Funktion, daß das Programm den Knopfdruck trotzdem während der Berechnung mitbekommt? Ja:

Application.ProcessMessages;
..in der Schleife sorgt dafür, daß auch während des Schleifendurchlaufes die Nachrichten an die Applikation verarbeitet werden. Du setzt also mit Deinen AbbruchButton ein Flag und überprüfst dies dann in der Schleife:

var
  blAbbruch : Boolean;

procedure TMainForm.Button1Click(Sender: TObject);
begin
  blAbbruch := True;
end;

procedure Schleife;
begin
  blAbbruch := False;
  while ... and (not blAbbruch) do begin
    Application.ProcessMessages;
    ...
  end;
end;
Wenn die Schleife nicht besonders lange dauert, dann ist es zu empfehlen, Application.ProcessMessages nur bei jedem n-ten Durchlauf aufzurufen. Das erhöht die Geschwindigkeit und die Ereignisse können dennoch abgefragt werden.

Es gibt auch noch folgende Alternative:

procedure Schleife;
begin
  blAbbruch := False;
  while ... do begin
    if (GetAsyncKeystate(VK_ESCAPE)) <> 0 then
      if MessageDlg('Wirklich abbrechen?', mtConfirmation, [mbYes,mbNo], 0) = mrYes then
        BREAK;
      end;
    ...
  end;
end; {Olaf Mertgen}
Damit kann der Anwender die Schleife mit ESC verlassen.

Gedrehten Text ausgeben

Da Delphi das Drehen von Fonts nicht als Funktionalität zur Verfügung stellt, muß man das selbst machen. Das geht folgendermassen (der gewählte Font sollte eine TrueType-Schriftart sein):

procedure TForm1.Button1Click(Sender: TObject);
var
  lf : TLogFont;
  tf : TFont;
begin
  with Form1.Canvas do begin
    Font.Name := 'Arial';
    Font.Size := 24;
    tf := TFont.Create;
    tf.Assign(Font);
    GetObject(tf.Handle, sizeof(lf), @lf);
    lf.lfEscapement := 450;
    lf.lfOrientation := 450;
    tf.Handle := CreateFontIndirect(lf);
    Font.Assign(tf);
    tf.Free;
    TextOut(20, Height div 2, 'gedrehter Text!');
  end;
end;

Wie kann man einfach Text verschlüsseln?

Wie kann man mirt einfachen Mitteln einen Text verschlüsseln, daß er nicht von jedem auf Anhieb gelesen werden kann? Die Verschlüsselungsmethode muß aber nicht höchsten Sicherheitsansprüchen genügen.

Zum Verschlüsseln gibt es verschiedene Möglichkeiten, z.B. mittels XOR:

var s : String;
    i : Integer;

// Kodieren
s:=Edit1.Text;
for i:=1 to length(s) do
  s[i]:=char(23 Xor Ord(s[i]));
SpeichereStringInDatei(s);

// Dekodieren
s:=LadeStringAusDatei;
for i:=1 to length(s) do
  s[i]:=char(23 Xor ord(s[i]));
Edit1.Text:=s;
23 ist bei dem Beispiel ein beliebiger Wert zwischen 1 und 255. Du kannst die Verschlüsselung auch noch verbessern, indem Du statt 23 Zufallszahlennimmst, so daß jeder Buchstabe mit einer anderen Zahl verschlüsselt wird. Dafür mußt Du mit Randseed den Startwert setzen, sonst kannst Du es nicht mehr entschlüsseln. Beachte dabei, daß die verschlüsselten Buchstaben auch ASCII-Werte kleiner 32 oder größer als 127 annehmen können. Der verschlüsselte string kann dann nicht mehr in einer Textdatei gespeichert oder z.B. in einem TMemo dargestellt werden.

Wie wandle ich DOS-ASCII-Text nach Windows-ANSI?

Diese Funktion wandelt DOS-ASCII-Text nach Windows-ANSI. Sie verarbeitet in der vorliegenden Form nur Strings bis 255 Buchstaben, man kann Sie durch Änderung der MaxLength-Konstanten aber leicht anpassen. Sie funktioniert in allen Delphi- und Windowsversionen:

function ASCII2ANSI(AText:string):string;
const MaxLength = 255;
var PText : PChar;
begin
  PText:=StrAlloc(MaxLength);
  StrPCopy(PText,AText);
  {$IFDEF WIN32}
  OEMToChar(PText,PText); {32Bit}
  {$ELSE}
  OEMToAnsi(PText,PText); {16Bit}
  {$ENDIF}
  Result:=StrPas(PText);
  StrDispose(PText);
end;
Den umgekehrten Weg beschreitet diese Funktion, sie wandelt Windows-ANSI-Text nach DOS-ASCII:

function ANSI2ASCII(AText:string):string;
const MaxLength = 255;
var PText : PChar;
begin
  PText:=StrAlloc(MaxLength);
  StrPCopy(PText,AText);
  {$IFDEF WIN32}
  CharToOEM(PText,PText); {32Bit}
  {$ELSE}
  AnsiToOEM(PText,PText); {16Bit}
  {$ENDIF}
  Result:=StrPas(PText);
  StrDispose(PText);
end;

Wie wandelt man zwischen HTML-Farbcode und einem Delphi-TColor-Wert?

In Delphi hat z.B. die Farbkonstante clRed (also "rot") den hexadezimalen Wert $FF:

clBlue   : $00FF0000
clGreen  : $0000FF00
clRed    : $000000FF
allgemein: $xxBBGGRR
In HTML sind die Bytes für die drei Grundfarben Rot, Grün und Blau aber anders angeordnet:

Rot    : #FF0000
Grün   : #00FF00
Blau   : #0000FF
allgem.: RRGGBB

Die Umwandlung eines Delphi-TColor-Farbwertes in einen HTML-Farbcode funktioniert recht einfach mittels der "ColorToRGB"-Funktion, die Ausgabe muß lediglich passend formatiert werden:

function ColorToHTMLColor(Color: TColor): String;
var
  C: packed record case Integer of
       0: (Int: LongInt);
       1: (B0, B1, B2, B3: Byte);
    end;
  H: Byte;
begin
  // convert negative (SysColor) values like clBtnFace
  C.Int := ColorToRGB(Color);
  // red value of TColor is in byte 0, of HTML in byte 2: swap
  H := C.B0;
  C.B0 := C.B2;
  C.B2 := H;
  // output hex value
  Result := Format('"#%.6x"', [C.Int]);
end; {Michael Winter}
Man kann sich übrigens nur auf eine 216-Farben Palette verlassen, die durch die Farbteilwerte 00, 33, 66, 99, CC und FF in jeweils R, G und B gebildet wird. Benutzt man andere, darf der Browser das mit Gewalt umbiegen und durch eine der Farben in der Palette ersetzen.

Wie wandelt man eine String-Variable in eine PChar-Variable um?

Windows-API-Funktionen benötigen anstelle von String-Variablen immer PChar-Variablen. Wie kann man eine String-Variable in eine PChar-Variable umwandeln?

Man muß dabei zwischen Strings mit definierter Länge und den ab D2 verfügbaren nicht längenbegrenzten HugeStrings unterscheiden.

1. HugeStrings:
Da PChar-Variablen ebenfalls keine feste Länge haben, sondern mit einem #0-Zeichen terminiert werden, geht die Umwandlung von HugeStrings in PChar einfach per Typcasting:

var PC : PChar;
     S : string;

PC:=PChar(S);
2. ShortStrings
Für kurze Strings (also die alten 255Byte-Strings aus TP und D1) funktioniert das Typecasting nicht, da gibt es zwei andere Möglichkeiten:

2a. StrPCopy
Für die beabsichtigte Umwandlung gibt es die Routine "StrPCopy". In diesem Falle muß man Speicher für die PChar-Variable reservieren, da der Inhalt der String-Variablen in eine neue PChar-Variable kopiert wird:

var PC : PChar;
    S  : string;

PC:=StrAlloc(255);
StrPCopy(PC,S);

...mach was mit PC...

StrDispose(PC);
2b. Zeiger übergeben
Eine PChar-Variable ist nichts anderes, als ein Zeiger auf eine Kette von Char-Zeichen, welche mit dem ASCII-Zeichen #0 terminiert ist. Man kann nun der PChar-Variablen einfach einen Zeiger auf das erste Zeichen des Strings im Speicher zuweisen. In diesem Falle muß man keinen neuen Speicher allozieren, man muß aber manuell dafür sorgen, daß der String durch das ASCII-Zeichen #0 abgeschlossen wird:

var PC : PChar;
    S  : string;

S:=S+#0;
PC:=@S[1];
Für die umgekehrte Umwandlung von PChar nach String kann man ebenfalls wieder ein einfaches Typecasting verwenden oder die Funktion "StrPas".

Umwandlung von Dezimalzahlen in andere Zahlensysteme

Die Funktion "NumbToStr" wandelt eine Dezimalzahl in eine Zahl eines beliebigen anderen Zahlensystems um:

type
  TNumbBase = 1..36;

function NumbToStr(Numb: LongInt; Base: TNumbBase): String;
const NumbDigits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
begin
  Result:=EmptyStr;
  while Numb>0 do begin
    Result:=NumbDigits[(Numb mod Base)+1]+Result;
    Numb:=Numb div Base;
  end;
  if Result=EmptyStr then
    Result:='0';
end; {Christian "NineBerry" Schwarz}
Im Parameter "Numb" übergibt man der Funktion die Dezimalzahl, im Parameter "Base" wird die Basis des Zahlensystems übergeben. Als Ergebnis für die Dezimalzahl "100" ergeben sich dann z.B. folgende Umwandlungsergebnisse:

Hexadezimal: 64
Oktal: 144
Dual: 1100100

Umwandlung von Dezimalzahlen in römische Zahlen

Nachdem man mit der o.g. Funktion "NumbToStr" eine Dezimalzahl in alle erdenklichen modernen Zahlensysteme umwandeln kann, fehlt eigentlich nur noch eine Umwandlung in das nicht mehr ganz taufrische römische Zahlensystem. In diesem Zahlensystem stehen folgende Buchstaben für die Zahlenwerte:

I : 1
V : 5
X : 10
L : 50
C : 100
D : 500
M : 1000

Die Funktion "DecToRoman" erledigt die fällige Umwandlung sehr elegant:

function DecToRoman(iDecimal: longint):string;
const
  aRomans: array [ 1..13 ] of string =
   ( 'I', 'IV', 'V', 'IX', 'X', 'XL',
     'L', 'XC', 'C', 'CD', 'D', 'CM', 'M' );
  aArabics: array [ 1..13 ] of integer =
   ( 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 );
var
  i : integer;
begin
  for i := 13 downto 1 do
    while ( iDecimal >= aArabics[i] ) do begin
      iDecimal := iDecimal - aArabics[i];
      Result := Result + aRomans[i];
    end;
end; {Carlos Alberto Longen}
Die umgekehrte Umwandlung von "römisch" nach "dezimal" erledigt die Funktion "RomanToDec" anch einer Idee von Joachim Mohr:

function RomanToDec(iRoman:string):integer;
const
  aRomans: array [ 1..13 ] of string =
   ( 'I', 'V', 'IV', 'X', 'IX', 'L',
     'XL', 'C', 'XC', 'D', 'CD', 'M', 'CM' );
  aArabics: array [ 1..13 ] of integer =
   ( 1, 5, 4, 10, 9, 50, 40, 100, 90, 500, 400, 1000, 900 );
var
  i : integer;

  procedure ConvertDigit(var AText:string; var IntRes:integer;
                         const Rom:string; const Arab:integer);
  var p:integer;
  begin
    Repeat
      p:=Pos(Rom, AText);
      if P>0 then begin
        inc(IntRes, Arab);
        Delete(AText, p, length(Rom));
      end;
    until P=0;
  end; {ConvertDigit}

begin
  Result:=0;
  for i:=13 downto 1 do
    ConvertDigit(iRoman, Result, aRomans[i], aArabics[i]);
end;

Die Anzahl der Stellen vor dem Komma bei einer Fließkommazahl ermitteln

Um zu ermitteln, wie viele Stellen vor dem Komma eine Fließkommazahl hat, ermittelt man deren dekadischen Logarithmus:

uses Math;

function IntDigits(X: Extended): Integer;
begin
   X := log10(Abs(X));
   if X >= 0 then
     Result := Trunc(X) + 1
   else
     Result := 0;
end; {Robert Rossmair}

Wie kann ich einen Link auf eine Internet-Homepage in mein Programm einbauen?

Dazu nimmt man am besten eine TLabel-Komponente (hier: "URLLabel") und gestaltet diese so, daß sie wie ein Link im Browser erscheint (hier ist es die URL der p.i.c.s.-Homepage):

Caption:='http://www.pics-software.de';
Font.Color:=clBlue;
Font.Style:=[fsUnderline];
Cursor:=crHandPoint;
Diese Einstellungen kann man natürlich auch im Objektinspektor zur Entwurfszeit vornehmen. Den Cursorstyle crHandPoint, der aussieht wie die Zeigehand, die auch in Browsern über Links erscheint, gibt es erst ab Delphi 3. Wie man für D1 und D2 selbstgezeichnete Cursor ins Programm einbindet, erfährt man im Kapitel "Applikationen" der FAQ.

Nun muß man nur noch dafür sorgen, daß nach einem Klick auf URLLabel eine Verbindung zur gewünschten URL aufgebaut wird. Also schreibt man in die OnClick.Methode des Labels einen ShellExecute-Aufruf mit der entsprechenden URL. Im Beispiel wird dazu die Beschriftung des Labels hergenommen:

ShellExecute(Application.Handle, 'open',
             PCHar(URLLabel.Caption), nil, nil,
             SW_ShowNormal);
Weitere Tips zum ShellExecute-Befehl gibt es im Kapitel Interaktion mit anderen Programmen der FAQ. Meine Komponente "TSRLabel" bietet bereits fertige Funktionalität, um als Link auf WWW-Adressen oder Mailadressen zu dienen. Man kann sie von meiner Komponentenseite laden.

Wie erkennt man eine offene Internet-Verbindung?

Zuerst einige grundsätzliche Bemerkungen zu diesem Thema, zusammengestellt von Udo Nesshoever:

Diese Frage kann programmtechnisch entgegen manch anderer Behauptungen nicht eindeutig beantwortet werden. Um zu wissen, ob man "online" ist, muss man zuerst definieren, was "online" bedeutet. "Online" kann sein: [Liste nicht vollstaendig]
- Dial-Up Network (DUN): Verbindung wurde per Modem/ISDN gewählt
- LAN: der Rechner ist per Router/Proxy mit dem Internet verbunden
- Nullmodem: es besteht eine Verbindung per NM-Kabel zu einem Rechner, der irgendwie mit dem Internet verbunden ist.
- serielles/paralleles Kabel: (wie Nullmodem)

Test mittels PING:
Meist ist das DUN so konfiguriert, daß bei Anfragen an nicht lokale (IP-)Adressen eine Wählverbindung aufgebaut wird. Dadurch würde das Testen, ob man "online" ist entweder eine Abfrage zum Verbindungsaufbau provozieren, oder aber immer "wahr" zurückliefern, da beim Test eine Verbindung hergestellt würde.

Test auf eine oder mehrere lokale IP-Adressen:
Ein Rechner mit mehreren lokalen IP-Adressen kann auch einfach ein Router zwischen zwei Subnetzen sein, ohne Verbindung zum Internet zu haben.

Test auf DUN-Verbindung:
Eine Verbindung, die mittels DUN hergestellt wurde, kann ebenso ein Fax, eine direkte Modem-Modem-Verbindung, eine PC-Direktverbindung per Kabel o.ä. sein, aber nicht sicher eine Verbindung zum Internet.

Fazit:
Wenn ich weiß, wie der Rechner, für den ich dieses Programm schreibe, die Verbindung zum Internet herstellt, kann ich auf genau diese Verbindung testen und so ziemlich(!) sicher sagen, ob der Rechner nun "online" ist. Eine allgemeingültige Funktion gibt es dafür aber nicht.

Verbindungen über das DFÜ-Netzwerk laufen über die Remote Access Services-API. Eine Delphi-Kapselung dieser RAS-API mit einem Beispielprojekt findet man auf meiner Komponentenseite.

Eine zuverlässige Möglichkeit, eine offene Internetverbindung auszuschließen, ist außerdem die Prüfung der lokalen IP-Adresse des Rechners. Wenn die lokale IP-Adresse "0.0.0.0" ist, besteht keine TCP/IP-Verbindung, also auch keine Internetverbindung. Dazu benutzt man am einfachsten eine beliebige TCP-Komponente. Ein Beispiel für die delphieigene TCP-Komponente:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if TCP1.LocalIp = '0.0.0.0' then
    ShowMessage('Your not connected!');
end;

Alle lokalen IP-Adressen eines Rechners ermitteln

Die Funktion "GetLocalIPs" ermittelt alle aktuellen IP-Adressen im System. Die einzelnen Adressen sind durch Zeilenwechsel getrennt und können so etwa der Text-Eigenschaft eines TStrings-Objekts zugewiesen werden:

function GetLocalIPs: String;
type PPInAddr= ^PInAddr;
var
  wsaData  : TWSAData;
  HostInfo : PHostEnt;
  HostName : Array[0..255] of Char;
  Addr     : PPInAddr;
begin
  Result:='';
  if WSAStartup($0102, wsaData) <> 0 then 
    Exit;
  try
    if GetHostName(HostName, SizeOf(HostName)) <> 0 then 
      Exit;
    HostInfo:= GetHostByName(HostName);
    if HostInfo=nil then
      Exit;
    Addr:=Pointer(HostInfo^.h_addr_list);
    if (Addr=nil) or (Addr^=nil) then
      Exit;
    Result:=StrPas(inet_ntoa(Addr^^));
    inc(Addr);
    while Addr^ <> nil do begin
      Result:=Result+^M^J+StrPas(inet_ntoa(Addr^^));
      inc(Addr);
    end;
  finally
    WSACleanup;
  end;
end;

Warum bleibt nach Programmende manchmal ein leerer Button in der Windows-Taskleiste zurück?

Manchmal verbleibt nach dem Beenden eines mit D3 geschriebenen Programms anstelle des Buttons für das Programm ein leerer Button in der Windows-Taskleiste, der erst nach einem Mausklick verschwindet. Woran liegt das und wie kann man das verhindern?

Peter Haas hat nach langwierigen Versuchen folgendes herausgefunden: Der leere Button in der Taskleiste bleibt zurück, wenn das Hauptformular beim Beenden den Bildschirm mindestens vollständig ausfüllt. (Es darf auch größer sein). Bereits ein Pixel, daß auf irgendeiner Seite hervorsieht und der leere Button tritt nicht auf. Nachvollziehbar bereits mit einem völlig leeren Formular (und natürlich Delphi 3).

Eine daraus resultierende Lösung ist es, das Hauptformular vor dem Beenden zu minimieren. Dazu kann man in der OnClose-Methode des Hauptformulars folgende Zeile einzufügen:

ShowWindow(Handle,SW_SHOWMINIMIZED);
Angeblich tritt der gleiche Fehler auch stellenweise bei D2-Programmen auf, von D4-Programmen wurde dieses Phänomen bisher noch nicht berichtet.

Wie man die Bedeutung von Fehlermeldungen richtig interpretiert

Manchmal ist es etwas schwierig herauszufinden, warum ein Laufzeitfehler aufgetreten ist und was eine Fehlernummer bedeutet. Peter Haas hat dazu einige Erklärungen zusammengestellt:

Man muß unterscheiden zwischen den Betriebssystemfehlern und den Laufzeitfehlern der Runtime Library. Vor Windows hat Borland die immer noch mitdokumentiert, inzwischen sind es zu viele geworden.

Die delphispezifischen Fehler findet man in der Delphi-Hilfe, Stichworte sind: 'Fehler', 'Laufzeitfehler', 'Schwere Fehler', 'I/O-Fehler'.

I/O-Fehler tauchen bei Ein-Ausgabeoperation durch Laufzeitfunktionen auf, entweder in IOResult oder EInOutError (das ist abhängig vom Compilerschalter I, siehe Hilfe unter $I). Die I/O-Fehler haben die Fehlernummern 100-149, real existieren allerdings nur 100-106.

Schwere Fehler sind solche, wie Division durch Null (200) oder der Aufruf von abstrakten Methoden (210), sehr beliebt ist auch der Fehler 216 (Zugriffsverletzung). Mit den Nummern wird man nur konfrontiert, wenn die Exceptionbehandlung noch nicht oder nicht mehr aktiv ist, also z.B. im initialization oder finalization-Bereichen von Units.

Und dann findet sich in der Delphi-Hilfe noch das Stichwort Betriebssystemfehler:
<Orakel> Alle Fehler mit Ausnahme der I/O-Fehler und der schweren Fehler werden mit der von der Win32-Fehlerfunktion GetLastError zurückgegebenen Fehlercode gemeldet. Die Werte des Fehlercodes hängen vom jeweiligen Betriebssystem ab, aber in der Win32-Dokumentation finden Sie eine Liste dieser Werte. </Orakel>

Ich will hier nur ergänzen, das auch Fehler, die vom Betriebssystem kommen, während der Ein/Ausgabe als I/O-Fehler ausgegeben werden, also u.U. auch als EInOutError.

Bei den Betriebssystemfehlern muß man nur wissen, das die Win32-Hilfe in englisch ist und man demzufolge dort nach dem Stichwort: 'error' suchen sollte. Bei 'error codes' wird man fündig.

Dort gibt es eine numerische und eine alphabetisch sortierte Liste. Erst in der numerischen Liste suchen:

16L ERROR_CURRENT_DIRECTORY

Jetzt können wir im alphabetischem Teil nachschlagen:

ERROR_CURRENT_DIRECTORY: The directory cannot be removed.

Die Fehlerbezeichnungen stehen übrigens als Konstanten in Windows.pas, man muß also in seinen Quelltexten nicht mit Nummern hantieren.

Wenn man den Fehler einer WinAPI-Funktion selber mit GetLastError abfragt, und einer Fehlermeldung per Exeption ausgeben möchte, kann man dazu RaiseLastWin32Error benutzen. Eines sollte man beachten. Die WinAPI-Funktionen setzen in der Regel die interne Variable zu GetLastError nicht zurück. Man muß also den Rückgabewert der Funktion auswerten, um zu erfahren, ob überhaupt ein Fehler vorliegt oder mit SetLastError(0) die Variable auf NO_ERROR oder ERROR_SUCCESS setzen.

Wenn man selber keine Fehlermeldungen erwartet, dann kann man das Auswerten des Rückgabewertes und Werfen der Exception auch von Win32Check erledigen lassen. Wenn man sich RaiseLastWin32Error ansieht, findet man dort auch eine Lösung um einen Windows-Fehler in Klartext zu erhalten:

function SysErrorMessage(ErrorCode: Integer): string;

Diese Funktion ist ebenfalls in SysUtils enthalten.

Beschreibung der BDE-Fehlernummern

Die Bedeutung der BDE-Fehlernummern kann man in der Datei "bde.int" im Delphi-Doc-Verzeichnis nachsehen. Übersichtlicher sind sie aber in dieser Liste zusammengefasst, deren Rohfassung mir Michael Tonner zur Verfügung gestellt hat.

Compiler-Fehlermeldung (Typ '<name>' benötigt Finalization) bei strings in records

Ich habe einen Record-Typen erstellt, der unter anderem ein Feld vom Typ "string" enthält. Nun erhalte ich beim kompilieren eine Fehlermeldung ("Typ '<name>' benötigt Finalization"). Was hat das zu bedeuten?

Der Typ "string" ist ab D2 ein Zeiger auf einen AnsiString mit dynamischer Länge. Die Variablen in einem Record müssen aber eine definierte Größe haben, auch um sie z.B. in eine typisierte Datei schreiben zu können. Man muß einen string-Typen mit fester Länge definieren, z.B. "string[255]". Alternativ dazu kann man in diesem Falle auch den Typen "shortstring" verwenden, der genau dem 255 Zeichen langen string-Typen aus Pascal und D1 entspricht.