Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

Modal Process

Posted on 2004-10-06
17
338 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
Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

 
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
 

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

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Need a Shell script to start a service checking the port 6 52
iSeries DB2 Query 2 97
sameEnds challenge 3 180
FizzBuzz challenge 9 82
Introduction: Finishing the grid – keyboard support for arrow keys to manoeuvre, entering the numbers.  The PreTranslateMessage function is to be used to intercept and respond to keyboard events. Continuing from the fourth article about sudoku. …
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
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.
With Secure Portal Encryption, the recipient is sent a link to their email address directing them to the email laundry delivery page. From there, the recipient will be required to enter a user name and password to enter the page. Once the recipient …

829 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