Make a Single-Instance Application lazarus / 34-Windows ---------------------------------------------------------------------- rick2691 · 2017-12-16 17:29 This is a windows dependent method for making a single-instance application. Its main virtue is that it protects a database or document file from dual use. But it also protects any configuration files from being changed during use. My application is named CmdData. It manipulates a custom database. I place "CmdData... " into the application's MainForm.Caption. I also append that string with the database file name. These procedures search for the "CmdData... " string within the caption. It won't always work if there is another program active with the same string in their caption. It catches the first app in the windows catalog of active files. If yours was after it, it would take the first. So you want to be careful, and try to have a unique search string. Outside of the "CmdData" name, the "... " is what makes mine reasonably unique. Prior to this method I had built a file-dependent method that would work with any operating system. Unfortunately it would mess up if there was a crash or you ended the program abruptly. The file would still be there, and you would have to manually delete the file in order to launch your software. I think the following is a safer method than using a temp-file. 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 // add these at the top of your project as global variables:    var  AppInstanceLaunch: boolean;  AppInstanceFound: boolean;    AppInstanceName: string;  AppInstanceHandle: hWnd;   // add this function at the top of your program -- no need to register it// I found it on the internet and it did not have an author disclosed// I also modified it so it would suite my needs// it searches for a string within the caption of a first instance app: function TMainFrm.FindWindowExtd(partialTitle: string): HWND;  // get with wildcardvar                                   // by Dorin Duminica, September 10, 2009  hWndTemp: hWnd;  iLenText: Integer;  cTitletemp: array [0..254] of Char;  sTitleTemp: string;begin  hWndTemp:= FindWindow(nil, nil);  if AppInstanceLaunch then     begin     AppInstanceFound:= false;     AppInstanceName:= '';     AppInstanceHandle:= 0;     end;  while hWndTemp <> 0 do        begin        iLenText:= GetWindowText(hWndTemp, cTitletemp, 255);        //sTitleTemp:= cTitletemp;  // I omitted this step and modified the next line        sTitleTemp:= UpperCase(copy(cTitleTemp, 1, iLenText));  // it was copy(sTitleTemp,        partialTitle:= UpperCase(partialTitle);        if pos(partialTitle, sTitleTemp) > 0 then  // was <>  // this section is what I added           begin         if (not AppInstanceFound) and (AppInstanceLaunch) then  // these two are safeguards              begin              windows.beep(200,600);  // tone,duration -- first of two tone alarm            AppInstanceFound:= true;  // _vvv_globals_vvv_  // these are globals if needed for checking              AppInstanceName:= sTitleTemp;                   // AppInstanceFound is the only critical variable              AppInstanceHandle:= hWndTemp;              end;           Break;           end;        hWndTemp:= GetWindow(hWndTemp, GW_HWNDNEXT);        end;  result:= hWndTemp;end;     // this makes a single-instance app  // add this code at the top of your FormCreate procedure  // it is what closes down your app if it is a second instance:    MainFrm.Caption:= 'CmdData Extant-Search';  // rename caption so SwitchApp will skip this app  AppInstanceLaunch:= true;  // this variable isn't necessary -- only a safeguard  AppInstanceFound:= false;  // this variable is critical and its setting is also a safeguard  if AppInstanceLaunch then     begin     SwitchApp('CmdData... ');  // 'CmdData... ' is my search string -- my app appends this with my database name     AppInstanceLaunch:= false; // the search string doesn't have to be a prefix -- it can be anywhere within a caption     end;  if AppInstanceFound then     begin     windows.beep(500,600);  // tone,duration -- second of two tone alarm     halt; // alternate methods: FormClose(self); application.terminate; Halt;     end else         begin         MainFrm.Caption:= 'CmdData...   no project assigned';         end;  // the two tone alarm tells me that each function is working, // and it informs the user that my app is already launched and active// after the tones it switches focus to the first instance app.// the reason for a tone alert is that your app is not visible during OnCreate. // all of this works best in Win10 if your app is not minimized// Win10 resists raising a minimized app, but will allow you to switch focus to an active one// WinXP will allow you to raise a minimized app, but you need to add that code // the FindWindowExtd function can additionally be used for switching to other active apps, such as NotePad and others.// the safeguards permit you to do so without interfering with the single instance check, and vice-versa ---------------------------------------------------------------------- balazsszekely · 2017-12-16 17:44 Did you try component Unique instance? http://wiki.freepascal.org/UniqueInstance ---------------------------------------------------------------------- Thaddy · 2017-12-16 19:23 Yup, someone (OP) is inventing the wheel again. Happens all the time and it ends up square or (optimistic) hexagonal.... https://en.wikipedia.org/wiki/Reinventing_the_wheel and: https://en.wikipedia.org/wiki/Not_invented_here Merry Christmas. ---------------------------------------------------------------------- RAW · 2017-12-16 19:53 There are a lot of WINDOWS dependent examples out there... MUTEX, SEMAPHORE, FILE MAPPING ... (but take care with the FileMapping versions...  ). BTW: Quotebegin      windows.beep(500,600);  // tone,duration -- second of two tone alarm      halt; // alternate methods: FormClose(self); application.terminate; Halt;      end else...... What's wrong with "EXIT" or "END." (LPR) instead of "HALT"... ---------------------------------------------------------------------- Cyrax · 2017-12-16 22:43 Please do not use halt. It will force application to terminate without executing normal resource cleaning up procedures. ---------------------------------------------------------------------- rick2691 · 2017-12-17 12:36 QuotePlease do not use halt. It will force application to terminate without executing normal resource cleaning up procedures. I have used halt because my application does not (to my knowledge) need any clean by the OnCreate procedure. I only initiate variables to default values, and that includes reading a disk file for custom default values ... nothing is saved. That only happens with the OnClose event. As to reinventing the wheel... it permits customization, and I know what is happening. Reinventing is a process for expanding knowledge and finding flexibility. In my case, I want to discover a simple and effective solution that provides control for additional programmers of like-mind. Thank you all for your generous support. Rick ---------------------------------------------------------------------- MacWomble · 2017-12-17 13:36 Quote from: Cyrax on December 16, 2017, 11:43:36 pmPlease do not use halt. It will force application to terminate without executing normal resource cleaning up procedures. In my opinion the destroy-parts will be executed after halt, so recource cleaning will be done - except you use an old version of Lazarus. Am I right? ---------------------------------------------------------------------- Cyrax · 2017-12-17 14:00 Quote from: MacWomble on December 17, 2017, 02:36:30 pmQuote from: Cyrax on December 16, 2017, 11:43:36 pmPlease do not use halt. It will force application to terminate without executing normal resource cleaning up procedures. In my opinion the destroy-parts will be executed after halt, so recource cleaning will be done - except you use an old version of Lazarus. Am I right? Oh, it seems that you are right. ---------------------------------------------------------------------- rick2691 · 2017-12-20 12:11 I have edited the comments in the code sample as previously posted. The primary comment is that the OnCreate section can be inserted at the top of your OnCreate procedure. I had previously placed mine at the bottom during some debugging exercises. Then I had left it there, but have now found that my improvements do not necessitate its being at the bottom. It can actually be anywhere. I like the top better, but it depends on what you are actually doing with the OnCreate event. Rick ---------------------------------------------------------------------- ASerge · 2017-12-20 23:53 An example solution (only for Windows) that uses system event to make sure the application is unique and wake up the first instance. And also uses page file to interact between different copies of the program. Unlike UniqueInstance, correctly activates the first instance. ---------------------------------------------------------------------- rick2691 · 2018-01-22 12:45 @ASerge... Thank you for posting your Single-Instance method. But I was not able to implement it. So I am still using my own method. @All... I have found that the FindWindowExtd function in my initial post doesn't really find a string. It finds a word, string of words, or a string in a word. So doing SwitchApp('CmdData...') only finds 'CmdData' because it treats the first '.' of '...' as a word terminator. It happened to work with my first post because there wasn't a third app with that string that was active. I discovered this when making a second project single-instance. The project CmdBlue had 'CmdBlue' in the Lazarus caption. So my project would not run or launch. I fixed this by changing my caption search. I did SwitchApp('CmdBlue ... '). The spaces before and after '...' made it treat the '...' as another word, and not a punctual terminator. So it searches for two whole words that are in series. If anyone is interested, I have also made the second instance pass its attached file parameter to the first instance, and the first instance loads the file (such as when you double-click an '.RTF' file and you are the default app for loading it). Rick ---------------------------------------------------------------------- vhanla · 2018-03-10 03:12 I have tried @ASerge 's example, and it works, but params cannot be passed from the second instance to the first one. ---------------------------------------------------------------------- taazz · 2018-03-10 05:55 Quote from: vhanla on March 10, 2018, 04:12:30 amI have tried @ASerge 's example, and it works, but params cannot be passed from the second instance to the first one. the proper (old?) way of handling startup params was to implement a DDE server on your application that handles it and the OS will pass them to you. The poor mans way is to have a tcpserver in your application and before you exit you send the parameters to it. The windows way is to use either a user message and wait until it has been processed or mailslots for IPC some eve use memory mapped files (a complete list of IPC mechanisms) ---------------------------------------------------------------------- ASerge · 2018-03-10 16:06 Quote from: vhanla on March 10, 2018, 04:12:30 amI have tried @ASerge 's example, and it works, but params cannot be passed from the second instance to the first one. Sorry, I forgot again that CreateFileMapping and OpenFileMapping use different access constants. Replace in uPageFileStream.pp line 75 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   FMapHandle := OpenFileMappingW(PAGE_READONLY, False, Pointer(MapName));with 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   FMapHandle := OpenFileMappingW(FILE_MAP_READ, False, Pointer(MapName));Archive with correction and test project included. ---------------------------------------------------------------------- rick2691 · 2018-03-10 18:02 The way that I handle passing a file parameter is that I have the secondary application create a text file and write the parameter string to the file. I call the file CmdParameter.000, and it is stored in the application folder. When the focus is received by the primary instance (by ApplicationActivate) it checks to see if that file exists, and if it does then it processes the file string (and deletes the file). As an added protection I have FormCreate delete the file if it exists, since it is a first-instance procedure. The secondary-instance will do the same (because it is the same program), but then it creates the parameter file. This way a problem where a crash or power-out event might leave the file on the disk is avoided. Rick ---------------------------------------------------------------------- vhanla · 2018-03-10 22:15 Thank you @ASerge, now it works great (at least on Windows). Does that code has any license? I will be using it on a MPL 2.0 licensed project, is it compatible and/or allowed? ---------------------------------------------------------------------- han · 2019-05-05 13:42 This is an old topic. Nevertheless I tried the UniqueInstance. It works well.  The component is however not standard available in Lazarus. The same setup is easy to implement with the simpleIPCserver. This allows customization. Attached my simple implementation including the sending of paramstr(1) from the second instance to the first instance prior to termination. I don't understand why in UniqueInstance the OnMessage event in combination with a timer is used. With an onMessageQueued event it is possible to implement it without a timer. I will post an issue at Github, https://github.com/blikblum/luipack/tree/master/uniqueinstance. Later:   Sending parameters only worked in Windows. For Linux there is  no  onMessageQueued event. If have do a SimpleIPCServer1.PeekMessage(1,True) driven by timer.    Attached an version which works both for Linux and Windows. The Linux part is timer driven. Han