显示隐藏控制台应用程序中的窗体。

3

我有一个主应用程序,它运行控制台应用程序。通常情况下,控制台应用程序会以隐藏的方式启动 (ProcessWindowStyle.Hidden),但出于测试目的,我可以让它显示窗口。

在控制台应用程序中,我可以加载和执行插件。其中一个插件尝试打开一个WinForm对话框。如果控制台应用程序可见,则正常工作,但如果控制台应用程序被隐藏,则无法工作。

我已经尝试过:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form());

我也尝试在一个新的线程中做同样的事情。

Thread t = new System.Threading.Thread(start);
t.Start();
t.Join();

start() 包含前面的内容。另外我尝试了 ShowDialog()

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var f = new Form();
f.ShowDialog();

没有任何一种方法显示窗口。

在WinDbg中,本机调用堆栈始终包括NtUserWaitMessage()

0:000> k
ChildEBP RetAddr  
0038dd58 7b0d8e08 USER32!NtUserWaitMessage+0x15

管理的调用堆栈始终包含WaitMessage()FPushMessageLoop()RunMessageLoop()

0:000> !clrstack
OS Thread Id: 0x47c4 (0)
ESP       EIP     
0045e560 76bff5be [InlinedCallFrame: 0045e560] System.Windows.Forms.UnsafeNativeMethods.WaitMessage()
0045e55c 7b0d8e08 System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32, Int32, Int32)
0045e5f8 7b0d88f7 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
0045e64c 7b0d8741 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
0045e67c 7b5ee597 System.Windows.Forms.Application.RunDialog(System.Windows.Forms.Form)
0045e690 7b622d98 System.Windows.Forms.Form.ShowDialog(System.Windows.Forms.IWin32Window)
0045e71c 7b622faf System.Windows.Forms.Form.ShowDialog()

如何从一个隐藏的控制台窗口中显示WinForms窗体?

SSCCE:

将此代码编译为Windows窗体应用程序:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var startInfo = new ProcessStartInfo("Console.exe");
        startInfo.WindowStyle = ProcessWindowStyle.Hidden;
        Process.Start(startInfo);
    }
}

将此代码编译为控制台应用程序:

class Program
{
    static void Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        var mainForm = new Form();
        // Enable next line to make it show
        // mainForm.Visible = true;
        Application.Run(mainForm);
    }
}
1个回答

3
使用 Winspector Spy,我发现窗口实际上是可用的,但它没有WS_VISIBLE样式。将该样式应用于表单使其可见,并显示在Windows任务栏中。
解决方案是在显示之前使表单可见,因此以下内容有效:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var f = new Form()
f.Visible = true;
Application.Run(f);

因为在我的情况下我想要获取返回值,所以我应该调用ShowDialog()。然而,在已经可见的窗体上调用ShowDialog()是不允许的,因此我坚持使用Application.Run(f)并自己检索结果:

var answer = configForm.DialogResult;

1
嗯,不,这个答案是无意义的。Application.Run()已经将Visible属性设置为true。用户是否能看到窗口是一个相当随机的结果,通常不会,您的窗口将被用户正在使用的任何窗口覆盖。您可以尝试使用BringToFront()将窗口推到用户面前,但这并不保证有效,如果Windows最近检测到前台窗口的输入,则Windows会主动阻止此操作。请确保ShowInTaskbar属性设置为True,以便用户看到闪烁的任务栏按钮。NotifyIcon是正确的解决方案。 - Hans Passant
@HansPassant:感谢您的批评性反馈。使用Winspector,我可以看到窗口存在,但是不可见(我已更新答案)。在Winspector中使用“Bring to front”没有任何作用。只有编辑窗口样式并应用WS_VISIBLE标志才能使窗口可见。这样做也会在任务栏中显示它。 - Thomas Weller
@HansPassant:我将代码简化为一个SSCCE并更新了问题。也许你想自己运行一下。虽然将Visible设置为true是一种解决方法,但我也想知道真正的原因,以及可能更好的解决方案。 - Thomas Weller
好吧,我应该这样做,但我不会这样做。前景窗口Z顺序问题是更严重的问题。 - Hans Passant
+1 我也遇到了同样的问题。我可以确认这是唯一有效的解决方案(尝试了BringToFront和其他方法)。有趣的是:MessageBox.Show可以直接使用,无需任何技巧。 - BudBrot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接