HwndHost for Windows Form - Win32 / WinForm 互操作性
Posted
技术标签:
【中文标题】HwndHost for Windows Form - Win32 / WinForm 互操作性【英文标题】:HwndHost for Windows Form - Win32 / WinForm Interoperability 【发布时间】:2015-06-23 03:38:30 【问题描述】:我需要在 Windows 窗体控件中托管一个 Win32 窗口。我在使用 WPF 时遇到了同样的问题,我通过使用 HwndHost
控件解决了这个问题。
我遵循了这个教程:
Walkthrough: Hosting a Win32 Control in WPF
Windows 窗体中是否有任何等效控件?
我有一个Panel
及其Handle
属性,我将此句柄用作我的 Direct2D 渲染目标窗口的父级:
// Register the window class.
WNDCLASSEX wcex = sizeof(WNDCLASSEX) ;
// Redraws the entire window if a movement or size adjustment changes the height
// or the width of the client area.
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = Core::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = HINST_THISCOMPONENT;
wcex.hbrBackground = nullptr;
wcex.lpszMenuName = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDI_APPLICATION);
wcex.lpszClassName = L"SVGCoreClassName";
RegisterClassEx(&wcex);
hwnd = CreateWindow(
L"SVGCoreClassName", // class name
L"", // window name
WS_CHILD | WS_VISIBLE, // style
CW_USEDEFAULT, // x
CW_USEDEFAULT, // y
CW_USEDEFAULT, // width
CW_USEDEFAULT, // height
parent, // parent window
nullptr, // window menu
HINST_THISCOMPONENT, // instance of the module to be associated with the window
this); // pointer passed to the WM_CREATE message
...
hr = d2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(hwnd, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY),
&renderTarget);
如果我将 HwndHost
父句柄与 WPF 一起使用,则该代码有效。但如果我使用 System.Windows.Forms.Panel
句柄,它不会呈现任何内容。
【问题讨论】:
您可能对NativeWindow
感兴趣。
所有 Winforms 控件都已经是 Win32 窗口,因此“托管”没有多大意义。您需要更好地描述此窗口的性质。谁拥有它?你是如何创建它的?
@HansPassant 请看我的编辑。
这段代码有错误检查吗?你需要 if (hwnd == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();现在你就会知道为什么它不起作用了。
@HansPassant 我的 hwnd 是一个有效的指针,它不为空。我之前使用 WPF 进行的所有检查仍然保持不变,并且所有测试都通过了。
【参考方案1】:
您必须在 WPF 中执行的创建有效 D2D1 目标窗口的操作在 Winforms 中是不必要的。这在 WPF 中是必要的,因为控件本身不是窗口,并且没有 Handle 属性,而 CreateHwndRenderTarget() 需要该属性。
在 Winforms 中,Panel 类已经是一个非常好的渲染目标,你可以使用它的 Handle 属性来告诉 D2D1 在哪里渲染。你只需要告诉它停止绘画本身。向您的项目添加一个新类并粘贴此代码:
using System;
using System.Windows.Forms;
class D2D1RenderTarget : Control
protected override void OnHandleCreated(EventArgs e)
base.OnHandleCreated(e);
if (!this.DesignMode)
this.SetStyle(ControlStyles.UserPaint, false);
// Initialize D2D1 here, use this.Handle
//...
编译。将新控件拖放到表单上,替换现有面板。
【讨论】:
我不明白我必须传递什么句柄作为 CreateWindow 函数的父参数。我尝试在 OnHandleCreated 方法中使用 Control.Handle 属性,但它不起作用。请也查看我的编辑。 此外,我注意到未调用 CreateParams 属性(当我在其中设置断点时)。 不要,当您将控件放在面板上时,它是完全自动的。如果您在代码中执行此操作,则必须使用表单的 Controls.Add() 方法将控件添加到面板。添加一些代码后,这看起来完全是不必要的。 CreateHwndRenderTarget() 所需要的只是一个有效的窗口句柄。面板的Handle属性已经够用了,再多帮点也没意义。 C++ 代码所做的与 Panel 所做的相同。很遗憾你发布了如此糟糕的sn-p。 对不起,我还是不明白。我有我发布的 C++ 代码,我在其中创建了一个窗口并设置了它的父级。然后我使用窗口句柄来创建我的 Direct2D 渲染目标。使用 WPF,我使用 HwndHost 控件和 BuildWindowCore 方法来获取句柄(CreateWindow 函数中的父参数),它可以工作。现在使用 Forms.Control 类我无法做同样的事情,即我不知道我必须在哪里、何时以及将什么句柄传递给我的 C++ 代码,就像我之前使用 WPF 所做的那样。你能说清楚一点吗? 您需要跳过 WPF 中的那些环节,因为控件没有 Handle 属性。在 Winforms 中不需要。【参考方案2】:听起来像一个 MDI 应用程序。像这样?
[DllImport("user32.dll")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
Form f = new Form();
Button b1 = new Button Text = "Notepad" ;
b1.Click += delegate
using (var p = Process.Start("notepad.exe"))
p.WaitForInputIdle();
SetParent(p.MainWindowHandle, f.Handle);
MoveWindow(p.MainWindowHandle, 50, 50, 300, 300, true);
;
f.Controls.Add(b1);
Application.Run(f);
【讨论】:
以上是关于HwndHost for Windows Form - Win32 / WinForm 互操作性的主要内容,如果未能解决你的问题,请参考以下文章
在 Windows 窗体中显示 textBox.Text,“idx”遍历 for 循环