craigsboyd
IS-IT--Management
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] [bigsmile] [bigsmile]](/data/assets/smilies/bigsmile.gif)
**********First Solution Using A Unique Window Property**********
faq184-2486
**********Second Solution Using A Mutex**********
faq184-2486
**********Third Solution Using A Semaphore**********
faq184-2486
Slighthaze = NULL
[ul][li]FAQ184-2483
An excellent guide to getting a fast and accurate response to your questions in this forum.[/li][/ul]
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] [bigsmile] [bigsmile]](/data/assets/smilies/bigsmile.gif)
**********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)
*!*
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)
*!*
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)
*!*
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]