Some applications open multiple instances when run. This is useful for some applications, but annoying for others.
For example, I want to be able to run only one instance of calculator whenevern I press the calculator key on they keyboard (or double click on its shortcut).
What would be the simplest way to accomplish that?
Use FindWindow to check if an instance is open, then send the commandline to the open instance (e.g. with WM_COPYDATA) and exit.
The usual way is to use CreateMutex(). If the mutex allready exist, GetLastError() returns ERROR_ALREADY_EXISTS -> in this case you must destroy the handle and exit the process.
Sorry, is not really a programming topic (thus why I placed it on the orphanage :lol ), but more of general running of applications. Is a pity that shortcuts do not have this feature. Neither a batch file.
The CreateMutex seem an interesting way. (I usually use FindWindow aproach).
Anyway, after some digging, I found a solution by using a third party program. (autohotkey). Now instead of running the shortcuts, I use a script. Not perfect, but does the work.
Hi xanatose,
Here is a CreateMutex example checking for the second instance of the executable.
Here is an alternative approach. Just replace the red text "blocco note" with your notepad title string (mine is Italian, of course).
Or try .if WinByTitle("Microsoft Word", 4)
include \masm32\MasmBasic\MasmBasic.inc ; download (http://www.masm32.com/board/index.php?topic=12460)
.data
hDrop DROPFILES <DROPFILES, <1, 1>, 0, 0>
Init
Let esi=CL$()+Chr$(0) ; get commandline
.if WinByTitle("- Blocco note", 4)
xchg eax, edi
invoke SetForegroundWindow, edi
lea eax, [Len(esi)+DROPFILES+2]
mov ebx, rv(GlobalAlloc, GHND or GMEM_DDESHARE, eax)
mov ecx, rv(GlobalLock, ebx)
Bmove offset hDrop, ecx, DROPFILES
add ecx, DROPFILES
lea eax, [Len(esi)+2]
Bmove esi, ecx, eax
invoke GlobalUnlock, ebx
invoke PostMessage, edi, WM_DROPFILES, ebx, 0
.else
Launch Cat$("Notepad.exe "+CL$())
.endif
Exit
end start
From:
http://board.flatassembler.net/topic.php?p=54500#54500
vid suggested a better way:
(comments are mine)
you should check for error this way:
Code:
invoke CreateMutex,NULL,FALSE,_mutex_name
test eax, eax ;if there was no error, don't use GetLastError
jne okay
invoke GetLastError
cmp eax,ERROR_ALREADY_EXISTS
je exit ;if program is already running
jmp other_error ;if another error occurred
okay: ;we're here if this is the only instance
exit: ;handle another instance of this program
other_error: ;handle other error
because:
1. CreateMutex is not quaranteed to change value of last error to 0, when it succeeds. Remember GetLastError gets last error that happened, not last return status.
2. CreateMutex can fail for other reasons than ERROR_ALREADY_EXISTS, you should always think of this
hth,
farrier
Thanks, farrier - I learnt something new.
WM_DROPFILES as shown above kicks in after "handle another instance". Your approach is probably better if it's your own code, while mine works also with MS Word etc....
Detecting another instance of the same application, CreateSemaphore works similar to CreateMutex :
invoke CreateSemaphore,NULL,0,1,ADDR ObjectName
mov hSemaphore,eax
invoke GetLastError
cmp eax,ERROR_ALREADY_EXISTS
jne @f
invoke MessageBox,0,ADDR errmsg,ADDR capt,MB_ICONEXCLAMATION
jmp finish
If the app is your own, I tend to still use FindWindow() using the window class. If the app is already running I just set the focus to the running instance then exit the instance that testing is done from. That way you just call the app normally and you will get the running instance or a new instance if its not running.
that sounds easiest :bg
the class name is a semaphore, of sorts
Here is another example using FindWindow The sample code creates a new class based on the default dialog box class #32770
RegisterNewClass PROC USES esi hMod:HMODULE,OldClassName:DWORD,NewClassName:DWORD
LOCAL wc:WNDCLASSEX
lea esi,wc
mov wc.cbSize,SIZEOF WNDCLASSEX
invoke GetClassInfoEx,hMod,OldClassName,esi
push hMod
pop wc.hInstance
mov eax,NewClassName
mov WNDCLASSEX.lpszClassName[esi],eax
invoke RegisterClassEx,esi
ret
RegisterNewClass ENDP
fasm style :8)
format PE GUI 5.0
entry wmain
include 'win32ax.inc'
section '.code' code readable executable
wmain:
cmp [instances],1
je .err
invoke InterlockedExchangeAdd,instances,1
invoke MessageBox,0,'Single Instance Only','Info',MB_OK
invoke InterlockedExchangeAdd,instances,-1
jmp .exit
.err:
invoke MessageBox,0,'Already Running','Info',MB_ICONERROR +MB_OK
.exit:
invoke ExitProcess,0
section '.shared' data readable writeable shareable
instances dd 0
section '.data' data readable writeable
nop
section '.idata' import data readable
library kernel32,'kernel32.dll',\
user32,'user32.dll'
include 'api\kernel32.inc'
include 'api\user32.inc'
Quote from: farrier on September 25, 2011, 05:33:04 AM
From:
http://board.flatassembler.net/topic.php?p=54500#54500
vid suggested a better way:
(comments are mine)
you should check for error this way:
Code:
invoke CreateMutex,NULL,FALSE,_mutex_name
test eax, eax ;if there was no error, don't use GetLastError
jne okay
invoke GetLastError
cmp eax,ERROR_ALREADY_EXISTS
je exit ;if program is already running
jmp other_error ;if another error occurred
okay: ;we're here if this is the only instance
exit: ;handle another instance of this program
other_error: ;handle other error
because:
1. CreateMutex is not quaranteed to change value of last error to 0, when it succeeds. Remember GetLastError gets last error that happened, not last return status.
2. CreateMutex can fail for other reasons than ERROR_ALREADY_EXISTS, you should always think of this
hth,
farrier
hang on :red
according to the documentation, CreateMutex returns a handle, even if the mutex already exists
i.e., in the above code, GetLastError will not be executed unless there is some other type of error
should SetLastError be used prior to the call ?
or - should OpenMutex be used ?
i am a little confused :P
this seems to work
INVOKE SetLastError,0
INVOKE CreateMutex,NULL,TRUE,offset szMutexName
mov hMutex,eax
INVOKE GetLastError
or eax,eax
jz FirstInstance
cmp eax,ERROR_ALREADY_EXISTS
jz SubsequentInstance
OtherError:
don't forget to CloseHandle,hMutex
I am trying to modify this so it checks to see if explorer.exe is running.
DlgBox db 'DLGBOX',0,0
ObjectName db 'explorer.exe',0
errmsg db 'The application is already running',0
capt db 'Error',0
.data?
hMutex dd ?
.code
start:
invoke CreateMutex,NULL,FALSE,ADDR ObjectName
mov hMutex,eax
invoke GetLastError
cmp eax,ERROR_ALREADY_EXISTS
jne @f
invoke MessageBox,0,ADDR errmsg,ADDR capt,MB_ICONEXCLAMATION
jmp finish
@@:
;invoke GetModuleHandle,0
;invoke DialogBoxParam,eax,ADDR DlgBox,0,ADDR DlgProc,0
finish:
invoke CloseHandle,hMutex
invoke ExitProcess,eax
DlgProc PROC hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
.IF uMsg==WM_CLOSE
invoke EndDialog,hWnd,0
.ELSE
xor eax,eax
ret
.ENDIF
mov eax,1
ret
DlgProc ENDP
END start
hi,
you must enumerate all processes (http://msdn.microsoft.com/en-us/library/windows/desktop/ms682623(v=vs.85).aspx) and compare their names wiht your pattern.
there is (almost) always one instance of explorer.exe running :P
I am modifying this.
It terminates the app, but always shows that the app has been terminated even when it was never running.
start:
invoke LoadIcon,hInstance,200 ; icon ID
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
; with ReqNTPrivilege call, we ask for the 'SeShutdownPrivilege'
; note string names of possible privilege are in windows.inc
invoke ReqNTPrivilege, SADD("SeShutdownPrivilege")
.if eax == FALSE
invoke MessageBox,NULL,addr msg_NotPL,addr BoxName,MB_OK
invoke ExitProcess,NULL
.endif
invoke ExitProcess,eax
KillProcess proc lpszExecutable:LPSTR
LOCAL bLoop:BOOL
LOCAL bResult:BOOL
LOCAL pe32:PROCESSENTRY32
LOCAL hProcess:HANDLE
LOCAL hProcesses:HANDLE
mov bLoop,TRUE
mov bResult,FALSE
; Returns an open handle to the specified snapshot if successful or - 1 otherwise.
invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,0
mov hProcesses,eax ; Did not need 2 copies of your snap shot
mov pe32.dwSize,SIZEOF PROCESSENTRY32
invoke Process32First,hProcesses,ADDR pe32
.IF eax
.WHILE bLoop
invoke CompareString, LOCALE_USER_DEFAULT, NORM_IGNORECASE, addr pe32.szExeFile, -1, lpszExecutable, -1
.IF eax==2 ; check if strings are equal in lexical value
; With having addr pe32.th32ProcessID you were getting an invalid PID
invoke OpenProcess, PROCESS_TERMINATE, FALSE, pe32.th32ProcessID ; returns handle
.IF eax!=NULL
mov hProcess, eax ; Need to save the process handle to terminate
invoke TerminateProcess, hProcess, 0
invoke CloseHandle, hProcess ; fails if eax is zero
mov bResult,TRUE;
.endif
.endif
; why go on to next process ?
invoke Process32Next, hProcesses, ADDR pe32
; Retrieves information about the next process recorded in a system snapshot.
mov bLoop,eax
.endw
invoke CloseHandle,hProcesses
.endif
mov eax,bResult
ret
KillProcess endp
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
LOCAL tc :DWORD
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_BTNFACE+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ ; creates a "hidden" window
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_MINIMIZE ;Minimal window
invoke UpdateWindow, hwnd
; -------------------
; Close screen
; -------------------
invoke SendMessage,hwnd,WM_SYSCOMMAND,SC_CLOSE,NULL
;-----------------------------------
; Loop until PostQuitMessage is sent
;-----------------------------------
StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL bResult:BOOL
LOCAL bLoop:BOOL
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_CREATE
mov eax, -1 ;FALSE
mov bResult,eax
invoke MessageBox,0,OFFSET sucesstext,OFFSET started,MB_OK
invoke KillProcess,OFFSET ProcessName
ret
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
ReqNTPrivilege proc lpPrivilegeName:DWORD
; return TRUE (not zero) in eax if privilege is granted
; lpPrivilegeName parameter points to a string with request privilege name
LOCAL hProcess:DWORD
LOCAL hToken:DWORD
LOCAL phToken:DWORD
LOCAL RetLen:DWORD
LOCAL pRetLen:DWORD
LOCAL tkp:TOKEN_PRIVILEGES
LOCAL tkp_old:TOKEN_PRIVILEGES
;
invoke GetCurrentProcess
mov hProcess, eax
lea eax, hToken
mov phToken, eax
invoke OpenProcessToken, hProcess, \
TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, \
phToken
.if eax != FALSE
lea eax, tkp.Privileges[0].Luid
invoke LookupPrivilegeValue, NULL, \
lpPrivilegeName, \
eax
lea eax, RetLen
mov pRetLen, eax
mov tkp.PrivilegeCount, 1
mov tkp.Privileges[0].Attributes, SE_PRIVILEGE_ENABLED
invoke AdjustTokenPrivileges, hToken, \
NULL, \
addr tkp, \
sizeof tkp_old, \
addr tkp_old, \
pRetLen
.endif
ret
ReqNTPrivilege endp
end start
i am trying to absorb what you are doing :P
but, i noticed that WM_CREATE may not always return 0
if EAX (from bResult) is -1, it will cause the window to be destroyed
CreateWindowEx will return invalid handle
I want to not run a second instance of a program.
i find myself in a similar situation
i know that AV subjects are outside the scope of the forum - so i will leave that part out of the discussion
i want to run a single instance of a program whenever a browser is opened
in this case, i use IE and FF - but any could be added
so - i open the first instance of IE or FF
this is done by running my program, which does some stuff, then runs the browser
once the browser is closed, it does some other stuff
that example demonstrates a single instance of a browser
whenever i open subsequent instances of a browser (either one), it should not do the "stuff"
it should just run the browser
when all instances of the browsers are closed, my program should do the "other stuff", then close
i want to avoid running multiple instances of my program
i looked into event objects, mutex objects, and semaphore objects
none of those seem to be what i need
the current game-plan is to send the browser process handles to the first instance
it keeps a list of all browser process handles
when they are all terminated, the program does "other stuff", then exits
it isn't pretty :P
maybe someone has a better method
I had a clever idea but there is an "e" too much... see yourself:
include \masm32\include\masm32rt.inc
.code
AppName db "Masm32:", 0
start: mov esi, offset AppName
invoke GlobalFindAtom, esi
.if eax
MsgBox 0, "Oh my god, I will disappear immediately", "Atom found:", MB_OK
.else
invoke GlobalAddAtom, esi
push eax
MsgBox 0, "Try to launch a new one, he he!!", "I am the first one:", MB_OK
pop eax
invoke GlobalAddAtom, eax
.endif
exit
end start
I still don't understand what an atom table is. ??
n Microsoft® Windows®, an atom table is a system-defined table that stores strings and corresponding identifiers. An application places a string in an atom table and receives a 16-bit integer, called an atom, that can be used to access the string.
Hi Magnum,
An atom table is a global string array with a unique 16-bit identifier.
Just assemble my snippet and test it. Make sure to take out the e in .if eax
Cheers,
Jochen
Quote from: Magnum on October 22, 2011, 03:36:58 AM
I want to not run a second instance of a program.
In the attachment a function, which returns all instances of a program (PIDs)