Silverfrost Forums

Welcome to our forums

Preventing multiple instances of a program from running

10 Dec 2020 5:24 #26736

How does one make sure that multiple instances of a program are not running?

There are some times where it would be useful to limit a user's ability to wrap themselves around a proverbial axle when programs crash due to file conflicts.

I've tried searching the HELP files and this Forum, but either my queries are too ambiguous or there's nothing there on this subject.

Bill

10 Dec 2020 6:41 #26737

One way is to call the Microsoft function FindWindow. I can get details tomorrow if this is of interest.

10 Dec 2020 8:07 #26738

It is of interest to me. Sometimes my users don't remember that they have an instance running, then it will fail to open a file, and I get a frantic call (or e-mail). Better to let them know 'we don't really support this'.

Thanks, Bill

11 Dec 2020 8:09 #26742

In this sample one or other of the arguments for FindWindow can be set to CORE4(0) or CORE8(0).

module mymod
use mswin
contains
logical function start()
integer(7) hwnd,h1
logical L
start = .true.
hwnd = FindWindow('SalfordDialog','My Editor')
if(hwnd /= 0)then
 !Not sure how many of these you need... 
 L=SetForegroundWindow(hwnd)
 L=BringWindowToTop(hwnd)
 h1=SetActiveWindow(hwnd)
 h1=SetFocus(hwnd)
 start = .false.
endif  
end function start
end module mymod

winapp
program main
use mymod
integer iw
character*1025 buffer
if(start())then
buffer = ' '
iw = winio@('%ca[My Editor]&')
iw = winio@('%pv%30.10re', buffer)
endif
end program main
11 Dec 2020 2:34 (Edited: 11 Dec 2020 5:42) #26747

My 'clunky' solution is to use the presence of a file created by the first executable to block the second.

winapp
module test_mod
implicit none
integer :: block_unit=90

contains

! Checks to see if file exists, if it does STOP, otherwise create file.
  integer function init()
  integer ERROR_CODE
    if (FEXISTS@('status.txt', ERROR_CODE)) then
      STOP 'Already running'
    else
     open (unit=block_unit,file='status.txt',status='new')
    end if
  init = 1
  end function init

! Deletes file at end of this run in preparation for next run
  integer function deinit()
  integer ERROR_CODE
    if (FEXISTS@('status.txt', ERROR_CODE)) close (unit=block_unit,status = 'delete')
    deinit=1
  end function deinit

  integer function main_code()
  integer i
    i = init()

    ! Main program calls go here, eg:
    do i = 1, 100000
      print*, i
    end do

    i = deinit()
    main_code = 1
  end function main_code

end module test_mod

program main
use test_mod
i = main_code()
end program main
11 Dec 2020 2:46 #26748

I tried setting the second parameter to core4(0) to get ANY class named 'SalfordDialog'. This caused an Access Violation in MAKE_CSTRING# (Checkmate Win32) so I wrote a smaller test program (error below).

Runtime error from program:z:\fpstackfault\release\win32\forumtesting.exe Access Violation The instruction at address 1016ebd8 attempted to read from location 00000003

1016ebb0 MAKE_CSTRING# [+0028] 00401000 MAIN [+0059]

eax=00404320 ebx=0000484f ecx=00000004 edx=000000fe esi=0360fc64 edi=00000003 ebp=0360fc50 esp=0360fc2c IOPL=2 ds=002b es=002b fs=0053 gs=002b cs=0023 ss=002b flgs=00010606 [NC EP NZ SN UP NV]

1016ebd8 rep
1016ebd9 scasb
1016ebda cld

I created a new function definition as:

stdcall findwindow_new 'FindWindowA' (string,ref):integer(7)

This does not crash.

It (usually) finds another application with the class name 'SalfordDialog', but not always. Still trying to figure that one out.

Bottom line is: This can work!

11 Dec 2020 4:07 #26749

Bill

If you don't match the caption then FindWindow will report the first ClearWin+ executable that if finds. They all have the same class name.

If you can identify your application from part of the caption rather than the whole then you could call EnumWindows and use it to read and test all of the captions in turn. This is a bit more tricky and is probably best done in a C function.

11 Dec 2020 11:43 #26753

When I ran the test case (looking for SalfordDialog before my local window was created), I got mixed results.

So, I performed a getclassname() inside of the %sc call for the window that is created in the alternate path, while using the %hw to get the window handle in the 'normal path'.

In the normal case, it returned the value expected. In the other, it returns:

SalfordDialog_win_icon0

I do use %mi and use the string 'win_icon' to specify the resource.

Is it possible that since I make the window [invisible] initially that the class name is different?

Bill

12 Dec 2020 8:35 #26755

Bill

Yes you have discovered something new to me.

ClearWin+ does register a class for a %mi icon. At the moment I don't know why it does that or if it is necessary.

12 Dec 2020 12:05 #26756

Bill

I think that you could call FindWindow twice. Once using SalfordDialog and once using SalfordDialog_win_icon0. You would also use your special interface for FindWindow (assuming that you don't know the precise caption).

If your application has only one %mi then SalfordDialog_win_icon0 will be unique to the application. If you have two %mi's then the first will have the class name SalfordDialog_win_icon0 and the second SalfordDialog_win_icon1 etc.

I am guessing that you will only get SalfordDialog_win_icon0 when the application is minimised but trial and error will tell.

12 Dec 2020 2:37 #26758

I think that instead of using FindWindow(), you should use named mutex to prevent running multiple instances of a program.

I once used following with MiniBasic code (Should translate to C++ easily):

func GUIDstr(), wstring
	GUID pGUID
	wstring str

	if CoCreateGuid(pGUID) = 0
		StringFromGUID2(pGUID,str,39)
	endif

	return str
endf


func app_mutex(), BOOL
	HANDLE hMutex

	hMutex = OpenMutex(MUTEX_ALL_ACCESS,0,GUIDstr())
	if hMutex = 0
		hMutex = CreateMutex(0,0,GUIDstr())
		return TRUE
	endif
	CloseHandle(hMutex)
	return FALSE
endf


' Following is for testing the app_mutex() function... 
if app_mutex()
  print 'Accuired mutex!'
else
  print 'Already running!'
  do:until inkey$<> ''
  end
endif
 
do:until inkey$<> ''
12 Dec 2020 4:12 #26760

Paul, it's always good to discover something new (?)! Actually, I enjoy digging deep, but sometimes it can be frustrating!

Yes, if the two names are unique, I can use this. I'll experiment to see if the icon resource I reference is different then the name follows this change. Could be useful, actually.

One problem I just uncovered with this scheme. I have a utility window that ends with %lw. After the window is created, I got the class name and it was 'SalfordDialog_win_icon1'. Closing the window, then starting it again and the class name is 'SalfordDialog_win_icon2'.

Luckily for me, the main window that always remains open is '0'. I can live with that.

Something else to consider, something new!! [sarcasm]Oh joy![/sarcasm]

12 Dec 2020 4:34 #26761

If I change the icon displayed, the '_win_icon' changes to match the resource name used, but the sequential number remains and increments with each invocation.

Each window that is started by my 'standard' method increments this trailing number.

Windows that are not created as 'invisible' do have the default class name.

12 Dec 2020 9:26 #26764

More discoveries.

The format code %nc allows you to set the class name of a window after you set the icon (but only if the icon would be normally be displayed). However, I could only use this once and have that window open when the next window was created (menu selection for example). Stated another way, subsequent windows from this first open window that used the %nc format code were met with this error:

Runtime error from program:f:\cmasterf95\release\win32\c-master.exe Run-time Error Unable to register class C-Master

004208d0 WINDOW_SETUP [+0209] This builds the next window preamble and sets the same %nc value 00489060 CIMPORT_NEW [+04db] Menu selection starts the function call which build a new window 00420c90 WINDOW_ENDING [+0290] This starts the main window where the %nc is first used 00401970 NEWMAIN [+10a8] 00401000 main [+095a]

So, one cannot use this %nc format code unless all other windows that use this format code have been closed.

That said, when the class name was displayed in the first routine (NEWMAIN) (before the runtime error) there is no '_win_icon#' present. This means the name I had assigned is exactly what I intended, and I can then use that name without ambiguity if the same program starts again!

13 Dec 2020 10:05 #26765

Keep in mind that using FindWindow() is not ideal, if there is possibility that multiple programs are started almost at the same time. It's not a bullet proof solution and might fail!

13 Dec 2020 2:23 #26766

I have an application that prevents multiple invocations of an accessory but standalone app by broadcasting a message, which if answered appropriately prevents another launch. That method might be employed to stop a program loading for a second time. The facilities exist already in CW+

Bill says that this is to solve problems of two or more instances of a program trying to access the same file(s), and of course, that can be resolved by making each instance of a program use its own files. FTN95 has a routine to produce unique filenames for scratch files, for example.

Otherwise, one always has a choice between an MDI program or multiple instances of an SDI program if the user is to be allowed to operate on multiple datasets at the same time. I have always opted for the latter, largely because it does not require a fresh memory allocation for each MDI document – running multiple instances of an SDI program does it for you. Incidentally, I’m lost in admiration for those with the mental agility to program MDI ! Also, my personal experience of MDI apps is that sometimes I go to close off one window and accidentally shut off the lot – and that’s with commercial software where I would expect something better than a brief warning that is accepted with a mouse click ‘bounce’ ! I don’t do this so often with multiple SDI apps, particularly my own, as they usually display a nice big picture and that tells me better than anything else which one I want to shut down.

It does seem to me that if Paul was so minded, he might be better placed than anyone to create a routine to do Bill’s job: I suggest a name - FORBID_MULTIPLE_INSTANCES@

Eddie

13 Dec 2020 2:30 #26767

Eddie, thanks for the comments.

I think the %nc command for inter-process communications would work well. It would force the user to have only one routine capable of talking with its partner.

As far as MDI vs SDI, I have done MDI before, and it was tricky, but not overly so.

I'm good with Paul's efforts on my behalf to understand what is going on, and directing me in alternate paths. Using the CLASSNAME is tricky regardless of any other constraints. I have a skeletal framework in place to try it out, but have run into another snag that is really troubling and befuddling me!

13 Dec 2020 3:18 #26769

jalih,

Yes, you are are absolutely correct. Even I have started multiple instances with an aberrant 'double click'. And, the process I have in mind will likely not capture this.

I have been reading up on the mutex concept and it has relevance, for sure. From a quick check of the WIN32API interface, it would appear that all the MUTEX API calls are provided. Some of the others (like CoCreateGuid) are not in the standard library. Perhaps there are other ways to accomplish this same idea.

It's not that the users MUST NOT run multiple instances, just that they SHOULD NOT unless they know what they are doing. I had one user that had a dozen instances started because he misunderstood the concepts of a menu selection. His thought was once I go down a path, I cannot get back and do something else. So, you can see why there are problems!

13 Dec 2020 5:28 #26771

Quoted from wahorger jalih, I have been reading up on the mutex concept and it has relevance, for sure. From a quick check of the WIN32API interface, it would appear that all the MUTEX API calls are provided. Some of the others (like CoCreateGuid) are not in the standard library. Perhaps there are other ways to accomplish this same idea.

You can just use some null terminated string unique to your application as a name for mutex instead of using GUID as string. So, you only need OpenMutex() and CreateMutex() calls.

13 Dec 2020 7:30 #26772

Thanks for the hint! Saves me a lot of work!

Please login to reply.