Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Single instance using CreateMutex

241 views
Skip to first unread message

Alan Sloan

unread,
Jun 10, 2009, 4:53:54 PM6/10/09
to
I have a VB6 app which is an MDI application. I am using CreateMutex to
ensure that only a single instance of this app is running at one time. Upon
load, I call CreateMutex. If I don't get an ERROR_ALREADY_EXISTS error,
then the mutex is created sucessfully.

When a second instance is executed, it calls CreateMutex also, it will get
an ERROR_ALREADY_EXISTS error and the second app will pass execution to the
initial instance and end. This is all working fine.

Here's the problem. In this app, the user has the option of hiding the main
application window. This is done by setting the form's visible property to
False. When the form is "hidden", and a second instance starts up, the
CreateMutex call does NOT cause an ERROR_ALREADY_EXISTS error, thus allowing
the second instance to start.

Is there some reason that when the initial instance's window is not visible,
that the CreateMutex call would not get the ERROR_ALREADY_EXISTS error?

Thanks in advance for any help,
Alan


Ralph

unread,
Jun 10, 2009, 5:37:34 PM6/10/09
to

"Alan Sloan" <NoS...@comcast.net> wrote in message
news:ODmZS2g6...@TK2MSFTNGP06.phx.gbl...

>
> Is there some reason that when the initial instance's window is not
visible,
> that the CreateMutex call would not get the ERROR_ALREADY_EXISTS error?
>

Where during 'start-up' is your CreateMutex code located?

-ralph


Nobody

unread,
Jun 10, 2009, 7:13:41 PM6/10/09
to
Is the mutex name based on some property value? If so, what is it?

Post the code if possible, or code that duplicates the problem.


Alan Sloan

unread,
Jun 10, 2009, 6:22:14 PM6/10/09
to
The CreateMutex code is in Sub Main, before any forms are loaded. As long
as the main window is visible, it all works fine. But when the windows is
hidden, it doesn't "see" the existing mutex.


MikeD

unread,
Jun 10, 2009, 8:19:16 PM6/10/09
to

"Alan Sloan" <NoS...@comcast.net> wrote in message
news:OFSqpnh6...@TK2MSFTNGP02.phx.gbl...

> The CreateMutex code is in Sub Main, before any forms are loaded. As long
> as the main window is visible, it all works fine. But when the windows is
> hidden, it doesn't "see" the existing mutex.


If you're calling CreateMutex in Sub Main and Sub Main is your Startup
object, then any forms, visible or not (or even loaded or not), should have
nothing to do with it. I've used CreateMutex in VB6 applications which
don't even HAVE forms and it works fine.

I believe there must be something else going on. By any chance, is your app
running under Terminal Server (this also includes Fast User Switching
because that's essentially terminal server)? If so, is it a different user
starting the app and do you really want to prevent the 2nd user from
starting the app? If so, you need to make the mutex global. See the
documentation for CreateMutex for information on how to do this (basically,
you precede the mutex's name with the string "Global\". This creates the
mutex in the global namespace rather than a session namespace.

--
Mike

Nobody

unread,
Jun 11, 2009, 12:38:48 PM6/11/09
to
"Alan Sloan" <NoS...@comcast.net> wrote in message
news:u8aJJFr6...@TK2MSFTNGP03.phx.gbl...
> I have read about the "Global\" thing, and even though it doesn't apply, I
> tried it anyhow with no difference. I'm really at a loss of what could
> possibly be wrong.

Use process monitor to see you Mutex to find out why it's not working as
expected.

http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx


Alan Sloan

unread,
Jun 11, 2009, 12:34:23 PM6/11/09
to
No the Mutex name is a hard-coded string. Here's the code that's
setting/checking for the Mutex

***** Public Type that's in a module*****
Public Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type

Private Function IsPrevInstanceOfART(ByVal sMutex As String) As Boolean

Dim sa As SECURITY_ATTRIBUTES
Dim m_hMutex As Long
sa.nLength = Len(sa)

On Error GoTo IsPrevInstanceOfART_Error

If In_IDE Then 'Don't set the Mutex if we're in the IDE
IsPrevInstanceOfART = False 'Not (App.PrevInstance)
Else
' Ensures we don't run a second instance even
' if the first instance is in the start-up phase
m_hMutex = CreateMutex(sa, 1, sMutex) 'Attempt to create the Mutex
If (Err.LastDllError = ERROR_ALREADY_EXISTS) Then
'Mutex exists, return TRUE
CloseHandle m_hMutex
IsPrevInstanceOfART = True
End If
End If

On Error GoTo 0
Exit Function

IsPrevInstanceOfART_Error:
WriteErr "Error " & Err.Number & " (" & Err.Description & ") in
procedure IsPrevInstanceOfART(line:" & Erl & ") of Module StartHere"

End Function


"Nobody" <nob...@nobody.com> wrote in message
news:uKPTmEi6...@TK2MSFTNGP04.phx.gbl...

Alan Sloan

unread,
Jun 11, 2009, 12:25:47 PM6/11/09
to
The app is not running in Terminal server and no fast user switching, and in
fact, I'm just running it interactively as a user would. The only thing
that is "out of the ordinary" is that the main form is subclassed so that
the second instance can send messages to the first instance of the app.
This could introduce some weirdness, however as long as the main form is
visible it all works. It's only when the main form's visible property is
set to false does it stop "seeing" the mutex.

I have read about the "Global\" thing, and even though it doesn't apply, I
tried it anyhow with no difference. I'm really at a loss of what could
possibly be wrong.


"MikeD" <nob...@nowhere.edu> wrote in message
news:eBbcEpi6...@TK2MSFTNGP06.phx.gbl...

MikeD

unread,
Jun 11, 2009, 12:47:03 PM6/11/09
to

"Alan Sloan" <NoS...@comcast.net> wrote in message news:%23XI48Jr...@TK2MSFTNGP06.phx.gbl...

> No the Mutex name is a hard-coded string. Here's the code that's setting/checking for the Mutex
>
> ***** Public Type that's in a module*****
> Public Type SECURITY_ATTRIBUTES
> nLength As Long
> lpSecurityDescriptor As Long
> bInheritHandle As Long
> End Type
>
> Private Function IsPrevInstanceOfART(ByVal sMutex As String) As Boolean
>
> Dim sa As SECURITY_ATTRIBUTES
> Dim m_hMutex As Long
> sa.nLength = Len(sa)
>
> On Error GoTo IsPrevInstanceOfART_Error
>
> If In_IDE Then 'Don't set the Mutex if we're in the IDE
> IsPrevInstanceOfART = False 'Not (App.PrevInstance)
> Else
> ' Ensures we don't run a second instance even
> ' if the first instance is in the start-up phase
> m_hMutex = CreateMutex(sa, 1, sMutex) 'Attempt to create the Mutex
> If (Err.LastDllError = ERROR_ALREADY_EXISTS) Then
> 'Mutex exists, return TRUE
> CloseHandle m_hMutex
> IsPrevInstanceOfART = True
> End If
> End If


Don't close the handle.

--
Mike


MikeD

unread,
Jun 11, 2009, 12:53:58 PM6/11/09
to

"Alan Sloan" <NoS...@comcast.net> wrote in message news:%23XI48Jr...@TK2MSFTNGP06.phx.gbl...


Sorry, sent the previous reply accidently before I was done.

Don't close the handle. You should be specifying that you want to inherit an existing handle as such

sa.bInheritHandle = 1

What's happening is that you're destroying the mutex when you run the app a 2nd time. So even though the 1st instance is still
running, there's no mutex any longer.

Nobody

unread,
Jun 11, 2009, 1:30:47 PM6/11/09
to
"MikeD" <nob...@nowhere.edu> wrote in message
news:upuJ3Ur6...@TK2MSFTNGP05.phx.gbl...

> Sorry, sent the previous reply accidently before I was done.
>
> Don't close the handle. You should be specifying that you want to inherit
> an existing handle as such
>
> sa.bInheritHandle = 1
>
> What's happening is that you're destroying the mutex when you run the app
> a 2nd time. So even though the 1st instance is still running, there's no
> mutex any longer.

I agree with you about not closing the handle(I missed that), but
"bInheritHandle" only affects child processes that were created by your
process, like using CreateProcess() to start another process. That other
process can inherit the handles. Processes that start from Explorer don't
inherit the handle. bInheritHandle is perhaps used if you make a main app
that runs other apps created by you that need to use handles created by the
parent process, so in the OP's case it doesn't matter if it was set or not.

Nobody

unread,
Jun 11, 2009, 1:23:39 PM6/11/09
to
"Alan Sloan" <NoS...@comcast.net> wrote in message
news:%23XI48Jr...@TK2MSFTNGP06.phx.gbl...
> No the Mutex name is a hard-coded string. Here's the code that's
> setting/checking for the Mutex

The problem is that you are specifying a valid security descriptor that is
empty, change the parameter to As Long, and pass "ByVal 0&", instead of
"sa".

Basically Windows grants access to anyone if the security descriptor
parameter is null. If a security descriptors is specified, access is NOT
granted by default except to those who are specifically listed in the
security descriptor. Since "lpSecurityDescriptor" is 0 inside the structure,
no one gains access to the object. The second app to call "CreateMutex" gets
access denied error in this case(LastDllError = 5). This is called empty,
rather than null security descriptor. See here:

Null DACLs and Empty DACLs
http://msdn.microsoft.com/en-us/library/aa379286(VS.85).aspx

The same applies to any function that takes security descriptor as a
parameter.

Also, to debug EXE's easily, use OutputDebugString() API function to print
to an external debug viewer. Here is one that is free and does not require
installation:

DebugView:
http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx

Declaration:

Public Declare Sub OutputDebugString Lib "kernel32" Alias _
"OutputDebugStringA" (ByVal lpOutputString As String)

Usage:

OutputDebugString "ABC"

Wrapper sub:

Public Sub DebugPrint(ByRef s As String)
OutputDebugString s
End Sub


Alan Sloan

unread,
Jun 11, 2009, 3:35:51 PM6/11/09
to
I agree, but it's never actually making it to the line that closes the
handle. In this block of code, the first instance creates the mutex just
fine, doesn't hit the IF block that would close the handle. The second
instance also is not hitting the IF block, because the CreateMutex line
appears to be working with no error code, so the handle isn't closed there
either. It still baffles me that if the app's main window is visible,
everything works just fine.

m_hMutex = CreateMutex(sa, 1, sMutex) 'Attempt to create the Mutex
If (Err.LastDllError = ERROR_ALREADY_EXISTS) Then
'Mutex exists, return TRUE
CloseHandle m_hMutex
IsPrevInstanceOfART = True
End If

So far I've tried every suggestion that has been given and it's just not
making sense. My next option is going to be using API to hide the window
instead of Visible = False. Who knows. At this point I'll try anything.

"Nobody" <nob...@nobody.com> wrote in message

news:%238ykqpr...@TK2MSFTNGP03.phx.gbl...

Alan Sloan

unread,
Jun 11, 2009, 4:19:53 PM6/11/09
to
Ok, that didn't work either. Once again, the second instance of the app
seems to be able to create the Mutex again with no error. Here's the latest
version of the function. Both instances produce this MsgBox: CreateMutex
returned 148, LastDllError = 0

Private Function IsPrevInstanceOfART(ByVal sMutex As String) As Boolean

Dim m_hMutex As Long

On Error GoTo IsPrevInstanceOfART_Error

If In_IDE Then
IsPrevInstanceOfART = False


Else
' Ensures we don't run a second instance even
' if the first instance is in the start-up phase

m_hMutex = CreateMutex(ByVal 0&, 1, sMutex)
MsgBox "CreateMutex returned " & m_hMutex & ", LastDllError = " &
Err.LastDllError
If (Err.LastDllError = ERROR_ALREADY_EXISTS) Or (Err.LastDllError =
ERROR_ACCESS_DENIED) Then


CloseHandle m_hMutex
IsPrevInstanceOfART = True
End If

End If

On Error GoTo 0
Exit Function

IsPrevInstanceOfART_Error:


WriteErr "Error " & Err.Number & " (" & Err.Description & ") in
procedure IsPrevInstanceOfART(line:" & Erl & ") of Module StartHere"

End Function


"Nobody" <nob...@nobody.com> wrote in message

news:uqLMZ%23s6JH...@TK2MSFTNGP02.phx.gbl...


> "Alan Sloan" <NoS...@comcast.net> wrote in message

> news:OJ9JVvs6...@TK2MSFTNGP04.phx.gbl...


>>I agree, but it's never actually making it to the line that closes the
>>handle. In this block of code, the first instance creates the mutex just
>>fine, doesn't hit the IF block that would close the handle. The second
>>instance also is not hitting the IF block, because the CreateMutex line
>>appears to be working with no error code, so the handle isn't closed there
>>either. It still baffles me that if the app's main window is visible,
>>everything works just fine.
>>
>> m_hMutex = CreateMutex(sa, 1, sMutex) 'Attempt to create the Mutex
>

> Insert the following line here:
>
> MsgBox "CreateMutex returned " & m_hMutex & ", LastDllError = " &
> Err.LastDllError
>
> What I was trying to say is that if you get ERROR_ACCESS_DENIED, your code
> allows other instances to run, so it least you need to change your "If"
> line of code to:
>
> If (Err.LastDllError = ERROR_ALREADY_EXISTS) Or _
> (Err.LastDllError = ERROR_ACCESS_DENIED) Then
>
> Declaration:
>
> Private Const ERROR_ACCESS_DENIED = 5&
>
> But it's better to pass "ByVal 0&" in the first parameter rather than
> "sa". If you still have problems, post the latest revision of the
> function.

Nobody

unread,
Jun 11, 2009, 4:02:24 PM6/11/09
to
"Alan Sloan" <NoS...@comcast.net> wrote in message
news:OJ9JVvs6...@TK2MSFTNGP04.phx.gbl...

>I agree, but it's never actually making it to the line that closes the
>handle. In this block of code, the first instance creates the mutex just
>fine, doesn't hit the IF block that would close the handle. The second
>instance also is not hitting the IF block, because the CreateMutex line
>appears to be working with no error code, so the handle isn't closed there
>either. It still baffles me that if the app's main window is visible,
>everything works just fine.
>
> m_hMutex = CreateMutex(sa, 1, sMutex) 'Attempt to create the Mutex

Insert the following line here:

MsgBox "CreateMutex returned " & m_hMutex & ", LastDllError = " &
Err.LastDllError

What I was trying to say is that if you get ERROR_ACCESS_DENIED, your code
allows other instances to run, so it least you need to change your "If" line
of code to:

If (Err.LastDllError = ERROR_ALREADY_EXISTS) Or _
(Err.LastDllError = ERROR_ACCESS_DENIED) Then

Declaration:

Private Const ERROR_ACCESS_DENIED = 5&

But it's better to pass "ByVal 0&" in the first parameter rather than "sa".
If you still have problems, post the latest revision of the function.

> If (Err.LastDllError = ERROR_ALREADY_EXISTS) Then

Nobody

unread,
Jun 11, 2009, 4:53:17 PM6/11/09
to
"Alan Sloan" <NoS...@comcast.net> wrote in message
news:%23YLb8Ht...@TK2MSFTNGP06.phx.gbl...

> Ok, that didn't work either. Once again, the second instance of the app
> seems to be able to create the Mutex again with no error. Here's the
> latest version of the function. Both instances produce this MsgBox:
> CreateMutex returned 148, LastDllError = 0

I just tried your code with minimal changes, like removing "In_IDE" check,
and running the EXE twice, while MDIForm1 is hidden or not, it works fine in
both situations. I am using VB6+SP5 and XP+SP2. Below is the code I used. To
try it, start a new project, add Module1 and MDIForm1, and place a timer on
MDIForm1. Set the Startup object to Sub Main. To hide the form, click on it
and it will be hidden for 10 seconds, which gives you enough time to run a
second copy. The second copy in my case returns error ERROR_ALREADY_EXISTS.
Run this project from an EXE only.

There is a possibility that the mutex name is not the same in both calls,
try using a message box that display the mutex name to be sure.

' MDIForm1 code: ======================

Option Explicit

Dim ClickStartTime As Date

Private Sub MDIForm_Click()
ClickStartTime = Now
Me.Hide
Timer1.Enabled = True
End Sub

Private Sub MDIForm_Load()
Timer1.Enabled = False
Timer1.Interval = 1000
End Sub

Private Sub Timer1_Timer()
If DateDiff("s", ClickStartTime, Now) >= 10 Then
' 10 seconds has passed since last click
Timer1.Enabled = False
Me.Visible = True
End If
End Sub

' Module1 code ===================

Option Explicit

Public Const ERROR_ACCESS_DENIED = 5&
Public Const ERROR_ALREADY_EXISTS = 183&
Public Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexA" ( _
ByVal lpMutexAttributes As Long, ByVal bInitialOwner As Long, _
ByVal lpName As String) As Long
Public Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long) As Long

Public Sub Main()
Dim b As Boolean

b = IsPrevInstanceOfART("TestMutex")
MsgBox "IsPrevInstanceOfART = " & b
If Not b Then
MDIForm1.Show
End If
End Sub

Private Function IsPrevInstanceOfART(ByVal sMutex As String) As Boolean
Dim m_hMutex As Long

On Error GoTo IsPrevInstanceOfART_Error

' If In_IDE Then
' IsPrevInstanceOfART = False
' Else
' Ensures we don't run a second instance even
' if the first instance is in the start-up phase
m_hMutex = CreateMutex(ByVal 0&, 1, sMutex)

MsgBox "CreateMutex returned " & m_hMutex & ", LastDllError = " & _
Err.LastDllError
If (Err.LastDllError = ERROR_ALREADY_EXISTS) Or ( _


Err.LastDllError = ERROR_ACCESS_DENIED) Then
CloseHandle m_hMutex
IsPrevInstanceOfART = True
End If
' End If

On Error GoTo 0
Exit Function

IsPrevInstanceOfART_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & _
") in procedure IsPrevInstanceOfART(line:" & Erl & _

Dee Earley

unread,
Jun 12, 2009, 6:05:41 AM6/12/09
to
On 11/06/2009 18:23, Nobody wrote:
> Also, to debug EXE's easily, use OutputDebugString() API function to print
> to an external debug viewer. Here is one that is free and does not require
> installation:
>
> DebugView:
> http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
>
> Declaration:
>
> Public Declare Sub OutputDebugString Lib "kernel32" Alias _
> "OutputDebugStringA" (ByVal lpOutputString As String)
>
> Usage:
>
> OutputDebugString "ABC"
>
> Wrapper sub:
>
> Public Sub DebugPrint(ByRef s As String)
> OutputDebugString s
> End Sub

There is a bit more infor and advice on this at:
http://hashvb.earlsoft.co.uk/Debugging

--
Dee Earley (dee.e...@icode.co.uk)
i-Catcher Development Team

iCode Systems

Alan Sloan

unread,
Jun 15, 2009, 3:35:45 PM6/15/09
to
Thought I'd respond and let all of you that tried to help me know that I
have found the problem. It was actually another app that was closing the
first instance, thus clearing the mutex when the second instance started up,
there was no mutex.

I totally apreciate everyone's help on trying to nail this down. On another
note, during all of this I moved the Mutex code into a class and it makes it
much cleaner and easier to read and maintain.

Thanks Again!
Alan


0 new messages