【问题标题】:Embedding window into another process将窗口嵌入另一个进程
【发布时间】:2011-09-30 13:29:48
【问题描述】:

我在 StackOverflow 上阅读了一些帖子,但没有一个对我有用。这是我用来在表单上显示标准计算器窗口的代码:

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_FRAMECHANGED);

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

它确实在我的表单上显示了窗口,但是玻璃边框丢失了,有时(尤其是当我移动我的表单时),很难将焦点恢复到嵌入的窗口(我需要点击几次)。

这可能是什么原因造成的?另外,您是否发现使用此方法可能会遇到任何潜在问题?

感谢您的宝贵时间。

【问题讨论】:

  • 也许彼得下面的following post 与您相关。我没有看到您更改计算器的边框样式。
  • 谢谢,但删除标题栏会使窗口失去主菜单。
  • 众所周知,要让它发挥作用非常困难。为什么不嵌入原生 Delphi 计算器控件?
  • 我希望能够嵌入任何应用程序,计算器只是一个例子。
  • @Pateman 我建议不要尝试将应用程序嵌入到您的应用程序中。

标签: delphi winapi


【解决方案1】:

试试这个代码。我从我的一个较旧的源代码中获取它。您将失去玻璃框架,但主菜单可见,并且我没有注意到将焦点设置回嵌入式应用程序有任何问题。您应该可以使用 SetForegroundWindow() API 函数来执行此操作。每当您移动容器表单时,您的嵌入式应用程序就会失去焦点,因此您需要再次调用 SetForegroundWindow 来恢复焦点:

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;

你可以这样称呼它:

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

【讨论】:

  • 谢谢!我会尝试另一种方法。子窗口呢?比如,假设应用程序打开了标准的“打开文件”对话框。有没有办法将它也保存在我的应用程序中?你怎么看?
  • AttachThreadInput 函数的第三个参数 fAttach 设置为 False 是否正常?这不意味着你要分离线程吗?
  • 另外,AttachInputThread 必须失败,因为您提供了 processId。我认为你必须传递 ThreadId 这是 GetWindowThreadProcessId 的返回值
  • @loursonwinny,是的,您对 False 参数是正确的。我首先在另一个过程中编写了一个代码以与输入分离,然后在此过程中复制\粘贴该代码,并忘记将参数更改为 True。
  • @loursonwinny,同样是为了让AttachThreadInput失败,没错,我没有传递GetWindowThreadProcessId的返回值,而是在AttachThreadInput中使用了返回的进程id。感谢您指出了这一点。我更新了源代码来解决这个问题。
【解决方案2】:

为了你的理智,为了你的程序用户的理智,我认为你最好放弃这个想法:

我试图用我自己的软件(嵌入 64 位包装器的 32 位应用程序的窗口)来做这件事,但它从来没有 100% 工作,即使使用你在这里得到的其他答案的技巧.

  1. 要让它可靠地工作是非常困难的,有无数的小问题,你永远不会做对。如果你弄乱了其他应用程序的窗口,而这些应用程序不知道你的操作,通常你是在自找麻烦。

  2. 您正在改变用户期望 Windows 的行为方式。这会让他们感到困惑和意外。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-23
    • 1970-01-01
    • 1970-01-01
    • 2011-02-22
    • 2022-10-14
    相关资源
    最近更新 更多