The MASM Forum Archive 2004 to 2012

Miscellaneous Forums => The Orphanage => Topic started by: xanatose on September 23, 2011, 05:49:08 PM

Title: single instance
Post by: xanatose on September 23, 2011, 05:49:08 PM
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?
Title: Re: single instance
Post by: jj2007 on September 23, 2011, 06:01:19 PM
Use FindWindow to check if an instance is open, then send the commandline to the open instance (e.g. with WM_COPYDATA) and exit.
Title: Re: single instance
Post by: qWord on September 23, 2011, 06:14:38 PM
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.
Title: Re: single instance
Post by: xanatose on September 23, 2011, 07:00:19 PM
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.

Title: Re: single instance
Post by: Vortex on September 23, 2011, 07:42:34 PM
Hi xanatose,

Here is a CreateMutex example checking for the second instance of the executable.
Title: Re: single instance
Post by: jj2007 on September 23, 2011, 08:29:18 PM
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
Title: Re: single instance
Post by: 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
Title: Re: single instance
Post by: jj2007 on September 25, 2011, 06:44:24 AM
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....
Title: Re: single instance
Post by: Vortex on September 25, 2011, 08:59:16 AM
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


Title: Re: single instance
Post by: hutch-- on September 29, 2011, 09:24:28 AM
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.
Title: Re: single instance
Post by: dedndave on September 29, 2011, 02:54:20 PM
that sounds easiest   :bg
the class name is a semaphore, of sorts
Title: Re: single instance
Post by: Vortex on September 29, 2011, 05:16:33 PM
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
Title: Re: single instance
Post by: dancho on September 29, 2011, 07:19:42 PM
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'

Title: Re: single instance
Post by: dedndave on October 20, 2011, 07:14:41 PM
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
Title: Re: single instance
Post by: dedndave on October 20, 2011, 08:49:12 PM
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
Title: Re: single instance
Post by: Magnum on October 21, 2011, 11:34:51 PM
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

Title: Re: single instance
Post by: qWord on October 21, 2011, 11:44:53 PM
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.
Title: Re: single instance
Post by: dedndave on October 22, 2011, 12:31:21 AM
there is (almost) always one instance of explorer.exe running   :P
Title: Re: single instance
Post by: Magnum on October 22, 2011, 02:36:36 AM
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

Title: Re: single instance
Post by: dedndave on October 22, 2011, 03:26:03 AM
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
Title: Re: single instance
Post by: Magnum on October 22, 2011, 03:36:58 AM
I want to not run a second instance of a program.
Title: Re: single instance
Post by: dedndave on October 22, 2011, 03:47:16 AM
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
Title: Re: single instance
Post by: jj2007 on October 22, 2011, 06:26:40 AM
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
Title: Re: single instance
Post by: Magnum on October 22, 2011, 11:58:38 AM
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.
Title: Re: single instance
Post by: jj2007 on October 22, 2011, 03:58:11 PM
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
Title: Re: single instance
Post by: qWord on October 22, 2011, 04:20:04 PM
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)