October 30, 2025, 03:08:04 PM

News:

IWBasic runs in Windows 11!


check if a program is already running

Started by dossic, February 16, 2008, 01:24:34 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

dossic

Hi !

Sorry for using your time again, I have another question to ask for.

The question is (in theory) simple. Is there any way in EB to check if a program is already running?  I did that, or better I was advised to do that, in Liberty Basic using EnumWindows and callbacks. I found no way - with my present knowledge - to reproduce that in EB.  I know that callbacks are normally used - in opening windows, in timer and so on, but I found myself unable to do that in API call to EnumWindows.

May I ask for help?

Thanks    Carlo

Yours  Carlo

LarryMc

LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

JoaoAfonso

Ahoy.

The following code I got from don't know whom (but in Ionic Wind or Coding Monkeys forum), is what I use to check windows caption title. Basically, at my server I have a big CB program always running, but sometimes it crashes. I then decided to create a small program just to check windows captions, and when the window with the caption "Error" shows (appears when my main prog crashes), it closes such window and starts again the program. Maybe it can be useful to you, don't know.

Def w:Window
WINDOW w,0,0,700,200,@SIZE|@MINBOX|,0,"My Program Server reboot client",main
CENTERWINDOW w

FindWindowByTextInit
Declare "user32",SetForegroundWindow(hWnd As Int),Int
DECLARE "user32.dll",GetWindowText Alias GetWindowTextA(hwnd:INT, lpSTRING:STRING, cch:INT),INT
Declare "user32",APISendMessage Alias SendMessageA(hWnd As Int, wMsg As Int, wParam As Int, lParam As Int),Int
declare "user32",SetParent(hwndWin as int,hwndNewParent as int),int
declare "user32",GetDesktopWindow(),int
DECLARE "kernel32", MoveFileA(lpExistingFileName AS STRING,lpNewFileName AS STRING),INT
Def Title:String
def y,r,chras:int
def gen,gen1:file

setid "WM_CLOSE",16

y=-20
crash=0

starttimer w,1000,1



run = 1
WAITUNTIL run = 0
CLOSEWINDOW w:END

SUB main
SELECT @CLASS
CASE @IDCLOSEWINDOW
run = 0
case @IDTIMER
if @CODE=1
r=FindWindowByTextSB("Error")         :'Text to search for
If r                           :'Return Handle
y=y+20
GetWindowText(r,Title,254)         :'Get The Full Caption
Move w,0,y
Print w,"Window # "+Title+" # forced My Program Server reboot at "+date$("dd'/'MM'/'yy")+", "+time$
                SENDMESSAGE r,@WM_CLOSE,0,0
system(getstartpath+"prog.exe")
endif
r=FindWindowByTextSB("My Program Server")
if r
crash=1
endif
IF(OPENFILE(gen,getstartpath+"prognew.exe","R") = 0)
closefile gen
r=FindWindowByTextSB("My Program Server")         :'Text to search for
If r                           :'Return Handle
y=y+20
GetWindowText(r,Title,254)         :'Get The Full Caption
Move w,0,y
Print w,"New PROGNEW.EXE file forced My Program Server reboot at "+date$("dd'/'MM'/'yy")+", "+time$
                SENDMESSAGE r,@WM_CLOSE,0,0
while (openfile(gen,getstartpath+"prog.exe","R")=0)
closefile gen
deletefile(getstartpath+"prog.exe")
endwhile
COPYFILE(getstartpath+"prognew.exe",getstartpath+"prog.exe",0)
DELETEFILE(getstartpath+"prognew.exe")
' MoveFileA("prognew.exe","prog.exe")
system(getstartpath+"prog.exe")
for a=1 to 10000000
next a
endif
endif
r=FindWindowByTextSB("My Program Server")
if (r=0)&(crash=1)
y=y+20
Move w,0,y
Print w,"Unexpected crash forced a reboot at "+date$("dd'/'MM'/'yy")+", "+time$
system(getstartpath+"prog.exe")
endif
endif
ENDSELECT
RETURN


Sub FindWindowByTextInit
DECLARE "user32",GetDesktopWindowSB Alias GetDesktopWindow(),INT
DECLARE "user32",GetWindowTextSB Alias GetWindowTextA(hwnd:INT,text:STRING,length:INT),INT
DECLARE "user32",GetWindowLongSB Alias GetWindowLongA(hwnd:INT,index:INT),INT
DECLARE "user32",GetWindowSB Alias GetWindow(hwnd:INT,cmd:INT),Int
DECLARE FindWindowByTextSB(Name:String) As Int
SetId "GW_HWNDNEXT",2
SetId "GW_CHILD",5
Return

SUB FindWindowByTextSB(Name:String)
DEF hdesk,hnext,style,whnd:INT
DEF text:STRING
hdesk = GetDesktopWindowSB()
hnext = GetWindowSB(hdesk,@GW_CHILD)
While (hnext <> 0)
GetWindowTextSB(hnext,text,254)
If Instr(Ucase$(text),Ucase$(Name))
whnd = hnext
hnext = 0
EndIf
If (hnext <> 0) Then hnext = GetWindowSB(hnext,@GW_HWNDNEXT)
EndWhile
RETURN whnd


Ah, this client also aids me to update my program. It checks main directory for a file called "prognew.exe" (a new version of "prog.exe"), and when it finds, closes main program, deletes old version, rename prognew.exe to prog.exe and starts it again.

Again, hope this small code can help you and someone else.
JoÃÆ'ƒÂÃ,£o Afonso
Viriato
-----------------
Iberia MUD
www.iberiamud.com
iberiamud.com:5900

dossic

Thanks to all of you in EB forum!

It uses a similar strategy to what I did with EnumWindows in my LB programs. I enumerated all windows and checked if the name was the same.
I guess my problem is now solved, however I'll need anyway to familiarize with callback functions.

I must say, it is incredible what EB can do for you!

See you from Italy

Carlo




Ionic Wind Support Team

Parkers method of using a named mutex is the only proper way of preventing duplicate instances of a program on NT based systems (2000, XP, Vista).

Relying on the caption will fail as that OS is allowed to add text to your main windows title bar.  I am sure you all have seen the (Not Responding) text added to programs captions when they are no longer processing messges.

Paul.
Ionic Wind Support Team

dossic

Hi Paul, there's always something to learn for a chemist like me!   I was totally ignorant about mutex, I'll try to use it
However, It should be great for me having the software running on Win98, too;  do you think  mutex will work on Win98?

I really appreciate all your efforts to help me; thanks!

Yours  Carlo 

Ionic Wind Support Team

Yes mutex's are part of the OS and will work with any windows version.
Ionic Wind Support Team

LarryMc

rhuckle

I guess you didn't notice that this was resolved over 2 months ago with a reference to Parkers posting of 2006 and Paul's comments in Feb.

Also this info was posted by Paul in response to your question on the same subject.

It really serves no useful purpose to post the same solutions in different topics.

All the above is purely my opinion and an implied suggestion.  Not a criticism ;)

Larry
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

pistol350

Here is a Very interesting example written by Earn.


/* Example: Running a Single Instance of an application.
    Earn: August 2004
    Uses CommandLineToArgvW for commandline parsing
    which is not available on Win 9x.  For 9x systems you'd
    have to incorporate alternate commandline parsing routines.
*/
DECLARE "user32.dll",  BringWindowToTop(hwnd:INT),INT
DECLARE "shell32.dll", CommandLineToArgv alias CommandLineToArgvW(lpCmdLine:STRING, pNumArgs:WORD),POINTER
DECLARE "kernel32.dll",WideCharToMultiByte(codepage:UINT,dwFlags:UINT,lpWideStr:POINTER,cchWideStr:INT,lpMultiByteStr:POINTER,cchMultiByte:INT,lpDefaultChar:POINTER,lpUsedDefault:POINTER),INT
DECLARE "kernel32.dll",CreateMutex alias CreateMutexA(pMutexAttributes:POINTER, bInitialOwner:INT, lpName:STRING),INT
DECLARE "kernel32.dll",CreateEvent alias CreateEventA(pEventAttributes:POINTER, bManualReset:INT, bInitialState:INT, lpName:STRING),INT
DECLARE "kernel32.dll",CreateThread(pThreadAttributes:POINTER, dwStackSize:INT, lpStartAddress:INT, lpParameter:POINTER, dwCreationFlags:INT, lpThreadId:INT),INT
DECLARE "kernel32.dll",ReleaseMutex(hMutex:INT),INT
DECLARE "kernel32.dll",GetLastError(),INT
DECLARE "kernel32.dll",CloseHandle(hObject:INT),INT
DECLARE "kernel32.dll",GlobalFree(hMem:UINT),INT
DECLARE "kernel32.dll",GetCommandLine alias GetCommandLineW(),STRING
DECLARE "kernel32.dll",WaitForSingleObject(hHandle:INT, dwMilliseconds:INT),INT
DECLARE "advapi32.dll",RegCloseKey(hKey:INT),INT
DECLARE "advapi32.dll",RegOpenKeyEx alias RegOpenKeyExA(hKey:INT, lpSubKey:STRING, ulOptions:INT, samDesired:INT, phkResult:POINTER),INT
DECLARE "advapi32.dll",RegCreateKeyEx alias RegCreateKeyExA(hKey:INT, lpSubKey:STRING, Reserved:INT, lpClass:STRING, dwOptions:INT, samDesired:INT, lpSecurityAttributes:POINTER, phkResult:POINTER, lpdwDisposition:INT),INT
DECLARE "advapi32.dll",RegNotifyChangeKeyValue(hKey:INT, bWatchSubtree:INT, dwNotifyFilter:INT, hEvent:INT, fAsynchronus:INT),INT
DECLARE "advapi32.dll",RegSetValueEx alias RegSetValueExA(hKey:INT, lpValueName:STRING, Reserved:INT, dwType:INT, lpData:POINTER, cbData:INT),INT
DECLARE "advapi32.dll",RegQueryValueEx alias RegQueryValueExA(hKey:INT, lpValueName:STRING, lpReserved:INT, lpType:INT, lpData:POINTER, lpcbData:INT),INT
DECLARE "advapi32.dll",RegCreateKeyA(hkey:INT,subkey:STRING,pkey:POINTER),INT
DECLARE "advapi32.dll",RegDeleteValue alias RegDeleteValueA(hKey:INT, lpValueName:STRING),INT
WINDOW w1
INT hMutex, hThread
STRING SubKey
SubKey="SOFTWARE\\TestAppName"
CONST ERROR_ALREADY_EXISTS = 183
CONST INFINITE = 0xFFFFFFFF
CONST REG_OPTION_VOLATILE = 1  :'key will be deleted when the system shuts down
CONST REG_NOTIFY_CHANGE_LAST_SET = &H4
CONST HKEY_LOCAL_MACHINE = 0x80000002
CONST KEY_NOTIFY = &H10
CONST REG_SZ = 1
CONST REG_MULTI_SZ = 7
CONST KEY_ALL_ACCESS = 63
CONST KEY_QUERY_VALUE = &H1
CONST KEY_SET_VALUE = &H2
CONST KEY_CREATE_LINK = &H20
CONST KEY_CREATE_SUB_KEY = &H4
CONST KEY_ENUMERATE_SUB_KEYS = &H8
CONST STANDARD_RIGHTS_ALL = 0x1F0000
CONST SYNCHRONIZE = 0x100000
CONST CP_ACP = 0
hMutex = CreateMutex(NULL, FALSE, "SingleInstanceOfMyApp")
if (GetLastError() = ERROR_ALREADY_EXISTS)
   'Our application is already running.  This is the second instance.
   'Check the commandline to see if the user is trying to open additional files.
   'Writing the filenames to the registry will trigger the monitored event of the running application.
   If (WriteCLArgsToReg() = 1)
      'Popup an annoying message box.
      MessageBox 0, "You may open only one instance of this application!", "Already running!"
   EndIf
  CloseHandle(hMutex)
else
   OPENWINDOW w1,50,50,700,210,@MINBOX,NULL,"Single Instance Window",&handler
   CONTROL w1,@LISTBOX,"ListBox1",2,20,690,155,0x50B00140,1
   PRINT w1," Try to run a second copy of this application.   Drag & Drop some files on the executable."
   WriteCLArgsToReg()
   GetCLArgsFromReg()
   ' Cast a thread to monitor our registry key for changes
   hThread = CreateThread(NULL, 0, &MonitorReg, w1, 0, NULL)
   WAITUNTIL w1 = 0
   CloseHandle(hThread)
endif
ReleaseMutex(hMutex)
END
SUB handler
    IF @MESSAGE = @IDCLOSEWINDOW
        CLOSEWINDOW w1
    ENDIF
RETURN
ENDSUB
'Thread function that monitors changes to our registry key.
SUB MonitorReg()
INT hKey
INT hEvent
INT Disp
STRING filename
INT DataLen :DataLen=254
   ' Open a key.
   RegCreateKeyEx(HKEY_LOCAL_MACHINE, SubKey, 0, "", REG_OPTION_VOLATILE, KEY_QUERY_VALUE | KEY_NOTIFY, NULL, &hKey, NULL)
  ' Create an event.
  hEvent = CreateEvent(NULL, FALSE, FALSE, "")
   ' Watch the registry key for a change of value.
   RegNotifyChangeKeyValue(hKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, hEvent, TRUE)
  ' Wait for an event to occur.
  WaitForSingleObject(hEvent, INFINITE) :'uses no CPU cycles
   ' Event was triggered.
   ClearListBox(w1,1)
   ' Check registry for information.
   GetCLArgsFromReg()
   RegDeleteValue(hKey, "OpenFile")
   RegCloseKey(hKey) : CloseHandle(hEvent) :'don`t know if this is really needed.  Seems like the proper thing to do.
   ret = BringWindowToTop(w1.hWnd) :'this doesn`t work for some reason
   ' Restart the monitor.
   MonitorReg()
RETURN
ENDSUB
SUB WriteCLArgsToReg(),INT
POINTER pszArglist
iSTRING regBuffer[2048]
INT regBufferLen :regBufferLen=0
STRING strTemp
WORD nArgs
INT i
INT phkResult
POINTER pStr
   pStr = regBuffer
   pszArglist = CommandLineToArgv(GetCommandLine(), &nArgs)
   if (pszArglist <> NULL)
         'skip index 0 since we don't want the program name
         for i=1 to nArgs-1
            WideCharToMultiByte(CP_ACP,0,*<STRING>*<POINTER>(pszArglist+(i*4)),-1,strTemp,254,NULL,NULL)
            /*Note that a REG_MULTI_SZ string contains multiple null-terminated strings   and ends
               with two null characters, which must be factored into the length of the string.
               For example, a REG_MULTI_SZ string can be represented in memory as follows:
               string1\0string2\0string3\0laststring\0\0 */
            'Build a REG_MULTI_SZ string
            #<STRING>pStr = strTemp
            pStr += (LEN(strTemp)+1)
            regBufferLen += (LEN(strTemp)+1)
         next i
         #<CHAR>pStr = NULL
         regBufferLen++
         ret=RegOpenKeyEx(HKEY_LOCAL_MACHINE, SubKey, 0, KEY_ALL_ACCESS, &phkResult)
         RegSetValueEx(phkResult, "OpenFile", 0, REG_MULTI_SZ, regBuffer, regBufferLen)
         RegCloseKey(phkResult)
   endif
   'Free memory allocated for CommandLineToArgv arguments.
   GlobalFree(pszArglist)
RETURN nArgs
ENDSUB
SUB GetCLArgsFromReg()
iSTRING regBuffer[2048]
INT regBufferLen :regBufferLen=2048
INT phkResult
POINTER pStr
STRING FileName
   pStr = regBuffer
   ret=RegOpenKeyEx(HKEY_LOCAL_MACHINE, SubKey, 0, KEY_ALL_ACCESS, &phkResult)
   RegQueryValueEx(phkResult, "OpenFile", 0, NULL, regBuffer, &regBufferLen)
   RegCloseKey(phkResult)
   IF (regBuffer <> "")
      'Do something with the filenames.
      INSERTSTRING w1, 1, 0, "User wants to open the following file(s):"
      DO
         FileName = #<STRING>pStr
         pStr += (LEN(FileName)+1)
         INSERTSTRING w1, 1, 1, FileName
      UNTIL #<CHAR>pStr = NULL :'We reached the terminating double null.
   ENDIF
RETURN
ENDSUB
SUB ClearListBox(w:window, cid:int)
INT count
   count = GETSTRINGCOUNT (w,cid)
   for i=0 to count-1
      DELETESTRING (w,cid,0)
   next i
RETURN
ENDSUB

Regards,

Peter B.

JoaoAfonso

Hmm... and if to check if an external program is running?
JoÃÆ'ƒÂÃ,£o Afonso
Viriato
-----------------
Iberia MUD
www.iberiamud.com
iberiamud.com:5900

Boris

Back in the dark ages, I faced this problem using the original Ibasic STD. My solution was a bit simpler. If memory serves, it was something like this:

If you try to open a file that is already open, you get an error. So the first instance opens a dummy file, and any further instances will receive an error while trying to open the same file, so they just exit.
Thank you for not breeding

JoaoAfonso

Dunno about that... anyway it should exist something like mutex for other programs, no?
I still use the code to get caption of windows, which can fail as stated by Paul.
JoÃÆ'ƒÂÃ,£o Afonso
Viriato
-----------------
Iberia MUD
www.iberiamud.com
iberiamud.com:5900