3

我们有一个 wpf 应用程序,应该由旧版 win32 winform 应用程序“试点”。(我们不拥有代码;))

遗留应用程序应该通过我们应该作为写入 ini 文件的配置参数提供的 windowsclass 名称来引导我们的应用程序(最小化、置于前面、关闭等)。

问题是我们不能让它与 wpf 一起工作,因为如果我们插入 Spy++ 给我们的类名,什么也不会发生。关键是 Spi++ 返回类似这样的东西

 HwndWrapper[MyWpfProgram.exe;;16978ce2-3b8d-4c46-81ee-e1c6d6de4e6d]

每次运行时都会随机生成 guid。

有没有办法解决这个问题?

谢谢你。

4

3 回答 3

2

没有办法按照我的要求去做。但我们找到了一个转机。“简单地”将 xaml 窗口嵌入 Windows 窗体中。

这些是我们遵循的步骤:

1 - 将 Windows 窗体添加到项目中。

2 - 删除 app.xaml 并使新表单成为应用程序的入口点。

3 - 由于我们需要 main.xaml 的 hwnd,因此我们将此道具添加到其代码后面

    public IntPtr Hwnd
    {
        get { return new WindowInteropHelper(this).Handle; }
    }

4 - 然后从窗体的构造函数中创建 wpf 窗口类的实例

    private Main app;
    public ContainerForm()
    {
        InitializeComponent();

        app = new Main();
        ElementHost.EnableModelessKeyboardInterop(app);
    }

我们需要

 ElementHost.EnableModelessKeyboardInterop(app);

因为我们希望所有键盘输入都从 windows 窗体传递到 xaml 窗口

5 - 现在我们要将 xpf 窗口绑定到 winform。为了做到这一点,我们需要使用 Windows Api,并且我们在表单的 OnShow 事件中执行此操作(原因将在后面解释)。

    [DllImport("user32.dll", SetLastError = true)]
    private static extern long SetFocus(IntPtr hWnd);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);


    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);

    [DllImport("user32.dll", EntryPoint = "SetWindowLongA", SetLastError = true)]
    private static extern long SetWindowLong(IntPtr hwnd, int nIndex, long dwNewLong);
    private const int GWL_STYLE = (-16);
    private const int WS_VISIBLE = 0x10000000;

    private void ContainerForm_Shown(object sender, EventArgs e)
    {
        app.Show();
        SetParent(app.Hwnd, this.Handle);
        SetWindowLong(app.Hwnd, GWL_STYLE, WS_VISIBLE);
        MoveWindow(app.Hwnd, 0, 0, this.Width, this.Height, true);

        SetFocus(app.Hwnd);
    }

 SetParent(app.Hwnd, this.Handle);

wo做魔术,然后用

  SetWindowLong(app.Hwnd, GWL_STYLE, WS_VISIBLE);

我们从 wpf 窗口中删除所有 chrome(即使窗口定义为无边框,也有边框,不要问我为什么)

然后我们让wpf窗口填满winform的所有客户区

  MoveWindow(app.Hwnd, 0, 0, this.Width, this.Height, true);

然后我们聚焦 wpf 窗口

 SetFocus(app.Hwnd);

这就是我们在演出活动中做所有事情的原因。因为如果我们在窗体的构造函数中执行此操作,则 wpf 窗口将失去其焦点,因为在 winform 中,主窗口从操作系统获得了焦点。

我们不明白为什么此时我们需要添加其他 api 调用,但是如果我们将它们留在构造函数中,那么这个技巧就不起作用了。

无论如何,问题解决了;)

于 2013-08-07T15:34:43.403 回答
1

使用HwndSource

您可以使用本机 Windows API 调用来创建具有预期类名的窗口,然后使用 HwndSource 向其中添加 WPF 内容:

var source = HwndSource.FromHwnd(nativeWindowHandle);
source.RootVisual = mainGrid;

如果您需要使用 WPF 窗口,我认为您仍然可以使用“代理”窗口来解决这个问题,但它不会很漂亮:

  • 让您的 WPF 应用程序生成一个本机仅消息窗口。
  • 使用HwndSource.AddHook处理本机窗口上的 WM_CLOSE、WM_SIZE 等消息,并将它们传递给“真正的”WPF 窗口。
于 2013-08-07T15:19:05.977 回答
0

对于窗口句柄、标题和类名,Spy++ 使用相当简单的 Windows API。

FindWindowEx http://msdn.microsoft.com/en-us/library/windows/desktop/ms633500%28v=vs.85%29.aspx

EnumWindows http://msdn.microsoft.com/en-us/library/windows/desktop/ms633497%28v=vs.85%29.aspx

GetClassName http://msdn.microsoft.com/en-us/library/windows/desktop/ms633582%28v=vs.85%29.aspx

您可以创建一个“加载器”程序,它将...

  • 启动 wpf 应用程序
  • 使用上述 API 获取正确的类名和窗口句柄
  • 编辑旧版 INI
  • 启动旧版应用程序
于 2013-08-07T12:55:49.683 回答