【问题标题】:Why can't I embed these Applications in my Form?为什么我不能在我的表单中嵌入这些应用程序?
【发布时间】:2012-09-29 14:11:44
【问题描述】:

意图

使用以下代码,我设法在我的 Windows 窗体中加载了一些应用程序。

代码

这个函数的作用是……

  • 陈述过程
  • 将流程嵌入到我的表单面板中
  • 最大化嵌入式进程
  • 向面板添加调整大小事件处理程序以更新面板调整大小时嵌入进程的大小
  • 向表单添加关闭的事件处理程序以在表单关闭时终止嵌入式进程

用途

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

常量

const int   GWL_STYLE   = -16;
const long  WS_VISIBLE  = 0x10000000,
            WS_MAXIMIZE = 0x01000000,
            WS_BORDER   = 0x00800000,
            WS_CHILD    = 0x40000000;

功能

IntPtr LoadExtern(Control Panel, string Path)
{
    try
    {
        Process Process = Process.Start(Path);
        Process.WaitForInputIdle();
        IntPtr Handle = Process.MainWindowHandle;
        SetParent(Handle, Panel.Handle);
        SetWindowLong(Handle, GWL_STYLE, (int)(WS_VISIBLE+(WS_MAXIMIZE|WS_BORDER)));

        MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);

        Panel.Resize += new EventHandler(
             delegate(object sender, EventArgs e)
             {
                  MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);
             }
        );

        this.FormClosed += new FormClosedEventHandler(
             delegate(object sender, FormClosedEventArgs e) {
                  SendMessage(Handle, 83, 0, 0);
                  Thread.Sleep(1000);
                  Handle = IntPtr.Zero;
             }
        );

        return Handle;
    }
    catch (Exception e) { MessageBox.Show(this, e.Message, "Error"); }
    return new IntPtr();
}

DLL 导入

[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr Handle, int x, int y, int w, int h, bool repaint);

[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr Handle, int Msg, int wParam, int lParam);

结果

此代码适用于某些应用程序,例如 Windows 记事本。记事本已启动并包含在我的表单面板中。没有标题,也没有边框,应该是这样。

LoadExtern(panel1, "notepad.exe");

关闭表单后,嵌入式进程会按预期终止。

问题

不幸的是,我的代码不适用于其他一些(更大的)应用程序,例如 firefox 或 sublimetext。

LoadExtern(panel2, @"C:\Program Files (x86)\Mozilla Firefox\firefox.exe");

发生的情况是我的表单启动并且 firefox 启动,但是在它自己的窗口中。你能帮我在我的应用程序中包含 sublimetext 或 firefox 吗?

部分解决方案

感谢盛江的回答,我得到了它的更多应用程序。我所做的是等待主窗口句柄。

Process.WaitForInputIdle();
IntPtr Handle = new IntPtr();
for (int i = 0; Handle == IntPtr.Zero && i < 300; i++)
{
     Handle = Process.MainWindowHandle;
     Thread.Sleep(10);
}

但我仍然无法嵌入 Windows 资源管理器等应用程序。

【问题讨论】:

  • 这通常可以工作的唯一方法是如果另一个程序是该行为的一部分 - 它可能不会被编写为期望它的一个或多个窗口不是' t 顶级窗口(这是相当合理的 - 为什么那些开发人员要在他们不希望发生的事情上花费精力?)
  • “但我仍然无法嵌入 Windows 资源管理器之类的应用程序”是什么意思。 - 你的意思是你想嵌入资源管理器,但不能,或者你已经看到资源管理器嵌入了其他应用程序,并且你想做同样的事情(但是,通过引用包括我以前的 cmets - 它不会工作)
  • 我想在我的表单中嵌入一个文件浏览器窗口。不幸的是,我无法与我想嵌入的流程的开发人员一起工作。
  • 好吧,正如我所说的——这些程序不一定是为了期望嵌入到其他程序中而构建的。您可能想要它,但这并不意味着它是真实的或可用的。
  • MainWindowHandle 可能不是您所期望的。作为测试,您使用另一个工具 (spy++) 获取 hWnd 并将其嵌入以查看是否有效,如果有效,则需要枚举 windows int hat 进程以找到您想要的。

标签: c# winapi process embed handler


【解决方案1】:

巧合,您的代码运行良好。

  • WaitForInputIdle 不需要等待 UI 线程。例如,由其他程序创建的输入法或挂钩可能会创建一个简单的线程,该线程在 UI 线程仍在忙于初始化时变得空闲。
  • MainWindowHandle 搜索第一个可见的顶级窗口。它不会返回逻辑主窗口
    • 主窗口不是创建的第一个可见窗口(例如,首先创建登录对话框)
    • 主窗口不是以可见样式创建的(想想一个程序,它在系统托盘的通知区域中只有一个图标)
    • 根本没有创建主窗口(例如,某些应用程序在现有实例中打开新文档/url,如浏览器和 Windows 资源管理器)
    • 没有一个主窗口,而是多个具有相同状态的顶级窗口。想想 IE6/Outlook/Word。

即使主窗口是可见创建的,实际上是新进程中的第一个可见窗口,您仍然可能遇到问题。

来自 SetParent 的文档:

应用程序可以使用 SetParent 函数来设置弹出窗口、重叠窗口或子窗口的父窗口。

它并没有说您可以重新设置顶级窗口。事实上,顶层窗口提供了很多程序可能依赖的服务,例如

  • 作为衡量工具来确定全屏请求是否完成(与您要求新程序需要出现在面板中的要求相冲突)
  • 在新的 DDE 对话开始时、活动窗口/程序更改时、新硬件到达时、系统设置更改时、用户注销时、Windows 资源管理器启动时、用户按下 Enter 键时收到通知在嵌套对话框等上。仅发送到顶级窗口的窗口消息列表太长,无法在此处列出,
  • 如果程序选择,则充当模式对话框的默认所有者窗口(如果您也在程序中显示模式对话框,watch out for crashes

【讨论】:

  • 感谢您的回复!我现在可以使用等待主处理程序的代码来实现 sublimetext。
【解决方案2】:

此代码适用于大多数应用程序。我仅在表单上使用 webbrowser 控件嵌入了文件资源管理器,并将其 url 设置为文件位置。然后,Internet Explorer 控件神奇地变成了文件浏览器。

这是我的最终代码,您可以随意将其用于您自己的项目。

IntPtr EmbedProcess(Control Panel, string Path)
{
    string Name = NameFromPath(Path);

    foreach (Process Task in Process.GetProcesses())
    {
        if (NameFromPath(Task.ProcessName).Contains(Name))
        {
            try { Task.Kill(); }
            catch (Exception e) {  }
        }
    }

    try
    {
        Process Task = Process.Start(Path);
        Task.WaitForInputIdle();
        IntPtr Handle = new IntPtr();
        for (int i = 0; Handle == IntPtr.Zero && i < 10; i++) { Handle = Task.MainWindowHandle; Thread.Sleep(100); }
        SetParent(Handle, Panel.Handle);
        SetWindowLong(Handle, GWL_STYLE, (int)(WS_VISIBLE + (WS_MAXIMIZE | WS_BORDER)));

        MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);

        Panel.Resize += new EventHandler(delegate(object sender, EventArgs e) { MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true); });

        this.FormClosed += new FormClosedEventHandler(delegate(object sender, FormClosedEventArgs e)
        {
            SendMessage(Handle, 83, 0, 0);
            Thread.Sleep(100);
            Handle = IntPtr.Zero;
        });

        return Handle;
    }
    catch (Exception e) { MessageBox.Show(this, e.Message, "Error"); }
    return new IntPtr();
}

我有人对用于将窗口进程和控制台进程嵌入到表单中的 C# 漏洞类感兴趣,请查看 this github repository

【讨论】:

    猜你喜欢
    • 2016-12-15
    • 2022-10-01
    • 2018-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多