在 WPF 窗口中托管外部应用程序
我们正在 WPF 中开发一个布局管理器,它具有可由用户移动/调整大小等的视口。视口通常通过布局管理器中我们控制的提供者填充数据(图片/电影/等)。我的工作是检查是否也可以在视口中托管任何外部 Windows 应用程序(即记事本、计算器、adobe reader 等)。我遇到了很多问题。
大多数资源都指向使用 HwndHost 类。我正在尝试 Microsoft 本身的演练: http://msdn.microsoft.com /en-us/library/ms752055.aspx
我对此进行了调整,以便列表框被外部应用程序的窗口句柄替换。任何人都可以帮我解决这些问题:
- 该演练添加了一个额外的静态子窗口,其中放置了
ListBox
。我认为外部应用程序不需要它。如果我忽略它,我必须将外部应用程序设置为子窗口(使用 user32.dll 中的 Get/SetWindowLong 将 GWL_STYLE 设置为 WS_CHILD)。但如果我这样做,应用程序的菜单栏就会消失(由于WS_CHILD
样式),并且不再接收输入。 - 如果我确实使用子窗口,并使外部应用程序成为其子窗口,那么事情就可以正常工作,但有时外部应用程序无法正常绘制。
- 另外,我需要子窗口调整大小以适应视口。这可能吗?
- 当外部应用程序生成子窗口(即“记事本”->“帮助”->“关于”)时,该窗口不是由
HwndHost
托管(因此可以移动到视口之外)。我有什么办法可以防止这种情况发生吗? - 由于我不需要外部应用程序和布局管理器之间的进一步交互,所以我假设我不需要捕获和转发消息是否正确? (本演练将 HwndSourceHook 添加到子窗口以捕获列表框中的选择更改)。
- 当您运行(未修改的)示例 VS2010 并关闭窗口时,VS2010 不会看到程序结束。如果你破坏所有,你最终会在没有源的情况下进行组装。有臭味发生,但我找不到它。
- 演练本身的编码似乎非常草率,但我还没有找到关于这个主题的任何更好的文档。还有其他例子吗?
- 另一种方法是不使用
HwndHost
而是使用WindowsFormHost
,如 此处。它有效(而且更简单!),但我无法控制应用程序的大小?另外,WinFormHost 并不是真正用于此目的吗?
感谢您提供正确方向的指示。
We are developing a layout manager in WPF that has viewports which can be moved/resized/etc by a user. Viewports are normally filled with data (pictures/movies/etc) via providers that are under our control in the layout manager. My job is to examine if its also possible to host any external Windows app (i.e. notepad, calc, adobe reader, etc) in a viewport. I encounter a number of problems.
Most resources point to using the HwndHost class. I am experimenting with this walkthrough from Microsoft itself: http://msdn.microsoft.com/en-us/library/ms752055.aspx
I've adapted this so the list box is replaced with the windows handle from the external application. Can anybody help me out with these questions:
- The walkthrough adds an extra static sub window in which the
ListBox
is placed. I don't think I need that for external apps. If I ommit it, I have to make the external app a child window (using Get/SetWindowLong from user32.dll to setGWL_STYLE
asWS_CHILD
). But if I do that, the menu bar of the app dissapears (because of theWS_CHILD
style) and it no longer receives input. - If I do use the sub window, and make the external app a child of that things work reasonably, but sometimes the external app does not paint ok.
- Also, I need the child window to resize to the viewport. Is this possible?
- When the exernal app spawns a child window (i.e. Notepad->Help->About), this window is not hosted by the
HwndHost
(and thus can be moved outside the viewport). Is there any way I can prevent that? - Since I need no further interaction between the external application and the layout manager, am I right in assuming I do not need to catch and forward messages? (the walkthrough adds a HwndSourceHook to the sub window to catch selection changes in the listbox).
- When you run the (unmodified) example VS2010 and close the window, VS2010 does not see that the program ended. If you break-all, you end up in assembly without source. Something smelly is going on, but I cannot find it.
- The walkthrough itself seems to be very sloppy coded, but I have not found any better documentation on this subject. Any other examples?
- Another approach is not to use
HwndHost
butWindowsFormHost
as discussed here. It works (and is much simpler!) but I do not have control over the size of the application? Also, WinFormHost is not really meant for this?
Thanks for any pointers in the right direction.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
好吧...如果这个问题是在 20 年前提出的,人们会回答,“当然,看看‘OLE’!”,这里有一个链接到什么是“对象链接和嵌入”:
http://en.wikipedia.org/wiki/Object_Linking_and_Embedding
如果您阅读本文,您将看到接口的数量定义这个规范,不是因为它的作者认为它很有趣,而是因为它在技术上很难实现在一般情况下
它实际上仍然受到一些应用程序的支持(主要是微软的应用程序,因为微软几乎是唯一的赞助商) OLE...)
您可以使用称为 DSOFramer 的东西嵌入这些应用程序(请参阅此处的链接:MS 站点缺少 MS KB311765 和 DsoFramer),该组件允许您托管 OLE 服务器(即:作为另一个进程运行的外部应用程序)
视觉上在应用程序内部。这是微软几年前发布的某种大型黑客攻击,现在不再受支持,以至于二进制文件很难找到!
它(可能)仍然适用于简单的 OLE 服务器,但我想我在某处读到它甚至不适用于新的 Microsoft 应用程序,例如 Word 2010。
因此,您可以将 DSOFramer 用于支持它的应用程序。你可以尝试一下。
对于其他应用程序,今天,在我们生活的现代世界中,您不托管应用程序,而是在外部进程中运行,您托管组件,并且它们位于一般应该在进程中运行。
这就是为什么你通常会很难做你想做的事情。。您将面临的一个问题(尤其是最近版本的 Windows)是安全性:我不信任的您的进程如何能够合法地处理我的创建的窗口和菜单按照我的流程:-)?
尽管如此,您仍然可以使用各种 Windows hack 来逐个应用程序执行大量操作。
SetParent 基本上是所有黑客之母:-)
这是一段代码,它扩展了您指向的示例,添加了自动调整大小,并删除了标题框。
它演示了如何隐式删除控制框、系统菜单,作为示例:
这基本上是所有 Windows“传统”黑客。您还可以删除不喜欢的项目菜单,如下所述:http://support .microsoft.com/kb/110393/en-us(如何从表单的控制菜单框中删除菜单项)。
您还可以将“notepad.exe”替换为“winword.exe”,它似乎可以工作。但这是有限制的(键盘、鼠标、焦点等)。
祝你好运!
Well... if the question had been posed like 20 years ago, one would have answer, "Sure, look at 'OLE'!", here is a link to what is "Object Linking and Embedding":
http://en.wikipedia.org/wiki/Object_Linking_and_Embedding
If you read this article, you will see the number of interfaces this spec defined, not because its author thought it was fun, but because it's technically difficult to achieve in the general cases
It's actually still supported by some apps (mostly Microsoft ones, as Microsoft was almost the only sponsor of OLE...)
You can embed these apps using something called DSOFramer (see links here on SO: MS KB311765 and DsoFramer are missing from MS site), a component that allows you to host OLE server (ie: external apps running as another process)
visually inside an application. It's some kind of a big hack Microsoft let out a few years ago, that is not supported anymore to the point that the binaries are quite difficult to find!
It (may) still works for simple OLE servers, but I think I read somewhere it does not even work for new Microsoft applications such as Word 2010.
So, you can use DSOFramer for application that support it. You can try it.
For others applications, well, today, in the modern world we live in, you don't host applications, ran in external process, you host components, and they are in general supposed to run inprocess.
That's why you will have great difficulties to do what you want to do in general. One problem you will face (and not the least with recent versions of Windows) is security: how can your process I don't trust can legitimately handle my windows and menus created by my process :-) ?
Still, you can do quite a lot application by application, using various Windows hack.
SetParent is basically the mother of all hacks :-)
Here is a piece of code that extends the sample you point, adding automatic resize, and the removal of the caption box.
It demonstrates how to implicitely remove the control box, the system menu, as an example:
This is basically all Windows "traditional" hacks. You could also remove item menus you don't like, as explained here: http://support.microsoft.com/kb/110393/en-us (How to Remove Menu Items from a Form's Control-Menu Box).
You can also replace "notepad.exe" by "winword.exe" and it seems to work. But there are limitations to this (keyboard, mouse, focus, etc.).
Good luck!
Simon Mourier 的回答写得非常好。然而,当我用自己制作的winform应用程序尝试时,失败了。
可以更换
,一切顺利。
感谢您提出的好问题以及大家的回答。
Simon Mourier's answer is extremely well written. However, when I tried it with a winform app made by myself, it failed.
can be replaced by
and everything goes smoothly.
Thank you for the great question and all of you for your answers.
在阅读了本线程中的答案并自己进行了一些试验和错误之后,我最终得到了一些效果很好的东西,但当然有些事情需要您在特殊情况下注意。
我使用 HwndHostEx 作为我的主机类的基类,您可以在这里找到它: http://microsoftdwayneneed.codeplex.com/SourceControl/changeset/view/69631#1034035
示例代码:
HWND、NativeMethods 和枚举也来自 DwayneNeed 库 (Microsoft.DwayneNeed.User32)。
只需将 NotepadHwndHost 添加为 WPF 窗口中的子窗口,您就应该看到托管在那里的记事本窗口。
After reading the answers in this thread and doing some trial and error myself I ended up with something that works pretty well, but of course some things will need your attention for special cases.
I used the HwndHostEx as base class for my host class, you can find it here: http://microsoftdwayneneed.codeplex.com/SourceControl/changeset/view/69631#1034035
Example code:
The HWND, NativeMethods and enums comes from the DwayneNeed library as well (Microsoft.DwayneNeed.User32).
Just add the NotepadHwndHost as a child in a WPF window and you should see the notepad window hosted there.
解决方案非常复杂。很多代码。这里有一些提示。
首先,你走在正确的轨道上。
您必须使用 HwndHost 和 HwndSource 东西。如果不这样做,您将得到视觉伪影。就像闪烁一样。警告,如果你不使用 Host 和 Source,看起来它会起作用,但最终不会——它会随机出现一些愚蠢的小错误。
看一下这个以获得一些提示。它并不完整,但它将帮助您朝着正确的方向前进。
http://microsoftdwayneneed.codeplex.com/SourceControl/changeset/view/50925#1029346
您必须进入 Win32 才能控制您所询问的很多内容。您确实需要捕获并转发消息。您确实需要控制哪些窗口“拥有”子窗口。
经常使用 Spy++。
The solution is incredibly involved. Lots of code. Here's a few hints.
First, you are on the right track.
You do have to use the HwndHost and HwndSource thing. If you don't, you'll get visual artifacts. Like flicker. A warning, if you don't use the Host and Source, it will seem like it will work, but it won't in the end -- it will have random little stupid bugs.
Take a look at this for some hints. It's not complete, but it will help you go in the right direction.
http://microsoftdwayneneed.codeplex.com/SourceControl/changeset/view/50925#1029346
You have to get into Win32 to control a lot of what you are asking about. You do need to catch and forward messages. You do need to control which windows "own" the child windows.
Use Spy++ alot.
我已经在生产环境中运行了该程序,并且到目前为止在 WPF 应用程序中运行得很好。确保从拥有
window
的 UI 线程调用SetNativeWindowInWPFWindowAsChild()
。这是我使用的 Native Win32 API。 (这里有额外的内容,因为我在设置窗口后调整了窗口的大小/焦点)
I have this running in production and so far so good in a WPF application. Make sure you call
SetNativeWindowInWPFWindowAsChild()
from UI thread that ownswindow
.Here is the Native Win32 API I used. (There are extras in here because I size/focus the window after it's set)
查看我的答案:如何在内部运行应用程序wpf 应用程序?
我设法让记事本示例在没有 DwayneNeed jiggery 的情况下工作。我刚刚添加了 SetParent() 和繁荣...她的工作方式就像 DwayneNeed 示例一样。
Check out my answer to: How to run an application inside wpf application?
I managed to get the notepad example working without DwayneNeed jiggery. I just added SetParent() and boom... she works just like the DwayneNeed example.