我一直在阅读很多关于如何从C#程序(Process.Start())中触发应用程序的内容,但是我还没有找到任何关于如何在一个面板中运行这个新应用程序的信息。我的C#程序。例如,我想点击一下按钮在我的应用程序中打开notepad.exe,而不是从外部打开。
答案 0 :(得分:97)
使用win32 API可以“吃掉”另一个应用程序。基本上,您可以获得该应用程序的顶部窗口,并将其父级设置为要放置的面板的句柄。如果您不想要MDI样式效果,则还必须调整窗口样式以使其最大化并且删除标题栏。
这是一些简单的示例代码,其中我有一个带有按钮和面板的表单:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Process p = Process.Start("notepad.exe");
Thread.Sleep(500); // Allow the process to open it's window
SetParent(p.MainWindowHandle, panel1.Handle);
}
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
}
}
我刚看到另一个例子,他们调用WaitForInputIdle而不是sleep。所以代码就像这样:
Process p = Process.Start("notepad.exe");
p.WaitForInputIdle();
SetParent(p.MainWindowHandle, panel1.Handle);
代码项目在整个过程中有一篇很好的文章:Hosting EXE Applications in a WinForm project
答案 1 :(得分:14)
我不知道这是否仍然是推荐使用的东西,但“对象链接和嵌入”框架允许您将某些对象/控件直接嵌入到您的应用程序中。这可能只适用于某些应用程序,我不确定Notepad是否是其中之一。对于像记事本这样非常简单的事情,你可能会更容易使用你正在使用的任何媒体提供的文本框控件(例如WinForms)。
这是一个启动OLE信息的链接:
答案 2 :(得分:4)
此代码帮助我在Windows窗体中停靠了一些可执行文件。像NotePad,Excel,word,Acrobat reader等等......
但它不适用于某些应用程序。有时当你开始某个应用程序的进程....等待空闲时间...并尝试获取它的mainWindowHandle ....直到主窗口句柄变为空..... .....
所以我做了一个解决这个问题的技巧
如果你将主窗口句柄作为null ...然后搜索sytem上的所有运行进程并找到你的进程...然后获取进程的主要进程并将set面板作为其父进程。
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "xxxxxxxxxxxx.exe";
info.Arguments = "yyyyyyyyyy";
info.UseShellExecute = true;
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Maximized;
info.RedirectStandardInput = false;
info.RedirectStandardOutput = false;
info.RedirectStandardError = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForInputIdle();
Thread.Sleep(3000);
Process[] p1 ;
if(p.MainWindowHandle == null)
{
List<String> arrString = new List<String>();
foreach (Process p1 in Process.GetProcesses())
{
// Console.WriteLine(p1.MainWindowHandle);
arrString.Add(Convert.ToString(p1.ProcessName));
}
p1 = Process.GetProcessesByName("xxxxxxxxxxxx");
//p.WaitForInputIdle();
Thread.Sleep(5000);
SetParent(p1[0].MainWindowHandle, this.panel2.Handle);
}
else
{
SetParent(p.MainWindowHandle, this.panel2.Handle);
}
答案 3 :(得分:3)
使用WinForm容器进行外部应用程序的另一个有趣解决方案如下:
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
private void Form1_Load(object sender, EventArgs e)
{
ProcessStartInfo psi = new ProcessStartInfo("notepad.exe");
psi.WindowStyle = ProcessWindowStyle.Minimized;
Process p = Process.Start(psi);
Thread.Sleep(500);
SetParent(p.MainWindowHandle, panel1.Handle);
CenterToScreen();
psi.WindowStyle = ProcessWindowStyle.Normal;
}
从ProcessWindowStyle.Normal到ProcessWindowStyle.Minimized
的步骤消除了恼人的延迟。
答案 4 :(得分:1)
如果你想在你的应用程序中运行记事本,你可能会更好地使用文本编辑器组件。显然有一个WinForms附带的基本文本框,但我怀疑在interweb上可以找到提供Notepad功能(或更好)的更高级组件。
答案 5 :(得分:1)
我知道如果其他应用程序可以将自身附加到win32窗口句柄,这是可能的。例如,我们有一个单独的C#应用程序,它在其中一个窗口中托管DirectX应用程序。我不熟悉如何实现它的具体细节,但我认为只需将面板的win32 Handle
传递给另一个应用程序就足以让该应用程序附加其DirectX表面。
答案 6 :(得分:1)
我注意到所有先前的答案都使用较旧的Win32用户库函数来完成此任务。我认为这将在大多数案例中有效,但随着时间的推移会不那么可靠。
现在,没有这样做,我无法告诉你它的效果如何,但我知道当前的Windows技术可能是更好的解决方案:Desktop Windows Manager API。
DWM是一种技术,可让您使用任务栏和任务切换器UI查看应用的实时缩略图预览。我相信它与远程终端服务密切相关。
我认为当你强迫app成为不是桌面窗口的父窗口的子窗口时可能发生的问题是某些应用程序开发人员会对设备上下文(DC),指针(鼠标)做出假设)位置,屏幕宽度等,当它被嵌入时可能导致不稳定或有问题的行为。在主窗口中。
我怀疑您可以通过依赖DWM来帮助您管理必要的翻译,以便可靠地呈现应用程序的窗口并与其他应用程序的容器窗口进行交互,从而在很大程度上消除这些问题。
该文档假定C ++编程,但我发现一个人已经制作了他声称的开源C#包装器库:https://bytes.com/topic/c-sharp/answers/823547-desktop-window-manager-wrapper。帖子很旧,而且源不在像GitHub,bitbucket或sourceforge这样的大型存储库中,所以我不知道它的当前状态。
答案 7 :(得分:-1)
没有
简短回答:只有当其他应用程序被设计为允许它时,才能提供组件供您添加到自己的应用程序中。