Как запустить другую программу

У каждой программы на C++ есть как минимум один поток, запускаемый средой выполнения C++, – поток, выполняющий функцию main() . Затем программа может запустить дополнительные потоки, точкой входа в которые служит другая функция. После чего эти потоки и начальный поток выполняются одновременно. Аналогично завершению программы при выходе из main() поток завершается при возвращении из функции, указанной в качестве точки входа.

std::thread

Основной класс для создания новых потоков в C++ – это std::thread .

  • Объект класса представляет собой один поток выполнения.
  • Новый поток начинает выполнение сразу же после построения объекта std::thread . Выполнение начинается с функции верхнего уровня, которая передаётся в качестве аргумента в конструктор std::thread .
  • Возвращаемое значение этой функции игнорируется, а если в ней будет брошено исключение, которое не будет обработано в этом же потоке, то вызовется std::terminate .
  • Передать возвращаемое значение или исключение из нового потока наружу можно через std::promise или через глобальные переменные (работа с которыми потребует синхронизации, см. std::mutex и std::atomic ).
  • Объекты std::thread также могут быть не связаны ни с каким потоком (после default construction, move from, detach или join), и поток выполнения может быть не связан ни с каким объектом std::thread (после detach).
  • Никакие два объекта std::thread не могут представлять один и тот же поток выполнения; std::thread нельзя копировать (не является CopyConstructible или CopyAssignable), но можно перемещать (является MoveConstructible и MoveAssignable).

Потоки запускаются созданием объекта std::thread , в котором определяется выполняемая в потоке задача. В простейшем случае эта задача представляет собой обычную функцию. Эта функция выполняется в собственном потоке, пока не вернет управление, после чего поток останавливается. Что бы ни собирался делать поток, и откуда бы он ни запускался, его запуск с использованием стандартной библиотеки C++ всегда сводится к созданию объекта std::thread :

DevelNext #29 — как запустить игру или программа с компьютера в DevelNext?


void do_some_work(); std::thread my_thread(do_some_work);

std::thread работает с любым вызываемым типом, поэтому конструктору std::thread можно также передать экземпляр класса с оператором вызова функции:

class background_task < public: void operator()() const < do_something(); do_something_else(); >>; background_task f; std::thread my_thread(f);

В данном случае предоставленный функциональный объект копируется в хранилище, принадлежащее вновь созданному потоку выполнения, и вызывается оттуда. Поэтому важно, чтобы копия действовала аналогично оригиналу, иначе результат может не соответствовать ожидаемому.

С помощью лямбда-выражения предыдущий пример можно записать следующим образом:

std::thread my_thread([]< do_something(); do_something_else(); >);

После запуска потока, нужно принять однозначное решение, ждать ли его завершения (join) или пустить его на самотек (detach). Если не принять решение до уничтожения объекта std::thread , то программа завершится (деструктор std::thread вызовет std::terminate() ). Решение нужно принимать до того, как объект std::thread будет уничтожен.

Читайте также:
Программа для того чтобы рисовать аниме

Python в .EXE ► КАК?

Сам же поток вполне мог бы завершиться задолго до его присоединения или отсоединения. Если его отсоединить, то при условии, что он всё еще выполняется, он и будет выполняться, и этот процесс может продолжаться еще долго и после уничтожения объекта std::thread . Выполнение будет прекращено, только когда в конце концов произойдет возвращение из функции потока. Если не дожидаться завершения потока, необходимо убедиться, что данные, к которым он обращается, будут действительны, пока он не закончит работать с ними.

Дождаться завершения потока можно, вызвав join() для связанного экземпляра std::thread . Вызов join() приводит к очистке объекта std::thread , поэтому объект std::thread больше не связан с завершенным потоком. Мало того, он не связан ни с одним потоком. Это означает, что join() можно вызвать для конкретного потока только один раз: как только вызван метод join() , объект std::thread утрачивает возможность присоединения, а метод joinable() вернет значение false .

Вызов метода detach() для объекта std::thread позволяет потоку выполняться в фоновом режиме, непосредственное взаимодействие с ним не требуется. Возможность дождаться завершения этого потока исчезает: если поток отсоединяется, получить ссылающийся на него объект std::thread невозможно, поэтому такой поток больше нельзя присоединить. Отсоединенные потоки фактически выполняются в фоновом режиме, владение и управление ими передаются в библиотеку среды выполнения C++, которая гарантирует правильное высвобождение ресурсов, связанных с потоком, при выходе из него. Как правило, такие потоки являются весьма продолжительными, работая в течение практически всего времени жизни приложения и выполняя фоновую задачу, например отслеживая состояние файловой системы, удаляя неиспользуемые записи из кэш-памяти объектов или оптимизируя структуры данных. Метод detach() нельзя вызывать для объекта std::thread , не имеющего связанного с ним потока выполнения. Это требование аналогично тому, которое предъявляется к вызову метода join() , и проверку можно провести точно таким же образом – вызывать для объекта t типа std::thread метод t.detach() возможно, только если метод t.joinable() вернет значение true .

Передача аргументов вызываемому объекту или функции сводится к простой передаче дополнительных аргументов конструктору std::thread . Но важно учесть, что по умолчанию аргументы копируются во внутреннее хранилище, где к ним может получить доступ вновь созданный поток выполнения, а затем передаются вызываемому объекту или функции как r-значения (rvalues), как будто они временные. Так делается, даже если соответствующий параметр в функции ожидает ссылку. Рассмотрим пример:

void f(int i,std::string const std::thread t(f,3,»hello»);

В результате создается новый поток выполнения, связанный с t , который вызывает функцию f(3,»hello») . Обратите внимание: даже если f в качестве второго параметра принимает std::string , строковый литерал передается как char const* и преобразуется в std::string только в контексте нового потока. Это становится особенно важным, когда, как показано далее, предоставленный аргумент является указателем на локальную переменную:

void f(int i,std::string const void oops(int some_param)

Здесь это указатель на буфер локальной переменной, который передается в новый поток. И высока вероятность того, что выход из функции oops произойдет, прежде чем буфер будет в новом потоке преобразован в std::string , что вызовет неопределенное поведение. Решением является приведение к типу std::string перед передачей буфера в конструктор std::thread :

Читайте также:
Что такое программа зум для дистанционного обучения

void f(int i,std::string const void oops(int some_param)
void update_data_for_widget(widget_id w, widget_data void oops_again(widget_id w)

Хотя update_data_for_widget ожидает, что второй параметр будет передан по ссылке, конструктор std::thread не знает об этом, он не обращает внимания на типы аргументов, которые ожидает функция, и слепо копирует предоставленные значения. Но внутренний код передает скопированные аргументы в качестве r-значений, чтобы работать с типами, предназначенными только для перемещений, и пытается таким образом вызвать update_data_for_widget с r-значением. Этот код не скомпилируется, так как нельзя передать r-значение функции, ожидающей не-const-ссылку. Для тех, кто знаком с std::bind , решение будет очевидным: аргументы, которые должны быть ссылками, следует заключать в std::ref . В этом случае при изменении вызова потока на:

std::thread t(update_data_for_widget,w,std::ref(data));

update_data_for_widget будет корректно передана ссылка на данные, а не временная копия данных, и код успешно скомпилируется. Если работать с std::bind уже приходилось, то в семантике передачи параметров не будет ничего нового, поскольку и операция конструктора std::thread , и операция std::bind определены в рамках одного и того же механизма.

Чтобы вызвать в отдельном потоке метод какого-ибо объекта, нужно передать указатель на объект в качестве первого аргумента этого метода:

class X < public: void do_lengthy_work(); >; X my_x; std::thread t(my_x);

Этот код вызовет my_x.do_lengthy_work() в новом потоке, поскольку в качестве указателя на объект предоставляется адрес my_x .

Еще один интересный сценарий предоставления аргументов применяется, когда аргументы нельзя скопировать, а можно только переместить. Примером может послужить тип std::unique_ptr , обеспечивающий автоматическое управление памятью для динамически выделяемых объектов. В одно и то же время на данный объект может указывать только один экземпляр std::unique_ptr , и когда этот экземпляр уничтожается, объект, на который он указывал, удаляется. Перемещающий конструктор и перемещающий оператор присваивания позволяют передавать права владения объектом между экземплярами std::unique_ptr . В результате этого исходный объект остается с нулевым указателем. Такое перемещение значений позволяет принимать объекты данного типа в качестве параметров функции или возвращать их из функций. Если исходный объект временный, перемещение выполняется автоматически, но если источником является именованное значение, передача должна быть запрошена напрямую путем вызова метода std::move() . В следующем примере показано использование std::move для передачи потоку права владения динамическим объектом:

void process_big_object(std::unique_ptr); std::unique_ptr p(new big_object); p->prepare_data(42); std::thread t(process_big_object,std::move(p));

Поскольку при вызове конструктора std::thread указан метод std::move(p) , право владения big_object сначала передается внутреннему хранилищу вновь созданного потока, а затем переходит к process_big_object .

Как запустить одну программу из другой?

Существует несколько способов запуска одной программы из другой.

1. WinExec — устаревшая функция, используется только для совместимости с 16-битной Windows. Не рекомендуется к использованию в Win32-приложениях.

2. CreateProcess — замена WinExec для Win32.

BOOL CreateProcess(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,

LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation

параметры командной строки

атрибуты безопасности процесса (имеет смысл только в NT/2000)

Читайте также:
Какие программы нужны для программирования на unity

атрибуты безопасности главного потока (имеет смысл только в NT/2000)

если bInheritHandles == TRUE, то созданный процесс (запущенная программа), наследует дескрипторы (handles) запускающей программы

параметры создания. Здесь можно указать класс приоритета создаваемого процесса и некоторые дополнительные параметры

указатель на блок окружения или NULL, тогда используется блок окружения родителя

текущая директория или NULL, тогда используется текущая директория родителя

указатель на структуру STARTUPINFO, которая определяет положение главного окна

сюда будет записана информация о созданном процессе

Пример запуска notepad.exe.

STARTUPINFO si;
PROCESS_INFORMATION pi;
GetStartupInfo(
if(!CreateProcess(
«c:\windows\notepad.exe»,
NULL,
NULL,

NULL,
FALSE,
NULL,
NULL,
// обработка ошибок

Следует обратить внимание на то, что указан полный путь к файлу notepad.exe.

CreateProcess не производит поиск файлов в путях, указанных в переменной PATH.

HINSTANCE ShellExecute(
HWND hwnd,
LPCTSTR lpOperation,
LPCTSTR lpFile,
LPCTSTR lpParameters,
LPCTSTR lpDirectory,

INT nShowCmd

Эта функция возвращает значение >32 в случае успешного выполнения и значение

дескриптор родительского окна

строка, описывающая операцию: «open» — открытие (запуск), «print» — печать, «explore» — открыть окно с заданной папкой

параметры командной строки

текущая директория для запускаемой программы

параметр, указывающий, как будет показано приложение при открытии. Имеет тот же смысл, что и nCmdShow в WinMain

Пример запуска notepad.exe:

if((UINT)ShellExecute(
NULL,
«open»,
«notepad.exe»,
NULL,
NULL,
SW_SHOWNORMAL) 32)
// обработка ошибок
if((UINT)ShellExecute(
NULL,
«open»,
NULL,
NULL,
SW_SHOWNORMAL) 32)
// обработка ошибок

WINSHELLAPI BOOL WINAPI ShellExecuteEx(
LPSHELLEXECUTEINFO lpExecInfo

Формат структуры SHELLEXECUTEINFO:

typedef struct _SHELLEXECUTEINFO
DWORD cbSize; // размер структуры
ULONG fMask; // маска, указывающая
HWND hwnd;
LPCTSTR lpVerb; // команда
LPCTSTR lpFile; // имя файла

LPCTSTR lpParameters; // параметры командной строки
LPCTSTR lpDirectory; // текущая директория
int nShow; // параметр, указывающий, как будет показано приложение при открытии.
HINSTANCE hInstApp; // сюда будет записан hInstance приложения

// Optional members
LPVOID lpIDList; // IDL, определяющий файл для выполнения
LPCSTR lpClass; // имя класса файла или GUID
HKEY hkeyClass; // дескриптор ключа в реестре для класса файла
DWORD dwHotKey; // горячая клавиша

HANDLE hIcon; // иконка для класса файла
HANDLE hProcess; // дескриптор процесса
> SHELLEXECUTEINFO, FAR *LPSHELLEXECUTEINFO;

Пример запуска notepad.exe:

ZeroMemory(
SHExecInfo.cbSize = sizeof(SHExecInfo);
SHExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
SHExecInfo.nShow = SW_SHOWNORMAL;
SHExecInfo.lpFile = «notepad.exe»;
if(!ShellExecuteEx(http://www.cyberguru.ru/sources/cpp/dll-applications/kak-zapustit-odnu-programmu-iz-drugoj.html» target=»_blank»]www.cyberguru.ru[/mask_link]

Русские Блоги

Как запустить другое приложение из одного приложения в Android

Основной код выглядит следующим образом

1. Первый, знайте имя пакета приложения и имя класса запущенного Activity.

Intent intent = new Intent(); ComponentName cn = new ComponentName(«com.example.test03», «com.example.test03.IntentActivity»); intent.setComponent(cn); intent.setAction(Intent.ACTION_VIEW); startActivity(intent);

2. Второй метод знает только название пакета приложения.

Intent launchIntentForPackage = this.getPackageManager().getLaunchIntentForPackage(«com.example.test03»); this.startActivity(launchIntentForPackage);

3.

Третий тип знает имя пакета приложения и имя класса запущенного Activity, но этот класс Activity не является действием входа в приложение.

Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ComponentName cn = new ComponentName(«com.example.test03», «com.example.test03.IntentActivity»); intent.setComponent(cn); intent.setAction(Intent.ACTION_VIEW); startActivity(intent);

== Особое внимание ==

++ Если класс или действие, которое нужно запустить, не является входом Activity приложения, используйте первый метод, произойдет следующая ошибка: ++

E/AndroidRuntime(3594): Caused by: java.lang.SecurityException: Permission Denial: starting Intent < act=android.intent.action.VIEW cmp=com.example.test03/.IntentActivity >from ProcessRecord

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru