开发者

Embedding window into another process

I have read some posts here on StackOverflow, but none has worked for me. Here is the code I am using to display the window of the standard Calculator on my form:

procedure TForm1.Button1Click(Sender: TObject);
var
  Tmp: Cardinal;
  R: TRect;
begin
  CalcWindow := FindWindow(nil, 'Calculator');
  if (CalcWindow <> 0) then
  begin
    GetWindowThreadProcessID(CalcWindow, CalcProcessID);

    Tmp := GetWindowLong(CalcWindow, GWL_STYLE);
    Tmp := (Tmp and not WS_POPUP) or WS_CHILD;
    SetWindowLong(CalcWindow, GWL_STYLE, Tmp);
    GetWindowRect(CalcWindow, R);

    SetForegroundWindow(CalcWindow);
    Windows.SetParent(CalcWindow, Panel1.Handle);
    SetWindowPos(CalcWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_FRAMECH开发者_StackOverflow社区ANGED);

    AttachThreadInput(GetCurrentThreadID(), CalcWindow, True);
  end;
end;

It does display the window on my form, but the glass border is lost and sometimes (especially when I move my form), it is hard to restore the focus to the embedded window (I need to click several times).

What may be causing this? Also, do you see any potential issues I may get into with using this method?

Thank you for your time.


Try this code. I took it from one of my older source codes. You will lose glass frame, but main menu is visible, and I didn't notice any problem in setting focus back to the embedded app. You should be able to do so using SetForegroundWindow() API function. Whenever you move your container form, your embedded app loses focus, so you need to call SetForegroundWindow again to restore focus :

procedure ShowAppEmbedded(WindowHandle: THandle; Container: TWinControl);
var
  WindowStyle : Integer;
  FAppThreadID: Cardinal;
begin
  /// Set running app window styles.
  WindowStyle := GetWindowLong(WindowHandle, GWL_STYLE);
  WindowStyle := WindowStyle
                 - WS_CAPTION
                 - WS_BORDER
                 - WS_OVERLAPPED
                 - WS_THICKFRAME;
  SetWindowLong(WindowHandle,GWL_STYLE,WindowStyle);

  /// Attach container app input thread to the running app input thread, so that
  ///  the running app receives user input.
  FAppThreadID := GetWindowThreadProcessId(WindowHandle, nil);
  AttachThreadInput(GetCurrentThreadId, FAppThreadID, True);

  /// Changing parent of the running app to our provided container control
  Windows.SetParent(WindowHandle,Container.Handle);
  SendMessage(Container.Handle, WM_UPDATEUISTATE, UIS_INITIALIZE, 0);
  UpdateWindow(WindowHandle);

  /// This prevents the parent control to redraw on the area of its child windows (the running app)
  SetWindowLong(Container.Handle, GWL_STYLE, GetWindowLong(Container.Handle,GWL_STYLE) or WS_CLIPCHILDREN);
  /// Make the running app to fill all the client area of the container
  SetWindowPos(WindowHandle,0,0,0,Container.ClientWidth,Container.ClientHeight,SWP_NOZORDER);

  SetForegroundWindow(WindowHandle);
end;

You can call it this way:

  ShowAppEmbedded(FindWindow(nil, 'Calculator'), Panel1);


For the sake of your sanity, and the sanity of your program's users, I think you'd better abandon this idea:

I tried to do exactly this thing with my own software (a window from 32-bit app embedded into 64-bit wrapper), and it never worked 100%, even using the tricks from the other answers you've got here.

  1. It is very hard to make it work reliably, there are zillion little subtle issues that you'll never get right. If you're messing with windows of other applications, which are not aware of your manipulation, generally you're asking for trouble.

  2. You're changing the way users expect Windows to behave. This will be confusing and unexpected for them.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜