News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

single instance

Started by xanatose, September 23, 2011, 05:49:08 PM

Previous topic - Next topic

xanatose

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?

jj2007

Use FindWindow to check if an instance is open, then send the commandline to the open instance (e.g. with WM_COPYDATA) and exit.

qWord

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.
FPU in a trice: SmplMath
It's that simple!

xanatose

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.


Vortex

Hi xanatose,

Here is a CreateMutex example checking for the second instance of the executable.

jj2007

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
.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

farrier

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
It is a GOOD day to code!
Some assembly required!
ASM me!
With every mistake, we must surely be learning. (George...Bush)

jj2007

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....

Vortex

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



hutch--

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.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

dedndave

that sounds easiest   :bg
the class name is a semaphore, of sorts

Vortex

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

dancho

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'


dedndave

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

dedndave

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