我有一个通过 Visual Studio 安装项目部署的应用程序,但是,我必须创建一些自定义 Windows 窗体来收集用户的一些特定数据。这些表单显示在应用程序的 Installer 类的 Install() 方法中,就在安装项目(即 MSI)部署了应用程序文件之后。问题是当我的表单出现时,它们出现在安装项目的窗口下,而不是屏幕上最上面的表单。然后,如果用户注意到它,则必须通过单击任务栏中的图标以将其调出来手动聚焦该表单。
问问题
4064 次
4 回答
4
我所要做的就是从进程列表中选择安装项目的窗口,并将其设置为自定义表单的所有者,因为我正在显示它们。以下是我使用的步骤的细分:
- 浏览名为“msiexec”的进程列表,并获取显示 Setup .msi 窗口的进程的句柄,可通过 MainWindowTitle(窗口标题)识别。
- 你现在有了这个进程的句柄(MainWindowHandle),但是你如何使用它呢?您可以在调用 ShowDialog 时通过采用 IWin32Window 的参数指定 Form 的所有者;问题是 IWin32Window 不允许您设置窗口的句柄。这可以通过使用扩展 IWin32Window 的包装类来解决。
- 最后,您所要做的就是在调用 ShowDialog() 时设置表单的所有者,例如 CustomForm.ShowDialog(new WindowWrapper(process.MainWindowHandle), message 等)。
我自己创建了一个方法,它将安装项目的窗口作为 WindowWrapper 返回,然后在每个 Installer 类的方法(安装、提交、卸载和回滚)中使用它来设置我正在创建的每个表单和消息框的所有者。
此外,不要更改自定义表单的任何子表单或消息框的所有者(可能更改为“this”),因为它们将归显示它们的自定义表单所有;否则它们将显示在安装项目的窗口上,但在自定义表单下,而不是我们想要的。
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
try
{
ExecuteSqlScript();
}
catch (Exception ex)
{
throw ex;
}
}
private void ExecuteSqlScript()
{
IntPtr hwnd = IntPtr.Zero;
WindowWrapper wrapper = null;
Process[] procs = Process.GetProcessesByName("msiexec");
if (null != procs && procs.Length > 0)
hwnd = procs[0].MainWindowHandle;
wrapper = new WindowWrapper(hwnd);
//Set the windows forms owner to setup project so it can be focused and
//set infront
frmInstance objInstance = new frmInstance();
if (null != wrapper)
objInstance.ShowDialog(wrapper);
else
objInstance.ShowDialog();
}
public class WindowWrapper : System.Windows.Forms.IWin32Window
{
public WindowWrapper(IntPtr handle)
{
_hwnd = handle;
}
public IntPtr Handle
{
get { return _hwnd; }
}
private IntPtr _hwnd;
}
于 2012-04-09T13:00:14.763 回答
1
选择列表的第一个元素不是好的做法,下面的代码已经过测试并且可以正常工作。
internal static IntPtr InstallerWindow()
{
IntPtr hwnd = IntPtr.Zero;
foreach (var proc in Process.GetProcessesByName("msiexec"))
{
if (proc.MainWindowHandle == IntPtr.Zero)
continue;
if (string.IsNullOrEmpty(proc.MainWindowTitle))
continue;
hwnd = proc.MainWindowHandle;
break;
}
return hwnd;
}
于 2014-02-06T17:42:41.087 回答
0
使用以下代码。此代码专注于您的表单
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
/// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary>
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(HandleRef hWnd);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
myform.Show();
uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
uint appThread = GetCurrentThreadId();
const uint SW_SHOW = 5;
if (foreThread != appThread)
{
AttachThreadInput(foreThread, appThread, true);
BringWindowToTop(myform.Handle);
ShowWindow(objFrmViewer.Handle, SW_SHOW);
AttachThreadInput(foreThread, appThread, false);
}
else
{
BringWindowToTop(myform.Handle);
ShowWindow(myform.Handle, SW_SHOW);
}
myform.Activate();
}
于 2012-04-09T08:04:20.537 回答
0
MSDN 解释了如何将表单置于最前面并将其保持在最前面。
于 2012-04-09T08:06:09.393 回答