Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

I've been working on some code with 2

Status
Not open for further replies.

craigsboyd

IS-IT--Management
Nov 9, 2002
2,839
US
There have been a lot of suggestions and discussions regarding how to limit instances of a VFP application running on a user's computer (the usual is only wanting one instance running at a time and bring that previous instance to the front rather than starting a second instance). Here I will discuss some of the ways I've seen proposed and then propose a few of my own. Please consider my expressed views and thoughts regarding the different solutions to be IMHO (in my humble opinion).

Having only one instance of App, brings previous instance window to front
faq184-1998
This code loops through the running windows using GetWindow and if it finds a previous instance it bring the window to the front
It is reliant on the window's title, so it is useful if your caption is static.


Only one instance of an application
faq184-1767
Utilizes FindWindow API call to search for a previous instance and if it finds a previous instance it bring the window to the front
It is reliant on the window's title, so it is useful if your caption is static.



How to ensure that only one instance of my application is running in my desktop.
faq184-839
Pretty unique and ingenious code utilizing dynamic data exchange (DDE)
Is not dependent on the windows caption which is good but DDE is a Win3x 16-bit technology which was pretty much replaced with OLE back in the day so bear this in mind
A link to a discussion regarding DDE and VFP

one instance of program without use of WINDOW TITLE
faq184-2442
Another rather unique solution utilizing the Global Atom Table
Is not dependent on the windows caption which is good but if your application crashes before you are able to delete the entry out of the global atom table then your application will not start again until you reboot the system
A link to some information regarding the atom tables


In Kilofox ("1001 Things You Wanted to Know About Visual Foxpro") there are a couple of other ideas
given copyright laws I can't really post them here but they involve:

Creating a file on the harddrive and delete it once your application exits
This suffers from a similar problem as the Global Atom Table solution above in that if your application crashes the file will remain and subsequent attempts to start the application will fail, even after a reboot. The user would need to manually delete the file.

Creating a hidden window in your application with a unique caption
This is a unique idea as well and was based off some earlier work by Christof Lange
It allows you to change the caption of your application's main window and still check for a previous instance of the application on startup using the FindWindow API call (searching for the hidden window, not the application's main window)


Ok so we've seen what is pretty much available so far so let's look at 3 new ones. All of these examples will need to be cut-n-paste into a prg file and compiled into an exe in order to test them. The code below is not dependent on your application's caption and everything will be ok even if your application crashes. It will show you techniques for adding a property to the _vfp object, using mutexes, and using semaphores and some pretty devent API work in VFP. The last solution using a semaphore actually will allow you to control the number of instances you allow to run on the user's computer, so you could say let the user run up to 3 instances but then stop them at the 4th. So copy, paste, compile and have fun! [bigsmile]

**********First Solution Using A Unique Window Property**********
Code:
*!* Drawback to this solution is that it loops through the windows no matter what when your application starts

IF PreviousInstance() &&Previous instance already running?
	Do ShutdownProc
ENDIF

ON SHUTDOWN Do ShutdownProc
oForm = CREATEOBJECT("form")
oForm.SHOW(1)

***************************
FUNCTION PreviousInstance()
***************************
	#DEFINE GW_CHILD		5 && 0x00000005
	#DEFINE GW_HWNDNEXT		2 && 0x00000002
	#DEFINE SW_MAXIMIZE		3 && 0x00000003
	#DEFINE SW_NORMAL		1 && 0x00000002

	LOCAL lcUniqueProperty, lnHwnd, llReturn

	DO DeclareAPIs

	*!* The GUID below was created using
	*!* the following two lines in the command window
	*!* then it was just a matter of pasting it in
	*!* oTypeLib = CreateObject("scriptlet.typelib")
	*!*	_cliptext = substr(oTypeLib.GUID, 2, 36)
	*!*
faq184-2486
Code:
- Thanks Mike Gagnon
	lcUniqueProperty = "E2429959-D873-4733-8182-7A3F14780A27" && GUID to insure uniqueness
	
	llReturn = .F.
	lnHwnd = GetWindow(GetDesktopWindow(), GW_CHILD)
	DO WHILE lnHwnd != 0 && loop through all windows
		IF GetProp(lnHwnd, lcUniqueProperty) = 1  && does window have our unique property?
			BringWindowToTop(lnHwnd)
			ShowWindow(lnHwnd,SW_NORMAL) && or send SW_MAXIMIZE
			llReturn = .T.
			EXIT
		ENDIF
		lnHwnd = GetWindow(lnHwnd, GW_HWNDNEXT)
	ENDDO
	CloseHandle(lnHwnd)
	IF !llReturn && if no previous instance then create/set our unique property
		=SetProp(_VFP.HWND, lcUniqueProperty, 1)
	ENDIF
	CLEAR DLLS "BringWindowToTop", "GetDesktopWindow", ;
				"GetProp", "GetWindow", ;
				"SetProp", "ShowWindow"
	RETURN (llReturn)
ENDFUNC

***************************
PROCEDURE DeclareAPIs()
***************************
	DECLARE INTEGER BringWindowToTop IN Win32API INTEGER HWND
	DECLARE INTEGER CloseHandle IN Kernel32 INTEGER hObject 
	DECLARE INTEGER GetDesktopWindow IN User32
	DECLARE INTEGER GetProp IN User32 INTEGER HWND, STRING  lpString
	DECLARE INTEGER GetWindow IN User32 INTEGER HWND, INTEGER uCmd
	DECLARE INTEGER SetProp IN User32 INTEGER HWND, STRING lpString, INTEGER hData
	DECLARE INTEGER ShowWindow IN Win32API INTEGER HWND, INTEGER nCmdShow
ENDPROC

***************************
PROCEDURE ShutdownProc()
***************************
	ON SHUTDOWN
	QUIT
ENDPROC

**********Second Solution Using A Mutex**********
Code:
*!* Improves on the first solution by checking a mutex before looping through the windows

IF PreviousInstance() &&Previous instance already running?
	Do ShutdownProc
ENDIF

ON SHUTDOWN Do ShutdownProc
oForm = CREATEOBJECT("form")
oForm.SHOW(1)

***************************
FUNCTION PreviousInstance()
***************************
	#DEFINE ERROR_ALREADY_EXISTS	183 && 0x000000B7
	#DEFINE GW_CHILD				5 && 0x00000005
	#DEFINE GW_HWNDNEXT				2 && 0x00000002
	#DEFINE SW_MAXIMIZE				3 && 0x00000003
	#DEFINE SW_NORMAL				1 && 0x00000002

	LOCAL lcUniqueProperty, lcUniqueMutex, lnhMutex, lnHwnd, llReturn

	DO DeclareAPIs

	*!* The GUIDs below were created using
	*!* the following two lines in the command window
	*!* then it was just a matter of pasting them in
	*!* oTypeLib = CreateObject("scriptlet.typelib")
	*!*	_cliptext = substr(oTypeLib.GUID, 2, 36)
	*!*
faq184-2486
Code:
 - Thanks Mike Gagnon
	lcUniqueMutex = "968360BF-C7AD-4B62-A045-0A06D597EF18"
	lcUniqueProperty = "E2429959-D873-4733-8182-7A3F14780A27" && GUID to insure uniqueness
	lnhMutex = CreateMutex(0, 1, lcUniqueMutex)
	IF GetLastError()= ERROR_ALREADY_EXISTS
		DO DeclareMoreAPIs
		llReturn = .T.
		lnHwnd = GetWindow(GetDesktopWindow(), GW_CHILD)
		DO WHILE lnHwnd != 0 && loop through all windows
			IF GetProp(lnHwnd, lcUniqueProperty) = 1  && does window have our unique property?
				BringWindowToTop(lnHwnd)
				ShowWindow(lnHwnd,SW_NORMAL) && or send SW_MAXIMIZE
				llReturn = .T.
				EXIT
			ENDIF
			lnHwnd = GetWindow(lnHwnd, GW_HWNDNEXT)
		ENDDO
		CloseHandle(lnHwnd)
		CloseHandle(lnhMutex)
		CLEAR DLLS "BringWindowToTop", "GetDesktopWindow", ;
					"GetProp", "GetWindow", "ShowWindow", ;
					"CloseHandle"
	ELSE
		=SetProp(_VFP.HWND, lcUniqueProperty, 1)
		_screen.AddProperty("MutexHandle",lnhMutex)
		llReturn = .F.
	ENDIF
	CLEAR DLLS "CreateMutex", "GetLastError", ;
				"SetProp"
	RETURN (llReturn)
ENDFUNC

***************************
PROCEDURE DeclareAPIs()
***************************
	DECLARE INTEGER CloseHandle IN Kernel32 INTEGER hObject 
	DECLARE INTEGER CreateMutex  IN Win32API INTEGER lpMutexAttributes, INTEGER bInitialOwner, STRING lpName
	DECLARE INTEGER GetLastError IN Win32API
	DECLARE INTEGER SetProp IN User32 INTEGER HWND, STRING lpString, INTEGER hData
ENDPROC

***************************
PROCEDURE DeclareMoreAPIs()
***************************
	DECLARE INTEGER BringWindowToTop IN Win32API INTEGER HWND
	DECLARE INTEGER GetDesktopWindow IN User32
	DECLARE INTEGER GetProp IN User32 INTEGER HWND, STRING  lpString
	DECLARE INTEGER GetWindow IN User32 INTEGER HWND, INTEGER uCmd
	DECLARE INTEGER ShowWindow IN Win32API INTEGER HWND, INTEGER nCmdShow
ENDPROC

***************************
PROCEDURE ShutdownProc()
***************************
	ON SHUTDOWN
	IF PEMSTATUS(_screen,"MutexHandle",5) 
		DECLARE INTEGER ReleaseMutex IN Win32API INTEGER hMutex
		DECLARE INTEGER CloseHandle IN Kernel32 INTEGER hObject
		ReleaseMutex(_screen.MutexHandle)
		CloseHandle(_screen.MutexHandle)
		CLEAR DLLS "ReleaseMutex", "CloseHandle"
	ENDIF
	QUIT
ENDPROC

**********Third Solution Using A Semaphore**********
Code:
*!* Improves on the first solution by checking a semaphore before looping through the windows
*!* Improves on the second solution by adding the ability to control the total number of instances allowed

*!* Argument sent to TooManyInstances tells it
*!* how many instances are allowed to run on this
*!* computer at a time
IF TooManyInstances(1) &&Too many instance already running?
	Do ShutdownProc
ENDIF

ON SHUTDOWN Do ShutdownProc
oForm = CREATEOBJECT("form")
oForm.SHOW(1)

***************************
FUNCTION TooManyInstances(lnInstancesAllowed)
***************************
	#DEFINE GW_CHILD				5 && 0x00000005
	#DEFINE GW_HWNDNEXT				2 && 0x00000002
	#DEFINE SW_MAXIMIZE				3 && 0x00000003
	#DEFINE SW_NORMAL				1 && 0x00000002
	#DEFINE WAIT_OBJECT_0			0 && 0x00000000
	
	LOCAL lcUniqueProperty, lcUniqueSemaphore, lnhSemaphore, lnHwnd, llReturn
	IF PCOUNT() = 0
		lnInstancesAllowed = 1 && default
	ELSE
		lnInstancesAllowed = MAX(lnInstancesAllowed,1) &&At least one
	ENDIF
	DO DeclareAPIs

	*!* The GUIDs below were created using
	*!* the following two lines in the command window
	*!* then it was just a matter of pasting them in
	*!* oTypeLib = CreateObject("scriptlet.typelib")
	*!*	_cliptext = substr(oTypeLib.GUID, 2, 36)
	*!*
faq184-2486
Code:
- Thanks Mike Gagnon
	lcUniqueSemaphore = "968360BF-C7AD-4B62-A045-0A06D597EF18"
	lcUniqueProperty = "E2429959-D873-4733-8182-7A3F14780A27" && GUID to insure uniqueness
	lnhSemaphore = CreateSemaphore(0,lnInstancesAllowed,lnInstancesAllowed,lcUniqueSemaphore)
	IF lnhSemaphore != 0 AND WaitForSingleObject(lnhSemaphore, 0) != WAIT_OBJECT_0
		DO DeclareMoreAPIs
		llReturn = .T.
		lnHwnd = GetWindow(GetDesktopWindow(), GW_CHILD)
		DO WHILE lnHwnd != 0 && loop through all windows
			IF GetProp(lnHwnd, lcUniqueProperty) = 1  && does window have our unique property?
				BringWindowToTop(lnHwnd)
				ShowWindow(lnHwnd,SW_NORMAL) && or send SW_MAXIMIZE
				llReturn = .T.
				EXIT
			ENDIF
			lnHwnd = GetWindow(lnHwnd, GW_HWNDNEXT)
		ENDDO
		CloseHandle(lnHwnd)
		CloseHandle(lnhSemaphore)
		CLEAR DLLS "BringWindowToTop", "GetDesktopWindow", ;
					"GetProp", "GetWindow", "ShowWindow", ;
					"CloseHandle"
	ELSE
		=SetProp(_VFP.HWND, lcUniqueProperty, 1)
		_screen.AddProperty("SemaphoreHandle",lnhSemaphore)
		llReturn = .F.
	ENDIF
	CLEAR DLLS "CreateSemaphore", "GetLastError", ;
				"SetProp"
	RETURN (llReturn)
ENDFUNC

***************************
PROCEDURE DeclareAPIs()
***************************
	DECLARE INTEGER CloseHandle IN Kernel32 INTEGER hObject 
	DECLARE INTEGER CreateSemaphore IN Kernel32 INTEGER lpSemaphoreAttributes, INTEGER lInitialCount, INTEGER lMaximumCount, STRING lpName 
	DECLARE INTEGER SetProp IN User32 INTEGER HWND, STRING lpString, INTEGER hData
	DECLARE INTEGER WaitForSingleObject IN kernel32 INTEGER hHandle, INTEGER dwMilliseconds 
ENDPROC

***************************
PROCEDURE DeclareMoreAPIs()
***************************
	DECLARE INTEGER BringWindowToTop IN Win32API INTEGER HWND
	DECLARE INTEGER GetDesktopWindow IN User32
	DECLARE INTEGER GetProp IN User32 INTEGER HWND, STRING  lpString
	DECLARE INTEGER GetWindow IN User32 INTEGER HWND, INTEGER uCmd
	DECLARE INTEGER ShowWindow IN Win32API INTEGER HWND, INTEGER nCmdShow
ENDPROC

***************************
PROCEDURE ShutdownProc()
***************************
	ON SHUTDOWN
	IF PEMSTATUS(_screen,"SemaphoreHandle",5) 
		DECLARE INTEGER ReleaseSemaphore IN kernel32 INTEGER hSemaphore, INTEGER lReleaseCount, INTEGER @lpPreviousCount 		
    	DECLARE INTEGER CloseHandle IN Kernel32 INTEGER hObject
		ReleaseSemaphore(_screen.SemaphoreHandle,1,0)
		CloseHandle(_screen.SemaphoreHandle)
		CLEAR DLLS "ReleaseSemaphore", "CloseHandle"
	ENDIF
	QUIT
ENDPROC



Slighthaze = NULL

[ul][li]FAQ184-2483
An excellent guide to getting a fast and accurate response to your questions in this forum.[/li][/ul]
 
Hi, thanks for the effort and the code
Only one thing:

Creating a file on the harddrive and delete it once your application exits
This suffers from a similar problem as the Global Atom Table solution above in that if your application crashes the file will remain and subsequent attempts to start the application will fail, even after a reboot. The user would need to manually delete the file.


This is not necesarly true if the app creates the file with FCREATE() low level function and leave it open. If the file exists but is open, the FCREATE() function from the second instance will fail. If the app crashes, then the file handle is closed and next time it can be created again .

 
badukist,

My comments were directly related to the solution proposed on pages 10-11 of Kilofox where the code checks for the existance of the file using FILE(). I do agree with you that a file could be created and left locked (in use) and then the return value of FCREATE() could be checked as you describe. Perhaps you could work up some code showing this technique and create a FAQ from it, would give members another alternative.

Slighthaze = NULL

[ul][li]FAQ184-2483
An excellent guide to getting a fast and accurate response to your questions in this forum.[/li][/ul]
 
Slighthaze,
Some good ideas above. To add to the thread though, here is a snippet I wrote years before that book came out, so I know there is no copyright issue.
If the test file already exists because of abnormal shutdown, the app will try to open it read write. If that succeeds, we know there was an abnormal shutdown and can carry on. If it fails, there is already an instance of the app running.
If the file doesn't exist, there was a clean shutdown and we can just create the test file:

STORE 0 TO chk
IF FILE('C:\SomeFile.zzz')
chk = FOPEN('C:\SomeFile.zzz', 2) &&... 2 = read/write
IF chk < 0
DO quitnow &&... 'App is already running....' message
RETURN
ENDIF ( tryit < 0 )
ELSE
chk = FCREATE('c:\SomeFile.zzz')
ENDIF

.
.
.
Application continues to run, and at termination (CLEAR EVENTS), test file is deleted:

CLEAR EVENTS
=FCLOSE(chk)
ERASE C:\SomeFile.zzz
*... rest of clean up code

.
.
.


PROCEDURE QuitNow
MessageBox(&quot;App is already running....&quot;, 16, 'Title')
RETURN


-Dave S.-
[cheers]
Even more Fox stuff at:
 
This is exactly what I've done too.
Basically this idea can be used to prevend multiple instances and to detect if a session was abnormally terminated and do maintenance (REINDEX)
 
Hi

I am visiting this from a direction thread.
This is a star worthy compilation and has not been marked so far with a star. So my star now.

:)

____________________________________________
ramani - (Subramanian.G) :)
 
Thanks for the star ramani. [smile]

boyd.gif

 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top