Exe instance question

Just starting out? Need help? Post your questions and find answers here.
Berikco
Administrator
Administrator
Posts: 1326
Joined: Wed Apr 23, 2003 7:57 pm
Location: Belgium
Contact:

Post by Berikco »

Here an small example

Code: Select all

ERROR_ALREADY_EXISTS = 183
Global WM_ACTIVATEOLDINST
WM_ACTIVATEOLDINST = RegisterWindowMessage_("WM_ACTIVATEOLDINST_PB")
MutexName$ = "mylittletestapp"

Procedure MyWindowCallback(WindowID, Message, wParam, lParam)  
  Result = #PB_ProcessPureBasicEvents  
  If message = WM_ACTIVATEOLDINST
    CloseHandle_(hMutex)
    OpenConsole()
    PrintN("closing app")
    Delay(2000)
    End
  EndIf
  ProcedureReturn Result  
EndProcedure  

hMutex = CreateMutex_(0, 0, @MutexName$)
If GetLastError_() = ERROR_ALREADY_EXISTS 
  SendMessage_(#HWND_BROADCAST, WM_ACTIVATEOLDINST, 0, 0)
  Delay(100)
EndIf


If OpenWindow(0,100,150,450,200,#PB_Window_SystemMenu,"MyLittleTestApp " + Str(Random(500)))
  SetWindowCallback(@MyWindowCallback()) 
  Repeat
    ev=WaitWindowEvent()
  Until ev=#PB_EventCloseWindow
EndIf

If hMutex <> 0 
 CloseHandle_(hMutex)
EndIf

End
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

> Here an small example

Here's an even smaller example: :twisted:

Code: Select all

; These two lines prevent this app from being run more than once.
; You must put these lines at the start of your app's code (obviously).
MutexID=CreateMutex_(0,1,"YourAppName") : MutexError=GetLastError_()
If MutexID=0 Or MutexError<>0 : ReleaseMutex_(MutexID) : CloseHandle_(MutexID) : End : EndIf
;
; Your app's code now goes here, until quitting time, which executes the next line.
;
CloseHandle_(MutexID) : End
Berikco
Administrator
Administrator
Posts: 1326
Joined: Wed Apr 23, 2003 7:57 pm
Location: Belgium
Contact:

Re: Exe instance question

Post by Berikco »

PB wrote:> Here an small example

Here's an even smaller example:
Cor wrote:The first instance must be closed and the second must be opened.
No PB, you did not read the whole question, to fast again.
First closed.....second....
:mrgreen: :mrgreen: :mrgreen:
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Exe instance question

Post by PB »

> No PB, you did not read the whole question, to fast again.
> First closed.....second....

Image
Thade
Enthusiast
Enthusiast
Posts: 266
Joined: Sun Aug 03, 2003 12:06 am
Location: Austria

Post by Thade »

Just for curiosity ...

What situation could make it worth to start the same program twice only to close the first one immediately after you started the second one?
Or did I miss the point somehow?
I mean: what can the same program do at its second start what it cannot do at first try?
--------------
Yes, its an Irish Wolfhound.
Height: 107 cm; Weight: 88 kg
dagcrack
Addict
Addict
Posts: 1868
Joined: Sun Mar 07, 2004 8:47 am
Location: Argentina
Contact:

Post by dagcrack »

Hmm Dunno! maybe his proggy is a trial release and each time you open it you have less runs to go ? :wink: Just wondering heh..
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Here is a modification of the above code!
It let you do single instance running just like any "good" application!

Run the program, minimize the window. Then run it again!
If all goes well the program won't start again,
but instead message the old instance and restore it and bring it to the foreground!

Only tested on XP Pro, but should behave in a similar way on all windows versions. not entirely sure if W95 behave exactly the same or not, test to be sure!

Code: Select all

ERROR_ALREADY_EXISTS = 183
Global WM_ACTIVATEOLDINST
WM_ACTIVATEOLDINST = RegisterWindowMessage_("WM_ACTIVATEOLDINST_PB")
MutexName$ = "mylittletestapp"

Procedure MyWindowCallback(WindowID, message, wParam, lParam)
 Result = #PB_ProcessPureBasicEvents
 If message = WM_ACTIVATEOLDINST
  ShowWindow_(WindowID,#SW_RESTORE)
  SetForegroundWindow_(WindowID)
  EndIf
 ProcedureReturn Result
EndProcedure

hMutex = CreateMutex_(0, 0, @MutexName$)
If GetLastError_() = ERROR_ALREADY_EXISTS
  SendMessage_(#HWND_BROADCAST, WM_ACTIVATEOLDINST, 0, 0)
  End
EndIf


hwnd=OpenWindow(0,100,150,450,200,#PB_Window_SystemMenu|#PB_Window_MinimizeGadget,"MyLittleTestApp ")
If hwnd
  SetWindowCallback(@MyWindowCallback())
  Repeat
   ev=WaitWindowEvent()
   If showoldwindow
   EndIf
  Until ev=#PB_EventCloseWindow
EndIf

If hMutex <> 0
 CloseHandle_(hMutex)
EndIf

End
I'll leave it up to you as a coding exercise to find out how to send a message to the old (first) app with a filepath. (like many editors do)
Shouldn't be hard, the messaging "example" is already here so. Hehe!
dibor
Enthusiast
Enthusiast
Posts: 160
Joined: Wed May 20, 2020 5:19 pm
Location: The 3rd planet in the Solar System
Contact:

Re: Exe instance question

Post by dibor »

Code: Select all

;--------Prevent app from being run more than once.---------------
*a = CreateSemaphore_(NULL,0,1,GetProgramName())
  If *a < > 0 And GetLastError_()= #ERROR_ALREADY_EXISTS
   CloseHandle_(*a)
   End
 EndIf
;---------------------------------------------------------
Mac Studio M1Max, PB 6.12 Arm64 and x64.
Macbook Air M2, PB 6.12 Arm64 and x64.
Windows 10, PB 6.12 x64 and x86.
Quin
Addict
Addict
Posts: 1127
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Exe instance question

Post by Quin »

Using a mutex works if you simply want to exit the new instance, but what if you want to focus the window of the previous one (e.g. if the user clicks on the desktop icon to restore a minimized application)? I found some code around here somewhere using filemappings, but it's seemingly broken on 6.1x. Does anyone have a method for doing this?
Thanks.
TassyJim
Enthusiast
Enthusiast
Posts: 183
Joined: Sun Jun 16, 2013 6:27 am
Location: Tasmania (Australia)

Re: Exe instance question

Post by TassyJim »

Quin wrote: Wed Jul 24, 2024 5:09 am Using a mutex works if you simply want to exit the new instance, but what if you want to focus the window of the previous one (e.g. if the user clicks on the desktop icon to restore a minimized application)? I found some code around here somewhere using filemappings, but it's seemingly broken on 6.1x. Does anyone have a method for doing this?
Thanks.
I send the commandline parameters to the running instance using TCPIP before closing the second instance.
Using TCPIP can also be used by other programs to test for a running instance instead of using a mutex

Jim
Axolotl
Addict
Addict
Posts: 802
Joined: Wed Dec 31, 2008 3:36 pm

Re: Exe instance question

Post by Axolotl »

This question has nothing to do with this (very) old thread.
But if you are on windows try this
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: Exe instance question

Post by RASHAD »

Simple and concise
For Windows

Code: Select all

#MyApp = "Test TEST"

Repeat 
  stx+1
  hwnd = FindWindow_(0,#MyApp)
Until hwnd Or stx > 1000
If hwnd
  SendMessage_(hwnd, #WM_SYSCOMMAND, #SC_CLOSE, 0)
EndIf

flags = #PB_Window_SystemMenu| #PB_Window_MaximizeGadget| #PB_Window_MinimizeGadget| #PB_Window_ScreenCentered | #PB_Window_SizeGadget
OpenWindow(0,0,0,400,300,"Test TEST",Flags)
StringGadget(0,10,10,120,30,"First Window")
Repeat           
  Select WaitWindowEvent()      
      Case #PB_Event_CloseWindow
            Quit = 1 
      Case #PB_Event_Gadget
        Select EventGadget()
          Case 0
            SetGadgetText(0,"Second Test")
        EndSelect
  EndSelect 
Until Quit = 1
End
Egypt my love
User avatar
TI-994A
Addict
Addict
Posts: 2700
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Exe instance question

Post by TI-994A »

Cor wrote: Wed May 07, 2003 9:15 pm...The first instance must be closed and the second must be opened.

Is there a way to do this?
Hi Cor. Here's a Windows-only solution:

How to reject double run?
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
mk-soft
Always Here
Always Here
Posts: 6207
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Exe instance question

Post by mk-soft »

Titles could be duplicated. Perhaps it would be better to work with your own ClassName.
Was already desired as an optional parameter.

Code: Select all

;-TOP
; Comment : New Window with own ClassName
; Author  : ?
; Author  : mk-soft
; Version : v1.01.3
; Create  : 23.12.2012
; Update  : 21.07.2022

Import ""
  PB_Window_ProcessEvent(a,b,c,d)
  PB_Window_Icon
  PB_Window_Cursor
  PB_Window_Objects
  PB_Object_GetOrAllocateID(*Object,id)
EndImport

Procedure OpenClassWindow(Window ,x ,y , Width, Height, Title.s, ClassName.s, Flags= #WS_VISIBLE | #WS_OVERLAPPEDWINDOW, Parent=0)
  Protected r1
  Protected WindowClass.wndclass
  Protected *PB_Object.integer
  Protected hWnd.i
  Protected rc.rect
  
  With WindowClass
    \style          = #CS_HREDRAW | #CS_VREDRAW
    \lpfnWndProc    = @PB_Window_ProcessEvent()
    \hInstance      = GetModuleHandle_(0)
    \hIcon          = PB_Window_Icon
    \hCursor        = PB_Window_Cursor
    \lpszClassName  = @ClassName
    \hbrBackground  = #COLOR_WINDOW
    \cbWndExtra     = 0
    \cbClsExtra     = 0
  EndWith
  
  If RegisterClass_(WindowClass)
    SetRect_(rc, 0, 0, Width, Height)
    AdjustWindowRectEx_(rc , Flags, 0, #WS_EX_WINDOWEDGE)
    If x = #PB_Ignore Or y = #PB_Ignore
      x = #CW_USEDEFAULT
      y = #CW_USEDEFAULT
    EndIf   
    hWnd = CreateWindowEx_(#WS_EX_WINDOWEDGE, ClassName, Title, Flags, x, y, rc\right-rc\left, rc\bottom-rc\top, Parent, 0, GetModuleHandle_(0), 0)
    If hWnd
      *PB_Object = PB_Object_GetOrAllocateID(PB_Window_Objects, Window)
      If *PB_Object
        *PB_Object\i = hWnd
        If Window = #PB_Any
          SetProp_(hWnd, "Pb_WindowID", *PB_Object + 1)
        Else
          SetProp_(hWnd, "Pb_WindowID", Window + 1)
        EndIf
        UseGadgetList(hWnd)
        If Window = #PB_Any
          r1 = *PB_Object
        Else
          r1 = hWnd
        EndIf
      Else
        CloseWindow_(hwnd)
        UnregisterClass_(GetModuleHandle_(0), ClassName)
      EndIf
    Else
      UnregisterClass_(GetModuleHandle_(0), ClassName)
    EndIf
  EndIf
  ProcedureReturn r1
EndProcedure

; ********

;-Test

CompilerIf #PB_Compiler_IsMainFile
  
  ; ----
  #MyWindowClassName = "MyDataWindow#001"
  
  Procedure IsRunning()
    Protected hWnd, state
    
    hWnd = FindWindow_(#MyWindowClassName, 0)
    If hWnd
      ShowWindow_(hWnd, #SW_RESTORE)
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
  EndProcedure
  
  Procedure Main()
    Protected dx, dy
    
    hMainWnd = OpenClassWindow(0,#PB_Ignore, #PB_Ignore, 400, 200, "My Data Window", #MyWindowClassName)
    
    dx = WindowWidth(0)
    dy = WindowHeight(0)
    
    ListViewGadget(0, 10, 10, dx - 20, dy - 20)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
          
      EndSelect
    ForEver
    
  EndProcedure
  
  If Not IsRunning()
    Main()
  EndIf
  
  
CompilerEndIf
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Randy Walker
Addict
Addict
Posts: 989
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Re: Exe instance question

Post by Randy Walker »

RASHAD may have the better solution.
This is what I have been using quite successfully. You MUST compile and run the sample for it top work properly.
Verified it works in both Windows PB 5.40 and 6.20

Code: Select all

; Sample code I built from bits and pieces collected from this forum
; to illustrate how to force single instance of your app.
; YOU MUST compile and run this sample to see it work properly!!!

; Pay attention to these two globals:
Global windowname$ = "One Instance" ; Full display name for the window.
Global TargetName$ = "One Instanc" ; first 11 characters to satisfy Gt_Prog() procedure below.

; The following globals and FOUR prodecures MUST BE at the top of your program.
Global targetWindow$, hWin.i, m_hWnd.i, active.W
Procedure ReallySetForegroundWindow(m_hWnd.i)
  ; http://www.drdobbs.com/184405755
  hOtherWnd.i = GetForegroundWindow_()
  ;
  ; get thread handles on our window and foreground window
  hMyThread.i = GetWindowThreadProcessId_(m_hWnd, 0)
  hOtherThread.i = GetWindowThreadProcessId_(hOtherWnd,0)
  ;
  ; attach our thread to foreground thread, take foreground, and detach threads
  AttachThreadInput_(hMyThread,hOtherThread, #True)
  SetForegroundWindow_(m_hWnd)
  AttachThreadInput_(hMyThread,hOtherThread, #False)
  ; AttachThreadInput_(hOtherThread,hMyThread, #True); backward set
  ; SetForegroundWindow_(m_hWnd)
  ; AttachThreadInput_(hOtherThread,hMyThread, #False)
  ;
  ; Now that our window "thread" has fisrt place in the queue...
  ; make sure our "window" is visible
  If IsIconic_(m_hWnd)
    ShowWindow_(m_hWnd,#SW_RESTORE)
  Else
    ShowWindow_(m_hWnd,#SW_SHOW)
  EndIf
  SetActiveWindow(GetDlgCtrlID_(m_hWnd))
  WaitWindowEvent()
  SetForegroundWindow_(m_hWnd)
EndProcedure 

Procedure.i ListWindows(Window, Parameter) ; used inside Gt_Prog()
  WindowTitle.s = Space(255) 
  GetWindowText_(Window, WindowTitle, 255)
  WindowTitle = LCase(WindowTitle)
  If LCase(WindowTitle) = targetWindow$
    active.W = 1
    hWin.i = Window
  ElseIf Left(WindowTitle,11) = targetWindow$
    active.W = 1
    hWin.i = Window
  ElseIf Left(WindowTitle,6) = Left(targetWindow$,6) ; "From: "
    If Right(WindowTitle,9) = Right(targetWindow$,9) ; " - E-mail"
      If Left(WindowTitle,6) <> Right(targetWindow$,6)
        active.W = 1
        hWin.i = Window
      EndIf
    EndIf
  EndIf 
  ProcedureReturn #True  
EndProcedure
Procedure Gt_Prog(dmy$)   ;   input : 11 char PROCESS name  ,  active.w = true if running : hWin.i holds Window handle
  targetWindow$ = LCase(dmy$)
  active.W = 0
  hWin.i = 0
  EnumWindows_(@ListWindows(), 0) ; Windows CallBack operation.
  ProcedureReturn active.W
EndProcedure
If Gt_Prog("One Instanc")   ; Using 1st 11 characters to Prevent 2nd instance if already running
  SetForegroundWindow_(hWin.i) ; AND bring current instance into foreground.
  ShowWindow_(hWin.i, #SW_RESTORE)
  Delay(250)
  End ; Quit from this duplicate instance.
EndIf
If OpenWindow(0, 0, 0, 270, 140,windowname$, #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  TextGadget(0, 40,  20, 250, 20, "Bury this window under other window(s)")
  TextGadget(1, 40,  40, 250, 20, "Then TRY to open a 2nd instance", #PB_Text_Center)
  TextGadget(2, 40,  70, 250, 20, "It won't happen.  You can ONLY")
  TextGadget(3, 40, 90, 250, 20, "bring the fhe 1st instace to the top.")
  ButtonGadget  (4, 80,  110, 100, 20, "CLOSE")
  
  Repeat ; This loop only here to enable and simplify program exit.
    Event = WaitWindowEvent() ; Real magic is in the proceedures above.
    
    Select Event
        
      Case #PB_Event_Gadget
        Select EventGadget()
          Case 4
            Event = #PB_Event_CloseWindow
        EndSelect
    EndSelect
  Until Event = #PB_Event_CloseWindow
EndIf

- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
Post Reply