Page 1 of 1

Instance checker

Posted: Wed Jan 19, 2011 7:46 am
by Psych
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.

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 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:

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
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.