PDA

View Full Version : MessageBox from Managed CA appears Minimized



gkriggs
07-24-2008, 01:12 PM
Hello,

In my C# Managed Custom Action, if an error occurrs, I display the error using a MessageBox. Unfortunately, the message box appears minimized, so if the user isn't paying close attention they may not see the error message, and may wonder why the install appears to have locked up

I tried using WPF's MessageBox.Show method and I tried using PInvoke to User32.dll MessageBox, both have the same problem of appearing minimized. Any ideas on how to get the message box to appear normally (not minimized)?

Thanks,
Greg

joshstechnij
07-24-2008, 04:02 PM
The MessageBox is probably using the wrong parent window (most likely the Desktop window). When this happens, especially on Vista, the MessageBox tends to appear behind the install dialogs.

The only way to resolve the behavior is to pass the correct parent window handle (the MSI or InstallScript dialog's handle) to the hWnd parameter on the Win32 MessageBox API. You may be able to use the FindWindow API (http://msdn.microsoft.com/en-us/library/ms633499.aspx) to obtain the parent window handle.

An alternate approach would be to use Windows Installer to display the error. You can use the MsiProcessMessage API (http://msdn.microsoft.com/en-us/library/aa370354.aspx) to display errors, warnings, or informational messages and take action based on a user's selection.

gkriggs
08-04-2008, 08:53 PM
Hello,

Thanks for the suggestions!

I'm unsure how to pass an MSI handle to my C# custom action. I'm not up to speed yet on how to use "Deployment Tools Foundtion (DTF)" which may work better for this problem.

FWIW, here is the workaround I'm using for now (in case it might help someone else):

Your explanation of the problem gave me the idea to try a system-modal message box displayed using PInvoke call to MessageBoxIndirect, which seems to be working okay for me. I call it from a deferred custom action during the install sequence (only if an error occurrs -- I give retry/abort/ignore options). I found the following article that explains how to do this:

http://www.codeproject.com/KB/dialog/MessageBoxIndirectCS.aspx

I also needed to display a WPF dialog from a custom action during the UI sequence, and I ran into the same problem where the dialog was appearing minimized. Since a button click in the InstallShield wizard causes the WPF dialog to appear, I'm hoping it's safe to assume the foreground window is going to be the InstallShield wizard, so I use a PInvoke call to GetForegroundWindow. I then found the following article that explains howto set a WPF window's owner to an HWND:

http://blogs.msdn.com/wpfsdk/archive/2007/04/03/centering-wpf-windows-with-wpf-and-non-wpf-owner-windows.aspx

Here's a quick example of this:

using System.Windows.Interop;
using System.Runtime.InteropServices;

[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();

public void ShowWindow()
{
MyDialog gui = new MyDialog();
IntPtr parentWindowHWND = GetForegroundWindow();
WindowInteropHelper guiHelper = new WindowInteropHelper(gui);
guiHelper.Owner = parentWindowHWND;
gui.WindowStartupLocation = WindowStartupLocation.CenterScreen;
gui.ShowDialog();
}

-- Greg

JohnnyEnglish
08-05-2008, 11:00 AM
I am attaching a document that is a work in progress to accompany the managed custom action sample (C:\Program Files\InstallShield\2009\Samples\WindowsInstaller\Managed Custom Actions). To see an example of using an MSI handle in a managed custom action you should take a look at the MsiWrapper Class in the solution we provide and the description of this in the word doc.

Basically, you can use InstallShield.Interop to retrieve the MSI handle and from there do with as you wish. To do this you will need to configure the IsClrWrap table so that the Interop dll is found at run time. All of this is outlined in the doc attached under the WrapperMsi section.

gkriggs
08-06-2008, 05:32 PM
Thanks for pointing out the sample managed custom action code.

The InstallShield online help mentions if you want to pass an MSIHandle, you should specify "MsiHandle" when you specify the parameter value for the custom method signature (when using the InstallShield Custom Action wizard). However, the online help doesn't explain what you need to do on the C# side.

The example document you provided explains everything except the part about specifying "MsiHandle" for the value. If I leave it blank, as the example seems to imply you should do, it just crashes. However, if I specify "MsiHandle" for the value it works okay.

When I put together the online help and your example document, I was able to figure out how to get it to work.

So, quick summary:

In your C# project add a reference to <ISProductFolder>\system\InstallShield.Interop.Msi.dll

Create your C# managed CA method, for example:

using InstallShield.Interop;
public void ManagedCAMethod(Int32 handle)
{
using (Msi.Install install = Msi.CustomActionHandle(handle))
{
// get and set properties
string installDir = install.GetProperty("INSTALLDIR");
install.SetProperty("SOME_PROPERTY", 1);
}
}

Then in InstallShield, when you create your managed Custom Action, specify "MsiHandle" as the value for the handle parameter.

Goto the Additonal Tools/Direct Editor, edit the ISClrWrap table and add a new row where Action is the name of your custom action, Name is "Dependency0", and Value is "<ISProductFolder>\system\InstallShield.Interop.Msi.dll " (as was explained in the example document).

I was concerned if the Int32 handle would work on Server 2003 x64, but when I tested it, it worked okay.

Thanks!
Greg

Christopher Painter
08-06-2008, 07:00 PM
You really, really want to check out WiX DTF. It works just fine with InstallShield and has a much cleaner interop / clr hosting process.

MichaelU
08-07-2008, 10:22 AM
I was concerned if the Int32 handle would work on Server 2003 x64, but when I tested it, it worked okay.Yeah, this is a bit surprising, but if you go to the underlying header files, MSIHANDLE is a LONG instead of a pointer type; this remains 32 bits on Win64.

As for "much cleaner", while DTF certainly has advantages, there are tradeoffs in build steps. I don't know which is the right choice for this particular usage, but I'll be happy to hear more about it if there are things we can change upstream to make e.g. the [STAThread] requirement work out better. (A simple test case for that would be grand; I know very little about WPF.)

Christopher Painter
08-08-2008, 08:29 PM
I use VSIP/TFS/MSbuild so building is simple. I create a WiX Managed CA project using Votive and drop it into a solution with my InstallShield project and just set the project dependencies so that the CA builds first. I tell the C# project to output in a directory that InstallShield can find and I wire it up as a Type 1 CA. The only thing simpler is an InstallScript CA with no external dependencies. :-)

Extremly Simple.

The `cleaner` part I'm referring to is 1) not tatooing the msiexec process with a specific CLR version and 2) a very nice interop class model.