If someone can point me in the direction of a thread discussing a BMax way to prevent an app running more than once, I would be very grateful.
The easiest way: Have your program check the list of running processes upon startup. If it finds more than one copy of itself, exit.
(The other one that was already running will continue to run, and you'll still have that single instance)
Here's a piece of code that should help under Windows (Created by Fredborg)
' By Fredborg
SuperStrict
Import maxgui.drivers
Extern "win32"
Function CreateToolhelp32Snapshot:Int(flags:Int, th32processid:Int)
Function Process32First:Int(snapshot:Int, entry:Byte Ptr)
Function Process32Next:Int(snapshot:Int, entry:Byte Ptr)
Function CloseHandle:Int(_Object:Int)
EndExtern
Const TH32CS_SNAPPROCESS:Int = $2
Const INVALID_HANDLE_VALUE:Int = -1
Type TWinProc
Global _list:TList = New TList
Field dwSize:Int = 296
Field cntUsage:Int
Field th32ProcessID:Int
Field th32DefaultHeapID:Int
Field th32ModuleID:Int
Field cntThreads:Int
Field th32ParentProcessID:Int
Field pcProClassBase:Int
Field dwFlags:Int
Field szExeFile:String
Field kids:TList = New TList
Method New()
_list.AddLast Self
EndMethod
Method Free()
_list.Remove Self
EndMethod
Method ToString:String()
Local ret:String
ret = "Name : " + szExeFile
ret :+ "Usage : " + cntUsage
ret :+ "Proc ID : " + th32ProcessID
ret :+ "Heap ID : " + th32DefaultHeapID
ret :+ "Mod ID : " + th32ModuleID
ret :+ "Threads : " + cntThreads
ret :+ "Parent : " + th32ParentProcessID
ret :+ "ClasBas : " + pcProClassBase
ret :+ "Flags : " + dwFlags
EndMethod
Function CreateFromBank:TWinProc( bank:TBank )
Local p:TWinProc = New TWinProc
p.dwSize = PeekInt(bank,0)
p.cntUsage = PeekInt(bank,4)
p.th32ProcessID = PeekInt(bank,8)
p.th32DefaultHeapID = PeekInt(bank,12)
p.th32ModuleID = PeekInt(bank,16)
p.cntThreads = PeekInt(bank,20)
p.th32ParentProcessID = PeekInt(bank,24)
p.pcProClassBase = PeekInt(bank,28)
p.dwFlags = PeekInt(bank,32)
Local offset:Int = 36
While offset<p.dwSize-1
If PeekByte(bank,offset)
p.szExeFile :+ Chr(PeekByte(bank,offset))
Else
Exit
EndIf
offset :+ 1
Wend
Return p
EndFunction
Function GetProcesses:Int()
_list.Clear()
Local snap:Int = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
Local bank:TBank = CreateBank( 296 )
PokeInt bank,0,296
If snap <> INVALID_HANDLE_VALUE
If Process32First(snap, BankBuf(bank))
Local nextproc:Int
Repeat
TWinProc.CreateFromBank( bank )
nextproc = Process32Next (snap, BankBuf(bank))
Until nextproc = 0
EndIf
'
' Arrange so kids are attached etc
For Local p:TWinProc = EachIn TWinProc._list
ArrangeProcess(p)
Next
CloseHandle(snap)
Return True
Else
Return False
EndIf
End Function
Function ArrangeProcess( p:TWinProc )
For Local q:TWinProc = EachIn _list
If p <> q
If p.th32ProcessID = q.th32ParentProcessID
p.kids.AddLast q
_list.Remove q
ArrangeProcess(q)
EndIf
EndIf
Next
EndFunction
Function Find:TWinProc( name:String, list:TList = Null )
If list = Null
list = _list
EndIf
For Local p:TWinProc = EachIn list
If p.szExeFile = name
Return p
EndIf
Local ret:TWinProc = Find( name, p.kids )
If ret
Return ret
EndIf
Next
EndFunction
'
' Count the number of times a process is running
'
Function Count:Int( name:String, list:TList = Null )
Local cnt:Int
If list = Null
list = _list
EndIf
For Local p:TWinProc = EachIn list
If p.szExeFile = name
cnt :+ 1
EndIf
cnt :+ Count( name, p.kids )
Next
Return cnt
EndFunction
End Type
' ---------------------------------------------------
' DEMO A, Check how many times a process is running..
' ---------------------------------------------------
TWinProc.GetProcesses()
Local file:String = "svchost.exe"
'
' Try with:
' file = stripdir(appfile)
'
If TWinProc.Find( file )
Notify file+" is running "+TWinProc.Count( file )+" time(s)"
EndIf
' -----------------------------------------------------------------------------
' DEMO B, Show the processes
' -----------------------------------------------------------------------------
AppTitle = "Process Tree..."
Local window:TGadget = CreateWindow ("Process Tree...", 300, 200, 500, 350)
Local tree:TGadget = CreateTreeView (0, 0, ClientWidth (window), ClientHeight (window) - 30, window)
Local root:TGadget = TreeViewRoot (tree)
SetGadgetLayout tree, 1, 1, 1, 1
Local button:TGadget = CreateButton ("Refresh list", 0, ClientHeight (window) - 25, 150, 21, window)
SetGadgetLayout button, 1, 0, 0, 1
Local menu:TGadget = CreateMenu ("&File", 0, WindowMenu (window))
CreateMenu "&Refresh", 1, menu
CreateMenu "", 2, menu
CreateMenu "&About", 3, menu
CreateMenu "E&xit", 4, menu
UpdateWindowMenu window
FillProcessTree (root)
Repeat
Select WaitEvent ()
Case EVENT_WINDOWCLOSE
End
Case EVENT_MENUACTION
Select EventData ()
Case 1
FreeGadget tree
tree = CreateTreeView (0, 0, ClientWidth (window), ClientHeight (window) - 30, window)
root = TreeViewRoot (tree)
SetGadgetLayout tree, 1, 1, 1, 1
FillProcessTree (root)
Case 3
Notify "Process Tree..." + Chr (10) + Chr (10) + "An amazing Hi-Toro production, public domain 2003."
Case 4
End
End Select
Case EVENT_GADGETACTION
Select EventSource ()
Case button
FreeGadget tree
tree = CreateTreeView (0, 0, ClientWidth (window), ClientHeight (window) - 30, window)
root = TreeViewRoot (tree)
SetGadgetLayout tree, 1, 1, 1, 1
FillProcessTree (root)
End Select
End Select
Forever
' -----------------------------------------------------------------------------
' Fill treeview gadget...
' -----------------------------------------------------------------------------
Function FillProcessTree(root:TGadget)
TWinProc.GetProcesses()
For Local p:TWinProc = EachIn TWinProc._list
InsertProcess(p,root)
Next
End Function
Function InsertProcess(p:TWinProc,root:TGadget)
Local node:TGadget = AddTreeViewNode( p.szExeFile, root)
For Local q:TWinProc = EachIn p.kids
InsertProcess(q,node)
Next
EndFunction
One of the 'demo' functions in this program is to tell you how many instances of svchost.exe are currently running in windows. Just replace that name with the name of your own .exe , and if the result is more than one when you check, exit your program.
(Of course for this to work you would need to make sure that your executable name is somewhat unique, and that there wouldn't reasonably be another program with the same name running on a system. Don't call it explorer.exe for example, or your program would always find Windows explorer already runnig and exit itself)