概要
C++BuilderはFireMonkey等の素晴らしいコンポーネントが沢山あります。
しかし、PythonやVC++等でしか出来ない事というのが少なからずあり、それと組み合わせて何かを作るというのは仕事に幅を与えてくれます。
ここでは、外部アプリケーションとの連携をするためのアプリケーションを出来るだけ小さい構成で作る方法を記述しました。
参考
下記URLを参考にして作成しました。
起動したアプリケーションを終了させる
http://akky.xrea.jp/mfc/appclose.html
起動したアプリケーションの終了を取得する
http://akky.xrea.jp/mfc/getapend.html
検証環境
Windows 10 Pro
C++Builder 10.1 Berlin
手順
1.プロジェクトの作成
ファイル->新規作成->マルチデバイスアプリケーション->空のアプリケーション
で新規プロジェクト作成し、適当な名前でディレクトリを作成して保存。
2.フォームにオブジェクトを配置
フォーム(TButton)上にボタン2つと、ラベル(TLabel)を1つ配置。
Button1は他アプリケーションの起動に、
Button2は他アプリケーションを終了させるボタンに、
Label1は現在の状態を表示させます。
3.スレッドクラスを作成
を選択。ダイアログが表示されるので適当なクラス名(今回はThreadClassと名付けました)をつけてOK。
一旦「すべて保存」で適当に保存。
4.フォームクラスに追記
ヘッダー
// 省略
// ↓スレッドクラスをインクルード
#include "Unit2.h"
// 省略
// ↓クラスのヘッダーに追記
private:
// スレッド側にあるアプリケーションのPROCESS_INFORMATION構造体のポインタ格納用
PROCESS_INFORMATION *form_ppi;
// スレッドのオブジェクト
ThreadClass* thread;
// 省略
実装
フォーム.fmxファイルを開いて、2個配置したボタンをダブルクリックして、それぞれのイベントを作成しておき、実装側に下記を追記します。
// 省略
// アプリケーションのハンドルからウィンドウハンドルを見つけて閉じるメッセージを送信
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) {
PROCESS_INFORMATION* pi = (PROCESS_INFORMATION*)lParam;
DWORD lpdwProcessId = 0;
::GetWindowThreadProcessId(hWnd, &lpdwProcessId);
if (pi->dwProcessId == lpdwProcessId) {
// 閉じるメッセージを送信
::PostMessage(hWnd, WM_CLOSE, 0, 0);
return FALSE;
}
return TRUE;
}
// ボタン1スクリプト
void __fastcall TForm1::Button1Click(TObject *Sender) {
// スレッド生成
thread = new ThreadClass(true);
Label1->Text = L"アプリケーション起動中";
// ラベルのポインタを渡す。
thread->th_label = Label1;
// スレッド実行
thread->Resume();
}
// ボタン2スクリプト
void __fastcall TForm1::Button2Click(TObject *Sender) {
if (!thread)
return;
EnumWindows(EnumWindowsProc, (LPARAM)thread->th_ppi);
Label1->Text = L"キャンセル中";
if (::WaitForSingleObject(thread->th_ppi->hProcess, 5000) == WAIT_TIMEOUT) {
if (MessageBox(NULL, L"強制終了させますか?", L"キャンセル", MB_YESNO) == IDYES) {
TerminateProcess(thread->th_ppi->hProcess, 0);
Label1->Text = L"強制的にキャンセルしました";
}
}
Label1->Text = L"キャンセルしました";
}
5.スレッドクラスに追記
ヘッダー
// 省略
// Borlandコンパイラの場合、TLabelを使うためにインクルードが必要
#include <FMX.StdCtrls.hpp>
// スレッドクラス内に追記
public:
// アプリケーションのPROCESS_INFORMATION構造体のポインタ
PROCESS_INFORMATION *th_ppi;
// フォーム側のラベルのポインタを格納するポインタ
TLabel *th_label;
// 省略
実装
// 省略
void __fastcall ThreadClass::Execute() {
// ---- ここにスレッド コードを記述します ----
// Execute() に追記
STARTUPINFO si = {sizeof(STARTUPINFO)};
PROCESS_INFORMATION pi;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
// 今回はメモ帳を起動。別のアプリケーションの場合はここを修正。
::CreateProcess(NULL, L"notepad.exe", NULL, NULL, FALSE, 0, NULL, NULL,
&si, &pi);
th_ppi = π
WaitForInputIdle(pi.hProcess, 10000);
th_label->Text = L"起動しました";
::WaitForSingleObject(pi.hProcess, INFINITE);
// エラー回避のための一時停止
Sleep(300);
th_label->Text = L"終了しました";
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
}
これで動作するはずです。
ちなみにWindows 32bit(Win32)の場合、Borlandコンパイラ(古い方)とClangコンパイラ(新しい方)がありますが、どちらでも動作すると思います。
プロジェクト->オプション->C++コンパイラ
の「従来のBorlandコンパイラを使用」のチェックで切り替えが出来ます。
書き漏らしがあれば随時書いていきます。
書き漏らしがあれば、ご指摘頂くと幸いです。