Почему я не могу вставить эти Приложения в мою форму?
умысел
Используя следующий код, мне удалось загрузить некоторые приложения в форме 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 или 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 Explorer.
2 ответа
Этот код работает для большинства приложений. Я встроил файловый менеджер, просто используя элемент управления веб-браузера в своей форме, и установил его 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# для встраивания оконных процессов и консольных процессов в вашу форму, ознакомьтесь с этим репозиторием github.
Ваш код работал по совпадению.
- WaitForInputIdle не обязательно будет ждать потока пользовательского интерфейса. Например, метод ввода или ловушка, созданные другой программой, могут создать простой поток, который становится бездействующим, пока поток пользовательского интерфейса все еще занят инициализацией.
- MainWindowHandle ищет первое видимое окно верхнего уровня. Это не вернет логическое главное окно, когда
- Главное окно не является первым видимым созданным окном (например, сначала создается диалог входа в систему)
- Главное окно не создано с видимым стилем (подумайте о программе, которая имеет только значок в области уведомлений на панели задач)
- Главное окно вообще не создается (например, некоторые приложения открывают новые документы / URL в существующем экземпляре, например в браузерах и Windows Explorer)
- Там нет главного окна, но несколько окон верхнего уровня, которые имеют одинаковый статус. Подумайте о IE6/Outlook/Word.
Даже если главное окно создано визуально и фактически является первым видимым окном в новом процессе, у вас все еще могут быть проблемы.
Из документации SetParent:
Приложение может использовать функцию SetParent, чтобы установить родительское окно всплывающего, перекрывающегося или дочернего окна.
В нем не сказано, что вы можете перерисовывать окно верхнего уровня. На самом деле окно верхнего уровня предлагает множество сервисов, на которые может положиться программа, таких как
- Выступать в качестве инструмента измерения, чтобы определить, завершен ли полноэкранный запрос (противоречит вашему требованию, что новая программа должна появиться внутри вашей панели)
- Получение уведомлений при запуске нового диалога DDE, при изменении активного окна / программы, при поступлении нового оборудования, при изменении системных настроек, при выходе пользователя из системы, при запуске проводника Windows, когда пользователь нажимает клавишу Enter на вложенном диалог и т. д. Список оконных сообщений, которые отправляются только в окна верхнего уровня, слишком длинный, чтобы перечислять их здесь,
- Выступать в качестве окна владельца по умолчанию модальных диалогов, если программа выбирает (и если вы также отображаете модальное диалоговое окно в своей программе, следите за сбоями)