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 Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Ensuring only one instance of an app

Status
Not open for further replies.
Apr 15, 2011
1
US
thread184-1190386

How can you prevent two separate users on a network from starting the same application on their separate machines.
 
Make a table with 1 record and one field. user c(50).
Put this file in a shared folder on the network.
If a user starts the application set the record/field to sys(0), so you can always see who started the app.
If the user quits the app clear the record/field.

if a user starts the app first check whether the record/field is empty. If empty it's OK, if filled then an other user is busy.

You can also work with mem files or any other flag method.

You should have a solution if a user shuts down the PC, without correct quiting. The record/field stays filled.
So you have a job for the rest of your life managing it ;-)
 
This is the method I have used:

1. Create a simple one-field, one-record table, and place it in a shared location. It doesn't matter what the field's data type is.

2. When the user starts the application, attempt to get a record lock on the record.

3. If the lock succeeds, allow the user to continue. If it fails, it means another user has locked the record, so disallow the current user.

The advantage over the method suggested by JackTheC (se above) is that, if the application crashes, the lock will be released.

It can be exteneded to cover any number of users. You just create one record per permitted user. Each user tries to lock each record in turn until either they find one that is unlocked, or they determine that all the records are locked.

It's simple and foolproof.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips, training, consultancy
 

My favorite solution introduced by Martin Krivka on foxite.com (Thread ID: 293475) is:

Code:
PRIVATE;
	instance
m.instance=only1run([{9444C726-8529-4E44-9654-EE0A860503DF}])
if isnull(m.instance)
	=messagebox("One instanc is already running ...")
	RETURN
endif
Code:
*
* only1run.prg
*
#define CLASS_NAME [pipetest]

#define INVALID_HANDLE_VALUE				(-1)
*#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)

* dwOpenMode
#define PIPE_ACCESS_DUPLEX				0x00000003
#define PIPE_ACCESS_INBOUND				0x00000001
#define PIPE_ACCESS_OUTBOUND			0x00000002
#define FILE_FLAG_FIRST_PIPE_INSTANCE	0x00080000
#define FILE_FLAG_WRITE_THROUGH		0x80000000
#define FILE_FLAG_OVERLAPPED				0x40000000
#define WRITE_DAC							0x00040000
#define WRITE_OWNER						0x00080000
#define ACCESS_SYSTEM_SECURITY			0x01000000

* dwPipeMode 
#define PIPE_TYPE_BYTE						0x00000000
#define PIPE_TYPE_MESSAGE					0x00000004
#define PIPE_READMODE_BYTE				0x00000000
#define PIPE_READMODE_MESSAGE			0x00000002
#define PIPE_WAIT							0x00000000
#define PIPE_NOWAIT						0x00000001
#define PIPE_ACCEPT_REMOTE_CLIENTS		0x00000000
#define PIPE_REJECT_REMOTE_CLIENTS		0x00000008

* nMaxInstances (1-)
#define PIPE_UNLIMITED_INSTANCES			0xff
*----------------------------------------------------------------------

parameters pipeGid

private o
m.o=createobject(CLASS_NAME)
if !empty(m.pipeGid)
	if !m.o.createPipe(m.pipeGid)
		return null
	endif
endif
return m.o

*----------------------------------------------------------------------
define class CLASS_NAME as session
	
	hPipe=INVALID_HANDLE_VALUE
	w32lastError=0
	
	function init
		declare long CreateNamedPipe in kernel32 as w32_CreateNamedPipe;
			string @ lpName,;
			integer dwOpenMode,;
			integer dwPipeMode,;
			integer nMaxInstances,;
			integer nOutBufferSize,;
			integer nInBufferSize,;
			integer nDefaultTimeOut,;
			string @ lpSecurityAttribute
		
		declare long GetLastError in kernel32 as w32_getlasterror
		
		declare integer CloseHandle in kernel32 as w32_closehandle;
			long hObject
		

	function destroy
		if this.hPipe!=INVALID_HANDLE_VALUE
			=w32_closeHandle(this.hPipe)
			this.hPipe=0
		endif
	
	function createPipe(name)
		this.hPipe=w32_createNamedPipe(;
			[\\.\pipe\]+m.name+chr(0),;
			PIPE_ACCESS_DUPLEX+FILE_FLAG_FIRST_PIPE_INSTANCE,;
			PIPE_TYPE_BYTE+PIPE_WAIT,;
			1,;
			500,;
			500,;
			2000,;
			0;
			)
		if this.hPipe=INVALID_HANDLE_VALUE
			this.w32lastError=w32_getlasterror()
			return .f.
		else
			this.w32lastError=0
			return .t.
		endif
	
	function connectPipe(name)
enddefine

I took it as it is, I do not really care how it works. Enjoy.

Martin@Vienna@Austria
 
@Mike. In your solution that table must always be open (in a certain workarea) and never be closed as long as the application is active.

I know i'am a bad programmer. I use CLOSE ALL a lot just to keep a overview and close things i don't need.

 
Jack,

I also use CLOSE ALL, but only at the very start of the app (to ensure a clean sheet when running in the development environment).

Personally, I wouldn't use CLOSE ALL at any other times. It's very much a global command, that is, one that affects all files (not just DBFs or databases) in all work areas in all data sessions. Such commands can have side-effects, and can break encapsulation.

Regardless of my suggestion for your original question, I would seriously consider removing CLOSE ALL from your application, except - as mentioned earlier - at the very start.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips, training, consultancy
 
Well, CLOSE ALL is really radical, not limited to current datasession, also closes files opened via FOPEN and dbf being in a transaction (I tried that).

There are other solutions, as Martin shows, but it's not very hard to do without CLOSE ALL.

Put the opening and locking into a session class and let that be some public var to live throughout the app and only CLOSE ALL will sobotage it.

Bye, Olaf.
 
You can extend the earlier mentioned file with a datetime field like laststamp t.
On the main form put a timer and let the timer update this laststamp field every 3 minutes or so with the current datetime().
If the application is not properly quit then the laststamp time will not be updated.

if another user starts (his or the shared) application then the check should be:
if not empty(user) and datetime()-laststamp<=185 then not OK to start a second app.

I use 185 to have a security margin of 5 seconds.

This way you don't need a table that's always open.


 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top