Запуск исполняемого файла из памяти. Часть 2 (из 3-х)
Продолжаем раскрывать тему предыдущего поста. Кто не читал предыдущий пост, тот может ничего и не понять. Мы рассмотрели как запустить программу из памяти. Но, в том случае сама программа находилась на диске, а сегодня мы запустим ее из нашего exe-файла (предварительно запихнув ее туда). Для начала подготовьте тестовый exe-файл, который будет загружаться из памяти. Вы можете взять его из предыдущего поста.
Вторым шагом мы создаем приложение-лаунчер (опять же смотрите предыдущий пост). Но его код немного отличается от предыдущего.
Вот так выглядит файл StartUpApplication.cs, в котором описана точка входа в программу (функция main)
class StartUpApplication
{
public static void Main()
{
Assembly assembly = Assembly.GetExecutingAssembly();
Stream input = assembly.GetManifestResourceStream("LaunchEmbeddedRes.test.exe");
if (input == null)
{
MessageBox.Show("null");
return;
}
Assembly a = Assembly.Load(new BinaryReader(input).ReadBytes((int)input.Length));
MethodInfo method = a.EntryPoint;
//если найдена точка входа
if (method != null)
{
//создаем экземпляр главной формы
object o = a.CreateInstance(method.Name);
//запускаем приложение через точку входа
method.Invoke(o, null);
}
}
}
Программа начинается с необходимости загрузить встроенный ресурс.
Assembly assembly = Assembly.GetExecutingAssembly() — с помощью этого получаем текущую сборку. А assembly.GetManifestResourceStream(«LaunchEmbeddedRes.test.exe») возвращает поток, который можно прочитать и записать в память. В строке LaunchEmbeddedRes.test.exe первое — это называние namespace нашего проекта, а test.exe — имя нашего ресурса. Соответственно у вас возможно будет по-другому.
Далее по коду: загружаем test.exe в память. MethodInfo method = a.EntryPoint — получаем ссылку на точку входа и далее отправляем на выполнение.
Теперь важное: как добавить ресурс в проект, чтобы его можно было потом прочитать по LaunchEmbeddedRes.test.exe.
Откройте Solution Explorer проекта в меню View. Правой кнопкой мыши на проекте и меню Add exists добавляем наш test.exe.

Теперь в свойствах этого файла установите поле Build Action установите Embedded Resource. Все, теперь файл test.exe будет компилироваться как встроенный ресурс.

При запуске нашего тестового проекта, в памяти можно наблюдать следующую картину:
Скриншот сделан в Process Explorer. На картинке видно что запущен LaunchEmbeddedRes, но самого test.exe не видно. По крайней мере в диспетчере задач
На сегодня все. Пробуйте, запускайте, комментируйте. Всего наилучшего.
Как обычно вы можете скачать тестовый проект к этой статье.
Популярность: 16%
Если у вас возникли вопросы, вы можете оставить их в комментариях



У меня срабатывает только на консольном приложении (когда оба консольные). Что интересно, консоль используется одна.
А вот на WinForms — в месте Invoke валится с исключением: System.Reflection.TargetParameterCountException
Вы читали первую часть?
У вас работает проект приложенный к статье?
Пришлите ваши исходники программы, которая запускает тестовый файл.
Судя по названию ошибки, у вас метод main запускаемой программы содержит аргументы, но в данном коде мы передаем null. Попробуйте поменять в method.Invoke(o, null) — null на то, что у вас прописано в методе main запускаемого exe-файла.
Как ни странно для меня, первый проект скачанный работает, да и второй тоже…
Вобщем, беру первый проект и переделываю.
Здесь переделываем под ресурсы:
public frmMain()
{
InitializeComponent();
string[] files = Assembly.GetExecutingAssembly().GetManifestResourceNames();
foreach (string file in files)
{
lstMain.Items.Add(file);
}
}
Здесь переделываем под вышенаписанное:
if (frm.ShowDialog() == DialogResult.OK)
{
//определяем путь к файлу на диске
string pathToFile = frm.lstMain.SelectedItem.ToString();
//создаем поток для чтения
var fs = Assembly.GetExecutingAssembly().GetManifestResourceStream(pathToFile);
//создаем побитовую читалку
var br = new BinaryReader(fs);
//bin содержит побайтово весь exe-файл
byte[] bin = br.ReadBytes((int)(fs.Length));
//закрываем поток
fs.Close();
//закрываем читалку
br.Close();
И получаем ту же самую ошибку
Пришлите мне на почту ваш нерабочий проект.
Я не нашёл на сайте ни одного адреса электронной почты
С простеньким приложением типа Test все получилось, а вот более сложное ругнулось: [Exception has been thrown by the target of an invocation на invoke.] Также не удалось запустить приложение которое написано не на С#, тут получил [ Could not load file or assembly '322352 bytes loaded from PrintTasks, Version=1.0.1.555, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Была сделана попытка загрузить программу, имеющую неверный формат. ]. Можно ли вашим методом запустить стороннее приложение или оба должны быть на C# ?
Я не пробовал использовать этот метод в сложных приложениях. Если можете, пришлите мне ваш тестовый проект, я посмотрю.
По поводу приложений, я думаю что они должны быть написаны с использованием .NET, т.е. нативные приложения, написанные на Deplhi или C++ работать не будут. По крайней мере не таким способом.
Добавил в ресурсы приложение
a.EntryPoint имеет такой вид : Int32 Main(System.String[])
Запускаемое приложение консольное, и запускается с параметрами, но при таком вызове :
method.Invoke(o, new string[] {key, path});
прогрмма отваливается с ошибкой не соответсвия количества параметров, подскажите пожалуйста, в чем может быть причина?
Если добавленное в ресурсы приложение запускать из командной строки с параметрами (installUtil.exe key path), то все нормально работает
Вам необходимо запускать с параметрами примерно так:
method.Invoke(o, new[] { new[] { key, path } });
А где третья статья, поучительная, но остался главный вопрос — а как же быть с нативными приложениями?
3-й не будет. Т.к. тот способ, который предполагался — не работает. А как с нативными — может кто-то другой подскажет.