Autostartverknüpfung zur Laufzeit. Wie?

Hallo,
ich bastle so ein wenig mit Delphi 2005 Personal.
Nun bin ich auf ein kleines Problem gestoßen was ich leider nicht über die Onlinehilfe der Programmierumgebung lösen konnte.

Wie kann ich eine Autostartverknüpfung zur Laufzeit erstellen. (Option mit Windows Starten z.B.) Und das ganze soll vom Nutzer natürlich auch per Mausklick rückgängig gemacht werden können.
Da ich aber nicht wirklich weiss wie die Registry arbeitet lasse ich davon lieber die Finger und will einfach nur eine Verknüpfung im Autostartmenü, die auch ein weniger erfahrener Nutzer notfalls von Hand entfernen kann.

Kann mir jemand in einfachen Worten erklären wie ich das am Besten anstelle? Vielleicht gibt es ja auch ein kleines Beispielprogramm was ich „zerlegen“ kann um es zu kapieren.

Danke schon mal
Mike

PS. Allen die mir hier schon ganz viel geholfen habe ein großes Dankeschön. So macht Softwarebasteln richtig Spaß.

Und allen Wer weiß was Nutzern ein paar schöne Feiertage.

Hi Mike,

Wie kann ich eine Autostartverknüpfung zur Laufzeit erstellen.
(Option mit Windows Starten z.B.)

Durch Ermitteln des Autostart-Startmenüordners des Benutzers und das Anlegen einer .lnk-Datei über COM in diesem Ordner. Klingt komplizierter als es ist :wink:

Da ich aber nicht wirklich weiss wie die Registry arbeitet
lasse ich davon lieber die Finger und will einfach nur eine
Verknüpfung im Autostartmenü, die auch ein weniger erfahrener
Nutzer notfalls von Hand entfernen kann.

Schade, dabei ist es über die Registrierung viel sauberer und wesentlich schneller gelöst.

uses [...], Registry, {für den AutoStart-Link} ComObj, ActiveX, ShlObj;
[...]

type
 TForm1 = class(TForm)
[...]
 public
 {Direkter Eintrag in die Registrierung}
 function EnableAutoStartRegistry(AllUsers, Enable: Boolean): Boolean;
 {Erstellen/Löschen eines Autostart-Links}
 function EnableAutoStartLink(AllUsers, Enable: Boolean): Boolean;
 end;

[...]

implementation

function TForm1.EnableAutoStartRegistry(AllUsers, Enable: Boolean): Boolean;
 var
 R: TRegistry;
 KeyName, KeyValue: string;
begin
 Result := False;
 KeyName := 'MyProgram.exe';
 KeyValue := 'C:\MyProgram.exe';
 R := TRegistry.Create;
 if AllUsers then
 R.RootKey := HKEY\_LOCAL\_MACHINE
 else
 R.RootKey := HKEY\_CURRENT\_USER;
 if R.OpenKey('Software\Microsoft\Windows\CurrentVersion\Run', False) then
 begin
 if Enable then
 begin
 R.WriteString(KeyName, KeyValue);
 Result := R.ValueExists(KeyName);
 end else
 begin
 if R.ValueExists(KeyName) then
 Result := R.DeleteValue(KeyName)
 else
 Result := True;
 end;
 R.CloseKey;
 end;
end;

Kann mir jemand in einfachen Worten erklären wie ich das am
Besten anstelle? Vielleicht gibt es ja auch ein kleines
Beispielprogramm was ich „zerlegen“ kann um es zu kapieren.

Einen Link zu erstellen ist etwas komplizierter, das erfordert wie gesagt ein bisschen COM-Programmierung, damit - einfach gesagt - Windows selbst den Link einrichtet. Du erstellst ein COM-Objekt, das für die Verknüpfungen zuständig ist, und sprichst es dann über die von den Schnittstellen bereitgestellten Funktionen an.

Um den Autostart-Ordner des Startmenüs zu ermitteln (dort, wo der Link = die .lnk-Datei dann drinnen liegen soll) muss die Registrierung gelesen werden, da steht der Pfad drin. Der Pfad zum Autostart-Ordner für alle Benutzer ist im Schlüssel HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders, Wert ‚Common Startup‘ gespeichert, der für den aktuellen Benutzer in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders im Wert ‚Startup‘. Wenn Du Regedit startest und die Schlüssel (die wie Ordner aussehen) sowie die darin befindlichen Werte (die wie Dateien in jenen Ordnern aussehen) anschaust, kannst Du das leichter nachvollziehen. Auch das obige Beispiel dürfte dann klarer werden, es macht nichts weiter als einen Wert im Run-Schlüssel anzulegen oder zu löschen, für den aktuellen oder für alle Benutzer. Die Registrierung ist nichts wovor man sich fürchten müsste - solange Du nichts kritisches löschst oder änderst, wovon Du nicht weißt, was es ist :smile:

function TForm1.EnableAutoStartLink(AllUsers, Enable: Boolean): Boolean;
var
 R: TRegistry;
 LinkName, LinkPath, LinkFileName, AutoStartPath: string;
 IObject: IUnknown;
begin
 Result := False;
 LinkName := 'MyProgram';
 LinkPath := 'C:\MyProgram.exe';
 R := TRegistry.Create;
 if AllUsers then
 R.RootKey := HKEY\_LOCAL\_MACHINE
 else
 R.RootKey := HKEY\_CURRENT\_USER;
 if R.OpenKey('Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders', False) then
 begin
 if AllUsers then
 AutoStartPath := R.ReadString('Common Startup')
 else
 AutoStartPath := R.ReadString('Startup');
 LinkFileName := AutoStartPath + '\' + LinkName + '.lnk';
 if Enable then
 begin
 IObject := CreateComObject(CLSID\_ShellLink);
 with IObject as IShellLink do
 begin
 SetDescription(PChar(LinkName));
 SetPath(PChar(LinkPath));
 SetWorkingDirectory(PChar(ExtractFilePath(LinkPath)));
 end;
 if FileExists(LinkFileName) then
 DeleteFile(LinkFileName);
 with IObject as IPersistFile do
 Save(PWChar(WideString(LinkFileName)), False);
 Result := FileExists(LinkFileName);
 end else
 begin
 if FileExists(LinkFileName) then
 DeleteFile(AutoStartPath + '\' + LinkName + '.lnk');
 Result := not FileExists(LinkFileName);
 end;
 end;
end;

Der Einfachheit halber habe ich beide Funktionen mit den gleichen Parametern gemacht. ‚AllUsers‘ gibt an, ob der Autostart für alle Benutzer oder nur den aktuellen gemacht werden soll, ‚Enable‘ gibt an, ob der Autostart eingefügt oder entfernt werden soll. Das Result der Funktion ist True, wenn alles glatt gelaufen ist, sonst False. Zum Test richten die Funktionen gleichermaßen einen Autostart für ein Programm namens ‚MyProgram‘ an, welches ‚C:\MyProgram.exe‘ ist.
Ich hoffe die Funktionen sind anhand der Variablennamen aussagekräftig genug, sonst frag ruhig nach.

Und das ganze soll vom Nutzer natürlich auch per Mausklick
rückgängig gemacht werden können.

Die Implementation mit Checkboxen/Mausklicks sei Dir überlassen, dürfte mit den Funktionen möglich sein.

Und allen Wer weiß was Nutzern ein paar schöne Feiertage.

Ebenso :smile:

Schönen Gruß,
Rudy

Schade, dabei ist es über die Registrierung viel sauberer und
wesentlich schneller gelöst.

Vielleicht hast du recht und ich sollte mich einfach mal mit der Registry beschäftigen. Wenn ich bei der Programmiererei bleibe, komme ich vermutlich eh nicht drumherum :wink:

KeyName := ‚MyProgram.exe‘;
KeyValue := ‚C:\MyProgram.exe‘;

Für KeyValue müßte ich ja wissen, in welche Ordner „MyProgramm.exe“ installiert wurde. Bisher war mir das egal da ich so programmiert habe, das sich alles im aktuellen Ordner abspielt.
Wie bekomme ich den genauen Pfad heraus ?
Das kann ich sicher auch für andere Projekte gut brauchen.

Danke schon mal für deine bisherige Hilfe. Ich werde mich damit bald in Ruhe auseinandersetzen um es nicht nur abzuschreiben sondern auch zu verstehen.

Tschüss
Mike

Hallo Mike,

Vielleicht hast du recht und ich sollte mich einfach mal mit
der Registry beschäftigen. Wenn ich bei der Programmiererei
bleibe, komme ich vermutlich eh nicht drumherum :wink:

Wenn man unter Windows ernsthaft programmiert, sollte man schon in Erfahrung bringen, wie die Registrierung zu nutzen ist. Früher nahm man ja .ini-Dateien um z.B. irgendwelche programmspezifischen Einstellungen zu speichern, Microsoft möchte das alles lieber in der Registrierung in HKEY_LOCAL_MACHINE|HKEY_CURRENT_USER\Software\\ stehen sehen :smile: Ist auch irgendwie ordentlicher, auch wenn sich über ‚was ist nun besser‘ streiten lässt. Gut an der Registrierung ist, dass sie übersichtlicher ist und sich die Einstellungen leichter vom aktuellen Windows-Benutzer abhängig ablegen lassen.
Gut an ini-Dateien ist, dass sie benutzerunabhängig sind und die Einstellungen nicht nach einer Neuinstallation verloren sind. Beides kann aber auch ein Nachteil sein, wenn man das nicht möchte.

COM-Programmierung ist auch keine Zauberei, wenn Du interfaces http://www.dsdt.info/grundlagen/sprache/interfaces.php benutzen kannst, dann wäre der nächste logische Schritt, sich das mal anzusehen: http://delphi.about.com/od/comoleactivex/OLE_COM_DCO…

Für KeyValue müßte ich ja wissen, in welche Ordner
„MyProgramm.exe“ installiert wurde.

Richtig.

Bisher war mir das egal da
ich so programmiert habe, das sich alles im aktuellen Ordner
abspielt.
Wie bekomme ich den genauen Pfad heraus ?

Den Pfad des aktuellen Programms? Mit ExtractFilePath(Application.ExeName)).

Danke schon mal für deine bisherige Hilfe.

Gerne.

Ciao,
Rudy

Hallo Rudy,
ich habe das mit dem Autostarteintrag über die Registry hinbekommen und ich finde das auch ziemlich gut.
Da geht es doch sicher auch das ich andere Programmmerkmale (in meinem speziellen Fall die Fensterposition) in der Registry speichere und bei Programmstart auslese.
Bisher habe ich das über eine INI Datei gelöst. Aber wenn ich nun schon mal dabei bin ist das sicher eine gute Möglichkeit mich langsam an die Registry ranzutasten.

Kannst du mir da evtl. auch einen Tipp geben ?

Danke schon mal
Mike

Fensterposition in der Registrierung speichern
Hi Mike,

ich habe das mit dem Autostarteintrag über die Registry
hinbekommen und ich finde das auch ziemlich gut.

Schön.

Da geht es doch sicher auch das ich andere Programmmerkmale
(in meinem speziellen Fall die Fensterposition) in der
Registry speichere und bei Programmstart auslese.

Genau dafür ist die Registrierung perfekt geeignet.

Bisher habe ich das über eine INI Datei gelöst. Aber wenn ich
nun schon mal dabei bin ist das sicher eine gute Möglichkeit
mich langsam an die Registry ranzutasten.

Sicher nicht verkehrt, zudem wird das Programm dann für mehrere Benutzer auf derselben Maschine leichter personalisiert.

Kannst du mir da evtl. auch einen Tipp geben ?

Sowas habe ich auch in manchen Programmen eingebaut, viele finden es in der Tat angenehm, das Fenster nach erneutem Öffnen wieder ‚an seinem Platz‘ vorzufinden. Probleme gibts nur, wenn einer mit Laptop einen 2.Bildschirm angeschlossen hat, das Fenster auf diesem schließt und daheim dann ohne 2.Bildschirm öffnet - aber das ist eine andere Geschichte :smile:

Um das für mehrere Fenster einer Applikation zu realisieren, musst Du eine Entscheidung treffen, und zwar ‚wie identifiziere ich mein Fenster‘. Da gibt es mehrere Möglichkeiten:

  1. Du orientierst dich am Titel des Fensters (Self.Caption), ist aber schlecht bei mehrsprachigen Applikationen
  2. Du verwendest den Tag des Fensters (Self.Tag), d.h. Du weist jedem Fenster im Objektinspektor oder zur Laufzeit eine eigene Nummer zu. Das sieht zwar in der Registrierung kryptisch aus, aber Hauptsache Du weißt, was die Tags bedeuten - ganz gut bei großen Projekten.
  3. Du verwendest den Namen des Fensters (Objektname = Self.Name), das ist dann auch einfacher für Dich mit Regedit zu lesen. Im Beispiel habe ich diese Möglichkeit verwendet.

Dann bleibt eigentlich nur noch das Speichern und Laden der Fensterposition. Um nicht zuviele Werte zu schreiben, würde ich pro Fenster einen Wert in den Schlüssel ‚Dialog Positions‘ (willkürlich gewählter Name) schreiben, in der Form einer Komma-getrennten Liste (Left,Top,Width,Height).
Gespeichert wird beim Schließen des Fensters (im OnClose), geladen im OnShow. Diese Liste speicherst Du z.B. einem Formatierten String (Format) oder durch händisches Zusammensetzen in der Registrierung.
Beim Laden spaltest Du sie dann auf, ich hab das mit einer TStringList gemacht (sicher nicht das performanteste, aber ist schneller gemacht und es sind ja nur 4 Werte) und weist die Werte der Form zu.
Die Ereignisse OnClose und OnShow der Form musst Du natürlich ausprogrammieren bzw. wenn Du das Beispiel unterhalb verwendest im Objektinspektor von Hand auf die vorhandenen Methoden zuweisen.

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, Registry, StrUtils;

type
 TForm1 = class(TForm)
 procedure FormShow(Sender: TObject);
 procedure FormClose(Sender: TObject; var Action: TCloseAction);
 private
 public
 function SaveWindowBounds: Boolean;
 function LoadWindowBounds: Boolean;
 end;

var
 Form1: TForm1;

implementation

{$R \*.dfm}

{ TForm1 }

const
 VENDORNAME = 'Mike';
 APPNAME = 'MikesApp';

function TForm1.LoadWindowBounds: Boolean;
 var
 R: TRegistry;
 BoundsList: TStringList;
begin
 Result := False;
 R := TRegistry.Create;
 try
 R.RootKey := HKEY\_CURRENT\_USER;
 if R.OpenKey('Software\' + VENDORNAME + '\' + APPNAME + '\Dialog Position', False) then
 begin
 if R.ValueExists(Self.Name) then
 begin
 BoundsList := TStringList.Create;
 try
 BoundsList.CommaText := R.ReadString(Self.Name);
 if BoundsList.Count = 4 then
 begin
 SetBounds(StrToIntDef(BoundsList[0], Left), {Links}
 StrToIntDef(BoundsList[1], Top), {Oben}
 StrToIntDef(BoundsList[2], Width), {Breite}
 StrToIntDef(BoundsList[3], Height)); {Höhe}
 Result := True;
 end;
 finally
 BoundsList.Free;
 end;
 end;
 R.CloseKey;
 end;
 finally
 R.Free;
 end;
end;

function TForm1.SaveWindowBounds: Boolean;
 var R: TRegistry;
begin
 Result := False;
 R := TRegistry.Create;
 try
 R.RootKey := HKEY\_CURRENT\_USER;
 if R.OpenKey('Software\' + VENDORNAME + '\' + APPNAME + '\Dialog Position', True) then
 begin
 R.WriteString(Self.Name, Format('%d,%d,%d,%d', [Left, Top, Width, Height]));
 R.CloseKey;
 Result := True;
 end;
 finally
 R.Free;
 end;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
 LoadWindowBounds;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 SaveWindowBounds;
end;

end.

Danke schon mal

Bitte.

Ciao,
Rudy