Visual Studio セットアップ プロジェクトを介して展開されているアプリケーションがありますが、ユーザーから特定のデータを収集するために、いくつかのカスタム Windows フォームを作成する必要がありました。これらのフォームは、アプリケーション ファイルが Setup Project (IE the MSI) によってデプロイされた直後に、アプリケーションの Installer クラスの Install() メソッドで表示されます。問題は、フォームが表示されたときに、画面の一番上のフォームではなく、セットアップ プロジェクトのウィンドウの下に表示されることです。次に、ユーザーが気付いた場合でも、タスクバーのアイコンをクリックしてフォームを手動で表示する必要があります。
4064 次
4 に答える
4
私がしなければならなかったことは、プロセスのリストから Setup プロジェクトのウィンドウを選択し、それをカスタム フォームの所有者として表示するように設定することだけでした。使用した手順の内訳は次のとおりです。
- 「msiexec」という名前のプロセスのリストを調べて、Setup .msi のウィンドウを表示しているプロセスのハンドルを取得します。これは MainWindowTitle (ウィンドウのタイトル) で識別できます。
- これで、このプロセスのハンドル (MainWindowHandle) を取得できましたが、それをどのように使用するのでしょうか? IWin32Window を受け取るパラメーターを介して ShowDialog を呼び出すときに、フォームの所有者を指定できます。問題は、IWin32Window ではウィンドウのハンドルを設定できないことです。これは、IWin32Window を拡張するラッパー クラスを使用することで回避できます。
- 最後に、CustomForm.ShowDialog(new WindowWrapper(process.MainWindowHandle), message など) のようなものを使用して、ShowDialog() を呼び出すときにフォームの所有者を設定するだけです。
私自身、Setup Project のウィンドウを WindowWrapper として返すメソッドを作成し、Installer クラスの各メソッド (Install、Commit、Uninstall、Rollback) でそれを使用して、作成中のすべてのフォームとメッセージボックスの所有者を設定しました。
また、カスタム フォームの子フォームまたはメッセージ ボックスの所有者を変更しないでください (おそらく "this" を除く)。そうしないと、Setup プロジェクトのウィンドウの上に表示されますが、カスタム フォームの下に表示されます。
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 に答える