home *** CD-ROM | disk | FTP | other *** search
/ Softwarová Záchrana 3 / Softwarova-zachrana-3.bin / StartRight / source.zip / UnitStartupRunner.pas < prev    next >
Pascal/Delphi Source File  |  2004-10-08  |  18KB  |  601 lines

  1. unit UnitStartupRunner;
  2. {
  3.     Purpose:
  4.         To execute all of StartRight's runkey and startup folder
  5.         programs.
  6.  
  7.         Called by FrmDummyRunner
  8.     Updates:
  9.  
  10.         Use Registry.OpenKeyReadOnly for new PowerUser functionality,
  11.         since StartRight's key requires Admin permission by default.
  12.     --------
  13.         New Pause/Abort/Status functionality
  14. }
  15. interface
  16.  
  17.  
  18. uses Windows, UnitMyRegistry, SysUtils, StrUtils, TLHelp32,
  19.     INIFiles, Contnrs, ShlObj, ShellAPI,
  20.     Forms {for Application object},
  21.     Classes {for StringList & other TStrings objects},
  22.     Dialogs,
  23.     UnitMyKeys, UnitStartupMover, UnitErrorLog;
  24.  
  25. type TStartupType = (STARTUP_RUNKEY, STARTUP_FOLDER, STARTUP_PRERUN);
  26.  
  27. type TStartupRunner = class(TObject)
  28.     private
  29.         r : TMyRegistry;
  30.         h : THandle;
  31.  
  32.         MyRunkeyList : TObjectList;
  33.         MyStartupFolderList : TObjectList;
  34.  
  35.         StartupDelayMilli,
  36.         RunkeyDelayMilli,
  37.         PrerunDelayMilli : integer;
  38.  
  39.         paused : boolean;
  40.         aborted : boolean;
  41.  
  42.         procedure ExecuteRunKeyValue(valuename, value : string);
  43.         function RunCommandLine(command : string) : longbool;
  44.         function RunShortcut(command : string; var errorcode : cardinal) : longbool;
  45.         function RunProgram(name, params : string) : longbool;
  46.  
  47.         procedure GatherMyRunkeyItems;
  48.         procedure GatherMyStartupItems;
  49.         procedure GetOptions;
  50.  
  51.         function  GetTunedCustomDelay(valuename : string; st : TStartupType) : integer;
  52.         procedure UpdatePostDelayStat(valuename : string);
  53.         procedure UpdatePreRunStat(valuename : string; var CPU : integer);
  54.     public
  55.         constructor Create(Handle : THandle);
  56.         destructor Destroy(); override;
  57.  
  58.         procedure ExecutePrerunDelay;
  59.         procedure ExecuteRunkeyPrograms;
  60.         procedure ExecuteStartupFolderPrograms;
  61.  
  62.         function GetMyRunkeyItemsCount : cardinal;
  63.         function GetMyRunkeyItem(index : cardinal) : TRunkeyItem;
  64.         function GetMyStartupFolderItemsCount : cardinal;
  65.         function GetMyStartupFolderItem(index : cardinal) : TStartupFolderItem;
  66.         function GetPrerunDelaySeconds : integer;
  67.         procedure RefreshItems;
  68.  
  69.         procedure SetPaused(value : boolean);
  70.         procedure Abort;
  71. end;
  72.  
  73. implementation
  74.  
  75. uses UnitFrmAbout, UnitFrmDummyRunner, UnitUtils, UnitCPUUsage,
  76.   UnitFrmOptions;
  77.  
  78. {}
  79. CONST
  80.     METHOD_COMMANDLINE_ALTERED = 'CMDALTERED';
  81.     METHOD_COMMANDLINE = 'CMDORIGNAL';
  82.     METHOD_PROGRAM = 'PROGRAM';
  83.     METHOD_SHORTCUT = 'SHORTCUT';
  84.     METHOD_APPPATH = 'APPPATH';
  85.     METHOD_SYSTEMROOT = 'SYSTEMROOT';
  86.  
  87.     PRERUN_CUSTOMEDELAY_NAME = 'SR_InitialPause';
  88.  
  89.  
  90. const PRE_STRING = '_PRE';
  91.       POST_STRING = '_POST';
  92.  
  93. constructor TStartupRunner.Create(Handle : THandle);
  94. begin
  95.     r := TMyRegistry.Create();
  96.     h := Handle;
  97.  
  98.     MyRunkeyList := TObjectList.Create();
  99.     MyStartupFolderList := TObjectList.Create();
  100.  
  101.     self.GetOptions;
  102.     self.GatherMyRunkeyItems;
  103.     self.GatherMyStartupItems;
  104. end;
  105.  
  106. destructor TStartupRunner.Destroy();
  107. begin
  108.     MyStartupFolderList.Clear;
  109.     MyRunkeyList.Clear;
  110.     MyStartupFolderList.Free;
  111.     MyRunkeyList.Free;
  112.     r.free;
  113.     inherited destroy;
  114. end;
  115.  
  116.  
  117. procedure TStartupRunner.GetOptions;
  118.     function GetInteger(r : TMyRegistry; value : string; default : integer) : integer;
  119.     begin
  120.         if not r.ValueExists(value) then begin
  121.             r.WriteInteger(value, default);
  122.         end;
  123.         result := r.ReadInteger(value);
  124.     end;
  125. const STARTUP_DELAY_MILLI = 'StartupDelayMilli';
  126.       RUNKEY_DELAY_MILLI = 'RunkeyDelayMilli';
  127.       PRERUN_DELAY = 'InitialPauseMilli';
  128.  
  129. begin
  130.     self.StartupDelayMilli := 2000;
  131.     r.ValueExistsInteger(SR_HOME_KEY, STARTUP_DELAY_MILLI ,self.StartupDelayMilli);
  132.     r.SetDataInteger(SR_HOME_KEY, STARTUP_DELAY_MILLI, self.StartupDelayMilli);
  133.  
  134.     self.RunkeyDelayMilli := 1000;
  135.     r.ValueExistsInteger(SR_HOME_KEY, RUNKEY_DELAY_MILLI, self.RunkeyDelayMilli);
  136.     r.SetDataInteger(SR_HOME_KEY, RUNKEY_DELAY_MILLI, self.RunkeyDelayMilli);
  137.  
  138.     self.PrerunDelayMilli := 5000;
  139.     r.ValueExistsInteger(SR_HOME_KEY, PRERUN_DELAY, self.PrerunDelayMilli);
  140.     r.SetDataInteger(SR_HOME_KEY, PRERUN_DELAY, self.PrerunDelayMilli);
  141.  
  142.     self.PrerunDelayMilli := self.GetTunedCustomDelay(
  143.         PRERUN_CUSTOMEDELAY_NAME,
  144.         STARTUP_PRERUN
  145.     );
  146. end;
  147.  
  148. procedure TStartupRunner.GatherMyRunkeyItems;
  149. var ri : TRunkeyItem;
  150.     i : integer;
  151.     s : string;
  152. begin
  153.  
  154.     for i := 0 to (UnitMyKeys.GetRunSortCount - 1) do begin
  155.         s := UnitMyKeys.GetRunSortData(i);
  156.         if (s <> '') then begin
  157.             ri := TRunkeyItem.Create();
  158.             ri.RunKey := UnitMyKeys.GetRunSortValue(i);
  159.             ri.RunValue := s;
  160.  
  161.             MyRunkeyList.Add(ri);
  162.         end;
  163.     end;
  164. end;
  165.  
  166. procedure TStartupRunner.GatherMyStartupItems;
  167. var sfi : TStartupFolderItem;
  168.     i, sortindex : integer;
  169. begin
  170.     MyStartupFolderList.Clear;
  171.  
  172.     if r.ValueExistsInteger(SR_STARTUPSORT_KEY,SR_SORTINDEX_VALUE, sortindex) then begin
  173.         for i := 0 to (sortIndex - 1) do begin
  174.             sfi := TStartupFolderItem.Create();
  175.             sfi.FullName := r.GetDataString(SR_STARTUPSORT_KEY, IntToStr(i));
  176.             MyStartupFolderList.Add( sfi );
  177.         end;
  178.     end;
  179. end;
  180.  
  181.  
  182.  
  183.  
  184.  
  185. function TStartupRunner.GetMyRunkeyItemsCount : cardinal;
  186. begin
  187.     result := MyRunkeyList.Count;
  188. end;
  189. function TStartupRunner.GetMyRunkeyItem(index : cardinal) : TRunkeyItem;
  190. begin
  191.     result := TRunkeyItem( MyRunkeyList.items[index] );
  192. end;
  193.  
  194. function TStartupRunner.GetMyStartupFolderItemsCount : cardinal;
  195. begin
  196.     result := MyStartupFolderList.Count;
  197. end;
  198. function TStartupRunner.GetMyStartupFolderItem(index : cardinal) : TStartupFolderItem;
  199. begin
  200.     result := TStartupFolderItem( MyStartupFolderList.items[index] );
  201. end;
  202.  
  203.  
  204.  
  205.  
  206.  
  207. procedure TStartupRunner.RefreshItems;
  208. begin
  209.     self.GatherMyRunkeyItems;
  210.     self.GatherMyStartupItems;
  211. end;
  212.  
  213.  
  214.  
  215.  
  216.  
  217.  
  218. //------------------------------------------------------------
  219. // Run all startup programs shadowed by this program
  220. //------------------------------------------------------------
  221.  
  222.  
  223.  
  224. function TStartupRunner.GetTunedCustomDelay(valuename: string;
  225.   st: TStartupType): integer;
  226. var precpu,
  227.     postcpu,
  228.     high : integer;
  229.     postdelta, predelta, alterdelta : double;
  230.     newresult : integer;
  231. begin
  232.     //
  233.     // let a user defined value override a tunned
  234.     result := UnitMyKeys.GetCustomDelay(valuename);
  235.     if result <> 0 then EXIT;
  236.  
  237.     // set defaults if no delay is currently defined
  238.     result := UnitMyKeys.GetTunnedCustomDelay(valuename);
  239.     if (result = 0) then begin
  240.         case ST of
  241.         STARTUP_RUNKEY : result := self.RunkeyDelayMilli;
  242.         STARTUP_FOLDER : result := self.StartupDelayMilli;
  243.         STARTUP_PRERUN : result := self.PrerunDelayMilli;
  244.         end;
  245.     end;
  246.  
  247.     if (not frmOptions.cbDisableAutoTune.Checked) then begin
  248.         // gather current / saved statistics
  249.         precpu := UnitMyKeys.ReadStat(valuename + PRE_STRING);
  250.         postcpu := UnitMyKeys.ReadStat(valuename + POST_STRING);
  251.         high := UnitMyKeys.GetHighCPUStat;
  252.  
  253.         if not (0 in [precpu, postcpu, high]) then begin
  254.             // - for every 10% CPU usage, add 1 extra second of delay
  255.             // - add up to 25% more delay [comparing post delay to highest]
  256.             predelta := (precpu - postcpu);
  257.             postdelta := (predelta) * 100;
  258.             alterdelta := (postcpu / high) / 4;
  259.  
  260.             newresult := result + trunc(
  261.                 postdelta  + (postdelta * alterdelta)
  262.             );
  263.  
  264.             // 50/50 exponential average
  265.             result := trunc((newresult * 0.5) + (result * 0.5));
  266.             // Delay's Cap / Limit
  267.             if result > 10000 then result := 10000;
  268.             if result < 1000 then result := 1000;
  269.         end;
  270.  
  271.         UnitMyKeys.SetTunnedCustomDelay(valuename, result);
  272.  
  273.     end;
  274. end;
  275.  
  276. procedure TStartupRunner.UpdatePreRunStat(valuename : string; var CPU : integer);
  277. begin
  278.     if (not frmOptions.cbDisableAutoTune.Checked) then begin
  279.         valuename := valuename + PRE_STRING;
  280.         //
  281.         // save the previous value, update the current value
  282.         //
  283.         UnitMyKeys.UpdatePreviousStat(valuename, UnitMyKeys.ReadStat(valuename));
  284.         CPU := trunc(UnitCPUUsage.GetCPUUsage);
  285.         UnitMyKeys.UpdateStat(valuename, CPU);
  286.     end;
  287. end;
  288.  
  289. procedure TStartupRunner.UpdatePostDelayStat(valuename: string);
  290. begin
  291.  
  292.     if (not frmOptions.cbDisableAutoTune.Checked) then begin
  293.         valuename := valuename + POST_STRING;
  294.         //
  295.         // save the previous value, update the current value
  296.         //
  297.         UnitMyKeys.UpdatePreviousStat(valuename, UnitMyKeys.ReadStat(valuename));
  298.         UnitMyKeys.UpdateStat(valuename, trunc(UnitCPUUsage.GetCPUUsage));
  299.     end;
  300. end;
  301.  
  302.  
  303.  
  304. procedure TStartupRunner.ExecuteRunkeyPrograms;
  305. var i,j, delay : integer;
  306.     ri : TRunkeyItem;
  307.     s, cpu : string;
  308. begin
  309.     //
  310.     // Run all managed runkey items
  311.     //
  312.  
  313.     for i := 0 to MyRunkeyList.Count - 1 do begin
  314.         if (self.aborted) then BREAK;
  315.  
  316.         while (self.paused) do begin
  317.             Application.ProcessMessages;
  318.         end;
  319.  
  320.         ri := TRunkeyItem( MyRunkeyList.Items[i] );
  321.         self.UpdatePreRunStat(ri.runkey, j);
  322.         cpu := IntToStr(j);
  323.         s := 'Run key ('+ IntToStr(i+1)+ '/' + IntToSTr(MyRunkeyList.Count) + ')'#13#10 +
  324.             ri.RunKey +': '
  325.              + ri.Runvalue
  326.             + #13#10'CPU% ' + cpu;
  327.  
  328.  
  329.         frmDummyRunner.Callback_ShowMessage(s);
  330.  
  331.         // DEBUG
  332.         //ErrorLog.Add(s);
  333.  
  334.         self.ExecuteRunKeyValue(ri.RunKey, ri.RunValue );
  335.  
  336.         // Use a custom sleep time, or the default.
  337.         // Tally statistics for the Auto-Tune process
  338.  
  339.         delay := self.GetTunedCustomDelay(ri.RunKey, STARTUP_RUNKEY);
  340.  
  341.         if (delay = 0) then begin
  342.             delay := self.RunkeyDelayMilli;
  343.         end;
  344.         frmDummyRunner.Callback_ShowMessage(
  345.             s + #13#10'(sleeping ' + IntToStr(delay) + ' milliseconds)'
  346.             );
  347.         UnitUtils.MySleep(delay);
  348.         self.UpdatePostDelayStat(ri.RunKey);
  349.  
  350.  
  351.  
  352.  
  353.         while (self.paused) do begin
  354.             Application.ProcessMessages;
  355.             sleep(10);
  356.         end;
  357.  
  358.     end;
  359. end;
  360.  
  361. procedure TStartupRunner.ExecuteStartupFolderPrograms;
  362. var i,j, delay : integer;
  363.     sfi : TStartupFolderItem;
  364.     s, cpu : string;
  365.     err : cardinal;
  366. begin
  367.     for i := 0 to MyStartupFolderList.Count - 1 do begin
  368.         if (self.aborted) then BREAK;
  369.         while (self.paused) do begin
  370.             Application.ProcessMessages;
  371.         end;
  372.  
  373.  
  374.         sfi := TStartupFolderItem( MyStartupFolderList.Items[ i ] );
  375.         self.UpdatePreRunStat(sfi.FullName, j);
  376.         cpu := IntToStr(j);
  377.         s :=  'Startup Folder ('+ IntToStr(i+1)+ '/' + IntToSTr(MyStartupFolderList.Count) + '): '#13#10 +
  378.             ExtractFileName(sfi.FullName) +
  379.             #13#10'CPU% ' + cpu;
  380.  
  381.  
  382.         frmDummyRunner.Callback_ShowMessage(s);
  383.         if (not self.RunShortcut( sfi.FullName, err )) then begin
  384.             ShowMessage('Error execution shortcut: ' + #13#10 +
  385.                 sfi.fullname + #13#10 +
  386.                 SysErrorMessage(err));
  387.         end else begin
  388.  
  389.             delay := self.GetTunedCustomDelay(sfi.Fullname, STARTUP_FOLDER);
  390.  
  391.  
  392.             frmDummyRunner.Callback_ShowMessage(
  393.                 s + #13#10'(sleeping ' + IntToStr(delay) + ' seconds)'
  394.                 );
  395.             UnitUtils.mySleep(delay);
  396.         end;
  397.         Self.UpdatePostDelayStat(sfi.FullName);
  398.  
  399.         while (self.paused) do begin
  400.             Application.ProcessMessages;
  401.             sleep(10);
  402.         end;
  403.  
  404.     end;
  405. end;
  406.  
  407.  
  408.  
  409.  
  410.  
  411.  
  412. procedure TStartupRunner.ExecuteRunKeyValue(valuename, value : string);
  413.     procedure RecordMethod(vname, method : string);
  414.     begin
  415.         r.SetDataString(SR_EXECMETHOD_KEY, vname, method);
  416.     end;
  417.  
  418.  
  419.     function RunCmdAltered(value : string) : boolean;
  420.     var name, params : string;
  421.     begin
  422.         // turn full names into short names and break apart the
  423.         // program and it's parameters
  424.  
  425.         if (LeftStr(value,1) = '"') then begin
  426.             value := StringReplace(value, '"', '', []);
  427.             value := StringReplace(value, '"', '', []);
  428.         end;
  429.  
  430.         name := UnitMyKeys.GetEXEPathFromRunValue(value);
  431.         params := StringReplace(value, name, '',[rfIgnoreCase]);
  432.  
  433.         result := (self.RunCommandLine('"' +  name + '" ' + Trim(params)))
  434.     end;
  435.  
  436.     function RunAppPath(value : string) : boolean;
  437.     var s : string;
  438.     begin
  439.         result := false;
  440.  
  441.         try
  442.         s := r.GetDataString(WINDOWS_APPSPATH_KEY + '\' + value, '');
  443.         finally
  444.         end;
  445.  
  446.         if (s <> '') then begin
  447.             result := self.RunCommandLine(s);
  448.         end;
  449.     end;
  450.     function RunSystemRoot(value : string) : boolean;
  451.     begin
  452.         result := self.RunCommandLine('%SystemRoot%\System32\' + value);
  453.     end;
  454.     function RunByMethod(value : string) : boolean;
  455.     var s : string;
  456.     begin
  457.         result := false;
  458.  
  459.         s := r.GetDataString(SR_EXECMETHOD_KEY, valuename);
  460.         if (s = METHOD_COMMANDLINE) then begin
  461.             result := self.RunCommandLine(value);
  462.         end else if (s = METHOD_COMMANDLINE_ALTERED) then begin
  463.             result := RunCmdAltered(value);
  464.         end else if (s = METHOD_APPPATH) then begin
  465.             result := RunAppPath(value);
  466.         end else if (s = METHOD_SYSTEMROOT) then begin
  467.             result := RunSystemRoot(value);
  468.         end;
  469.     end;
  470.  
  471. begin
  472.     // try using previous working method
  473.     if (RunByMethod(value)) then begin
  474.         EXIT;
  475.     end;
  476.  
  477.     // try as-is if the full path isn't found
  478.     if (pos(':', value) <> 0) then begin
  479.         if (RunCmdAltered(value)) then begin
  480.             RecordMethod(valuename, METHOD_COMMANDLINE_ALTERED);
  481.             EXIT;
  482.         end;
  483.     end else begin
  484.         if (self.RunCommandLine(value)) then begin
  485.             RecordMethod(valuename, METHOD_COMMANDLINE);
  486.             EXIT;
  487.         end;
  488.     end;
  489.  
  490.     // Look for an AppPath program
  491.     if (RunAppPath(value)) then begin
  492.         RecordMethod(valuename, METHOD_APPPATH);
  493.         EXIT;
  494.     end;
  495.  
  496.     // try SystemRoot if all else fails
  497.     if (RunSystemRoot(value)) then begin
  498.         RecordMethod(valuename, METHOD_SYSTEMROOT);
  499.         EXIT;
  500.     end;
  501. end;
  502.  
  503. function TStartupRunner.RunCommandLine(command : string) : longbool;
  504. var  StartInfo  : _StartupInfoA;
  505.      ProcInfo   : _PROCESS_INFORMATION;
  506. begin
  507.     // nil for the program name treats the program name
  508.     // as a command line to execute
  509.     FillChar(StartInfo, SizeOf(StartInfo), #0);
  510.     FillChar(ProcInfo, SizeOf(ProcInfo), #0);
  511.     StartInfo.cb := SizeOf(TStartupInfo);
  512.     StartInfo.wShowWindow := SW_NORMAL;
  513.     
  514.     result := CreateProcess(nil, PChar(command),
  515.                 nil, nil, False,
  516.                 CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS,
  517.                 nil, nil, StartInfo, ProcInfo);
  518.  
  519.     if (result) then begin
  520.         WaitForInputIdle(ProcInfo.hProcess, 5000);
  521.     end else begin
  522.         ErrorLog.Add(Windows.GetLastError(), command);
  523.     end;
  524. end;
  525.  
  526. function TStartupRunner.RunProgram(name, params : string) : longbool;
  527. var  StartInfo  : _StartupInfoA;
  528.      ProcInfo   : _PROCESS_INFORMATION;
  529. begin
  530.     // nil for the program name treats the program name
  531.     // as a command line to execute
  532.     FillChar(StartInfo, SizeOf(StartInfo), #0);
  533.     FillChar(ProcInfo, SizeOf(ProcInfo), #0);
  534.     StartInfo.cb := SizeOf(TStartupInfo);
  535.     StartInfo.wShowWindow := SW_normal;
  536.     result := CreateProcess(pchar(name), PChar(params),
  537.                 nil, nil, False,
  538.                 CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS,
  539.                 nil, nil, StartInfo, ProcInfo);
  540.  
  541.     if (result) then begin
  542.         WaitForInputIdle(ProcInfo.hProcess, 5000);
  543.     end else begin
  544.         ErrorLog.Add(Windows.GetLastError(), name + ' ' + params);
  545.     end;
  546. end;
  547.  
  548.  
  549. function TStartupRunner.RunShortcut(command : string; var errorcode : cardinal) : longbool;
  550. begin
  551.     command := ExtractShortPathName(command);
  552.     //c := ShellAPI.ShellExecute(h, nil, PChar( ExtractShortPathName(command) ), nil, nil, SW_SHOWNORMAL);
  553.     errorcode := ShellAPI.ShellExecute(h, nil, PChar( ExtractShortPathName(command) ), nil, nil, SW_NORMAL);
  554.     result := (errorcode > 32);
  555.     if (result) then begin
  556.        // Sleep(self.StartupDelayMilli);
  557.     end else begin
  558.         ErrorLog.Add(errorcode, command);
  559.         ErrorLog.Add(Windows.GetLastError(), command);
  560.     end;
  561. end;
  562.  
  563.  
  564.  
  565.  
  566.  
  567.  
  568.  
  569. procedure TStartupRunner.ExecutePrerunDelay;
  570. var CPU : integer;
  571. begin
  572.     if (FrmOptions.cbDisableAutoTune.Checked = false) then begin
  573.         UpdatePreRunStat(PRERUN_CUSTOMEDELAY_NAME, CPU);
  574.     end;
  575.     UnitUtils.MySleep(self.PrerunDelayMilli);
  576.     if (FrmOptions.cbDisableAutoTune.Checked = false) then begin
  577.         UpdatePostDelayStat(PRERUN_CUSTOMEDELAY_NAME);
  578.     end;
  579. end;
  580.  
  581. function TStartupRunner.GetPrerunDelaySeconds: integer;
  582. begin
  583.     result := trunc(self.PrerunDelayMilli / 1000);
  584. end;
  585.  
  586. procedure TStartupRunner.SetPaused(value: boolean);
  587. begin
  588.     self.paused := value;
  589. end;
  590.  
  591. procedure TStartupRunner.Abort;
  592. begin
  593.     self.aborted := true;
  594. end;
  595.  
  596.  
  597.  
  598.  
  599.  
  600. end.
  601.