WiX Custom Bootstrapper - Single Instance Check - wix

I use the following code to check if a Single Instance of the CustomBA is already running in the Run block of the CustomBA.
When the user starts the "setup.exe" (CustomBA) by double clicking it the code below returns true which is the expected behaviour.
However when the user right clicks and starts it as administrator the code returns false. Why is that?
private bool IsSingleInstanceOfSetupRunning()
{
bool result = true;
Process currentProcess = Process.GetCurrentProcess();
if (Process.GetProcessesByName(currentProcess.ProcessName).Length > 1)
{
result = false;
}
return result;
}

It appears that the WiX Engine detects that the process is running as admin and spins up the secondary process used for actually installing the MSIs. So there really are two processes running with the same name.
You can see the same behavior with the non-admin process once your CustomBA code calls Engine.Apply(). This is typically when the user sees a UAC prompt as the Engine spins up the second, elevated process to handle the actual MSI installs.
Since the main process is already running as admin, and no UAC prompting will occur by spinning up the second process, the Engine goes ahead and starts it immediately instead of waiting for the call to Engine.Apply().
Also note: if you're performing a Major Upgrade, the uninstall of the prior version will be run (in silent mode) during the upgrade, which would result in additional processes. You need to make sure you allow the uninstall process to run even if there is another process already running (your upgrade process).
One approach would be to use a mutex to do the checking, but only when running in DisplayMode Display.Full:
if (DisplayMode == Display.Full)
{
bool mutexCreated = false;
mutex = new Mutex(true, #"My Installer F1096BB9-CFDF-4AD1-91D8-9AA8805784A8", out mutexCreated);
if (!mutexCreated)
{
MessageBox.Show("Another instance of the installer is already running. You may only run one at a time.",
"Installer already running", MessageBoxButton.OK,
MessageBoxImage.Warning);
Log("Installer already running");
Exit(ActionResult.NotExecuted);
}
}
public void Exit(ActionResult actionResult)
{
if (mutex != null)
{
mutex.Close();
mutex = null;
}
Log(string.Format("Exiting with code {0}", actionResult));
Engine.Quit((int) actionResult);
}

Related

Handling ExecuteFilesInUse with Retry and resolving used files

I'm having a custom Managed Bootstrapper Application for my installer and I am hooking into the ExecuteFilesInUse event to show a UI to the user. In this UI I list the processes provided in the event and 2 buttons: Retry and Cancel. Everything seems to work fine. When I lock some of my files and I press retry, it checks again for files in use. If I press cancel the installation aborts. When I resolve the files in use by closing all applications the installation/uninstallation continues when pressing retry.
But then the problem starts: The msiexec.exe process gets stuck. It utilizes 1 CPU core at 100%. Almost as if it is in an endless loop doing nothing. The logfiles do not contain any details that something is done and nothing happens.
My code looks like this:
// bootstrapper.ExecuteFilesInUse += Bootstrapper_ExecuteFilesInUse;
private void Bootstrapper_ExecuteFilesInUse(object sender, ExecuteFilesInUseEventArgs e)
{
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.Invoke(new EventHandler<ExecuteFilesInUseEventArgs>(Bootstrapper_ExecuteFilesInUse), sender, e);
return;
}
IList<string> files = e.Files;
if (files == null || files.Count == 0)
{
e.Result = Microsoft.Tools.WindowsInstallerXml.Bootstrapper.Result.Ignore;
return;
}
var hasEmptyRecords = e.Files.Any(string.IsNullOrEmpty);
if (hasEmptyRecords)
{
e.Result = Microsoft.Tools.WindowsInstallerXml.Bootstrapper.Result.Retry;
return;
}
var window = new FilesInUseWindow();
window.DataContext = new FilesInUseViewModel(e.Files);
var result = window.ShowDialog();
if (result == true)
{
e.Result = Microsoft.Tools.WindowsInstallerXml.Bootstrapper.Result.Retry;
}
else
{
Cancelled = true;
e.Result = Microsoft.Tools.WindowsInstallerXml.Bootstrapper.Result.Cancel;
}
}
```
The default WiX bootstrapper by default only provides the options to close them automatically or to schedule a reboot afterwards. But I want the user to manually close everything clean and then continue with the installation. I also attached the debugger ot my MBA to check if ti might be still firing the event
Is this workflow not supported or am I simply doing something wrong here?
Update: I decided also to file an issue on the Wix project page as I was able to reproduce this hang also on a fresh project.

How to get notified when the process invoked using Apache Commons Exec has stopped?

I am invoking a java process using Apache exec library. I need to do some operation if the process is forcefully stopped ( using task manager or some other way). Is there any option available in exec library ? I found a waitfor() operation in ResultHandler, which is doing a busy wait. Is there any notification mechanism available ?
I would expect the exit status to reflect if the process had been forceably killed.
See the tutorial and in particular reference to your comment, this section:
Your worker thread will block until the print process has finished or
was killed by the watchdog. Therefore executing the print job
asynchronously will do the trick. In this example we create an
instance of 'ExecuteResultHandler' and pass it to the 'Executor'
instance in order to execute the process asynchronously. The
'resultHandler' picks up any offending exception or the process exit
code
This has been asked a long time ago but I stumbled upon it today...
Here is the solution: you just have to setup a ExecuteResultHandler and pass it to the execute method. Then no active wait involved. Like this:
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler() {
#Override
public void onProcessComplete(int exitValue) {
super.onProcessComplete(exitValue);
log.info("Process finished");
}
#Override
public void onProcessFailed(ExecuteException e) {
super.onProcessFailed(e);
log.info("Process failed miserably");
}
};
String process = "C:\\Program Files (x86)\\Adobe\\Acrobat Reader DC\\Reader\\AcroRd32.exe";
CommandLine cmdLine = new CommandLine(process);
cmdLine.addArgument("C:\\Users\\francois.marot\\Downloads\\UtiliserLesDernieres DLL.pdf");
DefaultExecutor executor = new DefaultExecutor();
// the watchdog purpose is to kill the process after a specified timeout
SphereExecutionWatchdog watchdog = new SphereExecutionWatchdog(5000);
executor.setWatchdog(watchdog);
executor.execute(cmdLine, resultHandler);

"Collection was modified; enumeration operation may not execute" error on deployment

I'm getting this error when I deploy a VB.NET application and for the life of me I cannot figure out why.
I do not get this error when I run the app from the IDE and the test machine I am deploying it to has a similar configuration to the dev machine...Windows 7 & .NET 3.51 SP1 and 4.0.
The app bombs out when the main form is loaded after logging in. I've narrowed it down to the main form because if I load another form from login and then open the main form, this happens.
Linked below is a screenshot of the stack trace.
Any ideas? I'm really lost here.
Thanks.
I do not see a way for ShapeCollection.Dispose() to throw that exception. Although it is manipulating a List<> that can indeed throw that exception, the code should not trigger it:
private void Dispose(bool disposing)
{
if (!this.m_Disposed && disposing)
{
for (int i = this.m_Shapes.Count - 1; i >= 0; i--)
{
this.m_Shapes[i].Dispose();
}
this.m_Shapes.Clear();
this.m_Shapes = null;
}
this.m_Disposed = true;
}
Well, this is from the PowerPacks version that I have. There have been a couple of versions of it floating around, it used to be distributed separately. Make sure you didn't accidentally deploy an old version.

How to start a windows service in Visual Studio 2008?

IN Visual Studio when I try to start a windows service project it tells me I cant because I have to use "NET Start" and so forth.
I remember in VS 2003 that when I pressed play it started the service and stop stopped it. Is there any way that when I press play or start for that windows service project I can have this same functionality.
What I currently do is install them using installutil and I put a pre-processor command with System.Diagnostics.Debug.Launch() when I have a compilation variable defined and when I use the service manager it shows me the window to select the debugger. Still this method is somewhat cumbersome.
For anyone else reading this, remember to try to debug ONE thread at a time.
I usually allow for a command line switch that I can pass to my service using the command line argument settings in the IDE. When this switch is on I can run my service as a regular app. The only issue here is that you need to remember that services usually run under accounts with restricted permissions, so debugging as an app in your user context may behave differently when accessing secured resources. Here is example code:
static void Main()
{
if (IsDebugMode())
{
MyService svc = new MyService();
svc.DebugStart();
bool bContinue = true;
MSG msg = new MSG();
// process the message loop so that any Windows messages related to
// COM or hidden windows get processed.
while (bContinue && GetMessage(out msg, IntPtr.Zero, 0, 0) > 0)
{
if (msg.message != WM_QUIT)
DispatchMessage(ref msg);
else
bContinue = false;
}
}
else
{
ServiceBase.Run(new MyService());
}
}
public void DebugStart()
{
this.OnStart(null);
}
static bool IsDebugMode()
{
return (System.Environment.CommandLine.IndexOf("debug") > -1);
}

Install ClickOnce without running

When you install a ClickOnce application, the program runs after the install. Is it possible to install without running?
I know I can use a setup and deployment project and create an installer, but I'd prefer to use ClickOnce.
I guess you could fake it. Introduce an "IsInstalled" boolean property, defaulted to false. Then in Program.cs, change your Main() method to look like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!Properties.Settings.Default.IsInstalled)
{
Properties.Settings.Default.IsInstalled = true;
Properties.Settings.Default.Save();
MessageBox.Show("Install Complete");
return;
}
Application.Run(new Form1());
}
So now when the app is first installed, it checks that property and simply displays a message to the user and then quits.
If you wanted to get tricky then you could look at parsing the Activation URI for the deployment and have a URI parameter which specifies whether the program should run when it's first installed or just close silently.
You can do this by editing the application manifest in Mage. There is a checkbox to stop the application running after installation.
If you are not comfortable editing a manifest manually or with Mage then you can use the built-in deployment class to check whether this is the first time the application has run.
using System.Deployment.Application
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (ApplicationDeployment.CurrentDeployment.IsFirstRun)
{
MessageBox.Show("Install Complete");
return;
}
Application.Run(new Form1());
}
After trying all the suggested solutions and still running into the same problems, I fiddled with this for a while and combined several solutions to one that actually works.
The problem with just setting an "isInstalled" property is the value is retained after upgrades, so every time you install the new version, it runs the app again. But using an application manifest file and Mage is just too much work and too complicated just to solve this little problem.
So what I did was acquire the current build # of the running version of the app, save that to a property, then check the property against the running version each time. This works because each publish increments the version #.
1) Change your Assembly version to use wildcards in AssemblyInfo.cs:
[assembly: AssemblyVersion("1.0.*")]
2) If that throws a "Deterministic" error on Build, open your .csproj file and set Deterministic to false in the PropertyGroup section
<Deterministic>false</Deterministic>
3) Add this fool-proof function to acquire the running assembly version:
private Version GetRunningVersion()
{
try
{
return System.Deployment.Application.ApplicationDeployment.CurrentDeployment.CurrentVersion;
}
catch
{
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
}
}
4) In your project's properties, open the Settings tab, and add a setting named lastVersion (String, User). Leave the Value empty.
5) Add this property to use to determine whether this is the first time the application is running after installation.
private bool isFirstRun
{
get { return Properties.Settings.Default.lastVersion != GetRunningVersion().ToString(); }
}
6) Then in your code, add this after you check for isFirstRun:
if (isFirstRun)
{
Properties.Settings.Default.lastVersion = GetRunningVersion().ToString();
Properties.Settings.Default.Save();
}