OnlyOne Module Crossplattform

Share your advanced PureBasic knowledge/code with the community.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

OnlyOne Module Crossplattform

Post by ts-soft »

Code: Select all

;======================================================================
; Module:          OnlyOne.pbi
;
; Author:          Thomas (ts-soft) Schulz
; Date:            Feb 16, 2014
; Version:         1.2
; Target Compiler: PureBasic 5.2+
; Target OS:       All
; License:         Free, unrestricted, no warranty whatsoever
;                  Use at your own risk
;======================================================================

; History:
; Version 1.2
;   - Removed the autoclean on #PB_Event_CloseWindow
;   + ReleaseOne() to remove Timer and delete LockFile
;   + changed Example to reflect the changes

; Version 1.1
;   + IsWindow() in CloseWinCB()
;   + Compiler OS Directive for PostEvent in CloseWinCB()
;
; Version 1.0
;   change timestamp to filedate
;   + some optimise
;
; Version 0.9
;   + timestamp for lockfile (lockfile is outdated after 5 minutes)
;   + PostEvent to CloseWinCB()

; Version 0.8
;   + unbind timer
;   + some small corrections
;
; Version 0.7
;  + some sanichecks
;  + removetimer
;  + unbindevent
;
; Version 0.6
;  + createmutex for windows, to make sure it works after crash (lockfile not deleted!)

DeclareModule OnlyOne
  EnableExplicit
  
  Declare.i InitOne(wID,                                      ; ID of the main window.
                    OnlyOneName.s,                            ; Unique name for Lockfile.
                    CustomEvent = #PB_Event_FirstCustomValue, ; User-defined event in which the parameters are processed.
                    TimerID = 1,                              ; TimerID to use.
                    TimeOut = 2000)                           ; Interval in ms, to check the passed parameter queries.
                    
  Declare.i ReleaseOne()                                      ; Delete LockFile and removes Timer.
  
  Declare.s GetParameters()                                   ; The parameter string, separated with #LF$
EndDeclareModule

Module OnlyOne
  Global.i Event, win, timer
  Global.s LockFile, Paras
  
  Procedure TimerCB()
    Static oldtime = 0
    If oldtime = 0 : oldtime = Date() : EndIf
    Protected FF, time
    
    If FileSize(LockFile) > 0
      FF = ReadFile(#PB_Any, LockFile)
      If FF
        Paras = ""
        While Not Eof(FF)
          Paras + ReadString(FF) + #LF$
        Wend
        CloseFile(FF)
        FF = CreateFile(#PB_Any, LockFile)
        If FF
          CloseFile(FF)
        EndIf
        PostEvent(Event)
      EndIf
    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_Windows
      CompilerDefault
        Else
          time = AddDate(oldtime, #PB_Date_Minute, 3)
          If time < Date()
            oldtime = Date()
            SetFileDate(LockFile, #PB_Date_Modified, Date())
          EndIf
      CompilerEndSelect
    EndIf

  EndProcedure
  
  Procedure.i ReleaseOne()
    RemoveWindowTimer(win, timer)
    UnbindEvent(#PB_Event_Timer, @TimerCB(), win, timer)
    ProcedureReturn DeleteFile(LockFile)
  EndProcedure
  
  Procedure.s GetParameters()
    ProcedureReturn Paras
  EndProcedure
  
  Procedure InitOne(wID, OnlyOneName.s, CustomEvent = #PB_Event_FirstCustomValue, TimerID = 1, TimeOut = 2000)
    Protected.i FF, i, j, time, result = #False
    
    If Not IsWindow(wID)
      Debug "You have to open a window, before you call this function!"
      ProcedureReturn #False
    EndIf
    win = wID
    timer = TimerID
    Event = CustomEvent
    LockFile = GetTemporaryDirectory() + OnlyOneName

    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_Windows
        Protected Mutex = CreateMutex_(0, 0, OnlyOneName)
        If GetLastError_() = #ERROR_ALREADY_EXISTS
          ReleaseMutex_(Mutex)
          CloseHandle_(Mutex)
        Else
          DeleteFile(LockFile)
        EndIf
      CompilerDefault
        time = AddDate(GetFileDate(LockFile, #PB_Date_Modified), #PB_Date_Minute, 5)
        If time < Date()
          DeleteFile(LockFile)
        EndIf
    CompilerEndSelect  

    If FileSize(LockFile) = -1
      FF = CreateFile(#PB_Any, LockFile)
      If FF
        CloseFile(FF)
        AddWindowTimer(wID, TimerID, TimeOut)
        BindEvent(#PB_Event_Timer, @TimerCB(), wID, TimerID)
        j = CountProgramParameters()
        If j
          For i = 0 To j
            Paras + ProgramParameter(i) + #LF$
          Next
          Paras = Left(Paras, Len(Paras) - 1)
          PostEvent(Event)
        EndIf
        HideWindow(wID, #False)
        result = #True
      EndIf
    Else
      FF = OpenFile(#PB_Any, LockFile, #PB_File_Append)
      If FF
        j = CountProgramParameters()
        If j
          For i = 1 To j
            Paras + ProgramParameter(i - 1) + #LF$
          Next
          Paras = Left(Paras, Len(Paras) - 1)
          WriteStringN(FF, Paras)
        EndIf
        CloseFile(FF)        
      EndIf
      End
    EndIf
    
    ProcedureReturn result
  EndProcedure
EndModule

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
  
  Enumeration #PB_Event_FirstCustomValue
    #Event_NewParams
  EndEnumeration
  
  OpenWindow(0, #PB_Ignore, #PB_Ignore, 640, 480, "Test", #PB_Window_SystemMenu | #PB_Window_Invisible)
  ListViewGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)
  OnlyOne::InitOne(0, "Example_Application", #Event_NewParams)

  Define i, j, paras.s
  
  Repeat
    Select WaitWindowEvent()
      Case #PB_Event_CloseWindow
        OnlyOne::ReleaseOne()
        Break
      
      Case #Event_NewParams
        SetActiveWindow(0)
        CompilerIf #PB_Compiler_OS = #PB_OS_Windows
          SetForegroundWindow_(WindowID(0))
        CompilerEndIf
        paras = OnlyOne::GetParameters()
        j = CountString(paras, #LF$)
        For i = 1 To j
          AddGadgetItem(0, -1, StringField(paras, i, #LF$))
        Next
    EndSelect
  ForEver
CompilerEndIf
Have fun
Last edited by ts-soft on Sun Feb 16, 2014 1:29 am, edited 7 times in total.
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: OnlyOne Module Crossplattform

Post by ts-soft »

Update:
History wrote:; Version 0.6
; + createmutex for windows, to make sure it works after crash (if lockfile not deleted!)
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: OnlyOne Module Crossplattform

Post by srod »

That is pretty cool Thomas - works well here.

If I were you, I would also issue some UnBindEvent() statements just as the window is being closed. I have found that, especially with timers, if you then recreate another window with the same ID and another timer with the same timerID then the original event proc will continue to be called. You could well end up with the timer proc being called twice as often as you would like if you then proceed to use BindEvent() again. :) I cannot make my mind up if this is a bug with PB or not since UnbindEvent() does sort the issue out? :)
I may look like a mule, but I'm not a complete ass.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: OnlyOne Module Crossplattform

Post by ts-soft »

Thanks stephen.

Update:
History wrote:; Version 0.7
; + some sanichecks
; + removetimer
; + unbindevent
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: OnlyOne Module Crossplattform

Post by srod »

You'll want to unbind the #PB_Event_Timer event as well. ;) Just to be safe.

Thanks for sharing.
I may look like a mule, but I'm not a complete ass.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: OnlyOne Module Crossplattform

Post by ts-soft »

Update:
History wrote:; Version 0.8
; + unbind timer
; + some small corrections
If someone have a idea to add a function like "named-mutex" for linux and macos, please tell me :wink:
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
User avatar
Tenaja
Addict
Addict
Posts: 1959
Joined: Tue Nov 09, 2010 10:15 pm

Re: OnlyOne Module Crossplattform

Post by Tenaja »

ts-soft wrote:If someone have a idea to add a function like "named-mutex" for linux and macos, please tell me :wink:
Thanks for sharing! I have found about a dozen Single-Instance examples on the forum, but never one that passed parameters (cross platform). I made my own version for a text editor, but without parm passing, you can't double-click a file to open it into the running program. Since it was not critical to me, I haven't yet made the time to implement parameter passing; I will probably switch mine out for this. Thanks again.

One idea for Linux and Mac, since you cannot name a Mutex (with native PB) is to write a time stamp in the file. If you write it maybe only once every few minutes, you can test to see if the time-stamp is old enough to account for a crash, and then display a MessageBox to verify, if it is within an allowable margin (maybe 2x).
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: OnlyOne Module Crossplattform

Post by ts-soft »

Tenaja wrote:One idea for Linux and Mac, since you cannot name a Mutex (with native PB) is to write a time stamp in the file
done, thanks

Update:
History wrote:; Version 0.9
; + timestamp for lockfile (lockfile is outdated after 5 minutes)
; + PostEvent to CloseWinCB()
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: OnlyOne Module Crossplattform

Post by srod »

Thomas, rather than writing a time stamp could you not just use GetFileDate() with the modified flag instead?
I may look like a mule, but I'm not a complete ass.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: OnlyOne Module Crossplattform

Post by ts-soft »

srod wrote:Thomas, rather than writing a time stamp could you not just use GetFileDate() with the modified flag instead?
I have test this in my first version, but without success (only on linux tested).
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: OnlyOne Module Crossplattform

Post by srod »

Ah, did wonder how reliable the values returned from that function might be. Never used it myself.
I may look like a mule, but I'm not a complete ass.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: OnlyOne Module Crossplattform

Post by ts-soft »

Okay, it works with FileDate :wink:

Update:
History wrote:; Version 1.0
; change timestamp to filedate
; + some optimise
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: OnlyOne Module Crossplattform

Post by srod »

Works great.

Thanks. Will save this one for future use.

One question; the PostEvent(#PB_Event_CloseWindow, win, 0)... is that needed on Linux then because on Windows the main #PB_Event_CloseWindow event loop runs automatically and exits the loop without the PostEvent() ?
I may look like a mule, but I'm not a complete ass.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: OnlyOne Module Crossplattform

Post by ts-soft »

Yes, the postevent() is required on linux or you have to press the closebutton twice :wink:

Should i add a IsWindow() between the postevent and removeanything?
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: OnlyOne Module Crossplattform

Post by srod »

It doesn't cause any problems on Win 7 PB 5.21 LTS since the bound event procedure runs first. Beside's your code doesn't actually close the window anyhow!

I should have Ubuntu up and running tomorrow so I can get back to testing with Linux. :)
I may look like a mule, but I'm not a complete ass.
Post Reply