4

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

8
  • Perhaps following post by Peter Below is of relevance to you. I don't see you changing the calculater's borderstyle. Commented Sep 30, 2011 at 13:34
  • Thank you, but removing the caption bar makes the window lose its main menu. Commented Sep 30, 2011 at 13:42
  • It's notoriously hard to get this to work. Why not just embed a native Delphi calculator control? Commented Sep 30, 2011 at 13:52
  • I want to be able to embed any application, Calculator's just for an example. Commented Sep 30, 2011 at 13:56
  • 6
    @Pateman I'd suggest not trying to embed applications inside your app. Commented Sep 30, 2011 at 14:15

2 Answers 2

10

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); 
Sign up to request clarification or add additional context in comments.

7 Comments

Thank you! I will try that plus another approach. What about child windows? Like, let's say the application opens the standard "Open file" dialog. Is there a way to keep it inside my application, too? What do you think?
Is it normal that fAttach, the 3rd param of the AttachThreadInput function is set to False ? Do that not means that you detach the threads ?
And another thing, as is, AttachInputThread must fail because you give the processId. I think you have to pass the ThreadId which is the return value of the GetWindowThreadProcessId
@loursonwinny, yes, you are right about False parameter. I first wrote a code in another procedure to detach from input, then copy\pasted that code in this procedure, and forgot to change the param to True.
@loursonwinny, Also for AttachThreadInput to fail, that is right, instead of passing the return value of GetWindowThreadProcessId, I used the returned process id in AttachThreadInput. Thanks for pointing this out. I updated the source code to fix this.
|
2

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.