Attach form window to another window in C#
Asked Answered
P

2

17

I want to attach a form to another window (of another process). I try to do this by using

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

setParentWindow(myWindowHwnd, newParentHwnd);

In doing so my form becomes attached, but is also invisible. Question "Attach window .." solves this issue for a WPF Window, basically by using

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

I have tried to transfer this to my form, but I am unable to do so (e.g. how to handle src.RootVisual = (Visual)window.Content; ? -> Complete source).

Another comment says, I need to modify the windows style:

For compatibility reasons, SetParent does not modify the WS_CHILD or WS_POPUP window styles of the window whose parent is being changed. Therefore, if hWndNewParent is NULL, you should also clear the WS_CHILD bit and set the WS_POPUP style after calling SetParent. Conversely, if hWndNewParent is not NULL and the window was previously a child of the desktop, you should clear the WS_POPUP style and set the WS_CHILD style before calling SetParent.

Here I miss the corresponding API for doing so, can I do it directly from C# or have I to use another DllImport again?

Good or evil - SetParent() win32 API between different processes advises against attaching windows in different processes at all, but at least I want to try.

Question:

What would I need to do to get the form window visible? If the approach with WS_Child is the correct one, how would I set it? Or is the WPF approach the way to go, but how would I apply it to an windows form?

-- Findings (later added) --

Modify the windows style of another application using winAPI shows how to modify the style from C# / PInvoke

Find all windows styles here, C# syntax at the bottom.

-- Findings due to discussion with Alan --

I did run my program on Win XP to crosscheck (see Alan's answer below and the comments). At least I do now see something. Since I have added the coordinates as of Alan's examples, my window now shines through in notepad when moving over the other window near the left upper corner. You can still see the text typed in notepad as overlay. Under Win 7 (32) I do see nothing at all.

  1. Now I need to find out whether this can be written in a stable way, appearing on Win 7 as well.
  2. Nevertheless, I still cannot click any buttons on my form, needs to be solved too.

WinXP WinForm attached to notepad

Pourboire answered 27/5, 2012 at 9:40 Comment(3)
Out of curiosity, I followed these guidelines and implemented a primitive solution that seems to work, with the usual caveats of course (e.g. pointers are invalid in the other process etc.). So, is your question still relevant, now that you essentially answered it yourself?Boz
I still do not see my form window. I have tried to set the values as in the "WS_Child comment", with no success. If you have a running / working example in C#, I'd appreciate if you post it. This is a lot of trail and error, maybe I have missed something. I simply cannot get my form window to front / visible.Pourboire
Sure, no problem, just a sec.Boz
B
14

Here is a working example. The hosting app is a simple WinForms application with a blank form (not included here), while the "guest app" has a main form (code behind included below) with some controls, including a test button to display a message after changing the guest form's parent.

The usual caveats linked to in the OP's question apply to this, too.

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();
    }
  }
}
Boz answered 27/5, 2012 at 12:42 Comment(4)
Thanks, however not working in my scenario. Basically you are doing the same as I do. I try to attach to a notepad window, but have tried other windows as well. In the moment I call SetParent(..) my form window becomes invisible. I have tried BringToFront(), Show(). The only reason I can tell it is attached is that my form app terminates if the parent window is closed. Thanks for your effort, I really appreciate it.Pourboire
Horst, I have just tried it with notepad.exe on my machine and it works. Of course if the target is a non-WinForms process, other difficulties will arise along the way, but this should be OK so far. I am using XP SP3 32-bit, by the way, so if you're on 64-bit and/or Win7, that may be a reason why it doesn't show up for you.Boz
Win XP findings added above, so the comparison with you helped a lot.Pourboire
All right, at least there is some progress. To round this XP/W7 comparison out, all controls in the test window work on my XP machine even after parenting their form into notepad.exe, I can click buttons and event handlers fire correctly. Their rendering, however, is not preserved, notepad draws over my child window sometimes, but I suspect that is due to the way the underlying edit control works (likely RichEdit from RICHED20.DLL, whose custom painting techniques I've had many, many issues with). Everything works fine if I parent my host into another running WinForms app. Hope this all helps.Boz
I
3

@Horst Walter Hey man, I'm not sure if you've fixed the issue, but I just found a solution to this..

For me the issue was the transparency of the main form you want inside the other form.

Just disable transparency and it should work.

Imbrue answered 2/1, 2017 at 8:15 Comment(1)
Right, the transparency was the problem for me too, but the transparency of the container. However, thanks for your answer!Batholomew

© 2022 - 2024 — McMap. All rights reserved.