我正在开发一个多进程应用程序,其中每个文档都在一个新进程中打开。它的行为类似于谷歌浏览器。
一切正常,除了这个:
当嵌入式应用程序中的任何控件处于活动状态时,主窗口(包含嵌入式窗口)不会接收击键(例如 Ctrl + N 等)。我理解这样一个事实,嵌入式窗口在不同的进程中运行,这就是它不起作用的原因。
我需要做什么:
- 主窗口应检测嵌入窗口中按下的击键
- 如果主窗口为击键分配了某些内容,则主窗口应该接收键盘快捷键而不是嵌入的表单。
一些重要的细节:
- 我可以控制两个进程的源代码(这是同一个 exe,具有不同的行为)
- 我正在使用击键动作
- 更新:我将 WM_COPYDATA 用于 IPC(通过发送记录)
- 更新:只有一个 UI 进程(又名主窗口)
嵌入代码:
此代码将其他进程的窗口嵌入到主 UI 窗口中。它由 WM_COPYDATA 处理程序根据接收到的值直接调用。
procedure TWTextAppFrame.adoptChild(WindowHandle: Cardinal; Container: TWinControl);
var
WindowStyle : Integer;
FAppThreadID: Cardinal;
ts : TTabSheet;
begin
/// Set running app window styles.
WindowStyle := GetWindowLong(WindowHandle, GWL_STYLE);
WindowStyle := (WindowStyle and not WS_POPUP) or WS_CHILD;
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
/// The window will be embedded on a tabsheet
ts := TTabSheet.Create(Self);
ts.TabVisible := false;
ts.PageControl := WindowPages;
ts.Tag := WindowHandle;
Winapi.Windows.SetParent(WindowHandle,ts.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
);
/// This creates a small object which holds some info about the embedded window
/// (Document dirty status, embedded window handle, file name, etc. )
/// The main window uses this to identify an embedded window, and also for tracking the processes
AddHandleToControlList(WindowHandle,PChar('win'+IntToStr(WindowHandle)), 0, 0, 0, 0, alClient,ts);
/// Page control holding all the tabsheet
WindowPages.ActivePage := ts;
/// activate the embedded window
SetForegroundWindow(WindowHandle);
end;
这是子进程通知主窗口的方式:
/// Record sent through
TSendData = Packed Record
sender: Cardinal; /// The handle of the sender window
s : WideString; /// String data
i : Integer; /// Integer data, usually and identifier for a function (defined with constants)
i2 : Integer; /// Status, or other integer data
b : boolean; /// boolean information
End;
var WData: TSendData;
///...
WData.sender := Self.Handle;
WData.i := WE_SETPARENT;
/// SendCommand is a wrapper function around the WM_COPYDATA sending code
SendCommand(Self.Handle, MainWindow, WData);
到目前为止我有什么?
我将 WM_COPYDATA 用于 IPC。
目前还没有代码,只是一个想法,将热键列表传输到嵌入式表单,并将它们注册为那里的操作。当在嵌入表单中执行操作时,击键将被发送到主表单。
这个想法不是一个优雅的解决方案,我想知道是否有更好的解决方案,如果有,那会是什么。