////////////////////////////////////////////////////////////////////////////////
// What's new
//
// Release Version 1.0.0.0
// * Rebuilt in XP virtual machine to work on Win10
//
// Release Version 0.8.0.0
//  * [Major] API System Rewritten
//  * [Major] "Edit Config" added to menu
//
// Release Version 0.7.2.0
//  * [Major] GDI+ handling improved
//  * [Minor] Retray issue fixed, yet again
//  * [Minor] Options window can no longer be shown multiple times.
//
// Release Version 0.7.1.1
//  * [Critical] Reset switch has been removed, it has been confirmed it will
//               trigger the "Class factory not supplied" error for plugins
//               when trying to reinstall.
//  * [Minor] XML dump system will no longer try to dump if the file is in use
//  * [Minor] XML Dump works after Reload Config
//
// Release Version 0.7.1.0
//  * [Major] RGB color supported
//  * [Major] You can now dump sensors to XML (Networking solution)
//  * [Minor] Error of options not loading fixed
//  * [Minor] You can show/hide a form using events API
//  * [Minor] Events can now be performed on forms
//
// Release Version 0.7.0.0
//  * [Core] Added linked sensors "using @"
//  * [Core] Added ability to use "bottom" and "right" parameters on forms
//  * [Program] Retray code corrected
//
// Release Version 0.6.5.0
//  * Retray system rewritten (thanks Cyonix)
//  * Various bug fixes
//
// Release Version 0.6.3.0
//  * [Core] Event system bug corrected
//  * [Program] Exe filesize reduced by 60%
//  * [Program] Memory Optimization (Form Loading)
//
// Release Version 0.6.2.0
//  * [Program] Access violations will now be reported in a balloon hint/warning
//  * [Program] "Terminate" button will no longer show upon syntax errors. A dialog
//    will display, with the XML engines information.
//
// Release Version 0.6.1.0
//  * [Core] CML Version 1.3 introduced - http://wiki.coolmon.org/CML13
//  * [Core] New CMLx interface has been added to allow for large CMLX returns
//  * [Core] CMLx system refined
//  * [Program] "Reload Config" bug fixed
//
// Release Version 0.6.0.0
//  * [Core] CM2Form jumps back to original location after screen resolution
//           change
//  * [Core] CMLx now fully implemented (Still experimental though)
//  * [Core] Text tags containing only a space will now actually contain the space
//  * [Core] Doubleclick now possible as event
//  * [Program] Enables CMU to run last used config after updating (Release pending)
//  * [Program] Config forms no longer displays on the Alt+Tab dialog [Thanks NoName]
//  * [Program] CM2 no longer shows in taskbar
//  * [Program] Fixed a memory usage bug
//  * [Program] Fixed Variable Manager bug on terminate
//  * [Program] Fixed plugin terminate execute
//  * [Program] New Debug level (Config) will cause debugger only to be active
//              during config load
//  * [Program] Debug log auto indents
//
// Release Version 0.5.6.0
//  * New Splash screen (Thanks Douglas Dickson)
//  * Loading Progressbar fully 100% shaded
//  * Added GDI+ System
//  * Plugin shutdown system now loaded correctly
//  * Event Support Added
//  * Auto Suspend checkbox in options has been renamed (to avoid confusion)
//  * CPU usage reduced to normal when running "Show Desktop prevention"
//  * Hidden windows will no longer show when pressing "Show Desktop" the second time
//
// Release Version 0.5.3.0
//  * Fixed a Reload Config Bug
//  * ShowDesktop ... now prevented (Optionable)
//  * Always on top now Available
//  * Pin To Desktop now available (Thanks Svend)
//  * Auto suspend while on Desktop level fixed
//  * CML Comment-tag bug fixed
//  * Flashing through full screen games 
//
// Release Version 0.5.2.0
//  * Verbose window has been changed to accomodate realtime debug level
//    changing
//  * CML tree will no longer resolve comment tags
//  * After explorer.exe crash, CM2 will retray itself
//  * Added Options
//  * Debug level (use /levelXXXXXX - all, core, plugins, error)
//  * Added Suspend/Resume functions
//  * Added Auto suspend if running a fullscreen application
//  * Update all values on startup
//
// Release Version 0.5.1.0
//  * Recompiled with new core number
//
// Release Version 0.5.0.0
//  * Scans for missing plugin files on startup, if any is missing, they will be
//    removed from the plugin list in the registry.
//  * Form.Show reenabled, has for unknown reasons been disabled.
//  * Added /Reset switch
//  * Throws an error if a plugin used in the CML isn't installed 
//
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//
// SIT-REP
//
// There is a parameter bug which throws the <Sensor ...> tag as parameters to
// the plugin rather than what is in the <parameters>. This is only done at
// startup. Use Blert's EmailChecker to track the bug
//
////////////////////////////////////////////////////////////////////////////////
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, Unit2, CM2VisPlugTypes, CM2GenPlugTypes,
  CM2FormManager, CM2Tree, CM2VariableManager, Menus, lplTrayIcon, CM2PlugInTypes,
  ComObj, CM2MMRB, AppEvnts, Registry, filter, lpl_sysutils, enviroment, //unit21,
  lplOnlyOne, ImgList, PrefTypesNProcs, CM2FormUnit, CM2CMLXPlugTypes,
  FileVersion, XMLDom, verbose, XMLIntf, msxmldom, XMLDoc, pref, //GDIPAPI,
  GDIPenable;

type
  TRunningState = (rsRunning, rsSuspended, rsSuspendedByUser);
  TCM2MainForm = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    tray: TlplTrayIcon;
    PopupMenu1: TPopupMenu;
    Exit1: TMenuItem;
    About1: TMenuItem;
    Timer1: TTimer;
    ApplicationEvents1: TApplicationEvents;
    ReloadConfig1: TMenuItem;
    N2: TMenuItem;
    N1: TMenuItem;
    ShowConsole1: TMenuItem;
    lplOnlyOne1: TlplOnlyOne;
    Edit1: TEdit;
    CheckForSuspend: TTimer;
    trayicons: TImageList;
    Suspend1: TMenuItem;
    Options1: TMenuItem;
    fv: TFileVersion;
    XML: TXMLDocument;
    GDIPenable1: TGDIPenable;
    N3: TMenuItem;
    EditConfig1: TMenuItem;
    Procedure vProgress(PlugName: String; Loaded: Integer; Total: Integer);
    Procedure gProgress(PlugName: String; Loaded: Integer; Total: Integer);
    Procedure cProgress(PlugName: String; Loaded: Integer; Total: Integer);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Exit1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure About1Click(Sender: TObject);
    procedure ApplicationEvents1Exception(Sender: TObject; E: Exception);
    procedure ReloadConfig1Click(Sender: TObject);
    procedure ShowConsole1Click(Sender: TObject);
    function GenerateTagLine: String;
    procedure est1Click(Sender: TObject);
    procedure lplOnlyOne1Running(sender: TObject; ProgramRunning: HWND);
    procedure ApplicationEvents1Message(var Msg: tagMSG;
      var Handled: Boolean);
    procedure Send(aHandle: THandle; filename: String);
    procedure CheckForSuspendTimer(Sender: TObject);
    procedure Suspend1Click(Sender: TObject);
    procedure Options1Click(Sender: TObject);
    procedure EditConfig1Click(Sender: TObject);
  private
    PrefDlg: TPrefDlg;
    MsgTaskBarCreated: Cardinal;
    gdiPlusStartupInput: PGdiplusStartupInput;
    gdiPlusStartupOutput: PGdiplusStartupOutput;
    GDIPlusToken: Cardinal;
    Splash: TSplash;
    GPTC, VPTC, CPTC, PTC, PlugCount: Integer;
    PTCMultiPlier: Integer;
    ProgVersion: String;
    procedure WMCopyData(var M: TMessage); message WM_COPYDATA;
  protected
    Procedure WndProc(Var Msg: TMessage); override;
  public
    Options: TOptions;
    Proceed: Boolean;
    VisPlugInList: TVisPlugInList;
    GenPlugInList: TGenPlugInList;
    CMLXPlugInList: TCMLXPlugInList;
    FormMgr: TCM2FormMgr;
    VarMgr: TVarMgr;
    Tree: TCM2Tree;
    ConfigFile: String;
    RunningState: TRunningState;
    OverrideCookie: String;
    Procedure Suspend;
    Procedure Resume;
    Procedure SetFormVisible(FormName: string; DoShow: boolean; DoToggle: boolean = false);
    Procedure ONAPI(Owner: PVisObject; APIStr: PChar; Var CallBackStr: String);
  end;

Const
 TagLines = 66;

var
  CM2MainForm: TCM2MainForm;
  Firstrun: Boolean = true;
//  Form4: TForm4;

  TagLine: Array[0 .. Taglines - 1] of string = (
              'Atleast as cool as the first one',
              'Just as potent as C4',
              'More important than the President',
              'A way of life',
              'The Final Frontier',
              'This is what you''ve all been waiting for',
              'A product of 3000 hrs of nerding',
              'In the fight against boredom',
              'Just like mom used to make it',
              'Your new best friend',
              'Almost as good looking as Natasja R.',
              'Your new P.I.',
              'Vaporware my ass!',
              'My Precious',
              'Cooked from an ancient Chinese recipe',
              'The sequel',
              'Everything changes but the name',
              'Infinite posibilities',
              'Satisfied yet ?',
              'Runtime Error 3733',
              'Made In Denmark',
              'A GMT+1 Product', // 21
              '... and you thought CoolMon 1 was awesome',
              'IT''S ALIVE!',
              'The second coming of christ',
              'Sure it''s blasphemy, but you know you like it!',
              'The 11th Commandment . . . Monitor Thy System',
              'Because we got bored',
              'Because we felt like it',
              'Because we have too much time',
              'You can''t rush art',
              'Supercharge your desktop!',
              'Because knowledge IS power!',
              'This stuff is gonna blow you away',
              'You know you want too!', // 34
              'Better than sex',
              'Because we say so',
              'Almost as good looking as Clara Rugaard',
              'Press Any Key . . .',
              'Coming to a computerscreen near you',
              'A work in progress',
              'Can you dig'' it?',
              'A love story!',
              'So good it just HAS to be fattening',
              'Did anyone else feel that?',
              'Jackpot!',
              'It''s true - The best things in life really are free',
              'I M P R E S S I V E !', // 47
              'The last line of defense',
              'A work in progress',
              'Only $9.99',
              '10% Blood, sweat and tears - 90% Kickass software',
              'Almost as good looking as Stefanie Scott',
              'Did anyone say the C-Word ?',
              'Delivered AS-IS',
              'More extensive than echelon',
              'Warning: Can be Hazardous to your health',
              'Is it working ?',
              'Well spank my ass and call me charlie', //58
              'Well, I''ll be dammed...',
              'I can''t believe it''s still working after 8 years',
              'Oh My god ... it killed Kenny',
              'BeefCake!',
              'SWEEEEEET ...',
              'Dedicated to Luna',
              'Dedicated to Christopher "blert" Dilts'); // 65;

implementation

uses aboutform, RegMsg, CM2API;

{$D-} 
{
Maybe this has to be a plus, saw it outside when in notepad so hasn't been checked, but I assume 
that the D is for debug information and that is about 1 meg xtra in filesize

}

{$R *.dfm}

type
  PCopyDataStruct = ^TCopyDataStruct;
  TCopyDataStruct = record
    dwData: DWORD;
    cbData: DWORD;
    lpData: Pointer;
  end;

Function IsInList(Str: String; List: TStrings; CaseSensitive: Boolean = False): Boolean;
Var
 I: Integer;
Begin
 Result := False;
 For I := 0 to list.count - 1 do begin
  If CaseSensitive then begin
   If Str = List[I] then Result := true;
  end
   else
  begin
   If Uppercase(Str) = Uppercase(List[I]) then Result := true;
  end;
 end;
end;

procedure CheckNode(Node: IXMLNode; Var GenList: TStrings; Var VisList: TStrings);
Var
 I, X: Integer;
 S: String;
Begin
 For I := 0 to Node.ChildNodes.Count - 1 do begin
  If uppercase(Node.ChildNodes[I].NodeName) = 'VISUAL' then begin
   For X := 0 to Node.ChildNodes[I].AttributeNodes.Count - 1 do begin
    If uppercase(node.ChildNodes[I].AttributeNodes[X].NodeName) = 'NAME' then begin
     If Not IsInList(Node.ChildNodes[I].AttributeNodes[X].NodeValue, VisList) then VisList.Add(Node.ChildNodes[I].AttributeNodes[X].NodeValue);
    end;
   end;
  end;
  If uppercase(Node.ChildNodes[I].NodeName) = 'SENSOR' then begin
   For X := 0 to Node.ChildNodes[I].AttributeNodes.Count - 1 do begin
    If uppercase(node.ChildNodes[I].AttributeNodes[X].NodeName) = 'NAME' then begin
     S := Node.ChildNodes[I].AttributeNodes[X].NodeValue;
     S := GetFilt(S, 1, '.');
     If Not IsInList(S, GenList) then GenList.Add(S);
    end;
   end;
  end;
  CheckNode(Node.ChildNodes[I], GenList, VisList);
 end;
End;

procedure TCM2MainForm.Send(aHandle: THandle; filename: String);
var
  DataBuffer: TCopyDataStruct;
  Buf: PChar;
  BufSize: Integer;
begin
   aHandle := FindWindow('TCM2MainForm', 'CoolMon 2 - Running');
   If aHandle > 0 then begin
    { Allocate buffer }
    Edit1.Text := filename;
    BufSize := Edit1.GetTextLen + (1 * SizeOf(Char));
    Buf := AllocMem(BufSize);
    { Copy memo to buffer }
    Edit1.GetTextBuf(Buf, BufSize);
    try
      with DataBuffer do begin
        { Fill dwData with registered message as safety check }
        dwData := HandshakeMessage;
        cbData := BufSize;
        lpData := Buf;
      end;
      { NOTE: WM_COPYDATA message must be *sent* }
     SendMessage(aHandle, WM_COPYDATA, Handle,
        Longint(@DataBuffer));
//      PostMessage(aHandle, WM_User + 50, 0,0);
    finally
      FreeMem(Buf, BufSize);
    end;
   end
    else
   begin
    MessageDlg('no window', mtWarning, [mbOK], 0);
   end;
end;

procedure TCM2MainForm.WMCopyData(var M: TMessage);
begin
  if PCopyDataStruct(M.lParam)^.dwData = HandshakeMessage then begin
   Edit1.SetTextBuf(PChar(PCopyDataStruct(M.lParam)^.lpData));
   ConfigFile := Edit1.Text;
   ReloadConfig1Click(self);
  End;
end;

procedure TCM2MainForm.lplOnlyOne1Running(sender: TObject; ProgramRunning: HWND);
begin
 Send(ProgramRunning, ConfigFile);
 Application.Terminate;
end;

procedure TCM2MainForm.ApplicationEvents1Message(var Msg: tagMSG;
  var Handled: Boolean);
begin
  if Msg.Message = HandshakeMessage then begin
    PostMessage(Msg.wParam, HandshakeMessage, Handle, 0);
    Handled := True;
  end;
  if Msg.message = WM_COPYDATA then begin
   MessageDlg('WMCOPYDATA from event', mtWarning, [mbOK], 0);
  End;
end;

procedure TCM2MainForm.Button1Click(Sender: TObject);
begin
 Application.Terminate;
end;

Procedure TCM2MainForm.gProgress(PlugName: String; Loaded: Integer; Total: Integer);
Var
 B: Byte;
Begin
 GPTC := Loaded+1;
 B := GPTC * PTCMultiPlier;
 Splash.SetProgress(B);
 Application.ProcessMessages;
End;

Procedure TCM2MainForm.vProgress(PlugName: String; Loaded: Integer; Total: Integer);
Var
 B: byte;
Begin
 VPTC := Loaded+1;
 B := (VPTC + GPTC) * PTCMultiPlier;
 Splash.SetProgress(B);
 Application.ProcessMessages;
End;

Procedure TCM2MainForm.cProgress(PlugName: String; Loaded: Integer; Total: Integer);
Var
 B: Byte;
Begin
 CPTC := Loaded+1;
 B := (CPTC + VPTC + GPTC) * PTCMultiPlier;
 Splash.SetProgress(B);
 Application.ProcessMessages;
End;

procedure TCM2MainForm.FormCreate(Sender: TObject);
Var
 I: integer;
 Reg: TRegistry;
 SL: TStrings;
 S: String;
 DL: Integer;
//DIStartupStatus: Status;
 GenList, VisList: Tstrings;
begin
 Randomize;
 MsgTaskbarcreated := RegisterWindowMessage( 'TaskbarCreated' );
 GenList := TStringlist.Create;
 VisList := TStringList.Create;
 fv.Filename := application.ExeName;
 ProgVersion := fv.FileVersion;
 DL := dlAll; //debug level
 ConfigFile := 'InitConfig.cml';
 For I := 1 to paramcount do begin
  If uppercase(Paramstr(I)) = '/DEBUG' then Debug := true;
  if uppercase(paramstr(I)) = '/SILENTDEBUG' then Begin
   SilentDebug := true;
   Debug := true;
  End;
  If uppercase(ParamStr(I)) = '/SAFEMODE' Then UseSafeMode := True;
  If uppercase(ParamStr(I)) = '/NOCONSOLE' then Begin
   Self.ShowConsole1.Visible := False;
   Self.N1.Visible := False;
  End;
  If uppercase(copy(ParamStr(I), 1, length('/startup:'))) = '/STARTUP:' then begin
   OverrideCookie := Copy(ParamStr(I), 10, length(ParamStr(I)));
  end;
  if uppercase(copy(paramstr(I), 1, length('/LEVEL'))) = '/LEVEL' then begin
   S := uppercase(Paramstr(I));
   S := Copy(S, length('/LEVEL') + 1, length(s));
   If S = 'ALL' then DL := dlAll;
   If S = 'CORE' then DL := dlCore;
   If (S = 'PLUGINS') or (S = 'PLUGIN') then DL := dlPlugins;
   If S = 'CRUSIAL' then DL := dlCrusial;
   If S = 'ERROR' then DL := dlError;
   If S = 'CONFIG' then DL := dlConfig;
  end;
  If ParamStr(I)[1] <> '/' Then ConfigFile := ParamStr(I);
 End;
 CM2MainForm.Caption := 'CoolMon 2 - Initialing...'; // for detecting the window if already running
 If Debug Then begin
//  Form4 := TForm4.Create(nil);
  Application.CreateForm(TForm4, Form4);
  Form4.DebugLevel := DL;
  Form4.show;
  Form4.Add('CoolMon 2 - Core Version ' + CM2MMRB.WordToStr(CoreVersion), dlcrusial, mtinformation);
  Form4.Add('', dlcrusial, mtinformation);
  Form4.Add('Program Running ...', dlcrusial, mtwarning);
  Form4.add('Create splash ...', dlcore, mtinformation);
  If SilentDebug then Form4.hide;
  Form4.Update;
  If DL = dlConfig then Debug := false;
 End;
 Splash := TSplash.Create(nil);
 If overrideCookie <> '' then begin
  Splash.TagLine.Caption := OverrideCookie
 end
  else
 begin
  Splash.TagLine.Caption := GenerateTagLine;
 end;
 Splash.TagLineShadow.Caption := Splash.Tagline.Caption;
 Splash.Show;
 If Debug then Form4.Append(' done', dlcore, mtInformation);
 Splash.SetText('Loading GDI+ Engine...');
 new(gdiPlusStartupInput);
 gdiPlusStartupInput.GdiplusVersion := 1;
 gdiPlusStartupInput.DebugEventCallback := nil;
 gdiPlusStartupInput.SuppressBackgroundThread := false;
 gdiPlusStartupInput.SuppressExternalCodecs := false;
 new(gdiPlusStartUpOutPut);
 gdiPlusStartupOutput.NotificationHook := nil;
 gdiPlusStartupOutput.NotificationUnhook := nil;

 Sleep(300);
 Splash.SetText('Analysing CML File');

 Reg := TRegistry.Create;
 Reg.OpenKey('\software\The CoolMon Project\CoolMon2', true);
 Reg.WriteString('', Paramstr(0));
 Reg.WriteString('core', CM2MMRB.WordToStr(CoreVersion));
 Reg.WriteString('LastConfig', ConfigFile);
 GPTC := GenList.Count;
 VPTC := VisList.Count;
 CPTC := 0;
 PlugCount := 0;
 SL := TStringlist.Create;
 if Reg.OpenKey(CertifiedGenPluginsLocation, false) then begin
  reg.GetKeyNames(SL);
  Inc(GPTC, SL.Count);
 end;
 if Reg.OpenKey(CertifiedVISPluginsLocation, false) then begin
  reg.GetKeyNames(SL);
  Inc(VPTC, SL.Count);
 end;
 if Reg.OpenKey(CertifiedCMLXPluginsLocation, false) then begin
  reg.GetKeyNames(SL);
  Inc(CPTC, SL.Count);
 end;
 PTC := GPTC + VPTC + CPTC;
 PTCMultiPlier := round(100 / PTC);
 Reg.CloseKey;
 Reg.Free;
 Splash.SetText('General PlugIns');
 If Debug then Form4.add('Create General PlugIn List ... ', dlcrusial, mtinformation);
 GenPlugInList := TGenPlugInList.Create(Self.Handle);
 GenPlugInList.Progress := gProgress;
 If Debug then Form4.add('Verify General PlugIn List ... ', dlcrusial, mtinformation);
 // Check all general plugins exists
 Reg := TRegistry.Create;
 If Reg.OpenKey(CertifiedGENPluginsLocation, false) then begin
  Reg.GetKeyNames(SL);
  For I := 0 to Sl.Count - 1 do begin
   If Reg.OpenKey(pathtook(CertifiedGENPluginsLocation) + sl[I], false) then begin
    If Reg.ValueExists('filename') then begin
     S := Reg.ReadString('Filename');
     If not fileexists(S) then begin
      MessageDlg('PlugIn file:'+#13+#10+S+#13+#10+''+#13+#10+'Could not be found, it will be removed from the plugin list', mtError, [mbOK], 0);
      Reg.OpenKey(CertifiedGENPluginsLocation, true);
      Reg.DeleteKey(sl[I]);
     End;
    end;
   End;
  end;
 End;
 If Debug then Form4.Append('done', dlcore);
 If Debug then Form4.Add('General.LoadAll ... ', dlcrusial);

 GenPlugInList.LoadAll;

 If Debug then Form4.Append('done', dlcore);
 If Debug then Form4.Add('sleep', dlcore);
 Application.ProcessMessages;
 sleep(300);

 Splash.SetText('Visual PlugIns');
 If Debug then Form4.add('Create Visual PlugIn List ... ', dlcrusial, mtinformation);
 VisPlugInList := TVisPlugInList.Create;
 VisPlugInList.Progress := vProgress;
 If Debug then Form4.add('Verify Visual PlugIn List ... ', dlcrusial, mtinformation);
 // Check all visual plugins exists
 If Reg.OpenKey(CertifiedVISPluginsLocation, false) then begin
  Reg.GetKeyNames(SL);
  For I := 0 to Sl.Count - 1 do begin
   If Reg.OpenKey(pathtook(CertifiedVISPluginsLocation) + sl[I], false) then begin
    If Reg.ValueExists('filename') then begin
     S := Reg.ReadString('Filename');
     If not fileexists(S) then begin
      MessageDlg('PlugIn file:'+#13+#10+S+#13+#10+''+#13+#10+'Could not be found, it will be removed from the plugin list', mtError, [mbOK], 0);
      Reg.OpenKey(CertifiedVISPluginsLocation, true);
      Reg.DeleteKey(sl[I]);
     End;
    end;
   End;
  end;
 End;
 If Debug then Form4.Append('done', dlcore);
 If Debug then Form4.Add('Visual.LoadAll ... ', dlcrusial);

 VisPlugInList.LoadAll;
 If Debug then Form4.Append('done', dlcore);
 If Debug then Form4.Add('sleep', dlcore);
 Application.ProcessMessages;
 sleep(300);

 Splash.SetText('CMLX PlugIns');
 If Debug then Form4.add('Create CMLX PlugIn List ... ', dlcrusial, mtinformation);
 CMLXPlugInList := TCMLXPlugInList.Create(Self.Handle);
 CMLXPlugInList.Progress := cProgress;
 If Debug then Form4.add('Verify CMLX PlugIn List ... ', dlcrusial, mtinformation);
 // Check all visual plugins exists
 If Reg.OpenKey(CertifiedCMLXPluginsLocation, false) then begin
  Reg.GetKeyNames(SL);
  For I := 0 to Sl.Count - 1 do begin
   If Reg.OpenKey(pathtook(CertifiedCMLXPluginsLocation) + sl[I], false) then begin
    If Reg.ValueExists('filename') then begin
     S := Reg.ReadString('Filename');
     If not fileexists(S) then begin
      MessageDlg('PlugIn file:'+#13+#10+S+#13+#10+''+#13+#10+'Could not be found, it will be removed from the plugin list', mtError, [mbOK], 0);
      Reg.OpenKey(CertifiedCMLXPluginsLocation, true);
      Reg.DeleteKey(sl[I]);
     End;
    end;
   End;
  end;
 End;
 Reg.Free;
 SL.Free;
 If Debug then Form4.Append('done', dlcore);
 If Debug then Form4.Add('CMLX.LoadAll ... ', dlcrusial);
 CMLXPlugInList.LoadAll;
 If Debug then Form4.Append('done', dlcore);
 If Debug then Form4.Add('sleep', dlcore);
 Application.ProcessMessages;
 sleep(300);

 Splash.SetText('Form Manager');
 If Debug then Form4.Add('Create Form Manager ... ',dlcrusial);
 FormMgr := TCM2FormMgr.Create;
 If Debug then Form4.Append(' done', dlcore);
 If Debug then Form4.Add('Sleep', dlcore);
 Application.ProcessMessages;
 sleep(300);

 Splash.SetText('Variable Manager');
 If Debug then Form4.Add('Create Variable Manager ... ', dlcrusial);
 VarMgr := TVarMgr.Create(true);
 If Debug then Form4.append('done', dlcore);
 If Debug then Form4.Add('sleep', dlcore);
 Application.ProcessMessages;
 sleep(300);

 Splash.SetText('CML Engine');
 If Debug then Form4.Add('Create CML Tree ... ', dlcrusial);
 Tree := TCM2Tree.Create(Addr(FormMgr), Addr(VarMgr), Addr(GenPlugInList), Addr(VisPlugInList), Addr(CMLXPlugInList));
 If Debug then Form4.Append('done', dlcore);
 If DL = dlConfig then Debug := true;
 If Debug then Form4.Add('Load config ...', dlcrusial, mtCustom);
 Splash.SetText('Loading Config ...');
 IF not fileexists(ConfigFile) then Begin
  MessageDlg('The config file'+#13+#10+ConfigFile+#13+#10+'Could not be found', mtError, [mbOK], 0);
  Application.Terminate;
  Exit;
 End;
 Try
  Tree.Root.LoadFromFile(ConfigFile);
 except
  On ECMLError do begin
   Application.Terminate;
  End;
 end;
 Splash.SetProgress(100);
 If Debug then Form4.Add('Done loading Config', dlcrusial, mtCustom);
 If DL = dlConfig then debug := false;
 If Debug then Form4.Add('sleep', dlcore);
 Application.ProcessMessages;
 sleep(300);
 If Debug then Form4.Add('Free Splash', dlcore);
 Splash.Free;
 If Debug then Form4.Add('Set VarMgr, Timer1, Tray = Active ... ', dlcrusial);
 Timer1.Enabled := True;
 VarMgr.Active := true;
 Tray.Active := true;
 If Debug then form4.append('done', dlcore);
 If Debug then Form4.Add('Minimize to tray ... ', dlcrusial);
 Tray.MinimizeToTray;
 If Debug then Form4.Append('done', dlcore);
 FreeAndNil(GenList);
 FreeAndNil(VisList);
 FormMgr.UpdatePositions;
 Caption := 'CoolMon 2 - Running';
 InitOptions;
 LoadOptions;
 SetOptions;

end;

procedure TCM2MainForm.Exit1Click(Sender: TObject);
begin
 VarMgr.Suspend;
 FreeAndNil(Tree);
 FreeAndNil(VarMgr);
 FreeAndNil(FormMgr);
 FreeAndNil(VisPlugInList);
 FreeAndNil(GenPlugInList);
 FreeAndNil(CMLXPlugInList);
 If Assigned(Form4) then FreeAndNil(Form4);

 // Can't fint the GDIPAPI file anywhere - I'm sure there was a specific reason
 // for using the file. I seem to vaguely recall blert mentioned stabilty
 // issues.
 // GDIPAPI.GdiplusShutdown(GDIPlusToken);

 // A google search offered the following solution, but it's a shot in the dark
 GdiplusShutdown(GDIPlusToken);

 Dispose(gdiPlusStartupInput);
 Dispose(gdiPlusStartupOutput);
 Application.Terminate;
end;

procedure TCM2MainForm.Timer1Timer(Sender: TObject);
VAr
 I: Integer;
begin
 If Debug then Form4.Add('Process Children', dlcore);
 timer1.enabled := false;
 RunningState := rsRunning;
 SElf.Tree.Root.ProcessChildren;
 Self.Tree.Root.ProcessParameters;
 CM2API.APIObj.OnAPI := OnAPI;
 ///////////////
 // Post Init //
 ///////////////
 For I := 0 to Self.FormMgr.Count - 1 do begin
  Self.FormMgr[I].Show;
  Self.FormMgr[I].Update;
  Self.FormMgr[I].CheckforWM;
  Self.FormMgr[I].ReInitAllDC;
 End;
 Resume; // Just to set correct Tray Icon
 Self.VarMgr.DoPollAll; // Update all dynamic values, so not to wait for update to occur
 Tray.ReTray := false;
 Application.ProcessMessages;
 FormMgr.UpdatePositions;
 ShowWindow(application.handle, SW_HIDE);
end;

procedure TCM2MainForm.About1Click(Sender: TObject);
Var
 AboutDlg: TAboutDlg;
begin
 If Debug then Form4.Add('Show about ... ', dlcore);
 Try
  AboutDlg := TAboutDlg.Create(self);
  Aboutdlg.Execute;
  AboutDlg.Free;
  If Debug then Form4.Append('done', dlcore);
 Except
  If Debug then Form4.append('failure', dlcore);
 end;

end;

procedure TCM2MainForm.ApplicationEvents1Exception(Sender: TObject;
  E: Exception);
begin
 If Debug then Form4.Add('Exception: ' + E.ClassName + ' [' + E.Message + ']', dlerror, mtError);
// If E.ClassType = EAccessViolation then Tray.ShowBalloonTips(btWarning, 'CoolMon 2 has just reported an access violation' + #13#10 + 'Message: ' + #13#10 + E.Message, 'Access Violation detected');
 If E.ClassType = EDOMParseError then begin
  MessageDlg('Your CML config contains a syntax error.'+#13+#10+''+#13+#10+'The XML Engine provided this information:'+#13+#10+ E.Message +#13+#10+''+#13+#10+'CoolMon 2 will now close.', mtError, [mbOK], 0);
  Application.Terminate;
 end;
end;

procedure TCM2MainForm.ReloadConfig1Click(Sender: TObject);
Var
 I: Integer;
begin
 If Debug Then Form4.Add('Reload Config ...', dlcrusial);
 If Debug Then Form4.Add('Freeing Tree ... ', dlcrusial);
 FreeAndNil(Tree);
 If Debug Then Form4.Append('done', dlcore);
 If Debug Then Form4.Add('Freeing Form Manager ... ', dlcrusial);
 FreeAndNil(FormMgr);
 If Debug Then Form4.Append('done', dlcore);
 If Debug Then Form4.Add('Freeing Variable Manager ... ', dlcrusial);
 FreeAndNil(VarMgr);
 If Debug Then Form4.Append('done', dlcore);

 If Debug then Form4.Add('Create Form Manager ... ', dlcrusial);
 FormMgr := TCM2FormMgr.Create;
 If Debug then Form4.Append('done', dlcore);

 If Debug then Form4.Add('Create Variable Manager ... ', dlcrusial);
 VarMgr := TVarMgr.Create(true);
 If Debug then Form4.append('done', dlcore);

 If Debug then Form4.Add('Create CML Tree ... ', dlcrusial);
 Tree := TCM2Tree.Create(Addr(FormMgr), Addr(VarMgr), Addr(GenPlugInList), Addr(VisPlugInList), Addr(CMLXPlugInList));
 If Debug then Form4.Append('done', dlcore);
// Tree.Root :=
 If Debug then Form4.Add('Load config ...', dlcrusial);
 Try
  Tree.Root.LoadFromFile(ConfigFile);
 except
  On ECMLError do Begin
   MessageDlg('There was an error in the CML file.'+#13+#10+'CM2 will now terminate.', mtError, [mbOK], 0);
   Application.Terminate;
  end;
 end;
 For I := 0 to FormMgr.Count - 1 do begin
  FormMgr[I].Visible := True;
 End;
 If Debug then Form4.Add('Done loading Config', dlcrusial);
 If Debug then Form4.Add('Set VarMgr, Timer1, Tray = Active ... ', dlcore);
 Timer1.Enabled := True;
 SetOptions;
 VarMgr.Active := true;
 Tray.Active := true;
 If Debug then form4.append('done', dlcore);
 If Debug then Form4.Add('Minimize to tray ... ', dlcrusial);
 Tray.MinimizeToTray;
 If Debug then Form4.Append('done', dlcore);
 ShowWindow(application.handle, SW_HIDE);
end;

procedure TCM2MainForm.ShowConsole1Click(Sender: TObject);
var
  Pre, Post, s: string;
  handled: boolean;
  I: Integer;
  List: TStrings;
begin
  AllocConsole;
  try
    // Change color attributes
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
                                         FOREGROUND_RED OR FOREGROUND_GREEN OR FOREGROUND_BLUE
                                         OR FOREGROUND_INTENSITY);
    SetConsoleTitle(Pchar('CoolMon 2 Command Console'));
    Writeln('CoolMon 2 Command console');
    Repeat
     writeln('');
     Write('Command>');
     Readln(s);
     Handled := False;
     Pre := uppercase(GetFilt(S, 1, '.'));
     Post := DelFilt(S, 1, '.');
     If CountFilt(S, '.') = 1 Then Post := '';
     If (uppercase(S) = 'APPLICATION.PROCESSMESSAGES') or (uppercase(S) = 'UPDATE') or (Uppercase(s) = 'AP') then begin
      Handled := true;
      Write('Emptying message list ... ');
      Application.ProcessMessages;
      Writeln('done');
     End;
     // Small help
     If (uppercase(pre) = 'VERSION') then begin
      Handled := true;
      Writeln('');
      Writeln('CoolMon 2');
      writeln('Program Version: ' + progVersion);
      Writeln('Compiled with version ' + cm2mmrb.wordtoStr(coreversion) + ' of the CoolMon 2 core software');
      Writeln('');
     end;
     If (uppercase(pre) = 'HELP') or (Pre = '?') then begin
      HAndled := true;
      Writeln('Valid commands');
      Writeln('');
      Writeln('Get - Various state information');
      Writeln('FormMgr - Information about the form manager');
      Writeln('VarMgr - Information about the Variable Manager');
      Writeln('');
      Writeln('Help in sub catagories, can be archive by typing the group name followed by ".?"');
      Writeln('Eg. VarMgr.?');
      Writeln('');
      writeln('To exit console simply type "Quit" [Enter]');
      Writeln('');
     end;
     If Uppercase(Pre) = 'DUMP' then begin
      Handled := true;
      If uppercase(GetFilt(POST, 1, ' ')) = 'XML' then begin
       Post := DelFilt(POST, 1, ' ');
       S := Self.Tree.Root.Node[0].WriteXML;
       List := TStringList.Create;
       List.Text := S;
       List.SaveToFile(Post);
       FreeAndNil(List);
      end;
     end;
     If Uppercase(Pre) = 'SET' then begin
      handled := true;
      If Uppercase(getfilt(POST, 1, ' ')) = 'DEBUG' then begin
       If (uppercase(GetFilt(Post, 2, ' ')) = 'ON') or (uppercase(GetFilt(Post, 2, ' ')) = '1') then begin
        Application.CreateForm(TForm4, Form4);
        Form4.DebugLevel := dlPlugins;
        Form4.show;
        Debug := true;
        Application.ProcessMessages;
       end;
       If (uppercase(GetFilt(Post, 2, ' ')) = 'OFF') or (uppercase(GetFilt(Post, 2, ' ')) = '0') then begin
        Debug := False;
        Form4.Hide;
        FreeAndNil(Form4);
        Application.ProcessMessages;
       end;
      end;
     end;
     If uppercase(Pre) = 'GET' then begin
      Handled := True;
      If (Uppercase(Post) = 'DEBUG') or (Uppercase(Post) = 'DEBUGSTATE') Then begin
       Write('Debug State = ');
       If not Debug then writeln('Off')
        else
       begin
        Write('On [');
        Case form4.Debuglevel of
         dlAll: Write(' All ');
         dlCore: Write(' Core ');
         dlPlugins: Write(' Plugins ');
         dlError: Write(' Errors ');
         dlCrusial: Write(' Crusial ');
        end;
        writeln(']');
       end;
      end;
      If uppercase(Post) = 'SUSPENDSTATE' then begin
       Write('Suspend State = ');
       If RunningState = rsSuspended then writeln('Yes')
        else Writeln('No');
      end;
      If uppercase(Post) = 'RETRAYSTATE' then begin
       Write('Retray State = ');
       If Options.SystrayIcon.Retray then Writeln('Enabled [' + IntToStr(Options.SystrayIcon.RetrayInterval) + ']')
        else writeln('Disabled');
      end;
      If uppercase(Post) = 'LOADED' then begin
       Writeln(''); writeln('Loaded plugins');
       writeln(''); Writeln('General'); Writeln('-------');
       For I := 0 to GenPlugInList.Count - 1 do begin
        Writeln(GenPlugInList[I].Name);
       end;
       writeln(''); Writeln('Visual'); Writeln('------');
       For I := 0 to VisPlugInList.Count - 1 do begin
        Writeln(VisPlugInList[I].Name);
       end;
       writeln(''); Writeln('CMLx'); Writeln('----');
       For I := 0 to CMLxPlugInList.Count - 1 do begin
        Writeln(CMLxPlugInList[I].Name);
       end;
      end;
      If (uppercase(Post) = 'LIST') or (Post = '?') then begin
       Writeln('Valid Get Commands are');
       Writeln('');
       Writeln('SuspendState - Get the current state of flag');
       Writeln('RetrayState - Get the current state of flag');
       Writeln('DebugState - Get the current state of flag');
       Writeln('Loaded - Get list of current loaded plugins');
      end;
     end;
     If uppercase(Pre) = 'FORMMGR' then begin
      Handled := true;
      If (Post = '') or (Post = '?') then begin
       Writeln('Valid Form Manager Commands are');
       Writeln('');
       Writeln('List - list all forms');
       Writeln('List <form number> - returns form properties');
       Writeln('UpdatePositions - Update Right and Bottom values');
      End;
      If Uppercase(Post) = 'UPDATEPOSITIONS' then begin
       FormMgr.UpdatePositions;
       Application.ProcessMessages;
      end;
      If uppercase(GetFilt(post, 1, ' ')) = 'LIST' Then begin
       Writeln('FORM PROPERTY LISTING');
       If Uppercase(post) = 'LIST' then begin
        Writeln('Items' + #9 + 'Name');
        For I := 0 to Formmgr.Count - 1 do begin
         Writeln(IntToStr(I) + #9 + FormMgr[I].Caption);
        End;
       End
        else
       Begin
        POST := DelFilt(Post, 1, ' ');
        Try
         I := StrToInt(Post);
        except
         Writeln('ERROR no number present - Using 0');
         I := 0;
        End;
        Writeln('');
        Writeln('NAME: ' + FormMgr[I].Caption + ' [' + FormMgr[I].Name + ']');
        Write('Visible: ');
        If FormMgr[I].Visible Then Writeln('Yes')
         else Writeln('No');
        Writeln('DeviceContent (DC): ' + IntToStr(FormMgr[I].DC));
        Writeln('Color: ' + ColorToString(FormMgr[I].Color));
        Writeln('X, Y: ' + IntToStr(FormMgr[I].Left) + ', ' + IntToStr(FormMgr[I].Top));
        Writeln('W, H: ' + IntToStr(FormMgr[I].Width) + ', ' + IntToStr(FormMgr[I].Height));
        Writeln('R, B: ' + IntToStr(FormMgr[I].Right) + ', ' + IntToStr(FormMgr[I].Bottom));
        Write('Window Mode: ');
        Case FormMgr[I].WindowMode of
         wmUnknown: Writeln('Normal');
         wmPinToDesktop: Writeln('Pin to desktop');
         wmAOT: Writeln('Always on top');
        end;
        Write('Transparent: ');
        If FormMgr[I].Transparent then begin
         Writeln('Yes [' + ColorToString(FormMgr[I].TransparentColorValue) + ']');
        end
         else Writeln('No');
        Write('AlphaBlend: ');
        If FormMgr[I].AlphaBlend then begin
         Writeln('Yes [' + IntToStr(FormMgr[I].AlphaBlendValue))
        end
         else Writeln('No');
       End;
      End;
     End;
     If uppercase(Pre) = 'VARMGR' Then begin
      Handled := True;
      If (Post = '') or (Post = '?') then Begin
       Writeln('Valid Variable Manager commands are');
       Writeln('');
       Writeln('List - list all items');
       Writeln('List <items number> - returns item properties');
       writeln('');
       Writeln('Eg.');
       Writeln('VarMgr.List 0');
      End;
      If uppercase(GetFilt(post, 1, ' ')) = 'LIST' Then begin
       If Uppercase(post) = 'LIST' then begin
        Writeln('Items' + #9 + 'Name');
        For I := 0 to varmgr.Count - 1 do begin
         Writeln(IntToStr(I) + #9 + VarMgr[I].Name);
        End;
       End
        else
       Begin           
        POST := DelFilt(Post, 1, ' ');
        Try
         I := StrToInt(Post);
        except
         Writeln('ERROR no number present - Using 0');
         I := 0;
        End;
        Writeln('NAME: ' + VarMgr[I].Name);
        Write('State: ');
        If VarMgr[I].Active then writeln('Active')
         else Writeln('Not Active');
        Write('VALUE ');
        Case VarMgr[I].Result.SensorStyle of
         ssNumeric: Writeln('[Numeric]: ' + FloatToStr(VarMgr[I].Result.Value));
         ssNonNumeric: Writeln('[NonNumeric]: ' + VarMgr[I].Result.Str);
         else Writeln('N/A');
        End;
        Writeln('Parameters: ' + VarMgr[I].Parameters);
        Writeln('Last Ran: ' + VarMgr[I].LastRan);
        Write('Is Polling: ');
        If VarMgr[I].Polling then Writeln('Yes')
         else Writeln('No');
        If (VarMgr[I] is TGenItem) then begin
         Write('Dedicated: ');
         If (VarMgr[I] as TGenItem).RunningDedicated then writeln('Yes')
          else Writeln('No');
         Writeln('Update Every [In Sec]: ' + IntToStr((VarMgr[I] as TGenItem).UpdateFreq));
         If (VarMgr[I] as TGenItem).RunningDedicated then begin
          Writeln('Next update isn''t available when running dedicated');
         end
          else
         begin
          Writeln('Next update [In Sec]: ' + IntToStr((VarMgr[I] as TGenItem).CountDown));
         end;
        end;
        If (VarMgr[I] is TAdvTimedGenItem) then begin
         Writeln('Update String: ' + (VarMgr[I] as TAdvTimedGenItem).UpdateString);
        end;
       End;
      End;
      If uppercase(GetFilt(post, 1, ' ')) = 'POLL' Then begin
       POST := DelFilt(Post, 1, ' ');
       Try
        I := StrToInt(Post);
       except
        Writeln('ERROR no number present - Using 0');
        I := 0;
       End;
       Write('Polling ' + IntToStr(I) + ' ... ');
       VarMgr[I].Poll(self);
       Writeln('done');
      End;
     End;
     If not handled then Writeln('Unknown Command');
    until (uppercase(S) = 'QUIT') or (Uppercase(S) = 'EXIT');
  finally
    FreeConsole;
  end;
end;

function TCM2MainForm.GenerateTagLine: String;
Var
 I: Integer;
begin
 I := Random(TagLines);
 Result := TagLine[I];
end;

procedure TCM2MainForm.est1Click(Sender: TObject);
Var
 I: Integer;
begin
 For I := 0 to Self.FormMgr.Count - 1 do begin
  SElf.FormMgr[I].ReInitAllDC;
 End;
end;

procedure TCM2MainForm.CheckForSuspendTimer(Sender: TObject);
Var
 aHandle: THandle;
 aRect: TRect;
 len: Word;
 p: PChar;
 strBuff: String;
begin
 aHandle := GetForegroundwindow;
 GetWindowRect(aHandle, arect);
 if (arect.Left = 0) and (arect.Top = 0) and (screen.Width = (arect.Right - arect.Left)) and (screen.height = (arect.Bottom - arect.Top)) then begin
  If runningState = rsRunning then begin
   // Fullscreen application is runnning, suspend
   Len := GetWindowTextLength(aHandle) + 2;
   SetLength(strBuff, Len);
   P := pchar(strBuff);
   GetWindowText(aHandle, p, len);
   If p = 'Program Manager' then exit; // bypass desktop
   SetLength(strBuff, 1024);
   P := pchar(strBuff);
   GetClassName(aHandle, P, 1024);
   If p = 'WorkerW' then exit; // Bypass dekstop
   Tray.ReTray := false; // Prevent fullscreen flickering (The retray effect will cause that)
   Suspend;
  end;
 end
  else
 begin
  If runningState = rsSuspended then begin
   // Not fullscreen - Resume
   Resume;
  end;
 end;
end;

procedure TCM2MainForm.Resume;
Var
 Icon: TIcon;
begin
 Icon := TIcon.Create;
 TrayIcons.GetIcon(0, Icon);
 tray.Icon := Icon;
 FreeAndNil(Icon);
 VarMgr.Resume;
 Suspend1.Caption := 'Suspend';
 If Options.SystrayIcon.Retray and (not Tray.ReTray) then begin
  Tray.ReTray := false;
 end;
 FormMgr.ReInitWindowPosition_All;
 If Options.Suspend.PollOnResume then VarMgr.DoPollAll;
 RunningState := rsRunning;
end;

procedure TCM2MainForm.Suspend;
Var
 Icon: TIcon;
begin
 If Options.SystrayIcon.SwitchIconWhenSuspended then begin
  Icon := TIcon.Create;
  TrayIcons.GetIcon(1, Icon);
  tray.Icon := Icon;
  FreeAndNil(Icon);
 End;
 VarMgr.Suspend;
 Suspend1.Caption := 'Resume';
 RunningState := rsSuspended;
end;

procedure TCM2MainForm.Suspend1Click(Sender: TObject);
begin
 If runningstate = rsRunning then begin
  Suspend;
  CheckForSuspend.Enabled := false;
 end
  else
 begin
  Resume;
  CheckForSuspend.Enabled := True;
 end;
end;

procedure TCM2MainForm.Options1Click(Sender: TObject);
begin
 If assigned(PrefDlg) then exit;
 Try
  PrefDlg := TPrefDlg.Create(self);
  PrefDlg.Execute;
 finally
  FreeAndNil(PrefDlg);
 end;
 SaveOptions;
 SetOptions;
end;

procedure TCM2MainForm.WndProc( var Msg: TMessage);
begin
  inherited;
  if Msg.Msg = MsgTaskbarCreated then
  begin
    tray.DoRetray;
  end;
end;

procedure TCM2MainForm.SetFormVisible(FormName: string; DoShow: boolean; DoToggle: boolean = false);
Var
 I: Integer;
begin
 For I := 0 to Formmgr.Count - 1 do begin
  If uppercase(FormMgr[I].Caption) = uppercase(formname) then begin
   If not DoToggle then begin
    FormMgr[I].Visible := DoShow;
   end
    else
   begin
    FormMgr[I].Visible := Not FormMgr[I].Visible;
   end;
  end;
 end;
end;

procedure TCM2MainForm.ONAPI(Owner: PVisObject; APIStr: PChar;
  var CallBackStr: String);
begin
 If uppercase(Copy(APIStr, 1, length('SET_TOOLTIP:'))) = 'SET_TOOLTIP:' then begin
  CM2MainForm.tray.Hint := Copy(APIStr, length('SET_TOOLTIP:') + 1, length(APIStr));
 end;
 if uppercase(Copy(APIStr, 1, length('SHOW_FORM:'))) = 'SHOW_FORM:' then begin
  CM2MainForm.SetFormVisible(Copy(APIStr, length('SHOW_FORM:') + 1, length(APIStr)), true);
 end;
 if uppercase(Copy(APIStr, 1, length('HIDE_FORM:'))) = 'HIDE_FORM:' then begin
  CM2MainForm.SetFormVisible(Copy(APIStr, length('HIDE_FORM:') + 1, length(APIStr)), false);
 end;
 if uppercase(Copy(APIStr, 1, length('TOGGLE_FORM:'))) = 'TOGGLE_FORM:' then begin
  CM2MainForm.SetFormVisible(Copy(APIStr, length('TOGGLE_FORM:') + 1, length(APIStr)), false, true);
 end;
end;

procedure TCM2MainForm.EditConfig1Click(Sender: TObject);
Var
 Reg: TRegistry;
 EddieLoc: String;
begin
 Reg := TRegistry.Create;
 Reg.RootKey := HKEY_CURRENT_USER;
 EddieLoc := '';
 If reg.OpenKey('\Software\The CoolMon Project\CoolMon2\Editor', false) then begin
  EddieLoc := Reg.ReadString('');
 end;
 FreeAndNil(Reg);
 ExecApp(EddieLoc, '"/RELAUNCHCORE" ' + '"' + ConfigFile + '"');
 Application.Terminate;
end;

end.
