MFC Application Instance Control

MFC Application Instance Control

  • Comments 16

An interesting question, asked days ago in one of our C++ forums, was the following:

A possible approach, based in mutex (mutual exclusion) objects, was posted a few hours later. In the proposed schema you declare a mutex object inside the MFC application class (i.e. the class header):

  1. // MFC application class declaration
  2. class CYourAppClass::CWinApp
  3. {
  4.     ...
  5.     HANDLE m_Mutex_h;
  6.     UINT m_WinMsg_ui;
  7.     ...
  8. }

When you define the MFC application InitInstance() method, you attempt to create the mutex -whose creation will succeed the first time, fail afterward.-

  1. BOOL CYourAppclass::InitInstance()
  2. {
  3.     m_WinMsg_ui=RegisterWindowMessage(_T("ONNMESSAGE"));
  4.     m_Mutex_h=::CreateMutex(NULL, FALSE, STR_VIEWUTILITYMUTEX);
  5.  
  6.     if ((m_Mutex_h!=NULL)&&(GetLastError()!=ERROR_ALREADY_EXISTS))
  7.     {
  8.         // App is NOT running twice
  9.         CYourDlg dlg;
  10.         m_pMainWnd = &dlg;
  11.         INT_PTR nResponse = dlg.DoModal();
  12.     }
  13.     else
  14.     {
  15.         // App is running twice send message to running app,
  16.         // the app will now know a 2nd instance was started
  17.         ::SendMessage(HWND_BROADCAST, m_WinMsg_ui, 0, 0);
  18.     }
  19.  
  20.     // more initialization follows
  21.     ...
  22. }

About the else, the solution proponent (Bordon) said the following

The "SendMessage" is only needed if you want that the running application knows a 2nd instance was started. You send a registered windows message to your running application, and your running application will receive this message and knows that a 2nd instance was started. You can create i.e. code in your message handler to bring your app to the front.

Interesting. Was marked as solution by the original guy so it seems it worked. Would you have recommended another approach?

UPDATE: Please check in the comments section an entry posted by MS MFC expert Pat Brenner about this issue.

  • Well, it's certainly more elegant than checking the list of running processes, and a bit more pleasant than the solution used by some programs to try to open a socket.

  • I think it might be interesting to use a namespace to secure the mutex object and prevent a DOS attack.

  • @Yannick: Care to elaborate?

  • This is a good technique for ensuring one instance though it might fail in the desire to bring the original instanceto the foreground since Windows disallows background apps from stealiing focus without permission.

    To avoid this, the second instance would need to determine the original instance's process id and use AllowSetForegroundWindow( dwProcessId ) before broadcasting the message. Finding that process id involves other techniques or messages.

    John

  • There is a race condition in that code.

    If no instances are running and then two copies of the program are started at once, one may get the mutex but not have created its window yet, while the other doesn't get the mutex and tries to broadcast a message that nothing will receive.

    Not really a problem if you are just trying to make the window come to the front (it will do so anyway), but usually apps which do this aim to pass any filenames specified on their command-lines to the main instance, and then drop files when run in this way. You'd be surprised by how many apps (including major ones like Office) do this wrong.

    I'd also say that using HWND_BROADCAST isn't ideal as there's no reason to make every top-level window in the system process the message. Might as well find the window by class (or some other criteria).

  • s/receive/process/ in my previous comment.

  • Spy++ actually does something like this at startup, because only one instance can be running (since it uses global hooks to look at the message queues).  It follows a sequence like this:

    1) Create the mutex.  Don't check for failure on this.  If the mutex already exists, fine.

    2) Take the mutex.  If unable because another instance is initializing, exit.  The other instance will activate when it's done initializing.

    3) Search top-level windows for another instance (has a signature in the GWLP_USERDATA).  If found, call SetForegroundWindow and BringWindowToTop on that window, then exit.  Otherwise, continue initialization.

    4) When finished initializing, release the mutex.

  • An alternative is to use CreateNamedPipe with the FILE_FLAG_FIRST_PIPE_INSTANCE flag. That way you can use the pipe to communicate as well ;)

      HANDLE

      readPipe = CreateNamedPipe( "\\\\.\\pipe\\foo",

                                  FILE_FLAG_FIRST_PIPE_INSTANCE | PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,

                                  PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,

                                  4,

                                  1024,

                                  1024,

                                  2000,

                                  0 );

      if( readPipe == INVALID_HANDLE_VALUE )

      {

         HANDLE

         writePipe = CreateFile( "\\\\.\\pipe\\foo",

                                 GENERIC_WRITE,

                                 0,

                                 0,

                                 OPEN_EXISTING,

                                 FILE_ATTRIBUTE_NORMAL,

                                   0 );

         if( hWritePipe == INVALID_HANDLE_VALUE )

         {

            return -1;

         }

         else

         {

            ...

            DWORD written;

            WriteFile( hWritePipe, bar, strlen(bar)+1, &written, 0);

            CloseHandle( hWritePipe );

            return 0;

         }

      }

      ...

  • I had a feeling that MFC had a class or function to do this already... (not an MFC dev, which is my excuse for not being sure.)

  • how do we pass the data to the original instance. For e.g. if the second instance was tried to start with additional commandline arguments which need to be passed to original application to process it. In that case should we rely on shared data segment? Or use a shared memory? Which approach would be good?

  • I used the following technique for several GUI applications:

    - Give your main window's class unique name.

    - At startup try to find top-level window from your class

    - if nothing is found, go start the app

    - else send WM_SHOWWINDOW message to the found window

    The solution is simple, does not allocate mutex object, and has no problem with "background app trying to steal focus" syndrome.

    OK, there is a small chance for race condition, but our testing did not prove that.

    Nobody asks what would happen when the first application instance is hung (sometimes with no visible window at all)? No matter how you send the message to activate (window msg, pipes, etc.) it is still hung. Your app does not start anymore, and most users would need to restart their machines, which slows them down in their work.

    That is one reason why "limiting user to one instance of your app is not good idea". Give the users what they want - they might have found way to improve productivity by using multiple instances of your application. Do not put restrictions on your users - you might be on their place next time!

  • CString CS1, CS2, CSResult;

    int n1, n2, result;

    char str[20];

    m_num1.GetWindowText(CS1);

    m_num2.GetWindowText(CS2);

    n1= atoi(CS1);

    n2= atoi(CS2);

    result=n1+n2;

    _itoa(result,str, 10);

    CSResult= str;

    m_resultado.SetWindowTextW(CSResult);

  • SendMessage is subject to UIPI so that would not work in all cases.

    I´m using support.microsoft.com/.../243953 with an additional Global Event based on the Mutex Name that i can set from other instances. That way I ensure one instance on the whole system.

    With a simple change from Global to Local this could be made session based.

  • I usually write machine control software, which should normally be restricted to a single instance. I've been using the following technique with success for many years. There is probably a chance for a race condition, but I haven't seen it.

    My technique uses shared memory. The app's source file contains the following:

    #pragma data_seg( ".sAppShared" )

    LONG gs_nInstanceCount = -1;

    HWND gs_hndMainWnd = 0;

    #pragma data_seg()

    InitInstance() contains the following:

      // Make sure this is the only instance

      int nInstance = InterlockedIncrement( &gs_nInstanceCount );

      if( nInstance )

      {

         // Another instance is already running

         if( gs_hndMainWnd )

         {

            ::BringWindowToTop( gs_hndMainWnd );

            if( ::IsIconic( gs_hndMainWnd ) )

            {

               ::ShowWindow( gs_hndMainWnd, SW_RESTORE );

            }

         }

         return FALSE;

      }

    ExitInstance() contains the following:

      int nInstance = InterlockedDecrement( &gs_nInstanceCount );

    The DEF file contains the following:

    SECTIONS

      .sAppShared      SHARED

    What is NOT shown is when gs_hndMainWnd is set. My application is a dialog app. OnInitDialog() in the main dialog calls a method in the app to set the window handle.

    I don't need to send data to the original app. My technique simply checks if another instance is running. If one is, the second instance will bring it to the top and restore it if it was minimized. Then the second instance will close.

    If there is a race condition, the first instance that increments gs_nInstanceCount will be the only instance running. The second instance that increments gs_nInstanceCount may not find the handle to the first instance, but that only means the first instance cannot be forced as the top window by the second instance. But since this also means the first instance is still being created, so it should become the top instance on its own. This technique should work fine if you don't need to pass any data to the first instance.

    If you need to communicate with the first instance, I would suggest creating a named event. The first instance should set the event after it sets gs_hndMainWnd. All other instances should wait for the event before using the window handle. Of course you'll need to reset the event when the first instance closes and you'll need to come up with some other ways to make one of the other instances the main instance. This should be the easy part.

  • @TSprout

    This solution does not work. Yiu can run one instance as User, and another one as the same User using the "Run as Administrator" function. Shared memory is no longer shared...

Page 1 of 2 (16 items) 12