DPR / LPR - Program without any window or CMD window... lazarus / 34-Windows ---------------------------------------------------------------------- RAW · 2016-07-07 22:38 I figured out how to start two Windows-Explorer side by side with one click on my QuickLaunch-Button, so I don't need to use the two taskbar buttons anymore. That is very nice, but it only works as I expected with Delphi 7 (DPR- Program). Using the code in Lazarus 1.6 is working too, but only when I set the compiler to "win32" and "i386" and include "LCL" and "LCLBase" and use "{$Mode Delphi}. But even then I see a CMD-window in the background. Of course, I don't need a window at all and really don't need a cmd- window... looks nasty ...  When I change the mode to "{$Mode ObjFpc}{$H+}" then the compiler doesn't like my "SearchCallback" (EnumWindows). Looks like I need LongInt but the compiler found TStringlist. ??? Unfortunately I don't get it ...  Can anybody give me a helping hint... ? Is there an easy way to only use a LPR-Program without seeing any cmd- window at all ??? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i PROGRAM TwoWinEx; {$Mode Delphi} USES  Windows, Messages, Classes, Forms, SysUtils, ShellApi, Interfaces;  VAR  slFoundWnd: TStringlist;  WndHandle : THandle;  I         : Integer;  Procedure ErrorLog(ErrorInfo: String);  Var   slLoad     : TStringlist;   slSave     : TStringlist;   strErrorLog: String;   Procedure Free_slLoad;   Begin    Try     FreeAndNil(slLoad);    Except    End;   End;   Procedure Free_slSave;   Begin    Try     FreeAndNil(slSave);    Except    End;   End;   Function ErrorLogExists: Boolean;   Begin    Try     Result:= False;      If FileExists('ErrorLog.txt')      Then     Begin        Try         slLoad:= TStringlist.Create;         slLoad.LoadFromFile('ErrorLog.txt');          strErrorLog:= slLoad.Text;          Result:= True;        Finally         Free_slLoad;        End;       End;    Except    End;   End;  Begin  Try   Try    slSave:= TStringlist.Create;     slSave.Add(DateTimeToStr(Now));     slSave.Add(ErrorInfo);       If ErrorLogExists      Then       Begin        slSave.Add(' ');        slSave.Add(Trim(strErrorLog));       End;      slSave.SaveToFile('ErrorLog.txt');   Finally    Free_slSave;   End;  Except  End; End;  Function SearchCallback(Wnd: HWND; sl:TStringlist): BOOL; StdCall;  Var   ClassName: Array[0..255] Of Char; Begin  Try   If GetClassName(Wnd, ClassName, 255) > 0   Then    Begin     If Pos('CabinetWClass', String(ClassName)) <> 0     Then sl.Add(IntToStr(Wnd));    End;   Result:= True;  Except   On E: Exception   Do ErrorLog('SearchCallback'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Procedure SearchWnd; Begin  Try   EnumWindows(@SearchCallback, Integer(slFoundWnd));  Except   On E: Exception   Do ErrorLog('SearchWnd'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Procedure MinAllWnds; Begin  Try   Keybd_event(VK_LWIN,   0, 0, 0);   Keybd_event(Byte('M'), 0, 0, 0);   Keybd_event(Byte('M'), 0, KEYEVENTF_KEYUP, 0);   Keybd_event(VK_LWIN,   0, KEYEVENTF_KEYUP, 0);  Except   On E: Exception   Do ErrorLog('MinAllWnds'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Procedure StartTwoWinEx; Begin  Try   If DirectoryExists('I:\') And DirectoryExists('I:\(DOWNLOADS)')   Then  Begin     ShellExecute(0, Nil, PChar('I:\'),            Nil, Nil, SW_SHOW);     ShellExecute(0, Nil, PChar('I:\(DOWNLOADS)'), Nil, Nil, SW_SHOW);       Repeat       slFoundWnd.Clear;       SearchWnd;      Until slFoundWnd.Count >= 2;      MoveWindow(StrToInt(slFoundWnd[0]),                0,                0,                (Screen.WorkAreaWidth Div 2),                Screen.WorkAreaHeight,              True);      MoveWindow(StrToInt(slFoundWnd[1]),              (Screen.WorkAreaWidth Div 2),                0,                (Screen.WorkAreaWidth Div 2),                Screen.WorkAreaHeight,              True);    End   Else    Begin     ShellExecute(0, Nil, PChar('C:\'), Nil, Nil, SW_SHOW);     ShellExecute(0, Nil, PChar('D:\'), Nil, Nil, SW_SHOW);       Repeat       slFoundWnd.Clear;       SearchWnd;      Until slFoundWnd.Count >= 2;      MoveWindow(StrToInt(slFoundWnd[0]),                0,              0,                (Screen.WorkAreaWidth Div 2),                Screen.WorkAreaHeight,                True);      MoveWindow(StrToInt(slFoundWnd[1]),                (Screen.WorkAreaWidth Div 2),                0,                (Screen.WorkAreaWidth Div 2),                Screen.WorkAreaHeight,              True);    End;  Except   On E: Exception   Do ErrorLog('StartTwoWinEx'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Procedure Free_slFoundWnd; Begin  Try   FreeAndNil(slFoundWnd);  Except   On E: Exception   Do ErrorLog('Free_slFoundWnd'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Begin Try  slFoundWnd:= TStringlist.Create;   Try    SearchWnd;     If slFoundWnd.Count >= 1     Then      Begin       For I:= 0 To slFoundWnd.Count-1       Do        Begin         WndHandle:= StrToInt(slFoundWnd[I]);         SendMessage(WndHandle, WM_CLOSE, 0, 0);        End;      End;    MinAllWnds;    StartTwoWinEx;   Finally  Free_slFoundWnd;   End; Except  On E: Exception  Do ErrorLog('MAIN PRG'+#13#10+E.ClassName+#13#10+E.Message); End;End. ---------------------------------------------------------------------- molly · 2016-07-08 00:19 It's because of your declaration of the callback routine. You provide sl: TStringList as LParam. Formally it should be a LongInt (or at least an integer able to hold a pointer). So that seems to throw off objfpc mode. I haven't looked for the specific why. i solved with declaring the function as: Code: [Select]Function SearchCallback(Wnd: HWND; lparm: PtrInt): BOOL; StdCall; and adding: Code: [Select]Var   sl : TStringList absolute lparm; Please let know if this (doesn't) works for you. ---------------------------------------------------------------------- RAW · 2016-07-08 00:52 Stupid me !!! I figured it out just one minute ago... very very easy, but I couldn't see it... With {$Mode Delphi} it is working very fine .... I unchecked the Win32App-Box... that's the reason for the cmd-window ... holy s...    And with {$Mode ObjFpc}{$H+} it's working very fine too: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Function SearchCallback(Wnd: HWND; lParam: LongInt): BOOL; StdCall;  Var   ClassName: Array[0..255] Of Char; Begin  Try   If GetClassName(Wnd, ClassName, 255) > 0   Then    Begin     If Pos('CabinetWClass', String(ClassName)) <> 0     Then slFoundWnd.Add(IntToStr(Wnd));    End;   Result:= True;  Except   On E: Exception   Do ErrorLog('SearchCallback'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Procedure SearchWnd; Begin  Try   EnumWindows(@SearchCallback, 0);  Except   On E: Exception   Do ErrorLog('SearchWnd'+#13#10+E.ClassName+#13#10+E.Message);  End; End;   That's it very very easy... @molly: Thank you very much for your answer! I haven't tested if this is working too as 64bit-Program, maybe your PtrInt-Version is the better solution. I compiled the above code for "win32 / i386" OS and that is working perfectly... In the moment I don't need a 64bit-EXE... I'll try your version with 64bit... maybe it's working too... Thanks again... EDIT: I have never used something like this: Quotesl : TStringList absolute lparm;Interesting !!! ---------------------------------------------------------------------- molly · 2016-07-08 01:18 Quote from: SoE on July 08, 2016, 02:52:53 amThank you very much for your answer! No problem. I'm glad you figured it out yourself as well  not wanting to steer the direction of your program but in case you find it interesting. A small adjustment lets your code compile with FPC (no lazarus components/units required) as well. Code: [Select]Type   TSimpleScreen = class(TObject)    private     function GetWorkAreaLeft: Integer;     function GetWorkAreaRect: TRect;     function GetWorkAreaTop: Integer;     function GetWorkAreaHeight: Integer;     function GetWorkAreaWidth: Integer;    public     property WorkAreaRect: TRect read GetWorkAreaRect;     property WorkAreaHeight: Integer read GetWorkAreaHeight;     property WorkAreaLeft: Integer read GetWorkAreaLeft;     property WorkAreaTop: Integer read GetWorkAreaTop;     property WorkAreaWidth: Integer read GetWorkAreaWidth;    end; VAR   screen    : TSimpleScreen; function TSimpleScreen.GetWorkAreaLeft: Integer; begin   Result := WorkAreaRect.Left; end; function TSimpleScreen.GetWorkAreaRect: TRect; begin   SystemParametersInfo(SPI_GETWORKAREA, 0, @Result, 0); end; function TSimpleScreen.GetWorkAreaTop: Integer; begin   Result := WorkAreaRect.Top; end; function TSimpleScreen.GetWorkAreaHeight: Integer; begin   with WorkAreaRect do Result := Bottom - Top; end; function TSimpleScreen.GetWorkAreaWidth: Integer; begin   with WorkAreaRect do Result := Right - Left; end; // and in your try finally block at bottom add   Screen := TSimpleScreen.Create; // and   Screen.Free; Of course, you can also create simple functions for them. The above is an honest copy-paste from lazarus sources  QuoteEDIT: I have never used something like this: Quotesl : TStringList absolute lparm;Interesting !!! It is one of the best inventions since kindergarten  It comes in really handy when trying to address a variable inside an array or similar, e.g.: var   anArray       : packed array[0..3] of byte;   aLongWord : LongWord absolute anArray; Poof ! no more casting/shifting/masking  required ---------------------------------------------------------------------- RAW · 2016-07-08 02:46 Looks like PtrInt is the best solution otherwise I had to change LPARAM from LongInt to Int64 and back when I need 32 or 64bit... Then it will compile as 64bit without any problems... very nice... QuoteA small adjustment lets your code compile with FPC (no lazarus components/units required) as well. Sounds good, I only used "Forms" to get the Screen Object for the workarea... and all the other stuff I thought is needed to get rid of that unholy cmd-window....  but if I compile it with FPC, then I will see a cmd-window or is there another way ...? Normally everybody is complaining about my weird, paranoid exception- handling... but I'm used to it.... maybe one time I'll change that behaviour...  Quotevar  anArray: packed array[0..3] of byte;  aLongWord: LongWord absolute anArray; Hmmm, I need to keep that in my mind... need to play around with it a little bit... ---------------------------------------------------------------------- molly · 2016-07-08 03:06 Quote from: SoE on July 08, 2016, 04:46:50 amLooks like PtrInt is the best solution otherwise I had to change LPARAM from LongInt to Int64 and back when I need 32 or 64bit... Then it will compile as 64bit without any problems... very nice... There are some more of those types defined, such as PtrUInt and NativeInt for example. all listed here QuoteSounds good, I only used "Forms" to get the Screen Object for the workarea... and all the other stuff I thought is needed to get rid of that unholy cmd-window....  Well, for windows LCL application you can use the checkbox in project options as you figured out. Quotebut if I compile it with FPC, then I will see a cmd-window or is there another way ...? Well, surprisingly the input/output is initialized by default, so an cmd window is shown briefly. Seems that {$APPTYPE GUI} gets rid of that (how ironic is that  ) Keep in mind though: any attempt to read something from input or write to output/error will most probably result in GPF errors. In case that becomes problematic, then be sure to catch them all (exceptions i meant) and attach the handles manually. At least in theory that should do the trick. QuoteNormally everybody is complaining about my weird, paranoid exception-handling... but I'm used to it.... maybe one time I'll change that behaviour...  Nah (although my word isn't a very good one  ), my code looks like yours when error-handling is implemented. I always start out with the nicest of code, but those users *grmbl*  Edit: some relevant links: - $APPTYPE - SysInitStdIO ---------------------------------------------------------------------- RAW · 2016-07-08 21:24 Hey molly, very good Idea to use FPC {$APPTYPE GUI} and not to use "Forms"... The EXE is much smaller now: 32bit 246 kb and 64bit 335 kb... really nice !!!  No LCL, LCLBase needed... Maybe I can use TSimpleScreen in another application. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i PROGRAM TwoWinEx; {$APPTYPE GUI} USES  Windows, Messages, Classes, SysUtils, ShellApi;  VAR  slFoundWnd: TStringlist;  WndHandle : THandle;  I       : Integer;   Procedure ErrorLog(ErrorInfo: String);  Var   slLoad   : TStringlist;   slSave     : TStringlist;   strErrorLog: String;   Procedure Free_slLoad;   Begin    Try     FreeAndNil(slLoad);    Except    End;   End;   Procedure Free_slSave;   Begin    Try     FreeAndNil(slSave);    Except    End;   End;   Function ErrorLogExists: Boolean;   Begin    Try     Result:= False;      If FileExists('ErrorLog.txt')      Then       Begin        Try         slLoad:= TStringlist.Create;         slLoad.LoadFromFile('ErrorLog.txt');          strErrorLog:= slLoad.Text;          Result:= True;        Finally         Free_slLoad;        End;       End;    Except    End;   End;  Begin  Try   Try    slSave:= TStringlist.Create;     slSave.Add(DateTimeToStr(Now));     slSave.Add(ErrorInfo);       If ErrorLogExists      Then       Begin        slSave.Add(' ');        slSave.Add(Trim(strErrorLog));       End;      slSave.SaveToFile('ErrorLog.txt');   Finally    Free_slSave;   End;  Except  End; End;  Function SearchCallback(Wnd: HWND; lParam: PtrInt): BOOL; StdCall;  Var   ClassName: Array[0..255] Of Char; Begin  Try   If GetClassName(Wnd, ClassName, 255) > 0   Then    Begin     If Pos('CabinetWClass', String(ClassName)) <> 0     Then slFoundWnd.Add(IntToStr(Wnd));    End;   Result:= True;  Except   On E: Exception   Do ErrorLog('SearchCallback'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Procedure SearchWnd; Begin  Try   EnumWindows(@SearchCallback, 0);  Except   On E: Exception   Do ErrorLog('SearchWnd'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Procedure MinAllWnds; Begin  Try   Keybd_event(VK_LWIN,   0, 0, 0);   Keybd_event(Byte('M'), 0, 0, 0);   Keybd_event(Byte('M'), 0, KEYEVENTF_KEYUP, 0);   Keybd_event(VK_LWIN,   0, KEYEVENTF_KEYUP, 0);  Except   On E: Exception   Do ErrorLog('MinAllWnds'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Procedure StartTwoWinEx;  Var   Path1, Path2: String;   WorkArea    : TRect;  Begin  Try   If DirectoryExists('I:\') And DirectoryExists('I:\(DOWNLOADS)')   Then    Begin     Path1:= 'I:\';   Path2:= 'I:\(DOWNLOADS)';    End   Else    Begin     Path1:= 'C:\';     Path2:= 'D:\';    End;       ShellExecute(0, Nil, PChar(Path1), Nil, Nil, SW_SHOW);   ShellExecute(0, Nil, PChar(Path2), Nil, Nil, SW_SHOW);     Repeat     slFoundWnd.Clear;     SearchWnd;    Until slFoundWnd.Count >= 2;    SystemParametersInfo(SPI_GETWORKAREA, 0, @WorkArea,0) ;    MoveWindow(StrToInt(slFoundWnd[0]),              WorkArea.Left,              WorkArea.Top,              ((WorkArea.Right-WorkArea.Left) Div 2),              (WorkArea.Bottom- WorkArea.Top),              True);    MoveWindow(StrToInt(slFoundWnd[1]),              ((WorkArea.Right- WorkArea.Left) Div 2),              WorkArea.Top,              ((WorkArea.Right-WorkArea.Left) Div 2),              (WorkArea.Bottom- WorkArea.Top),              True);  Except   On E: Exception   Do ErrorLog('StartTwoWinEx'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Procedure Free_slFoundWnd; Begin  Try   FreeAndNil(slFoundWnd);  Except   On E: Exception   Do ErrorLog('Free_slFoundWnd'+#13#10+E.ClassName+#13#10+E.Message);  End; End;  Begin Try  slFoundWnd:= TStringlist.Create;   Try    SearchWnd;     If slFoundWnd.Count >= 1     Then      Begin       For I:= 0 To slFoundWnd.Count-1       Do        Begin         WndHandle:= StrToInt(slFoundWnd[I]);         SendMessage(WndHandle, WM_CLOSE, 0, 0);        End;      End;    MinAllWnds;    StartTwoWinEx;   Finally  Free_slFoundWnd;   End; Except  On E: Exception  Do ErrorLog('MAIN PRG'+#13#10+E.ClassName+#13#10+E.Message); End;End.  QuoteI always start out with the nicest of code, but those users *grmbl*  Yes.... ---------------------------------------------------------------------- RAW · 2016-07-09 03:06 QuoteEdit: some relevant links: - $APPTYPE - SysInitStdIO SysInitStdIO: Is this only needed with a console-window? I mean I can use Exceptions and I can write files to HDD/SSD without any problems when I use {$APPTYPE GUI}. Maybe I don't understand what Standard I/O means? Is there anywhere a more detailed description ??? Everything is working nice so far without SysInitStdIO and SysInitExceptions... !? Is it only for Readln or WriteLn inside the console-window? ---------------------------------------------------------------------- molly · 2016-07-09 03:57 Quote from: SoE on July 09, 2016, 05:06:13 amIs it only for Readln or WriteLn inside the console-window? That is the general idea there, yes  Suppose you have such a program , like yours, that is not allowed to show the console window. It does work that way (using apptype GUI) right now. But you could perhaps imagine that it is allowed to have some feedback reported back to the user, in case an error occurred, by using writeLn's. Or perhaps you'd like to ask user for input under certain conditions. In such case that you wish to inform using command-line you would have need for standard input and output handles directed to/from console window. The sysInitIO would take care of initializing those IO handles required for reading/writing. Do note though, it does not seem to take care of creating the console automagically for us. Of course, it is perfectly valid to 'catch'em all' and log that into a file, or otherwise pop-up a requester/message-window to/for the user. I was merely attempting to guide you to some topics that are perhaps relevant to your program (as you seem to be a bit unfamiliar, and me in dire need to educate). Another such topic that might be relevant (seeing your code) would be to write your own text-file device driver and use that driver by default. That way you can use WriteLn's and its output _could_ be automagically written to a file (or send to debug, or send over the internets, etc.). Write/implement such driver once, and it saves you from ever having the need to call ErrorLog() again  Oh, btw. maybe it is me or my setup but it seems that directly after placing the explorer windows, that some background on the desktop is scrambled. I'm guessing that the desktop area perhaps needs to have a small redraw somewhere (i do note again: it might be my specific setup). ---------------------------------------------------------------------- Thaddy · 2016-07-09 08:46 Some notes: That code would not work in any modern delphi version because (I guess after D7) longint was replaced for Windows API parameter passing into something more appropiate.. The fact that FPC now uses LPARAM and WPARAM is in fact introduced because of Delphi compatibility. And because of 64 bit code. I noticed that because of the 64bit version of KOL a couple of years ago. So the fact that it does not compile is not an FPC issue, but an incompatibility between Delphi versions themselves. Alas, it is more visible when you use FPC or Lazarus, because with Delphi you are pretty much stuck with the libraries they provided and in FPC this is more dynamic. (Although Delphi libraries for one version can sometimes be compiled for higher or lower versions, depending on language changes, e.g. D4 and D5) Furthermore PtrInt, PtrUINT, NativeInt and the likes were also introduced because of Delphi compatibility. And of course: When you starting code is Delphi code use {$mode delphi}, which is D2006+ compatible  or {$mode delphiunicode} which is almost D2010+ compatible. I guess you were using one of the old Delphi's? Like 5 or 7? (I still use them myself). Delphi changed a lot. FPC follows most of it soon after. Last: {$mode objfpc} is definitely not Delphi compatible. e.g. in that mode when you pass a procedural variable like in your code, you need to prefix it with an  @ in objfpc. ---------------------------------------------------------------------- molly · 2016-07-09 13:03 @SoE Hmz, maybe i misunderstood something in your latest code but, would the following ErrorLog() be more efficient ? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program duError; {$MODE OBJFPC} uses  Classes, SysUtils;  Procedure ErrorLog(ErrorInfo: String);const  sFileName = 'duErrorLog.txt';var  cErrorList : TStringList;begin  cErrorList := TStringList.Create;   try      if FileExists(sFilename) then cErrorlist.LoadFromFile(sFileName);      cErrorList.BeginUpdate;    cErrorList.Insert(0, ErrorInfo);    cErrorList.Insert(0, DateTimeToStr(Now));    cErrorList.EndUpdate;     cErrorList.SaveToFile(sFileName);  finally    cErrorList.Free;   end;end; procedure test;type  THelp = record    ClassName : string;  Message   : string;  end;var  E: THelp = (ClassName : 'SomeClassName'; Message: 'someMessage'); begin  ErrorLog('SearchCallback'+#13#10+E.ClassName+#13#10+E.Message);  sleep(1000);  ErrorLog('MinAllWnds'+#13#10+E.ClassName+#13#10+E.Messag e);end;  begin  test;end. It is missing some nested exception handling though But to get back on topic: i do not fully seem to understand why you would want to use two stringlists (one for loading, one for saving) or why you manually would read the text property to add it to the other stringlist again (with some additions/insertions). Perhaps you have good reason to do so, in which case: please don't mind me and my mumbling  On a more general note concerning logging: are you aware that such logging (reversed order) is going to kill performance when certain sizes of the log-file are met ? It is more than sufficient and effective for a small program as you are currently working on but might perhaps bites you in the proverbial ass when the same technique is used inside a larger program (which does some heavy logging). ---------------------------------------------------------------------- Thaddy · 2016-07-09 15:30 ErrorList.insert is indeed highly inefficient even on smaller stringlists. Why do you do not a simple ErrorList.add?Even if you want to display in LIFO mode, because of the display code reverse order display is probably faster anyway.For Downto ---------------------------------------------------------------------- molly · 2016-07-09 16:17 Thank you for your detailed notes regarding the LParam type Thaddy. I could imagine it being a sort of luxury not having to need to scroll down a logfile to view the latest additions manually (i'm usually also in the habit of simply pressing F3 in my filemanager to view a file) . Indeed, a small quick (reverse/sorted) logviewer is able to solve that issue. Personally, i would prefer to use a filestream for such similar logging. Opening for each write, positioning at the end and write in the new stuff. Easy to check for size as well. That way it is possible to truncate the logs automatically (and keep backups of the previous logs). Each log entry prefixed with timestamp code, individual fields separated with tabs (or other unique separator, for easy import) and a viewer/display program that knows what to do with all of that. I'm more than positive that an implementation as described has been done before ---------------------------------------------------------------------- RAW · 2016-07-09 18:06 Wow, that's pretty much a lot of information... a lot more than I had expected.  @molly: First of all thank you very much for the detailed information about the "I/O-Thing". QuoteOr perhaps you'd like to ask user for input under certain conditions. I've never thougth about doing it with a cmd-window, normally I create a little form for such a thing... but it is always good to try new things... Quote..text-file device driver... Sounds interesting, but I'm not sure if my knowledge is big enough to write a device driver. On the other hand I can of course do a quick search and find out if there are some interesting examples out there... I'll keep that in my mind... Quote.. that some background on the desktop is scrambled. That's weird... shouldn't be the case, on my machine I can't see anything like that. Someone tested this little code on an XP-PC and W8.1-PC and W10-Pc and told me, that it's working fine with other Windows-Versions...  Maybe you can change some parts or check your display-driver or something like that... Quote..why you would want to use two stringlists (one for loading, one for saving) or... Yes, I probably should check if I can reduce that little Log-creation. Maybe I should take a look at TFileStream. The main reason why it looks like this is probably the circumstance, that loading a file could fail and then I would like to save at least the lastest ErrorInfo. But it's always good to make things faster, more stable or use less overhead... I need to check that. Quote..is going to kill performance when certain sizes of the log-file are met.. Yes, I heard that for real logging you should always use a database. My little ErrorLog is more a helping hand during the creation of the program and normally I don't see anything of it later... I like it when I do some testing outside the IDE. Thanks again... @Thaddy: Thank you very much for the information about lParam & Co. ... QuoteI noticed that because of the 64bit version of KOL a couple of years ago. That's interesting too, I didn't know that there is a 64bit version of KOL. Once I downloaded almost everything from the KOL-Site but couldn't find the time to test it all. I heard someone did a 64bit version for XE4... maybe I should take a look at the website again... btw: I found a SearchProgram on that site: FileSearch 1.2 by Vladimir and since then I only use his fine, little FileSearch and this is exactly what I was ever searching for. And it can do a lot of things more than I normally need. (HDD: 55kb, RAM: 1,2MB) I don't like the Standard-Windows-Search-Bloatware...    Normally I don't install it with Windows... Delphi 7 is very nice to create small Tools with little RAM usage and small exe-files. I mean if you don't want to use API-Only or KOL... without doing some compiler/linker-tricks...  Now I've got a lot of things to look into... ---------------------------------------------------------------------- RAW · 2016-07-11 15:33 @molly: You were totally right.... look at this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Procedure ErrorLog(ErrorInfo: String);  Var   slSave: TStringlist; Begin  Try   slSave:= TStringlist.Create;    Try     If FileExists('ErrorLog.txt')     Then      Begin       Try        slSave.LoadFromFile('ErrorLog.txt');       Except       End;      End;       slSave.Text:= DateTimeToStr(Now)+#13#10+                  ErrorInfo+#13#10+                    ''+#13#10+                    slSave.Text;       slSave.SaveToFile('ErrorLog.txt');    Finally     slSave.Free;    End;  Except  End; End; I don't know why I didn't see this in the first place ... EDIT: This is the rest of it... ---------------------------------------------------------------------- molly · 2016-07-12 14:19 Sorry for the delay, Quote from: SoE on July 09, 2016, 08:06:33 pmThat's weird... shouldn't be the case, on my machine I can't see anything like that. Someone tested this little code on an XP-PC and W8.1-PC and W10-Pc and told me, that it's working fine with other Windows-Versions...  After some research, i concluded this is a windows specific issue. If the desktop is relatively clean then everything goes fine but, the moment you have your desktop cluttered with about 10+ open application windows then the problem becomes clearly visible. Might even be an application specific issue (i stopped right there as i could not be bothered as in basics the technique from your code works). QuoteYes, I probably should check if I can reduce that little Log- creation. Maybe I should take a look at TFileStream. The main reason why it looks like this is probably the circumstance, that loading a file could fail and then I would like to save at least the lastest ErrorInfo. But it's always good to make things faster, more stable or use less overhead... I need to check that. Of course the loading could fail. But ask yourself the question: What is the reason to _load_ the already stored loglines ? The only reason, until now, was that you 'inserted' new loglines in front of the already logged ones. If you would lift that requirement/restriction then there would be no need anymore to load them... now would there ? I also use logfiles to monitor program progress. So not only logging exceptions. That way i can 'follow' newly introduced code by letting the program "flip a switch" in case a new release causes issues with the newly introduced code. Additional settings allow for more verbose logging in case required. That could end up in logfiles that are getting a bit bigger, and even attaching new loglines at the bottom is causing performance issues in the end. Reason why i usually truncate logfiles around 100/200 MB. Of course, mileage may vary depending on the hardware. Another option is using something like (windows specific) OutputDebugString(), in which case a special app is able to visualize the logging for you. Most of the above not very interesting for a project as small as shown by you but, the following snippet might get yourself familiar with using a filestream. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program duError2; {$MODE OBJFPC} uses  Classes, SysUtils; Procedure ErrorLog(ErrorInfo: String);const  LogFileName = 'MyProgramName.log';Var  FileStream : TFileStream;  LogString : String;  BytesWritten : Integer;begin  // create or append ?  if not FileExists(LogFileName)   then FileStream := TFileStream.Create(LogFileName, fmCreate)  else FileStream := TFileStream.Create(LogFileName, fmOpenReadWrite);   try    // let stream position point to EOF    if (FileStream.Size > 0) then FileStream.Position := Pred(FileStream.Size);     // compose log string    WriteStr(LogString, DateTimeToStr(Now), LineEnding, ErrorInfo, LineEnding, LineEnding);     // Write the actual string to the stream    BytesWritten := FileStream.Write(LogString[1], Length(LogString));     // check if number of bytes written matches what was expected to write    if (BytesWritten <> Length(LogString)) then    begin      // Since number of bytes written to stream did not match the length of       // the string we were trying to write this could be considered a serious       // error.      // act accordingly, in case no exception was raised/handled    end;  finally    FileStream.Free;  end;end; procedure test;type  THelp = record    ClassName : string;    Message   : string;  end;var  E: THelp = (ClassName : 'SomeClassName'; Message: 'someMessage'); begin  ErrorLog('SearchCallback'+#13#10+E.ClassName+#13#10+E.Message);  sleep(1000);  ErrorLog('MinAllWnds'+#13#10+E.ClassName+#13#10+E.Message);end; begin  test;end.  You could always opt for copying the old logfile first and start a fresh one in case you really are worried about messing up existing logdata. But honestly, in case filestream is able to mess up things then this usually means there are disk/filesystem errors and, there is nothing that you as applicaton developer can do about those. ---------------------------------------------------------------------- RAW · 2016-07-15 19:47 @molly: Thank you very much for your TFileStream-Example... Quote..you have your desktop cluttered with about 10+ open application windows then the problem becomes clearly visible. Ok, that's possible, I normally don't use so much programs at the same time, so maybe that's the reason why I didn't see anything like that... My individual maximum is normally 4 or 5 PRG's at the same time. Quote...What is the reason to _load_ the already stored loglines ? For me it's quite interesting to see if there was maybe another problem ealier... In some cases it's even of interest how many ErrorMessages of the same type are in the log. Sometimes it's easier to calculate what the problem is. QuoteReason why i usually truncate logfiles around 100/200 MB. Ok, that's big... In my case those files are very small and mostly only important during the program creation... Of Course, when the program is ready I don't want to see anything like that..  Now I've got 3 Versions: See the attachment...  The Restore Version normally needs a Path-Correction, but I couldn't manage this without a Delay or Sleep or Timer... So I decided to stop trying for now... All steps that are necessary are separately watched were easy... but when it comes to put them all together, then a special timing is needed otherwise it wouldn't work. I mean this: 1. Bring the window to top 2. Send VK_F4 and VK_ESCAPE to the WinEx-Window 3. Find the EDIT-comp of the WinEx-Window 4. Send the Path with WM_SetText 5. Finally send VK_Return All steps are very easy, but all together in one step and very fast they are not that easy for me... and when I use Sleep then it takes too much time... compared to the other versions. But no problem at all... I've got my versions and they are running fine so far... (the first version is sometimes not able to bring the second window to front, but the other 2 Versions are running stable on my machine)