Solved

Modal Process

Posted on 2004-10-06
17
335 Views
Last Modified: 2013-11-20
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
Comment
Question by:akalmani
  • 8
  • 6
  • 2
  • +1
17 Comments
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 12245964
Hide App1 until App2 finishes?
0
 
LVL 6

Expert Comment

by:Amritpal Singh
ID: 12246005
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
 
LVL 6

Expert Comment

by:Amritpal Singh
ID: 12246021
0
 
LVL 3

Author Comment

by:akalmani
ID: 12248097
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
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 12248773
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
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 12248793
The DoModal of a dialog is similar - that disables the parent frame of the window owning the dialog.
0
 
LVL 3

Author Comment

by:akalmani
ID: 12256164
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
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 12256557
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
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Expert Comment

by:Popoi
ID: 12281175
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
 
LVL 3

Author Comment

by:akalmani
ID: 12284276
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
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 12284541
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
 
LVL 3

Author Comment

by:akalmani
ID: 12305316
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
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 12305495
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
 
LVL 3

Author Comment

by:akalmani
ID: 12316370
I tried LockWindowUpdate it does not work either.
0
 
LVL 44

Accepted Solution

by:
AndyAinscow earned 500 total points
ID: 12316445
If I have any other ideas I'll get back.
0
 
LVL 3

Author Comment

by:akalmani
ID: 12318579
Hi AndyAinscow I got the code working. Thanks for your help. Your moral support helped me to achieve the goal.
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 12318655
Thanks and glad to hear it helped.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Introduction: Ownerdraw of the grid button.  A singleton class implentation and usage. Continuing from the fifth article about sudoku.   Open the project in visual studio. Go to the class view – CGridButton should be visible as a class.  R…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now