Hi everyone,

I'm writing a program for a teacher and friend of mine. It's a simple math-aid prog aimed at children up to 10yo.

I was thinking that it would not only be a good idea that a child couldn't shut down the program without a password (obviously), but how do I ensure that only one instance of the program can run at any time?

Am I right in thinking that something should be written somewhere in the following section of code?

Thanks
David

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
        try
        {
                 Application->Initialize();
                 Application->Title = "Kids Fun";
                 Application->CreateForm(__classid(TMainForm), &MainForm);
             Application->CreateForm(__classid(TMathSetupForm), &MathSetupForm);
             Application->CreateForm(__classid(TAboutBox), &AboutBox);
             Application->CreateForm(__classid(TPasswordForm), &PasswordForm);
             Application->CreateForm(__classid(TSaveFileErrorBox), &SaveFileErrorBox);
             Application->Run();
        }
        catch (Exception &exception)
        {
                 Application->ShowException(&exception);
        }
        return 0;
}

Recommended Answers

All 26 Replies

There are several ways...
Create a file on startup, and remove it again on exit.
If on startup the file already exists, then refuse to start.

Or.. a better way: Create a mutex in the global namespace(CreateMutex @msdn) on startup and destroy it on exit. If the creation fails with something like 'mutex already exists', then you know that your program is already running and you can exit.

Thanks for that and the swiftness of your reply. Never heard of CreateMutex but I'll go and do some reading.

OK. I read a bit, typed a bit compiled, with no errors but I can still open multiple version of the program. The following shows what I've tried so far:

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
/*
    HANDLE ghMutex = CreateMutex(
        NULL,              // default security attributes
        TRUE,              // initially owned
        "AnyName");        // named mutex

    if (ghMutex)// != NULL)
    {
*/
        try
        {
             Application->Initialize();
             Application->Title = "Kids Fun";
             Application->CreateForm(__classid(TMainForm), &MainForm);
             Application->CreateForm(__classid(TMathSetupForm), &MathSetupForm);
             Application->CreateForm(__classid(TClockSetupForm), &ClockSetupForm);
             Application->CreateForm(__classid(TAboutBox), &AboutBox);
             Application->CreateForm(__classid(TPasswordForm), &PasswordForm);
             Application->CreateForm(__classid(TSaveFileErrorBox), &SaveFileErrorBox);
             Application->Run();
        }
        catch (Exception &exception)
        {
                 Application->ShowException(&exception);
        }
//        CloseHandle(ghMutex);

        return 0;
/*    }
    else
      Application->Terminate();

    return 0;
*/
}

and this (which is why the above is commented out)

__fastcall TMainForm::TMainForm(TComponent* Owner)
        : TForm(Owner)
{
    HANDLE ghMutex = CreateMutex(
        NULL,              // default security attributes
        TRUE,              // initially owned
        "AnyName");             // named mutex

    if (ghMutex)// != NULL)
    {
    Application->Terminate();
        Close();
    }

}

Everything compiles without error but doesn't do what it's supposed to :(

This is a case of reading the documentation carefully:

If the mutex is a named mutex and the object existed before this function call, the return value is a handle to the existing object, GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership. However, if the caller has limited access rights, the function will fail with ERROR_ACCESS_DENIED and the caller should use the OpenMutex function.

With other words, the case of 'The mutex exists' is not considered an error, so the return value will not be NULL.

What you need to do is call GetLastError() and compare it with ERROR_ALREADY_EXISTS

Ok,
I changed the following:

9.  if (GetLastError() == ERROR_ALREADY_EXISTS )

If I use the code in the WINAPI WinMain section the program doesn't start at all - meaning that some error(s) are being compared before the first run of the program.

And in the __fastcall TMainForm section I get the compiler warning:
[C++ Warning] MainU.cpp(30): W8004 'ghMutex' is assigned a value that is never used
The good news here is that only one instance starts.

Almost there:-/

Thanks again
David

I don't know what exactly you mean with 'if I use the code in WinMain it doesnt start' ... can you show us what you have?

The warning makes sense.. you are creating a variable that you never use. However, I guess IF something goes wrong with creating the mutex... you also do not want to run, so you should give an error message back after checking ghMutex.

In full. The compiler shows no objections.

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{

  HANDLE ghMutex = CreateMutex(
    NULL,              // default security attributes
    TRUE,              // initially owned
    "AnyName");        // named mutex

  if (GetLastError() == ERROR_ALREADY_EXISTS )//ghMutex)// != NULL)
  {
    try  //never gets here . . . read below
    {
      Application->Initialize();
      Application->Title = "Kids Fun";
      Application->CreateForm(__classid(TMainForm), &MainForm);
      Application->CreateForm(__classid(TMathSetupForm), &MathSetupForm);
      Application->CreateForm(__classid(TClockSetupForm), &ClockSetupForm);
      Application->CreateForm(__classid(TAboutBox), &AboutBox);
      Application->CreateForm(__classid(TPasswordForm), &PasswordForm);
      Application->CreateForm(__classid(TSaveFileErrorBox), &SaveFileErrorBox);
      Application->Run();
    }
    catch (Exception &exception)
    {
      Application->ShowException(&exception);
    }
    CloseHandle(ghMutex);

    return 0;
  }
  else
    Application->Terminate();   //rather goes straight here!!

  return 0;

}

Hmm, that is weird... maybe one of the mutexes during testing didn't clean up properly (even though windows closes any outstanding handles when a program exits).

Try a different name for the mutex.
Also, I think it is cleaner if you name it "Global\somename" - it is clearer to any reader of your code what the purpose of this mutex is then.

Edit, something silly:
try before calling CreateMutex to call

SetLastError(0);

No go - still does nothing.
I'll reboot and try again

@evstevemd
what is singleton? I'll look at this as well, but we'll try to get this sorted first - it's so near!

A singleton class is one that allows only 1 object of its type to be created..

But this will (as far as I know) do you no good, if you run two instances of your program there will be 1 object in each of those instances.. not violating the singleton 'pattern'


--


Unfortunately, I don't have time to test your code on my pc atm.. probably later I can

I rebooted before you edited your last post, but even with the alteration (SetLastError(0)) it's still a no-go.

Would the OS or the compiler make a difference?

GOT IT!!

I simply changed Line9 to:
if (GetLastError() != ERROR_ALREADY_EXISTS)

My excuse is that I wear glasses. What's your's?:?:

:icon_redface:

Many thanks for your input. A really great effort. (and I mean that sincerelly)

Would the OS or the compiler make a difference?

Your attempt is just a tad bit off, basically you could try

HANDLE H = CreateMutex(0, TRUE, _T("your_mutex_name_here"));

if(H == NULL)
{
    // Strict failure, GetLastError() will tell you more, perhaps ExitProcess()        
}
else if(GetLastError() == ERROR_ALREADY_EXISTS)
{
    // Prior instance detected, exit this instance.
}
else
{
    // No prior instance detected, execute the program.
    // The mutex will be automatically destroyed once the program exits.
}

I can't find it at the moment, but there's a WinAPI tutorial out there that tells you how to build a window. In that tutorial, it has you do something with the window handle(s) that makes it possible to only open one instance of the program. If you try to open more than one, it automatically changes to the existing window and closes the new instance.

@mitrmkar
@Fbody
Many thanks as well

>> Many thanks as well

I'm not sure how to interprete this, but just note that your change:

>> I simply changed Line9 to:
>> if (GetLastError() != ERROR_ALREADY_EXISTS)

is not adequate. You really need to check what CreateMutex() returns along with what GetLastError() tells you.

[EDIT]
More discussion about the topic Avoiding Multiple Instances of an Application

I was just sitting back thinking about this. The argument is wrong (ie !=) and that the '==' doesn't allow the program to start at all is odd; as you say, what each of the returns are should be checked. But what sort of error could be returned from either GetLastError() or ERROR_ALREADY_EXISTS for a program that hasn't even started running!?

Edit: or is my logic the wrong way round?

OK. Another thought.

Am I not creating a unique instance of Mutex (with case-sensitive name)?
Therefore:
if (Mutex1 == Mutex1)
Application->Terminate();
else
run . . .

So
if (Mutex1 != Mutex1)
run . . .
else
Application->Terminate();


Would fit, or not? I mean there is no danger that my program is doing anying it shouldn't be doing?

>> But what sort of error could be returned from either GetLastError() or ERROR_ALREADY_EXISTS for a program that hasn't even started running!?

Surely the program is running since we are talking about the return value of GetLastError() . (I don't quite get what you are saying there, sorry)

But, looking at the MSDN documentation, in case the named mutex has already been created;

If the mutex is a named mutex and the object existed before this function call, the return value is a handle (i.e. NOT NULL) to the existing object, GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership.

That should tell you what to check and how to interprete what GetLastError() returns.

My excuse is that I was doing 5 things at the same time ;) - but well spotted.

I already mentioned in one of my posts that the return value of CreateMutex must be checked, and some error returned if it failed (e.g. the handle is NULL).

I've lost where this thread is going..

>>Surely the program is running since we are talking about the return value of GetLastError()

@mitrmkar
Agreed, but only if a second attempt is made to run the program.
I'm quite out of my depth here. This is all very new territory for me.

P.S. did you see all the previous posts?

@thelamb
The prog works - but I want to be sure - it's still all very new territory for me.

Hmm, I see you've marked this thread as solved, but anyhow ..

>> The prog works - but I want to be sure

This far I've gathered that your program goes like the following ..

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
  HANDLE ghMutex = CreateMutex(
    NULL,              // default security attributes
    TRUE,              // initially owned
    "AnyName");        // named mutex

  if (GetLastError() != ERROR_ALREADY_EXISTS )
  {
    // I think this is the first instance ...

    try 
    {
      Application->Initialize();
      
      // <snip>

      Application->Run();
    }
    catch (Exception &exception)
    {
      Application->ShowException(&exception);
    }
    CloseHandle(ghMutex);

    return 0;
  }
  else
  {
    // I think this is NOT the first instance ...
    Application->Terminate();
  }
  return 0;
}

To test your program's behaviour, use the below console program, i.e. launch this program first and then begin launching new instances of the main program -- will the instance detection still work?

#include <iostream>
#include <windows.h>

int main()
{
  // Create an event (the name used here must match the name of your mutex)
  HANDLE hEvent = CreateEvent(0, FALSE, FALSE, "AnyName");
  
  if(hEvent)
  {
    std::cout   << "Event handle: " << hEvent << std::endl
                << "Now, start multiple instances of your main program ..." << std::endl
                << "Press Enter to exit.";
    std::cin.get();
    CloseHandle(hEvent);
  }
  else
  {
    std::cout << "Unable to create event, error: " << GetLastError() << std::endl;
  }
}
commented: Yupp, perfect example +3
commented: very neat idea +1

mitrmkar

thank you for your efforts

Did what you asked:
compiled and ran your program, then multiple instances of mine.

result: multiple, running instances of my program.

Without your program I can only open one instance of mine. And further; when an instance of my program is running, your console won't start.

I take it that all this is good news.

I had previously tried opening multiple instances of my prog, and happily with no success!

Later I did bit more reading, and discovered that there could be problems across LANs, but more of a concern is that there could be problems across desktops.

These last 2 points enter into a realm with which I have never worked and haven't got the slightest idea. I could test it across desktops by simply creating a dummy account, but I can't test LANs.

So the question now is: is it all safe?

Would this version of code handle these two points? Or have I (again) missed some point about the capabilities of what a Mutex can achieve?

The instance detection won't work if an Event with the same name (or any object with the same name in the Global namespace) exists (as you've discovered). In that case, as defined by the documentation after CreateMutex, GetLastError() returns ERROR_INVALID_HANDLE.

If lpName matches the name of an existing event, semaphore, waitable timer, job, or file-mapping object, the function fails and the GetLastError function returns ERROR_INVALID_HANDLE. This occurs because these objects share the same name space.

Using a sufficiently random name for the mutex should eliminate any accidental name clash. As I think we've already mentioned a few times... if CreateMutex fails, your program should also exit, you've still not added this check so if someone would want to circumvent your protection they just create an Event with the same name, as nicely pointed out by mitrmkar.

I don't know what you mean with problems across LANs. The mutex is local to your computer, it does not check all of the computers on the network to see if they have a mutex with the same name. So two computers on the same LAN can both run your program one time.

However across users, only one user on one PC can run it. So if I log in as UserA, run your program and then switch to UserB, I can not run it again.
The Mutex is created in the 'Global' namespace (hence my suggestion to prepend the Mutex name with Global\ some posts back.

My program, as I wrote right at the beginning, is aimed at quite a young age group, allbeit under supervision, and I am now happy that it can't be tampered with by these young inquisitive minds! It's a hobby and being written for a friend, and I'm not looking at putting it on the market - there are far too many professional products out there.
I'm now convinced that, at least this part, is safe.
Many thanks to all for your inputs.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.