• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 346
  • Last Modified:

Modal Process

I have 2 applications. Say App1 and App2. Both are MFC dialog based applications. App1 invokes App2 using ShowModal(). Now I want App1 to wait until App2 is finished. This can be accomplished by MsgWaitForMultipleObjects(). Here I if I recieve WM_PAINT message for App1 then I allow it to recieve to App1. Any other i/p has to be removed and App2 has to have focus.

My requirement is if the user tries to do anything(mouse click, ALT+TAB, keydown) with App1 when App2 is open. I always want App2 to have focus.
I tried catching all the messages (but it flickers and its not the right solution). I tried catching specific messages but no luck. Anyidea how to achieve this?

DWORD dwID = 0;
static WNDPROC pWndProc = NULL;

static LRESULT CALLBACK InvokeWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
      case WM_ACTIVATE:case WM_ACTIVATEAPP: case WM_LBUTTONUP: case WM_RBUTTONUP:
      case WM_NOTIFY:
            EnumThreadWindows(dwID, ModalEnumProc, 0);        
        break;
    }

    return CallWindowProc(pWndProc, hwnd, msg, wParam, lParam);
}

//Pass the full path of the .exe file. e.g "C:\App2.exe"
BOOL ShowModal(IN CString PcstrPath)
{
      BOOL fRetVal = FALSE;
      HANDLE hWait[2] = {NULL, NULL};

      do
      {
            if(PcstrPath.IsEmpty())//Cannot be empty
                  break;

            //Get the startup info
            STARTUPINFO sStartupInfo;
            ZeroMemory((void*)&sStartupInfo, sizeof(STARTUPINFO));
            sStartupInfo.cb = sizeof(STARTUPINFO);
            GetStartupInfo(&sStartupInfo);

            PROCESS_INFORMATION sProcessInfo;
            ZeroMemory((void*)&sProcessInfo, sizeof(PROCESS_INFORMATION));

            //Create the process
            if(!(fRetVal = CreateProcess(NULL, PcstrPath.GetBuffer(PcstrPath.GetLength()),
                  NULL, NULL, FALSE, 0, NULL, NULL, &sStartupInfo, &sProcessInfo)))
            {
                  break;
            }

            pWndProc = (WNDPROC)::SetWindowLong(AfxGetMainWnd()->m_hWnd,
                             GWL_WNDPROC, (LONG)&InvokeWindowProc);

            DWORD dwResult = 0;
            MSG msg;

            //Create the event so that we can break from the while loop in case we
            //recieve WM_QUIT, WM_QUERYENDSESSION or WM_CLOSE
            hWait[0] = CreateEvent(NULL, FALSE, FALSE, NULL);

            //Wait for the process to finish
            hWait[1] = sProcessInfo.hProcess;
            dwID = sProcessInfo.dwThreadId;

            //The message loop lasts until the other exe closes
            while(1)
            {
                  //Read all of the messages in this loop, first since MsgWaitForMultipleObjects
                  //will check the time and wait hence these messages might not be removed.
                  //Remove each message as we read it.
                  while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                  {
                        //Quit message, exit. Cannot be
                        if(WM_QUIT == msg.message || WM_CLOSE == msg.message ||
                              WM_QUERYENDSESSION == msg.message)  
                        {
                              SetEvent(hWait[0]);
                              break;
                        }
                        
                        
                        //Handle these messages to set focus to spawned application
                        switch(msg.message)
                        {
                        case WM_PAINT://Dispatch only WM_PAINT message
                              DispatchMessage(&msg); //Let it paint the window
                              break;
                        default:
                              break;
                        }
                  }//End of while message loop

                  //Wait for all messages sent or posted to this queue
                  //or for one of the passed handles be set to signaled.
                  dwResult = MsgWaitForMultipleObjects(2, &hWait[0],
                                                FALSE, INFINITE, QS_ALLINPUT);

                  if((WAIT_OBJECT_0 + 0) == dwResult || (WAIT_OBJECT_0 + 1) == dwResult)
                  {
                        //Our process was killed or Event was set
                        break;
                  }
                  else
                  {
                        //New messages have arrived.
                        continue;
                  }
            } //End of message while loop.
      }while(0);//End of Main while loop

      if(hWait[0])//Close our event
      {
            CloseHandle(hWait[0]);
            hWait[0] = NULL;
      }

      (void)::SetWindowLong(AfxGetMainWnd()->m_hWnd, GWL_WNDPROC, (LONG)prev_windowproc);

      return fRetVal;
}


BOOL CALLBACK ModalEnumProc(IN HWND PhWnd, IN LPARAM lParam)
{
      if(PhWnd)
      {
            //Get the foreground window
            //DWORD dwThreadID = ::GetWindowThreadProcessId(::GetForegroundWindow(), NULL);

            //DWORD dwCurThreadID = ::GetCurrentThreadId();

            //Connect to the actual foreground window thread
            //::AttachThreadInput(dwCurThreadID, dwThreadID, TRUE);

            //Set focus to this our spawned application
            ::SetForegroundWindow(PhWnd);
            ::SetFocus(PhWnd);

            //Disconnect the actual foreground windows thread
            //::AttachThreadInput(dwCurThreadID, dwThreadID, FALSE);
      }

    return TRUE;
}

0
akalmani
Asked:
akalmani
  • 8
  • 6
  • 2
  • +1
1 Solution
 
AndyAinscowCommented:
Hide App1 until App2 finishes?
0
 
Amritpal SinghCommented:
plz have a look at the following link

the text is in chinese but the code is legible :)

http://www.vchelp.net/vchelp/zsrc/dists.asp?type_id=25&class_id=1&cata_id=2&article_id=458&search_term=
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
akalmaniAuthor Commented:
AndyAinscow:
No I want the App1 to stay since the User might be surprised. I want to simulate a modal behavior.

amrit_82:
I do not want to disable ALT+TAB since this is not a good solution for a product. I managed to catch ALT+TAB please refer my code.

Only problem I am facing is when the user clicks on App1(anywhere on the dialog, button, control...), the focus is not set to App2. What messages do I need to catch in InvokeWindowProc() there lies the answer. Also I am sure that ModalEnumProc() which I have written does not setfocus to App2 correctly.
0
 
AndyAinscowCommented:
When you call App2 use
AfxGetMainWnd()->EnableWindow(false);
to disable the App1 (and conversely with true after App2 is closed to re-enable it).

Is that any use?
0
 
AndyAinscowCommented:
The DoModal of a dialog is similar - that disables the parent frame of the window owning the dialog.
0
 
akalmaniAuthor Commented:
AndyAinscow:
Yes I did try it before posting this code but no luck. I had the same impression before. But requirement is to setfocus to App2 whenever user tries to mess-up with App1. Also I will not know when to set the focus to App2 since I will not recieve any messages when I disable the window of App1.

Also the DoModal exist in the same process. But here we have a completely different scenario.
0
 
AndyAinscowCommented:
This might not work.
Disable App1.
Look for mouse messages in the mainframe of app1.
When you get a mouseclick you then use SetForeGroundWindow (SetFocus, post WM_ACTIVATEAPP) to activate App2.

Have you got a window handle for App2 after it is launched from App1?
0
 
PopoiCommented:
Reading your question makes me think i would use CreateProcess and then WaitForSingleObject....

If you create a regular dialog and domodal and you click the parent window it also flickers... so isn't that the behavior you are trying to emulate anyway?
0
 
akalmaniAuthor Commented:
AndyAinscow:
----------------
>>>>When you get a mouseclick you then use SetForeGroundWindow (SetFocus, post WM_ACTIVATEAPP) to activate App2.
Which mouse message do I track? There are many messages that are posted? Can you be specific. This is what I am looking for?

>>>>Have you got a window handle for App2 after it is launched from App1?
Not a problem I can store it, that means I have App2 window handle.

Popoi:
-------
>>>>>If you create a regular dialog and domodal and you click the parent window it also flickers... so isn't that the behavior you are trying to emulate anyway?

I have already tried this, only problem here is, you get "Not responding" and since its a blocking call, it does not WM_PAINT messages. In my opinion this is not the right solution.
0
 
AndyAinscowCommented:
Try WM_LBUTTONDOWN.

I haven't tried to do what you want so this may not work.  (I can't see a problem with the logic though so it ought to work).
0
 
akalmaniAuthor Commented:
AndyAinscow:
----------------
On Weekends I modified my InvokeWindowProc() and handled the corresponding messages. Now it works except in 1 situation.

I think surely we can simulate this behavior. When I move any other window/application(that has focus obivously), the title bar of App1 shows that it has focus (Blue color, hope u understand what i mean). This should not be the case since the other window/application has focus.
I know the solution lies in WM_PAINT, but I cannot setfocus to App2 since this might flicker the window, as too many WM_PAINT messages arrive at this time. Any hints for me, how it can be achieved. I think this will be a great if it works.

//Updated Code begins here. Rest all functions are same, no changes
HWND g_hWnd = NULL;        //This variable holds the handle of App2 in ModalEnumProc.

static LRESULT CALLBACK InvokeWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
     case WM_ACTIVATE:case WM_ACTIVATEAPP: case WM_MOUSEACTIVATE: case WM_KEYDOWN:
     case WM_NCLBUTTONDOWN: case WM_LBUTTONDOWN: case WM_NCLBUTTONUP: case WM_RBUTTONDOWN:
     case WM_NCRBUTTONDOWN: case WM_NCRBUTTONUP: case WM_LBUTTONDBLCLICK: case WM_NCLBUTTONDBLCLICK:
     case WM_RBUTTONDBLCLICK: case WM_NCRBUTTONDBLCLICK: case WM_NCACTIVATE: case WM_NCPAINT:
     case WM_SYSKEYDOWN: case WM_PRINTCLIENT: case WM_CTLCOLORDLG: case WM_ERASEBKGND:
     case WM_SYNCPAINT:
          if(NULL == g_hWnd)
                EnumThreadWindows(dwID, ModalEnumProc, 0);        
         else
         {
                 SetFocus(g_hWnd);
                 SetForegroundWindow(g_hWnd);
         }
        break;
    }

    return CallWindowProc(pWndProc, hwnd, msg, wParam, lParam);
}
0
 
AndyAinscowCommented:
You could try to turn window updating off for App2 temporarily.
LockWindowUpdate() and UnlockWindowUpdate()
This prevents window repainting and should reduce the WM_PAINT messages being fired.
0
 
akalmaniAuthor Commented:
I tried LockWindowUpdate it does not work either.
0
 
AndyAinscowCommented:
If I have any other ideas I'll get back.
0
 
akalmaniAuthor Commented:
Hi AndyAinscow I got the code working. Thanks for your help. Your moral support helped me to achieve the goal.
0
 
AndyAinscowCommented:
Thanks and glad to hear it helped.
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

  • 8
  • 6
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now