代码之家  ›  专栏  ›  技术社区  ›  danijar

为什么我不能将这些应用程序嵌入到我的表单中?

  •  1
  • danijar  · 技术社区  · 13 年前

    打算

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

    密码

    这个函数的作用是。。。

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

    Usings公司

    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或升华文本。

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

    发生的事情是,我的表单启动,firefox启动,但在它自己的窗口中。你能帮我在我的应用程序中包括升华文本或firefox吗?

    解决方案的一部分

    多亏了盛江的回答,我为更多的应用程序工作了。我所做的就是等待一个主窗口把手。

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

    但我仍然无法嵌入像windows资源管理器这样的应用程序。

    2 回复  |  直到 13 年前
        1
  •  3
  •   hurst    13 年前

    你的代码工作得很好纯属巧合。

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

    即使主窗口是以可见方式创建的,而且实际上是新流程中的第一个可见窗口,您仍可能存在问题。

    根据SetParent的文档:

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

    它并没有说你可以修复一个顶级窗口。事实上,顶级窗口提供了许多程序可能依赖的服务,例如

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

    此代码适用于大多数应用程序。我简单地使用表单上的网络浏览器控件嵌入了文件资源管理器,并将其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