Applikationen

Ein Programm ohne sichtbares Fenster und ohne Taskbareintrag starten

Wie kann ich verhindern, daß Delphi automatisch ein Fenster anzeigt? Ich möchte gerne, daß meine Anwendung unsichtbar im Hintergrund läuft.

Um ein Programm ohne sichtbares Fenster zu starten, muß man verhindern, daß das Hauptformular automatisch nach dem Programmstart gezeigt wird. Dazu macht man folgenden Eintrag im Projektquelltext nach createform:

Application.ShowMainForm:= false;
Um den Taskbar-Eintrag des Programms zu verstecken, muß man das Applikationsfenster (nicht die MainForm!) unsichtbar machen:

procedure TMainForm.FormShow(Sender: TObject);
var Owner : HWnd;
begin
  Owner:=GetWindow(Handle,GW_OWNER);
  ShowWindow(Owner,SW_HIDE);
end;
Wie kann ich verhindern, daß nach einem Minimize die Applikation wieder in der Taskbar erscheint?

procedure WMSysCommand(var Message: TWMSysCommand); message
WM_SysCommand;

procedure TMainForm.WMSysCommand(var Message: TWMSysCommand);
begin
  if Message.CmdType and $FFF0 = SC_MINIMIZE then
    Hide
  else
    inherited;
end;
Um ein Programm auch im Taskmanager zu verstecken (der mit Strg+Alt+Entf aufgerufen wird), benutzt man die Funktion RegisterServiceProcess, diese Funktionen stehen allerdings unter Windows NT nicht zur Verfügung:

function RegisterServiceProcess(dwProcessID, dwType: DWord): DWord;stdcall;

function RegisterServiceProcess; external 'KERNEL32.DLL' name 'RegisterServiceProcess';

RegisterServiceProcess(0,1); //zum verstecken!
RegisterServiceProcess(0,0); //zum anzeigen ! [Marco Gessinger]
Schließlich kann man den Task-Manager auch ganz stillegen, um z.B. zu verhindern, daß das eigene Programm verlassen wird. Dieser Tip steht hier nur der Vollständigkeit halber. Die Verwendung dieser Funktionen halte ich persönlich für bedenklich und auch nicht im Sinnen eines Multitasking-Betriebssystems.

{Task-Manager disablen}
SystemParametersInfo(SPI_SCREENSAVERRUNNING,1,Nil,0);

{Task-Manager enablen}
SystemParametersInfo(SPI_SCREENSAVERRUNNING,0,Nil,0);
Im Abschnitt "Formulare" der Delphi Fundgrube erfährt man, wie man auch für untergeordnete Dialogfenster Taskbareinträge anzeigen kann.

Ein TrayIcon in der Taskbar Notification Area (neben der Systemuhr) anzeigen

Wie erreicht man, daß anstelle des normalen Programm-Buttons in der Windows-Taskbar ein TrayIcon in der Taskbar Notification Area neben der Windows.Systemuhr angezeigt wird?

Dafür gibt es die API-Funktion Shell_NotifyIcon() und den Daten-Record TNotifyIconData. Und so bindet man das ganze in ein Programm ein (Beispiel von Heino Tiedemann):


uses
  ShellAPI;

const
  WM_TASKABAREVENT = WM_USER+1; //Taskbar message
[...]

type
  TMainForm = class(TForm)
[...]
private
    { Private-Deklarationen }
    procedure TaskbarEvent(var Msg: TMessage);
      Message WM_TASKABAREVENT;
[...]

{Message-Prozedur für das TrayIcon}
procedure TMainForm.TaskbarEvent(var Msg: TMessage);
var Point : TPoint;
begin

  { Die WM_TaskbarEvent-Message "Msg" gibt in Msg.LParam
    das genaue Ereignis an. Msg.LParam kann folgende Werte für
    Mausereignisse annehmen:

    WM_MouseMove
    WM_LButtonDown
    WM_LButtonUp
    WM_LButtonDblClk
    WM_RButtonDown
    WM_RButtonUp
    WM_RButtonDblClk }

  { Eventhandler-Beispiele von Robert Fischer: }
  case Msg.LParam of
    WM_LBUTTONDBLCLK:
      begin
        //Mach etwas nach einem Doppelklick...
      end;
    WM_LBUTTONUP:
      begin
        //Mach etwas nach einem Linksklick...
      end;
    WM_RBUTTONUP:
      begin
        // Rechtsklick
        // Diese Zeile ist wichtig, damit das PopupMenu korrekt
        // wieder geschlossen wird:
        SetForegroundWindow(Handle);
        // PopupMenu anzeigen:
        GetCursorPos(Point);
        PopupMenu1.Popup(Point.x, Point.y);
        //oder ohne Variable Point:
        //PopupMenu1.Popup(Mouse.CursorPos.x, Mouse.CursorPos.y);
      end;
  end;
end;

{TrayIcon mit dem Hauptformular erzeugen}
procedure TMainForm.FormCreate(Sender: TObject);
var
  NotifyIconData: TNotifyIconData;
begin
  Fillchar(NotifyIconData,Sizeof(NotifyIconData),0);
  NotifyIconData.cbSize := Sizeof(NotifyIconData);
  NotifyIconData.Wnd    := Handle;
  NotifyIconData.uFlags := NIF_MESSAGE
    or NIF_ICON
    or NIF_TIP;
  NotifyIconData.uCallbackMessage := WM_TASKABAREVENT;
  NotifyIconData.hIcon := Application.Icon.Handle;
  NotifyIconData.szTip := 'Hinweistext für das TrayIcon';
  Shell_NotifyIcon(NIM_ADD, @NotifyIconData);
end;

{TrayIcon mit dem Hauptformular zerstören}
procedure TMainForm.FormDestroy(Sender: TObject);
var
  NotifyIconData: TNotifyIconData;
begin
  FillChar(NotifyIconData,Sizeof(NotifyIconData),0);
  NotifyIconData.cbSize := Sizeof(NotifyIconData);
  NotifyIconData.Wnd    := Self.Handle;
  NotifyIconData.uFlags := NIF_MESSAGE
    or NIF_ICON
    or NIF_TIP;
  NotifyIconData.uCallbackMessage := WM_TASKABAREVENT;
  NotifyIconData.hIcon := Application.Icon.Handle;
  NotifyIconData.szTip := 'Punkt';
  Shell_NotifyIcon(NIM_DELETE, @NotifyIconData);
end;
Wie man den normalen Taskbar-Eintrag des Programms versteckt, findet man im ersten Thema dieses FAQ-Kapitels.

Untergeordnete Programm-Fenster in der Windows-Taskbar anzeigen updated

Wie kann ich erreichen, daß ein untergeordnetes Fenster einer Delphi-Anwendung in der Taskbar von Win95/NT erscheint?

Mit CreateParams. Im folgenden Sourcecode wird das besagte Fenster einfach zum Kindchen des Desktopfensters gemacht:


type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    private
      procedure CreateParams(var Params : TCreateParams); override;
  end;

var
  Form1 : TForm1;

implementation

{$R *.DFM}

procedure TForm1.CreateParams(var Params : TCreateParams);
begin
  inherited CreateParams(Params);
  Params.WndParent := GetDesktopWindow;
  Params.Caption:='Title';
end;
Eine etwas weniger drastische Variante schlägt Michael Winter vor:

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
end;
Für das Hauptfenster gibt es keinen Eintrag, dafür gibt es einen für die Anwendung. Wie du weißt, werkelt in jedem Delphi-Programm ein unsichtbares Fenster, das der Programmierer als Application kennt.

Üblicherweise hat die Application einen Taskbareintrag, und kein Formular hat einen. Wird nun die Application per Taskbarbutton aktiviert, dann wird eben nicht das Haupformular nach vorne gebracht, sondern das zuletzt aktive.

Abhilfe: Auch dem MainForm per obiger Methode einen Taskbarbutton verpassen. Nun ist nur noch der der Anwendung zuviel. Wie man den beseitigst, liest man im ersten Artikel dieses Themanabschnitts.

Verhindern, daß ein Programm mehrmals gestartet wird

Wie kann ich verhindern, daß mein Programm 2 mal (zur gleichen Zeit) gestartet wird ??

Unter 32Bit-Windows bedient man sich eines Mutex. Binde folgende Unit ein, du musst sie einfach nur deinem Projekt hinzufügen:


unit NichtDoppeltStarten;

interface

implementation

uses windows,Dialogs,sysutils;

var mHandle: THandle;    // Mutexhandle

Initialization
  mHandle := CreateMutex(nil,True,'xxxxx'); 
  // 'xxxxx' Der Anwendungsname ist hier einzutragen
  if GetLastError = ERROR_ALREADY_EXISTS then begin   
    // Anwendung läuft bereits
    {showMessage('Anwendung läuft bereits!!!!!');}   
    // Wenn du deine Meldung willst, mach die Klammern weg
    Halt;
  end;

finalization   // ... und Schluß
  if mHandle <> 0 then
    CloseHandle(mHandle)
end. {Dieter Hirt}
In Delphi 1 kann man das mit der Variablen "hPrevInst" aus der Unit "System" lösen. Diese Abfrage sollte man dazu weit vorne in der .DPR-Projektdatei einbauen:

if hPrevInst<>>0 then begin
  {showMessage('Anwendung läuft bereits!!!!!');}
  Exit;
end; {Marian Aldenhövel}
Das funktioniert offenbar unter NT nicht immer. Ob es funktioniert, scheint damit zusammenzuhängen, ob die 16-Bit-Applikationen jede in einer eigenenBox gestartet werden oder nicht.

Wie man die beim Start der zweiten Instanz übergebenen Parameter an die erste (laufende) Instanz der Anwendung weiterreicht, steht in diesem Text.

Wie erreiche ich, daß mein Programm eine Pause macht?

Generell gibt es da drei Möglichkeiten.

1. Sleep
Die einfachste funktioniert aber nur in 32Bit-Programmen und legt das Programm für die angegebene Zeit komplett lahm:


const Millisekunden = 1000;

Sleep(Millisekunden);
2. GetTickCount

procedure Delay(ATime:Integer);
var
  Start : Integer
begin
  Start:=GetTickCount;
  repeat
    Application.ProcessMessages;
  until GetTickCount-Start > ATime;
end;
3. Timer
Im OnTimer-Ereignus von PauseTimer wird PrgPause auf False gesetzt:

procedure Delay(Millisekunden : integer);
begin
  PrgPause := True;
  PauseTimer.Intervall := Millisekunden;
  PauseTimer.Enabled := True;
  While PrgPause Do
    Application.ProcessMessages;
  PauseTimer.Enabled := False;
end;

Eigene Cursor in Programme einbinden

Die Methode, die ich verwendet habe, ist aus einem Buch und hat prima funktioniert:

1. Als Namen fuer die Cursor in der .res-Datei werden Zahlen verwendet.
(Im Beispiel 13 und 14)
2. Ganz wichtig: Ressourcen-Datei einbinden.
3. Konstanten definieren, z.B.:
const crMyCursor = 1; crNochnCursor = 2;
4. Cursor laden:


procedure TForm1.FormCreate(Sender: TObject);
begin
  Screen.Cursors[crMyCursor]:=
    LoadCursor(HInstance,makeIntResource(13));
  Screen.Cursors[crNochnCursor]:=
    LoadCursor(HInstance,makeIntResource(14));
  Screen.Cursor:=crMyCursor;
end;
Diese Methode funktioniert aber nur mit dem aktuellen Formular, wie kann man den Cursor global verändern?

[Pilif:] Ich habe das Problem folgendermassen gelöst:

1) Zwei Variablen vom Typ THCursor mit genügendem Gültigkeitsbereich (Formvariablen),

2) Cursor einschalten:


type
  TForm1 = class(TForm)
  ..
  private
    var fic, foic : THCursor;

fic:=LoadCursor(hinstance, makeIntResource(1)); //Cursor laden
foic:=GetCursor; //Backup
SetSystemCursor(fic, OCR_NORMAL); //Cursor austauschen
Dies lädt den Cursor mit der ID 1 von den Resourcen, macht ein Backup des alten Cursors (notwenig. Windows tut das nicht!) und tauscht den Standard-Mauspfeil gegen das neue Exemplar aus.

3) Cursor ausschalten:


SetSystemCursor(foic, OCR_NORMAL);
Dies stellt den Ur-Cursor vom Backup wieder her.

Wie kann ich Videos, Bilder oder Texte als Resourcen in ein Programm einbinden?

Hier ein Lösungsvorschlag für eine Videodatei:

Zuerst bringen wir mal das AVI in eine RES-Datei:

1) Schnapp dir Notepad
2) Schreibe folgendes in die Datei "MeinVideo RCDATA MEINFILM.AVI"
3) Speichere das ganze als eine RC-Datei ab
4) Nun führe folgenden Befehl aus : BRC32 -r datei.rc
5) Voilà du besitzt nun eine datei.res in der sich das AVI befindet.
    Diese kannst du nun mit {$R datei.res} in deine EXE einbinden

6) Kleine Verschnauf/Zigarettenpause
7) Mach ein neues Projekt
8) Binde die neue RES-Datei mit ein
9) Platziere eine TAnimate-Komponente auf deinem Formular
10) Gehe nun in die Routine in der das Video abgespielt werden soll
        und schreibe:
            Aminate1.ResName := 'MeinVideo';
            Animate1.Play;

11) Freuen, wenns klappt

Für andere Resourcen muß folgendes in der .RC-Datei stehen:

Cursor : "CursorName Cursor Dateiname.cur"
Icons  : "IconName Icon Dateiname.ico"
Strintables :
    STRINGTABLE DISCARDABLE
    BEGIN
      10001 "Explorer Fenster"
      [..]
    END

In diesem Beispielprojekt wird ausführlich beschrieben, wie man Sounds in Form zweier Wavedateien in die EXE-Datei kompiliert.

Wie man einen formatierten (RTF-)Text als Resource in ein Programm einbindet

Sören Mühlbauer hat dazu folgende Vorgehensweise aufgezeigt: Mach dir eine Datei z.b. MyRTFRes.RC. Inhalt:

MYFIRSTRTF RCDATA MYRTF1.RTF
MYSECONDRTF RCDATA MYRTF2.RTF

Nun erzeugst Du daraus mittels des Resource Compileres eine .res-Datei :

brcc32 MyRTFRes.rc

Dann nimmst Du diese mittels {$R MyRTFRes.res} in dein Programm auf. Es folgt ein Beispiel, wie Du den RTF-Text nun auliest und in einem TRichEdit-Control anzeigst:


var 
  TempStream : TResourceStream;
begin
  TempStream := TResourceStream.Create(hInstance, 'MyFirstRTF', RT_RCDATA);
  try
    TempStream.Position := 0;
    RichEdit1.Lines.LoadFromStream(TempStream);
  finally
    TempStream.Free;
  end;
end;

Den Ordner der Programmdatei (*.exe) ermitteln

Wie ermittle ich den Ordner, in dem sich die EXE-Datei meines Programmes auf dem jeweiligen Rechner befindet?

In der Eigenschaft "ExeName" des TApplication-Objekts ist der komplette Dateiname der Programmdatei inklusive Pfad gespeichert. Um nur den Ordner der EXE-Datei zu erhalten, kann man diesen mit ExtractFilePath isolieren:


procedure TMainForm.FormCreate(Sender: TObject);
var ProgrammOrdner : string;
begin
  ProgrammOrdner:=ExtractFilePath(Application.ExeName);
end;

Die eigene Anwendung in den Vordergrund bringen

Wie kann ich das Programmfenster meiner eigenen Anwendung in den Vordergrund bringen und ihm den Eingebafokus verpassen? Ab Windows 98 blinkt ja normalerweise nur noch der Taskbar-Eintrag, wenn ich die Methode "Application.BringToFront" aufrufe.

Man muß vor dem In-den-Vordergrund-Bringen der Anwendung den Eingabfokus auf die eigene Applikation setzen. Das geschieht in dieser Prozedur von Michael Winter mit einem Aufruf der API-Funktion "AttachThreadInput". Anschließend wird dann das Applikations-Fenster per "SetForegroundWindow" nach vorne gebracht. Sollte dies mißlingen ist die Prozedur so freundlich, den Eingabefokus wieder an das ursprüngliche Vordergrundfenster zurückzugeben:


procedure ShowMe;
var
  Th1, Th2: Cardinal;
begin
  Th1 := GetCurrentThreadId;
  Th2 := GetWindowThreadProcessId(GetForegroundWindow, nil);
  AttachThreadInput(Th2, Th1, true);
  try
    SetForegroundWindow(Application.Handle);
  finally
    AttachThreadInput(Th2, Th1, false);
  end;
end; {Michael Winter}