在C#中将表单窗口附加到另一个窗口

17

我想将一个表单连接到另一个窗口(来自另一个进程)。我尝试使用以下方法实现:

[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

setParentWindow(myWindowHwnd, newParentHwnd);

这样做会使得我的表单被附加,但同时也会变得不可见。问题"Attach window .."解决了WPF窗口的这个问题,基本上通过使用

HwndSourceParameters parameters = new HwndSourceParameters();
...
HwndSource src = new HwndSource(parameters);

我已经尝试将这个方法应用到我的表单上,但是我无法实现(例如如何处理 src.RootVisual = (Visual)window.Content; ? -> 完整代码)。

另一条评论说,我需要修改窗口样式:

由于兼容性原因,SetParent 不会修改其父窗口正在更改的窗口的 WS_CHILD 或 WS_POPUP 窗口样式。 因此,如果 hWndNewParent 为 NULL,则在调用 SetParent 后还应清除 WS_CHILD 位并设置 WS_POPUP 样式。 相反,如果 hWndNewParent 不为 NULL 并且该窗口以前是桌面的子窗口,则在调用 SetParent 之前应清除 WS_POPUP 样式并设置 WS_CHILD 样式。

这里我错过了相应的 API,我能否直接从 C# 进行操作或者我必须再次使用另一个 DllImport

好与坏 - 在不同进程间使用 SetParent() win32 API 反对在不同进程中附加窗口,但至少我想尝试一下。

问题:

我需要做什么才能使窗体可见?如果使用 WS_Child 的方法是正确的,那么如何设置它?或者是 WPF 方法 是正确的方法,但如何将其应用于 Windows 窗体?

-- 发现(后添加)--

使用 winAPI 修改另一个应用程序的窗口样式 展示了如何从 C# / PInvoke 修改样式

在此处找到所有窗口样式,C# 语法在底部。

-- 由于与 Alan 的讨论而发现 --

我在 Win XP 上运行了我的程序以进行交叉检查(请参阅下面的 Alan 的答案和评论)。至少我现在看到了一些东西。由于我已经按照 Alan 的示例添加了坐标,所以当在接近左上角的其他窗口上移动时,我的窗口现在会在记事本中闪烁。您仍然可以看到叠加在记事本中键入的文本。在 Win 7(32)下我什么都看不到。

  1. 现在我需要找出是否可以以稳定的方式编写它,在 Win 7 上也能正常显示。
  2. 尽管如此,我仍然无法单击我的表单上的任何按钮,这也需要解决。

WinXP WinForm attached to notepad


出于好奇,我遵循了这些指南,并实现了一个原始的解决方案,似乎可以工作,当然还有一些常见的警告(例如,在其他进程中指针无效等)。那么,既然您本质上已经回答了自己的问题,那么您的问题是否仍然相关呢? - Alan
我仍然看不到我的窗体。我试过像“WS_Child comment”中设置值,但没有成功。如果您有C#上的运行/工作示例,如果您可以发布它,我将不胜感激。这是很多尝试和错误,也许我错过了什么。我简单地无法将我的窗口带到前面/可见。 - Horst Walter
没问题,稍等一下。 - Alan
2个回答

14

这里有一个可运行的示例。托管应用程序是一个简单的WinForms应用程序,其中包含一个空白表单(此处未包含),而“客户应用程序”具有一个主要表单(下面包括代码),其中包括一些控件,包括一个测试按钮,在更改客户表单的父级后显示消息。

与操作系统的问题中链接的常规警告也适用于此问题。

public partial class GuestForm: Form
{
  [DllImport("user32.dll")]
  public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

  [DllImport("user32.dll")]
  public static extern int GetWindowLong(IntPtr hWnd, int nIndex);

  [DllImport("user32.dll", SetLastError = true)]
  private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

  public static int GWL_STYLE = -16;
  public static int WS_CHILD = 0x40000000; 

  public GuestForm()
  {
    InitializeComponent();
  }

  private void button1_Click(object sender, EventArgs e)
  {
    MessageBox.Show("done");
  }

  private void button2_Click(object sender, EventArgs e)
  {
    Process hostProcess = Process.GetProcessesByName("HostFormApp").FirstOrDefault();
    if (hostProcess != null)
    {
      Hide();
      FormBorderStyle = FormBorderStyle.None;
      SetBounds(0, 0, 0, 0, BoundsSpecified.Location);

      IntPtr hostHandle = hostProcess.MainWindowHandle;
      IntPtr guestHandle = this.Handle;
      SetWindowLong(guestHandle, GWL_STYLE, GetWindowLong(guestHandle, GWL_STYLE) | WS_CHILD);
      SetParent(guestHandle, hostHandle);

      Show();
    }
  }
}

1
谢谢,但在我的情况下没有起作用。基本上您所做的与我所做的相同。我试图附加到一个记事本窗口,但也尝试了其他窗口。当我调用SetParent(..)时,我的窗体窗口变得不可见。我尝试了BringToFront()、Show()等方法。唯一能说明它已经连接的原因是,如果父窗口关闭,我的窗体应用程序将终止。非常感谢您的努力。 - Horst Walter
Horst,我刚刚在我的机器上使用notepad.exe尝试了一下,它可以工作。当然,如果目标是非WinForms进程,则在此过程中可能会出现其他困难,但到目前为止应该还好。顺便说一句,我正在使用XP SP3 32位,所以如果您使用的是64位和/或Win7,则可能是它不显示给您的原因。 - Alan
上面添加了Win XP的发现,所以与您的比较帮助很大。 - Horst Walter
好的,至少有一些进展。为了完整比较XP/W7,测试窗口中的所有控件在我的XP机器上都可以工作,即使将它们的表单父级设置为notepad.exe,我也可以点击按钮并正确触发事件处理程序。然而,它们的渲染效果没有被保留,notepad有时会覆盖我的子窗口,但我怀疑这是由于底层编辑控件的工作方式(可能是来自RICHED20.DLL的RichEdit,其自定义绘图技术给我带来了许多问题)。如果将主机父级设置为另一个正在运行的WinForms应用程序,则一切正常。希望这些都有所帮助。 - Alan

3

@Horst Walter 你好,我不确定你是否已经解决了问题,但我刚找到了一个解决方案。

对我来说,问题在于你想要放到另一个表单中的主表单的透明度。

只需禁用透明度即可解决问题。


没错,透明度对我也是个问题,但是容器的透明度。不过,感谢你的回答! - Dragos Stoica

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