【问题标题】:Attach form window to another window in C#将表单窗口附加到 C# 中的另一个窗口
【发布时间】:2012-05-27 09:40:05
【问题描述】:

我想将表单附加到另一个窗口(另一个进程)。我尝试通过使用来做到这一点

[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; ?-> Complete source)。

Another comment说,我需要修改windows样式:

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

这里我错过了相应的 API,我可以直接从 C# 中完成,还是让我再次使用另一个 DllImport

Good or evil - SetParent() win32 API between different processes 建议不要在不同的进程中附加窗口,但至少我想尝试。

问题:

我需要做什么才能使表单窗口可见?如果WS_Child 的方法是正确的,我将如何设置它?或者WPF approach 是要走的路,但我如何将它应用到 Windows 窗体?

-- 发现(后来添加)--

Modify the windows style of another application using winAPI 展示了如何从 C# / PInvoke 修改样式

在这里找到所有windows styles,C# 语法在底部。

-- 与 Alan 讨论的结果--

我确实在 Win XP 上运行了我的程序以进行交叉检查(请参阅下面 Alan 的回答和 cmets)。至少我现在确实看到了一些东西。由于我已经添加了 Alan 示例中的坐标,因此当在左上角附近的另一个窗口上移动时,我的窗口现在在记事本中闪耀。您仍然可以看到在记事本中键入的文本作为覆盖。在 Win 7 (32) 下,我什么都看不到。

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

【问题讨论】:

  • 出于好奇,我遵循了这些指南并实施了一个似乎可行的原始解决方案,当然还有一些常见的警告(例如,指针在其他进程中无效等)。那么,既然您基本上已经自己回答了,那么您的问题仍然相关吗?
  • 我仍然看不到我的表单窗口。我试图将值设置为“WS_Child 评论”中的值,但没有成功。如果您在 C# 中有一个正在运行/工作的示例,如果您发布它,我将不胜感激。这是很多线索和错误,也许我错过了一些东西。我根本无法将表单窗口放在前面/可见。
  • 没问题,等一下。

标签: c# winforms pinvoke


【解决方案1】:

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

OP 问题中的常见警告也适用于此。

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();
    }
  }
}

【讨论】:

  • 谢谢,但在我的场景中不起作用。基本上你和我做的一样。我尝试附加到记事本窗口,但也尝试过其他窗口。在我调用 SetParent(..) 的那一刻,我的表单窗口变得不可见。我试过 BringToFront()、Show()。我可以告诉它附加的唯一原因是,如果父窗口关闭,我的表单应用程序将终止。感谢您的努力,我真的很感激。
  • Horst,我刚刚在我的机器上使用 notepad.exe 进行了尝试,它可以工作。当然如果目标是非WinForms进程,一路上还会出现其他困难,不过目前为止应该没问题。顺便说一句,我使用的是 XP SP3 32 位,所以如果您使用的是 64 位和/或 Win7,这可能是它不显示给您的原因。
  • 上面添加了Win XP的发现,所以和你的比较很有帮助。
  • 好吧,至少有一些进展。为了完成这个 XP/W7 比较,测试窗口中的所有控件都可以在我的 XP 机器上工作,即使在将它们的表单放入 notepad.exe 之后,我也可以单击按钮和事件处理程序正确触发。然而,它们的渲染没有被保留,记事本有时会在我的子窗口上绘制,但我怀疑这是由于底层编辑控件的工作方式(可能是来自 RICHED20.DLL 的 RichEdit,我有很多很多的自定义绘画技术问题)。如果我将主机放在另一个正在运行的 WinForms 应用程序中,一切正常。希望这一切都有帮助。
【解决方案2】:

@Horst Walter 嘿伙计,我不确定你是否解决了这个问题,但我刚刚找到了解决方案..

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

只需禁用透明度,它应该可以工作。

【讨论】:

  • 对,透明度对我来说也是个问题,但是容器的透明度。不过,谢谢你的回答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多