From: Simon on
On 2010/04/16 07:47 PM, Hector Santos wrote:
> If I follow you design, wouldn't making the 2nd dialog a Style = Child
> do the trick?
>


The seconds dialog is a child, and it prevents the user from doing
anything at all, (the dialog is nothing more than a never ending
progress bar).

But when the user clicks anywhere on my app, (the app that called
CreateProcess( ... )), then it goes on top of all other windows
including the created process.

I would like the created process to go on top of my app.

Simon
From: Simon on
>
>
> Right.
>
> He indicated he wasn't too concern about access to the main window ("he
> user cannot really do anything"), so why not just this process
> 'watch/progress" dialog modal with a auto close when finish?

I am not sure I follow.

>
> Or is he talking about the child process window staying on top within
> the parent window?

Not within the parent window, just on top of the app.

>
> I guess, if its console, he can Allocate/Attach a Console which under
> XP/2003+ I believe you can tie it within a region the parent window.
> That use to be an old desire.
>
> If its a GUI, that would be interesting.
>

Simon
From: Hector Santos on
Simon wrote:

>> Or is he talking about the child process window staying on top within
>> the parent window?
>
> Not within the parent window, just on top of the app.

Hi Simon,

So if you don't for within, the two windows can be side by side?
Anyway, you can adjust the code I have here.

Maybe there is an more cleaner way to do this, but I played with it
and came up with the following:

This approach uses the parent window solely to control the placement
of the child process window which in this example is a GUI applet. In
other words, you don't have to change the child process. No matter
what is spawn, this logic will keep it on top of the parent applet.

1) Add the following to the parent main window class, in this case, I
have a parent EXE with a MFC dialog, CMyParentDlg.

class CMyParentDlg : public CDialog
{
....
public:
BOOL RunChildProcess(const char * szfn, const char * szopts);
static BOOL CALLBACK EnumWindowProc(HWND hwnd, LPARAM lParam);
afx_msg void OnMove(int x, int y);
public:
PROCESS_INFORMATION cpi;
HWND m_hWndChild;
}

2) Add the methods:

BOOL CALLBACK CMyParentDlg::EnumWindowProc(HWND hwnd, LPARAM lParam)
{
//
// find the child process thread id window handle
//
CMyParentDlg *pDlg = (CMyParentDlg *)lParam;
DWORD wndPid = 0;
DWORD wndTid = GetWindowThreadProcessId(hwnd, &wndPid);
if (wndTid == pDlg->cpi.dwThreadId) {
pDlg->m_hWndChild = hwnd;
}
return TRUE;
}


BOOL CMyParentDlg::RunChildProcess(const char * szfn, const char * szopts)
{
STARTUPINFO si = {0};
ZeroMemory(&cpi,sizeof(cpi));

//
// initialize the startup window position and size for the
// child process. Make it slightly smaller.
//

WINDOWPLACEMENT wp = {0};
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(&wp);

si.dwFlags |= STARTF_USEPOSITION | STARTF_USESIZE;
si.dwX = wp.rcNormalPosition.left;
si.dwY = wp.rcNormalPosition.top;
si.dwXSize = wp.rcNormalPosition.right-wp.rcNormalPosition.left-50;
si.dwYSize = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top-50;

//
// Start the process

CString sCmd(szfn);
if (szopts && szopts[0]) {
sCmd += " ";
sCmd += szopts;
}

if (!CreateProcess(szfn,(LPSTR)szopts,
NULL,NULL,FALSE,0,NULL,NULL,&si,&cpi)) {
return FALSE;
}

//
// Get the window handle for the child process
// See Note 1 about the EnumWindow Loop
//

DWORD waitTics = GetTickCount() + 5*1000; // wait 5 seconds
while (GetTickCount() < waitTics) {
EnumWindows((WNDENUMPROC)EnumWindowProc, (LPARAM) this);
if (m_hWndChild) break;
Sleep(250);
}

//
// make it top most. See note #3
//

if (m_hWndChild) {
::SetWindowPos(m_hWndChild, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
CloseHandle(cpi.hThread);
TRACE("- WAIT: child process\n");
while (WaitForSingleObject(cpi.hProcess, 1) == WAIT_TIMEOUT)
{
MSG msg;
if (PeekMessage(&msg,this->m_hWnd,0,0,PM_REMOVE)){
::TranslateMessage(&msg);
::DispatchMessage(&msg);
//
// See Note 2 about these breaks
//
if (msg.message == WM_QUIT) break;
if (msg.message == WM_CLOSE) break;
if (msg.message == WM_SYSCOMMAND) {
if (msg.wParam == SC_CLOSE) {
break;
}
}
}
}
CloseHandle(cpi.hProcess);
TRACE("- DONE: child process\n");
m_hWndChild = NULL;

return TRUE;
}


void CMyParentDlg::OnMove(int x, int y)
{
//
// Move child process window to follow the parent window
//
CDialog::OnMove(x, y);
if (m_hWndChild) {
WINDOWPLACEMENT wp = {0};
wp.length = sizeof(WINDOWPLACEMENT);
if (::GetWindowPlacement(m_hWndChild,&wp)) {
UINT cx = wp.rcNormalPosition.right-wp.rcNormalPosition.left;
UINT cy = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top;
::MoveWindow(m_hWndChild,x,y,cx,cy,true);
}
}
}

Using the above, you get what you want. You have to make the
adjustments with how the child window is initially displayed and move
as the parent window is move. You can also add a OnSize() to make
adjustments there as well.

There are two things to probably work on:

Note #1: EnumWindow Loop

When the child is started, the m_hWndChild may not exist. This allow
allows you to wait until there is a child window. Other ways:

1) Wait until the child is waiting for input before calling
EnumWindow. This might not always be reliable.

2) If you have source control of the child, have it create a Named
Mutex that the parent can easily detect (by trying to open it). This
is the preferred way, if you have child source control.

Note #2: TerminateProcess()

When the chile process is called and the parent is waiting for
completion, it needs to a message loop to keep the parent message pump
running. Depending on how much control the user has on the parent
process, it may close before the child process is complete.

So you probably have logic to determine if it needs to be terminated
with the various break messages, like WM_QUIT.

Note #3: Topmost

The topmost along with the OnMove() makes it more like its on top of
the parent applet. But its really a desktop top most. There is
probably some placement logic that you can do without making it a
desktop topmost.

Anyway, the above is a start. It shows how to control the child
process window to make it follow the parent placement rules
independent of making changes to the child process.

I appreciate other people's comment as I learned as most doing it, and
I won't be surprise if there is better logic. :)

--
HLS
From: David Ching on
"Hector Santos" <sant9442(a)nospam.gmail.com> wrote in message
news:e58jy5y3KHA.4332(a)TK2MSFTNGP02.phx.gbl...
> Maybe there is an more cleaner way to do this, but I played with it and
> came up with the following:
> ...

This could be made to work, but I recommend simply: don't show the child
window with the progress bar; just MINIMIZE the app until the launched one
finishes. Then RESTORE the app. If the first app is clicked on in the
tasbar, show a message box "Waiting for the second app to finish. Click OK"
and that's it.

Why waste your life managing the first app when it's not doing anything
useful?

-- David




From: Hector Santos on
Hector Santos wrote:

> Simon wrote:
>
>>> Or is he talking about the child process window staying on top within
>>> the parent window?
>>
>> Not within the parent window, just on top of the app.
>
> Hi Simon,
>
> So if you don't for within, the two windows can be side by side? Anyway,
> you can adjust the code I have here.
>
> Maybe there is an more cleaner way to do this, but I played with it and
> came up with the following:
>
> This approach uses the parent window solely to control the placement of
> the child process window which in this example is a GUI applet. In
> other words, you don't have to change the child process. No matter what
> is spawn, this logic will keep it on top of the parent applet.
>
> 1) Add the following to the parent main window class, in this case, I
> have a parent EXE with a MFC dialog, CMyParentDlg.
>
> class CMyParentDlg : public CDialog
> {
> ....
> public:
> BOOL RunChildProcess(const char * szfn, const char * szopts);
> static BOOL CALLBACK EnumWindowProc(HWND hwnd, LPARAM lParam);
> afx_msg void OnMove(int x, int y);
> public:
> PROCESS_INFORMATION cpi;
> HWND m_hWndChild;
> }
>
> 2) Add the methods:
>
> BOOL CALLBACK CMyParentDlg::EnumWindowProc(HWND hwnd, LPARAM lParam)
> {
> //
> // find the child process thread id window handle
> //
> CMyParentDlg *pDlg = (CMyParentDlg *)lParam;
> DWORD wndPid = 0;
> DWORD wndTid = GetWindowThreadProcessId(hwnd, &wndPid);
> if (wndTid == pDlg->cpi.dwThreadId) {
> pDlg->m_hWndChild = hwnd;
> }
> return TRUE;
> }
>
>
> BOOL CMyParentDlg::RunChildProcess(const char * szfn, const char * szopts)
> {
> STARTUPINFO si = {0};
> ZeroMemory(&cpi,sizeof(cpi));
>
> //
> // initialize the startup window position and size for the
> // child process. Make it slightly smaller.
> //
>
> WINDOWPLACEMENT wp = {0};
> wp.length = sizeof(WINDOWPLACEMENT);
> GetWindowPlacement(&wp);
>
> si.dwFlags |= STARTF_USEPOSITION | STARTF_USESIZE;
> si.dwX = wp.rcNormalPosition.left;
> si.dwY = wp.rcNormalPosition.top;
> si.dwXSize = wp.rcNormalPosition.right-wp.rcNormalPosition.left-50;
> si.dwYSize = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top-50;
>
> //
> // Start the process
>
> CString sCmd(szfn);
> if (szopts && szopts[0]) {
> sCmd += " ";
> sCmd += szopts;
> }
>
> if (!CreateProcess(szfn,(LPSTR)szopts,
> NULL,NULL,FALSE,0,NULL,NULL,&si,&cpi)) {
> return FALSE;
> }
>
> //
> // Get the window handle for the child process
> // See Note 1 about the EnumWindow Loop
> //
>
> DWORD waitTics = GetTickCount() + 5*1000; // wait 5 seconds
> while (GetTickCount() < waitTics) {
> EnumWindows((WNDENUMPROC)EnumWindowProc, (LPARAM) this);
> if (m_hWndChild) break;
> Sleep(250);
> }
>
> //
> // make it top most. See note #3
> //
>
> if (m_hWndChild) {
> ::SetWindowPos(m_hWndChild, HWND_TOPMOST, 0, 0, 0, 0,
> SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
> }
> CloseHandle(cpi.hThread);
> TRACE("- WAIT: child process\n");
> while (WaitForSingleObject(cpi.hProcess, 1) == WAIT_TIMEOUT)
> {
> MSG msg;
> if (PeekMessage(&msg,this->m_hWnd,0,0,PM_REMOVE)){
> ::TranslateMessage(&msg);
> ::DispatchMessage(&msg);
> //
> // See Note 2 about these breaks
> //
> if (msg.message == WM_QUIT) break;
> if (msg.message == WM_CLOSE) break;
> if (msg.message == WM_SYSCOMMAND) {
> if (msg.wParam == SC_CLOSE) {
> break;
> }
> }
> }
> }
> CloseHandle(cpi.hProcess);
> TRACE("- DONE: child process\n");
> m_hWndChild = NULL;
>
> return TRUE;
> }
>
>
> void CMyParentDlg::OnMove(int x, int y)
> {
> //
> // Move child process window to follow the parent window
> //
> CDialog::OnMove(x, y);
> if (m_hWndChild) {
> WINDOWPLACEMENT wp = {0};
> wp.length = sizeof(WINDOWPLACEMENT);
> if (::GetWindowPlacement(m_hWndChild,&wp)) {
> UINT cx = wp.rcNormalPosition.right-wp.rcNormalPosition.left;
> UINT cy = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top;
> ::MoveWindow(m_hWndChild,x,y,cx,cy,true);
> }
> }
> }
>
> Using the above, you get what you want. You have to make the adjustments
> with how the child window is initially displayed and move as the parent
> window is move. You can also add a OnSize() to make adjustments there
> as well.
>
> There are two things to probably work on:
>
> Note #1: EnumWindow Loop
>
> When the child is started, the m_hWndChild may not exist. This allow
> allows you to wait until there is a child window. Other ways:
>
> 1) Wait until the child is waiting for input before calling
> EnumWindow. This might not always be reliable.
>
> 2) If you have source control of the child, have it create a Named Mutex
> that the parent can easily detect (by trying to open it). This is the
> preferred way, if you have child source control.
>
> Note #2: TerminateProcess()
>
> When the chile process is called and the parent is waiting for
> completion, it needs to a message loop to keep the parent message pump
> running. Depending on how much control the user has on the parent
> process, it may close before the child process is complete.
>
> So you probably have logic to determine if it needs to be terminated
> with the various break messages, like WM_QUIT.
>
> Note #3: Topmost
>
> The topmost along with the OnMove() makes it more like its on top of the
> parent applet. But its really a desktop top most. There is probably
> some placement logic that you can do without making it a desktop topmost.
>
> Anyway, the above is a start. It shows how to control the child process
> window to make it follow the parent placement rules independent of
> making changes to the child process.
>
> I appreciate other people's comment as I learned as most doing it, and I
> won't be surprise if there is better logic. :)
>


Simon, I always get confused and need a refresher when it comes to
screen, relative screen positions and what functions to use. Here are
clean up for better placement and control of the child window.

Add a member to the class and WM_TIMER handler to the class:

WINDOWPLACEMENT wppLast;

and the changes to the functions:

BOOL CMyParentDlg::RunChildProcess(const char * szfn, const char * szopts)
{
STARTUPINFO si = {0};
ZeroMemory(&cpi,sizeof(cpi));

//
// initialize the startup window position and size for the
// child process
//

si.dwFlags |= STARTF_USEPOSITION | STARTF_USESIZE;

RECT rp;
GetClientRect(&rp);
ClientToScreen(&rp);
si.dwX = rp.left;
si.dwY = rp.top;
si.dwXSize = rp.right - rp.left-50;
si.dwYSize = rp.bottom - rp.top-50;

//
// Start the process

if (!CreateProcess(szfn,(LPSTR)szopts,NULL,
NULL,FALSE,0,NULL,NULL,&si,&cpi)) {
return FALSE;
}

//
// Get the window handle for the child process
// See Note about the Sleep
//

DWORD waitTics = GetTickCount() + 5*1000; // wait 5 seconds
while (GetTickCount() < waitTics) {
EnumWindows((WNDENUMPROC)EnumWindowProc, (LPARAM) this);
if (m_hWndChild) break;
Sleep(250);
}

if (m_hWndChild) {
::SetWindowPos(m_hWndChild, HWND_TOPMOST,
0, 0, 0, 0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
CloseHandle(cpi.hThread);
SetTimer(2,5,NULL);
return TRUE;
}

void CMyParentDlg::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == 2 && cpi.hProcess) {
if (WaitForSingleObject(cpi.hProcess, 0) != WAIT_TIMEOUT) {
KillTimer(nIDEvent);
CloseHandle(cpi.hProcess);
cpi.hProcess = 0;
m_hWndChild = NULL;
}
}
CDialog::OnTimer(nIDEvent);
}

void CMyParentDlg::OnMove(int x, int y)
{
//
// Move child process window to follow the parent window
//
WINDOWPLACEMENT wpp = {0};
wpp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(&wpp);
CDialog::OnMove(x, y);
if (m_hWndChild) {
int xdiff = wppLast.rcNormalPosition.left -
wpp.rcNormalPosition.left;
int ydiff = wppLast.rcNormalPosition.top -
wpp.rcNormalPosition.top;
TRACE("- 1 x: %6d xdiff: %6d | y: %6d ydiff: %6d\n",x,
xdiff,y,ydiff);
WINDOWPLACEMENT wpc = {0};
wpc.length = sizeof(WINDOWPLACEMENT);
if (::GetWindowPlacement(m_hWndChild,&wpc)) {
x = wpc.rcNormalPosition.left - xdiff;
y = wpc.rcNormalPosition.top - ydiff;
UINT cx = wpc.rcNormalPosition.right-wpc.rcNormalPosition.left;
UINT cy = wpc.rcNormalPosition.bottom-wpc.rcNormalPosition.top;
::MoveWindow(m_hWndChild,x,y,cx,cy,true);
}
}
wppLast = wpp;
}

I used a timer to watch for the process to end. Either way work, this
was cleaner because if you don't break out of the internal message
loop in the previous code, you can get a GPF. Also it shows you don't
need a separate thread to watch for the process to end.

And the OnMove() better moves the child window relative to the parent,
and like David said, doesn't overlap the title bar.

--
HLS
First  |  Prev  |  Next  |  Last
Pages: 1 2 3
Prev: PreTranslateMessage
Next: managed code and dll