Page 1 of 1

[Windows] Avoiding Multiple Instances of an Application

Posted: Mon Apr 09, 2012 11:07 pm
by Danilo
Here a code by Dr. Joseph M. Newcomer to avoid multiple instances of an application,
ported to PureBasic.

By using this way, you can define (with flags) what "one instance of an application" actually means,
for example one instance on the whole system, one instance for every user, or one instance for
every desktop on the system.

The Article by Dr. Joseph M. Newcomer:
- Avoiding Multiple Instances of an Application (his homepage)
- Avoiding Multiple Instances of an Application (at codeproject)

Code: Select all

;
; Avoiding Multiple Instances of an Application
;
; by Dr. Joseph M. Newcomer
;
; - http://www.flounder.com/nomultiples.htm
; - http://www.codeproject.com/Articles/538/Avoiding-Multiple-Instances-of-an-Application
;
; PB port by Danilo, April 2012
;
;
; SI_SESSION_UNIQUE                        - Allow one instance per login session
; SI_DESKTOP_UNIQUE                        - Allow one instance per desktop
; SI_TRUSTEE_UNIQUE                        - Allow one instance per user account
; SI_SESSION_UNIQUE | SI_DESKTOP_UNIQUE    - Allow one instance per login session,
;                                            instances in different login sessions
;                                            must also reside on a different desktop
; SI_TRUSTEE_UNIQUE | SI_DESKTOP_UNIQUE    - Allow one instance per user account,
;                                            instances in login sessions running a
;                                            different user account must also reside    
;                                            on different desktops.
; SI_SYSTEM_UNIQUE                         - Allow only one instance on the whole system
;
;
#SI_SESSION_UNIQUE = $0001 ; Allow only one instance per login session
#SI_DESKTOP_UNIQUE = $0002 ; Allow only one instance on current desktop
#SI_TRUSTEE_UNIQUE = $0004 ; Allow only one instance for current user
#SI_SYSTEM_UNIQUE  = $0000 ; Allow only one instance at all (on the whole system)

Procedure.s CreateUniqueName( name.s, mode.l = #SI_DESKTOP_UNIQUE )
    Protected output.s = "Global\"+name
    
    If mode & #SI_DESKTOP_UNIQUE
        Protected hDesk.i, cchDesk.l, userinfo.s
        output   + "-"
        hDesk    = GetThreadDesktop_( GetCurrentThreadId_() )
        cchDesk  = #MAX_PATH - Len(name) - 1
        userinfo = Space(#MAX_PATH)
        
        If GetUserObjectInformation_( hDesk, #UOI_NAME, @name, cchDesk, @cchDesk )
            output + Trim(name)
        Else
            output + "Win9x"
        EndIf
    EndIf
    If mode & #SI_SESSION_UNIQUE
        Protected hToken.i, cbBytes.l, *pTS
        If OpenProcessToken_( GetCurrentProcess_(), #TOKEN_QUERY, @hToken )
            If GetTokenInformation_( hToken, #TokenStatistics, #Null, cbBytes, @cbBytes )=0 And GetLastError_() = #ERROR_INSUFFICIENT_BUFFER
                *pTS = AllocateMemory( cbBytes )
                If *pTS
                    If GetTokenInformation_( hToken, #TokenStatistics, *pTS, cbBytes, @cbBytes )
                        output + "-" + RSet(Hex(PeekQ(*pts+8),#PB_Quad),16,"0")
                    EndIf
                    FreeMemory(*pTS)
                EndIf
            EndIf
        EndIf
    EndIf
    If mode & #SI_TRUSTEE_UNIQUE
        Protected user.s  , cchUser.l = 64
        Protected domain.s, cchDomain = 64
        user   = Space(cchUser)
        domain = Space(cchDomain)
        If GetUserName_( @user, @cchUser )
            output + "-" + Trim(user)
        EndIf
        cchDomain = GetEnvironmentVariable_( "USERDOMAIN", @domain, cchDomain )
        
        output + "-" + Trim(domain)
    EndIf
    
    ProcedureReturn Left(output,#MAX_PATH)
EndProcedure

Procedure IsInstancePresent( name.s , mode.l = #SI_DESKTOP_UNIQUE )
    Protected err, uniqueName.s
    Static hMutex = #Null
    If hMutex = #Null
        uniqueName = CreateUniqueName( name, mode )
        hMutex = CreateMutex_( #Null, #False, @uniqueName )
        err = GetLastError_()
        If err = #ERROR_ALREADY_EXISTS : ProcedureReturn #True : EndIf
        If err = #ERROR_ACCESS_DENIED  : ProcedureReturn #True : EndIf
    EndIf
    ProcedureReturn #False
EndProcedure

;------------------------------------------------------------------------------------------------

#APPGUID = "{B883B6AB-AE6C-4ADC-AA13-5B3D0195AD5F}"

; Debug CreateUniqueName(#APPGUID, #SI_SESSION_UNIQUE )
; Debug CreateUniqueName(#APPGUID, #SI_DESKTOP_UNIQUE )
; Debug CreateUniqueName(#APPGUID, #SI_TRUSTEE_UNIQUE )
; Debug CreateUniqueName(#APPGUID, #SI_SYSTEM_UNIQUE  )
; 
; Debug CreateUniqueName(#APPGUID, #SI_SESSION_UNIQUE | #SI_DESKTOP_UNIQUE )
; Debug CreateUniqueName(#APPGUID, #SI_TRUSTEE_UNIQUE | #SI_DESKTOP_UNIQUE )


If IsInstancePresent(#APPGUID, #SI_SYSTEM_UNIQUE )
    End ; already running
EndIf

;
; start of program
;
MessageRequester("Info","Program running...")
Best used with a unique GUID for every new program, so double names are avoided.
Make your own unique GUID by using the following code:

Code: Select all

;
; by ts-soft and Flype
;
; http://www.purebasic.fr/english/viewtopic.php?f=5&t=23233&start=11
;
Procedure.s MakeGUID()
  Protected guid.GUID, lpsz.s{78}
  If CoCreateGuid_(@guid) = #S_OK
    ProcedureReturn PeekS(@lpsz, StringFromGUID2_(guid, @lpsz, 78), #PB_Unicode)
  EndIf
EndProcedure

Debug MakeGUID()

Re: [Windows] Avoiding Multiple Instances of an Application

Posted: Tue Apr 10, 2012 1:30 pm
by Danilo
Changed the following in CreateUniqueName():

Code: Select all

RSet(Hex(PeekQ(*pts+8)),8,"0")
to

Code: Select all

RSet(Hex(PeekQ(*pts+8),#PB_Quad),16,"0")
Added check for AllocateMemory:

Code: Select all

*pTS = AllocateMemory( cbBytes )
If *pTS