PDA

View Full Version : Q110774: PRB: Add/Remove Programs Hangs When Launching an Application



AndrewRich
06-16-2008, 05:12 PM
Has there been any progress on fixing this? I went through a fruitless Support process a year ago and reported a bug, with no response.

The quick version:

Create a project that installs Notepad, Calc or similarly innocuous program. (See example project, attached)
Modify the OnMaintUIAfter event handler to launch the installed program after maintenance is complete. (See below)
Run the Repair or Modify sequence from Add-Remove Programs.
Observe: the installed program launches, but Add-Remove Programs cannot be closed.
Close the installed program.
Observe: Add-Remove Programs can now be selected and closed.


Plainly this is a serious bug that appeared somewhere between InstallShield 6.x (when it was not a problem) and 11.5 (definitely a problem). The Knowledge Base entry (http://support.installshield.com/kb/view.asp?articleid=Q110774) has been updated only to add more affected versions. Macrovision support (prior to the Acresso acquisition) essentially said "yeah, that's how it is" and declined to comment further.

Is there any progress at all? Any workaround? Anything?



//---------------------------------------------------------------------------
// OnMaintUIAfter
//
// The OnMaintUIAfter event called by OnShowUI after the file transfer
// of the setup when the setup is running in maintenance mode. By default
// this event displays UI that informs the end user that the maintenance setup
// has been completed successfully.
//
// Note: This event will not be called automatically in a
// program...endprogram style setup.
//---------------------------------------------------------------------------
function OnMaintUIAfter()
STRING szTitle, szMsg1, szMsg2, szOpt1, szOpt2;
NUMBER bvOpt1, bvOpt2;
begin

ShowObjWizardPages(NEXT);

// Added - Version 9.5 - Use appropriate strings for complete
// uninstall.
if( REMOVEALLMODE ) then
szTitle = SdLoadString(IFX_SDFINISH_REMOVE_TITLE);
szMsg1 = SdLoadString(IFX_SDFINISH_REMOVE_MSG1);
szOpt1 = "";
bvOpt1 = FALSE;
else
szTitle = SdLoadString(IFX_SDFINISH_MAINT_TITLE);
szMsg1 = SdLoadString(IFX_SDFINISH_MAINT_MSG1);
LoadStringFromStringTable ( "IDS_SHORTCUT_DISPLAY_NAME1", szOpt1 );
bvOpt1 = TRUE;
endif;

szMsg2 = "";
szOpt2 = "";
bvOpt2 = FALSE;

if ( BATCH_INSTALL ) then
SdFinishReboot ( szTitle , szMsg1 , SYS_BOOTMACHINE , szMsg2 , 0 );
else
SdFinish ( szTitle , szMsg1 , szMsg2 , szOpt1 , szOpt2 , bvOpt1 , bvOpt2 );
endif;
if ( bvOpt1 ) then
LaunchAppAndWait ( TARGETDIR ^ IFX_PRODUCT_KEY, "", LAAW_OPTION_NOWAIT );
endif;
end;

MichaelU
06-17-2008, 09:41 AM
If my hunch is correct, the Add/Remove programs applet is waiting for all child processes to complete before it accepts any further user input. This is not something we can change. Have you tried running your setup.exe in maintenance mode from a start /wait ...\setup.exe at the command prompt? This should help you verify one "level" of waiting.

It's possible that the applet only waits for a certain level of parent-child process relationships, and that if you can launch a process which will itself launch your intended process and immediately exit, that this will allow the applet to resume accepting input. As a simple test, perhaps launch cmd /c start c:\windows\notepad.exe instead of c:\windows\notepad.exe.

AndrewRich
06-17-2008, 03:45 PM
Thanks for replying to my question!


If my hunch is correct, the Add/Remove programs applet is waiting for all child processes to complete before it accepts any further user input.

That is what the KB entry says, yes.


This is not something we can change. Have you tried running your setup.exe in maintenance mode from a start /wait ...\setup.exe at the command prompt? This should help you verify one "level" of waiting.

Something changed, either in InstallShield or in the ARP behavior, because this was never a problem with earlier versions of InstallShield.

I haven't tried that test but I'm not sure what it would prove.


It's possible that the applet only waits for a certain level of parent-child process relationships, and that if you can launch a process which will itself launch your intended process and immediately exit, that this will allow the applet to resume accepting input. As a simple test, perhaps launch cmd /c start c:\windows\notepad.exe instead of c:\windows\notepad.exe.

I did try this, actually, along with every other method of indirectly spawning a new process I could think of. None had any discernable effect. Some methods I tried:

cmd /c (exename)
cmd /c start (exename)
write a batch file which contains cmd /c start (exename) and execute it with cmd /c start (batchfilename)
WinAPI CreateProcess
WinAPI ShellExecute

All had various quirks but none broke the link between ARP and the launched executable.

DevinEllingson
06-17-2008, 09:04 PM
OK, Thanks for the same project, I looked into this issue further.

On Win2K and later when you run a setup from Add/Remove the operating system automatically waits for the launched process and all child processes to complete before continuing.

It turns out that in Pro6 we inadvertently bypassed this functionality (which in this case turned out to be beneficial) by launching a separate engine process (IKernel.exe) thru COM, this resulted in the engine process not being considered as a sub-process of Setup.exe, thus any processes created by the setup in Pro 6 would not be considered child processes and thus would not be waited for.

In Pro7 and later we use IKernel.dll which is loaded into the Setup.exe process, this provides many benefits but does result in this change.

At any rate, the operating system is waiting for these processes by creating a Job object and placing any created processes into the job object, including the launched process in this case. Thus, to avoid this problem it is necessary to use the CREATE_BREAKAWAY_FROM_JOB flag when creating the child process. Unfortunately, the job object created by Windows does not set the appropriate flags to allow this, however it is possible to update the job object information to allow this. I have provided sample code below.

Please note that this code takes advantage of knowing the name of the Add/Remove job object as "ARP Job". This may change in future versions of Windows. I tested this code on XP SP2 and Vista but not other platforms. Also, I would assume that Admin privs are required to do this (including on Vista)


// Included header files ----------------------------------------------------
#include "ifx.h"

// Windows Constants --------------------------------------------------------
#define CREATE_BREAKAWAY_FROM_JOB 0x01000000
#define JOB_OBJECT_SET_ATTRIBUTES 0x0002
#define JOB_OBJECT_QUERY 0x0004
#define JobObjectExtendedLimitInformation 9
#define JOB_OBJECT_LIMIT_BREAKAWAY_OK 0x00000800

// Note: JOB_NAME_ARP was determined by veiwing named objects, and is not
// documented in Windows, and may change in the future, use with Caution.
#define JOB_NAME_ARP "ARP Job"

// Windows Structures

// _IO_COUNTERS -------------------------------------------------------------
typedef _IO_COUNTERS
begin
number ReadOperationCountHigh;
number ReadOperationCountLow;
number WriteOperationCountHigh;
number WriteOperationCountLow;
number OtherOperationCountHigh;
number OtherOperationCountLow;
number ReadTransferCountHigh;
number ReadTransferCountLow;
number WriteTransferCountHigh;
number WriteTransferCountLow;
number OtherTransferCountHigh;
number OtherTransferCountLow;
end;

// _LARGE_INTEGER -----------------------------------------------------------
typedef _LARGE_INTEGER
begin
number LowPart;
number HighPart;
end;

// _JOBOBJECT_BASIC_LIMIT_INFORMATION ---------------------------------------
typedef _JOBOBJECT_BASIC_LIMIT_INFORMATION
begin
_LARGE_INTEGER PerProcessUserTimeLimit;
_LARGE_INTEGER PerJobUserTimeLimit;
number LimitFlags;
number MinimumWorkingSetSize;
number MaximumWorkingSetSize;
number ActiveProcessLimit;
number Affinity;
number PriorityClass;
number SchedulingClass;
number nPadding; // So that the structure will be the correct size.
end;

// _JOBOBJECT_EXTENDED_LIMIT_INFORMATION ------------------------------------
typedef _JOBOBJECT_EXTENDED_LIMIT_INFORMATION
begin
_JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
_IO_COUNTERS IoInfo;
number ProcessMemoryLimit;
number JobMemoryLimit;
number PeakProcessMemoryUsed;
number PeakJobMemoryUsed;
end;

// Windows API prototypes ---------------------------------------------------
prototype number Kernel32.OpenJobObjectA( byval number, byval BOOL, byval string );
prototype BOOL Kernel32.QueryInformationJobObject( byval number, byval number, byval _JOBOBJECT_EXTENDED_LIMIT_INFORMATION pointer, byval number, byref number );
prototype BOOL Kernel32.SetInformationJobObject( byval number, byval number, byval _JOBOBJECT_EXTENDED_LIMIT_INFORMATION pointer, byval number );

//---------------------------------------------------------------------------
// OnMoved
//
// The OnMoved event is called as a result of the setup calling
// FeatureTransferData or FeatureMoveData. The event is called when
// all file transfer operations are completed except for batch
// self-registration which is performed immediately after this event returns.
// During uninstallation this event sent after uninstallation is completed,
// therefore you should not modify system state in this event.
//---------------------------------------------------------------------------
function OnMoved()
number hJob, nSize, nResult;
_JOBOBJECT_EXTENDED_LIMIT_INFORMATION jbeli;
BOOL bResult;
begin

if( REMOVEALLMODE ) then

// Attempt to add the JOB_OBJECT_LIMIT_BREAKAWAY_OK flag to the job created by
// the OS when running from ARP, this is needed to allow CREATE_BREAKAWAY_FROM_JOB
// to be specified for LaunchApplication.

// Note: Job objects are only supported on Win2K and later.
// On previous platforms no job objects are created when running from ARP
try

// Get the job object, if not found then no job object with the right name.
hJob = Kernel32.OpenJobObjectA( JOB_OBJECT_QUERY | JOB_OBJECT_SET_ATTRIBUTES, TRUE, JOB_NAME_ARP );
if( hJob ) then
// Get the existing job object info.
bResult = Kernel32.QueryInformationJobObject( hJob, JobObjectExtendedLimitInformation, &jbeli, SizeOf(jbeli), nSize );
if( bResult ) then
// Add flag
jbeli.BasicLimitInformation.LimitFlags = jbeli.BasicLimitInformation.LimitFlags | JOB_OBJECT_LIMIT_BREAKAWAY_OK;

// Set information.
Kernel32.SetInformationJobObject( hJob, JobObjectExtendedLimitInformation, &jbeli, SizeOf(jbeli) );
// Ignore return value.
endif;

// Close job handle
CloseHandle( hJob );

// Add CREATE_BREAKAWAY_FROM_JOB so that new processes will not be associated with the job.
LAAW_PARAMETERS.dwCreationFlags = LAAW_PARAMETERS.dwCreationFlags | CREATE_BREAKAWAY_FROM_JOB;

endif;
catch
endcatch;

// Launch test application.
nResult = LaunchApplication( WINDIR ^ "Notepad.exe", "", "", SW_SHOW, INFINITE, LAAW_OPTION_NOWAIT );
if( nResult < ISERR_GEN_FAILURE ) then
MessageBox( "Failed to launch application", INFORMATION );
endif;

endif;
end;

Devin Ellingson
Software Developer
Accresso Software

AndrewRich
06-18-2008, 02:33 PM
OK, Thanks for the same project, I looked into this issue further.

Devin, thanks for your time and effort on this issue.


Please note that this code takes advantage of knowing the name of the Add/Remove job object as "ARP Job". This may change in future versions of Windows. I tested this code on XP SP2 and Vista but not other platforms.

Can you describe how you found the name of the job object, so the technique you describe here can be kept up-to-date? Also, with regard to actually updating the ARP job information, would you do this once per setup or once per application to be launched from the setup? I'm thinking it's the former but I'd like confirmation of that.


hJob = Kernel32.OpenJobObjectA( JOB_OBJECT_QUERY | JOB_OBJECT_SET_ATTRIBUTES, TRUE, JOB_NAME_ARP );
if( hJob ) then
// Get the existing job object info.
bResult = Kernel32.QueryInformationJobObject( hJob, JobObjectExtendedLimitInformation, &jbeli, SizeOf(jbeli), nSize );
if( bResult ) then
// Add flag
jbeli.BasicLimitInformation.LimitFlags = jbeli.BasicLimitInformation.LimitFlags | JOB_OBJECT_LIMIT_BREAKAWAY_OK;

// Set information.
Kernel32.SetInformationJobObject( hJob, JobObjectExtendedLimitInformation, &jbeli, SizeOf(jbeli) );
// Ignore return value.
endif;

// Close job handle
CloseHandle( hJob );

// Add CREATE_BREAKAWAY_FROM_JOB so that new processes will not be associated with the job.
LAAW_PARAMETERS.dwCreationFlags = LAAW_PARAMETERS.dwCreationFlags | CREATE_BREAKAWAY_FROM_JOB;

endif;

DevinEllingson
06-18-2008, 04:40 PM
You would have to update the job object once per setup, you would have to use CREATE_BREAKAWAY_FROM_JOB for every process you launched, though if you update the LAAW_PARAMETERS structure it would keep the value until/if you called LaunchApplicationInit to reinitialize LaunchApp related structures.

I determined the name of the job using the 'Process Explorer' app from www.sysinternals.com, you can also get it by listing the objects in the global namespace using 'WinObj.exe' also from www.sysinternals.com. Note that on Vista the name does not appear to be available to these tools, but the code still seems to work.

Devin Ellingson
Software Developer
Acresso Software

jayleung
07-13-2009, 08:42 PM
[QUOTE=DevinEllingson;419417]You would have to update the job object once per setup, you would have to use CREATE_BREAKAWAY_FROM_JOB for every process you launched, though if you update the LAAW_PARAMETERS structure it would keep the value until/if you called LaunchApplicationInit to reinitialize LaunchApp related structures.

I determined the name of the job using the 'Process Explorer' app from www.sysinternals.com, you can also get it by listing the objects in the global namespace using 'WinObj.exe' also from www.sysinternals.com. Note that on Vista the name does not appear to be available to these tools, but the code still seems to work.


Can you show me how you get the job name using process explorer or WinObj? I downloaded both programs to try but couldn't figure out how the name "ARP Job" is retrieved... :confused:
Thanks in advance.

jayleung
07-14-2009, 02:03 PM
Can you show me how you look for the job name using Process Explorer or WinObj.exe? I didn't seem to be able to find the job name with the two applications. Any help is appreciated. Thanks.

s_tenlly
10-08-2012, 07:59 AM
I tried the code on Win7 64 bit, unfortunately it doesn't work. OpenJobObjectA returns '0'. In the ProcessExplorer the JobName is "<Unnamed Job>".

Any ideas how to get the job handle on Win7?

Thanks!

s_tenlly
10-10-2012, 08:35 AM
I found the solution, with IS 2010 only the following code is enough:

LAAW_PARAMETERS.dwCreationFlags = LAAW_PARAMETERS.dwCreationFlags | CREATE_BREAKAWAY_FROM_JOB;