|
Boost : |
From: Vladimir Prus (ghost_at_[hidden])
Date: 2006-07-13 03:50:52
On Wednesday 12 July 2006 20:13, Bronek Kozicki wrote:
Hi Bronek,
I'm replying on list, so that Win32 experts can comment too.
> attached .diff is addressing 3 bugs in execnt.c
> - memory leak in is_parent_child
> - closing unrelated dialogs (displayed by descendants of explorer.exe)
> - missing dialogs displayed by csrss.exe ("... - Unable To Locate
> Component")
>
> there are also other improvements and changes that I tested much more
> thoroughly than previous update to execnt.c
> --- C:\Documents and Settings\Bronek Kozicki\Desktop\execnt.cThu Jun 8 02:25:58 2006 UTC
> +++ E:\DEVEL\BOOST_RTEST\boost\tools\jam\src\execnt.cSun Jun 25 18:55:27 2006 UTC
> @@ -1001,8 +1001,10 @@
> int is_parent_child(DWORD parent, DWORD child)
> {
While we're at it, can be have a comment for this function, at least saying what are 'parent' and
'child' arguments. Are they pids or something else?
> HANDLE process_snapshot_h = INVALID_HANDLE_VALUE;
>
> + if (!child)
> + return 0;
> if (parent == child)
> return 1;
>
> process_snapshot_h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
> @@ -1015,49 +1017,100 @@
> ok = Process32First(process_snapshot_h, &pinfo);
> ok == TRUE;
> ok = Process32Next(process_snapshot_h, &pinfo) )
> {
> - if (pinfo.th32ProcessID == child && pinfo.th32ParentProcessID)
> + if (pinfo.th32ProcessID == child)
> + {
> + CloseHandle(process_snapshot_h);
> + if (!stricmp(pinfo.szExeFile, "explorer.exe"))
This will be clearer if written like:
if (stricmp(pinfo.szExeFile, "explorer.exe") == 0)
> + {
> + /* explorer.exe is orphaned and process_id of its parent may
> + accidentally match process_id of process we are after. We must not
> + close dialog boxes displayed by children of explorer.exe even
> + though (thanks to its parent process id) it might appear to be
> + our child. This is not very reliable - there might be more
> + orphaned processes or shell might be something else than
> + explorer.exe, but this is most common and important scenario */
The last line of this comment is longer than 80 characters. At the same time, thanks for
detailed comments! This is tricky code so it's good to have explanations.
> + return 0;
> + }
> + if (!stricmp(pinfo.szExeFile, "csrss.exe"))
> + {
> + /* csrss.exe may display message box like following:
> + xyz.exe - Unable To Locate Component
> + This application has failed to start because boost_foo-bar.dll
> + was not found. Re-installing the application may fix the problem
> + This actually happens when starting test process that depends on
> + dynamic library which failed to build. We want to automatically
> + close these message boxes even though csrss.exe is not our
> + child process. We may depend on the fact that (in all current
> + versions of Windows) csrss.exe is indirectly child of System which
> + always has process id == 4 */
> + if (is_parent_child(4, pinfo.th32ParentProcessID))
> + return 1;
Oh, closing those message box is cool. The comment has lines longer than 80 characters, though ;-)
> + }
> return is_parent_child(parent, pinfo.th32ParentProcessID);
> + }
> }
>
> CloseHandle(process_snapshot_h);
> }
>
> return 0;
> }
>
> -int related(HANDLE h, DWORD p)
> +int related(DWORD d, DWORD p)
> {
> - return is_parent_child(get_process_id(h), p);
> + return is_parent_child(d, p);
> }
If this function just forwards to is_parent_child, can we just change all callers to call is_parent_child?
> +typedef struct PROCESS_HANDLE_ID {HANDLE h; DWORD pid;} PROCESS_HANDLE_ID;
> +
> BOOL CALLBACK window_enum(HWND hwnd, LPARAM lParam)
> {
> - char buf[10] = {0};
> - HANDLE h = *((HANDLE*) (lParam));
> + char buf[7] = {0};
> + PROCESS_HANDLE_ID p = *((PROCESS_HANDLE_ID*) (lParam));
> DWORD pid = 0;
> + DWORD tid = 0;
> +
> + if (!IsWindowVisible(hwnd))
> + return TRUE;
>
> - if (!GetClassNameA(hwnd, buf, 10))
> - return TRUE; // failed to read class name
> + if (!GetClassNameA(hwnd, buf, 7))
> + return TRUE; /* failed to read class name; presume it's not a dialog */
Use sizeof(buf), perhaps?
> if (strcmp(buf, "#32770"))
While we're at it, it will be clearer if written like:
if (strcmp(buf, "#32770") != 0)
I realize this is not originally yours code, but anyway.
> - return TRUE; // not a dialog
> + return TRUE; /* not a dialog */
BTW, what does this code does? Do all dialogs have class name of #32770?
Or this is specific class of dialogs we're interested in?
> - GetWindowThreadProcessId(hwnd, &pid);
> - if (related(h, pid))
> + tid = GetWindowThreadProcessId(hwnd, &pid);
> + if (tid && related(p.pid, pid))
What's the point of check for 'tid'?
> {
> - PostMessage(hwnd, WM_QUIT, 0, 0);
> - // just one window at a time
> + /* ask really nice */
> + PostMessageA(hwnd, WM_CLOSE, 0, 0);
> + /* now wait and see if it worked. If not, insist */
> + if (WaitForSingleObject(p.h, 200) == WAIT_TIMEOUT)
> + {
> + PostThreadMessageA(tid, WM_QUIT, 0, 0);
> + if (WaitForSingleObject(p.h, 500) == WAIT_TIMEOUT)
> + {
> + PostThreadMessageA(tid, WM_QUIT, 0, 0);
> + WaitForSingleObject(p.h, 500);
Is posting WM_QUIT message twice better than posting it once?
> + }
> + }
> return FALSE;
> }
>
> return TRUE;
> }
>
> void close_alert(HANDLE process)
> {
> - EnumWindows(&window_enum, (LPARAM) &process);
> + DWORD pid = get_process_id(process);
> + /* If process already exited or we just cannot get its process id, do not go any further */
> + if (pid)
> + {
> + PROCESS_HANDLE_ID p = {process, pid};
> + EnumWindows(&window_enum, (LPARAM) &p);
> + }
> }
>
> static int
> my_wait( int *status )
> @@ -1093,23 +1146,29 @@
> }
>
> if ( globs.timeout > 0 )
> {
> + unsigned int alert_wait = 1;
> /* with a timeout we wait for a finish or a timeout, we check every second
> to see if something timed out */
> - for (waitcode = WAIT_TIMEOUT; waitcode == WAIT_TIMEOUT;)
> + for (waitcode = WAIT_TIMEOUT; waitcode == WAIT_TIMEOUT; ++alert_wait)
> {
> waitcode = WaitForMultipleObjects( num_active, active_handles, FALSE, 1*1000 /* 1 second */ );
> if ( waitcode == WAIT_TIMEOUT )
> {
> /* check if any jobs have surpassed the maximum run time. */
> for ( i = 0; i < num_active; ++i )
> {
> double t = running_time(active_handles[i]);
> +
> + /* periodically (each 5 secs) review and close alert dialogs hanging around */
> + if ((alert_wait % ((unsigned int) 5)) == 0)
> + close_alert(active_handles[i]);
> +
Is the point here to close dialogs right away, and not wait possibly long time for timeout? If so,
maybe this should be added to comment?
Overall, looks reasonable. Would you like to address the comments and sent the patch again,
or it will be more convenient if I commit the patch right now and you'll send further patches
when you have the time?
Thanks,
Volodya
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk