akalmani
asked on
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())//C annot be empty
break;
//Get the startup info
STARTUPINFO sStartupInfo;
ZeroMemory((void*)&sStartu pInfo, sizeof(STARTUPINFO));
sStartupInfo.cb = sizeof(STARTUPINFO);
GetStartupInfo(&sStartupIn fo);
PROCESS_INFORMATION sProcessInfo;
ZeroMemory((void*)&sProces sInfo, sizeof(PROCESS_INFORMATION ));
//Create the process
if(!(fRetVal = CreateProcess(NULL, PcstrPath.GetBuffer(PcstrP ath.GetLen gth()),
NULL, NULL, FALSE, 0, NULL, NULL, &sStartupInfo, &sProcessInfo)))
{
break;
}
pWndProc = (WNDPROC)::SetWindowLong(A fxGetMainW nd()->m_hW nd,
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(AfxG etMainWnd( )->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 (::GetFore groundWind ow(), NULL);
//DWORD dwCurThreadID = ::GetCurrentThreadId();
//Connect to the actual foreground window thread
//::AttachThreadInput(dwCu rThreadID, dwThreadID, TRUE);
//Set focus to this our spawned application
::SetForegroundWindow(PhWn d);
::SetFocus(PhWnd);
//Disconnect the actual foreground windows thread
//::AttachThreadInput(dwCu rThreadID, dwThreadID, FALSE);
}
return TRUE;
}
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())//C
break;
//Get the startup info
STARTUPINFO sStartupInfo;
ZeroMemory((void*)&sStartu
sStartupInfo.cb = sizeof(STARTUPINFO);
GetStartupInfo(&sStartupIn
PROCESS_INFORMATION sProcessInfo;
ZeroMemory((void*)&sProces
//Create the process
if(!(fRetVal = CreateProcess(NULL, PcstrPath.GetBuffer(PcstrP
NULL, NULL, FALSE, 0, NULL, NULL, &sStartupInfo, &sProcessInfo)))
{
break;
}
pWndProc = (WNDPROC)::SetWindowLong(A
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(
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(AfxG
return fRetVal;
}
BOOL CALLBACK ModalEnumProc(IN HWND PhWnd, IN LPARAM lParam)
{
if(PhWnd)
{
//Get the foreground window
//DWORD dwThreadID = ::GetWindowThreadProcessId
//DWORD dwCurThreadID = ::GetCurrentThreadId();
//Connect to the actual foreground window thread
//::AttachThreadInput(dwCu
//Set focus to this our spawned application
::SetForegroundWindow(PhWn
::SetFocus(PhWnd);
//Disconnect the actual foreground windows thread
//::AttachThreadInput(dwCu
}
return TRUE;
}
Hide App1 until App2 finishes?
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=
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=
ASKER
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.
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.
When you call App2 use
AfxGetMainWnd()->EnableWin dow(false) ;
to disable the App1 (and conversely with true after App2 is closed to re-enable it).
Is that any use?
AfxGetMainWnd()->EnableWin
to disable the App1 (and conversely with true after App2 is closed to re-enable it).
Is that any use?
The DoModal of a dialog is similar - that disables the parent frame of the window owning the dialog.
ASKER
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.
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.
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?
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?
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?
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?
ASKER
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.
----------------
>>>>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.
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).
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).
ASKER
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);
}
----------------
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);
}
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.
LockWindowUpdate() and UnlockWindowUpdate()
This prevents window repainting and should reduce the WM_PAINT messages being fired.
ASKER
I tried LockWindowUpdate it does not work either.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hi AndyAinscow I got the code working. Thanks for your help. Your moral support helped me to achieve the goal.
Thanks and glad to hear it helped.