Instance checker
Posted: Wed Jan 19, 2011 7:46 am
Hi, while browsing the forums looking for instance checking methods, I was dismayed when the mutex didn't seem to work on my XP machine.
Furthermore, there didn't seem to be a way to inform the running application that another instance was attempted, so I wrote this function.
It returns the threadID of any process with the same filename and fingerprint of the process running the procedure (or 0 if none are found), meaning you can postthreadmessage to that application (useful for consoles, which is where I have tested it).
It only returns the first thread it comes across, I am assuming that if processes are closed after encountering a duplicate running process, there will only ever be one match in any case.
Usage:
The usage is just an example, the initial PeekMessage is required to force creation of a message queue, although I am sure thread messages posted to windowed apps are passed to the main window and can be read via normal window callbacks, in which case it can be omitted.
Quite useful if you have an app in the tray, and you need it to pop up when you attempt to run it from a shortcut, etc.
Furthermore, there didn't seem to be a way to inform the running application that another instance was attempted, so I wrote this function.
Code: Select all
Procedure GetThreadOfCloneProcess()
Protected hp=CreateToolhelp32Snapshot_(#TH32CS_SNAPPROCESS,0)
If hp
Protected p.PROCESSENTRY32
Protected fnp${100},fnm${100},fp${1000}
p\dwSize=SizeOf(PROCESSENTRY32)
If Process32First_(hp,@p)
Repeat
fnp$=PeekS(@p\szExeFile[0])
If p\th32ProcessID<>GetCurrentProcessId_() And fnp$=GetFilePart(ProgramFilename())
Protected hm=CreateToolhelp32Snapshot_(#TH32CS_SNAPMODULE,p\th32ProcessID)
If hm
Protected m.MODULEENTRY32
m\dwSize=SizeOf(MODULEENTRY32)
If Module32First_(hm,@m)
Repeat
fnm$=PeekS(@m\szModule[0],100)
fp$=PeekS(@m\szExePath[0],1000)
If fnm$=GetFilePart(ProgramFilename())
Protected ht=CreateToolhelp32Snapshot_(#TH32CS_SNAPTHREAD,p\th32ProcessID)
If ht
Protected t.THREADENTRY32
t\dwSize=SizeOf(THREADENTRY32)
If Thread32First_(ht,@t)
Repeat
If t\th32OwnerProcessID=p\th32ProcessID And t\th32ThreadID<>GetCurrentThreadId_()
If SHA1FileFingerprint(ProgramFilename())=SHA1FileFingerprint(fp$)
CloseHandle_(ht)
CloseHandle_(hm)
CloseHandle_(hp)
ProcedureReturn t\th32ThreadID
EndIf
EndIf
Until Not Thread32Next_(ht,@t)
EndIf
CloseHandle_(ht)
EndIf
EndIf
Until Not Module32Next_(hm,@m)
EndIf
CloseHandle_(hm)
EndIf
EndIf
Until Not Process32Next_(hp,@p)
EndIf
CloseHandle_(hp)
EndIf
EndProcedure
It only returns the first thread it comes across, I am assuming that if processes are closed after encountering a duplicate running process, there will only ever be one match in any case.
Usage:
Code: Select all
Global WM_NEW_INSTANCE=RegisterWindowMessage_(@"UNIQUESTRING"),t.l,INITMSG.MSG
PeekMessage_(@INITMSG,#Null,0,0,#PM_NOREMOVE)
t=GetThreadOfCloneProcess()
If t
PostThreadMessage_(t,WM_NEW_INSTANCE,0,0)
End
EndIf
Repeat
If PeekMessage_(@INITMSG,#Null,0,0,#PM_REMOVE)
If INITMSG\message=WM_NEW_INSTANCE
MessageRequester("Oops","I'm running")
EndIf
EndIf
Forever
Quite useful if you have an app in the tray, and you need it to pop up when you attempt to run it from a shortcut, etc.