Introduction

備忘録。
C++/C#の連携で、C++アプリから起動したC#アプリを、C++アプリから正常終了させる方法。
基本は、

  1. CreateProcessの戻りのプロセスIDを保持
  2. 終了させる際、プロセスIDからウィンドウハンドルを取得
  3. PostMessageでWM_CLOSEを送る

です。

ソースは下記になります

Demo

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "stdafx.h"

HWND GetWindowHandle(const DWORD target_id)
{
auto hWnd = GetTopWindow(nullptr);
do {
if (GetWindowLong(hWnd, GWLP_HWNDPARENT) != 0 || !IsWindowVisible(hWnd))
continue;

DWORD process_id;
GetWindowThreadProcessId(hWnd, &process_id);
if (target_id == process_id)
return hWnd;
} while ((hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)) != nullptr);

return nullptr;
}

int main()
{
// Launch application
STARTUPINFO si;
PROCESS_INFORMATION pi;
::ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
::CreateProcessW(L"Wpf.exe", nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);

_tprintf(_T("Terminate application: %d\n"), pi.dwProcessId);

// Wait until application completely starts to create window handle
Sleep(1000);
auto handle = GetWindowHandle(pi.dwProcessId);
::PostMessage(handle, WM_CLOSE, 0, 0);

// Terminate forcibly if application does not quit in 5sec.
if (::WaitForSingleObject(pi.hProcess, 5000) == WAIT_TIMEOUT)
{
_tprintf(_T("Terminate forcibly"));
TerminateProcess(pi.hProcess, 0);
}

::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);

return 0;
}

GetWindowHandleで、プロセスIDからウィンドウハンドルを探し出しているのが肝です。
そして、このサンプルではプロセス起動後、Sleepで少し待機しています。
そうしないと、ウィンドウハンドルが取れないので…
コメントにも書いたように、おそらくウィンドウハンドルが生成され切っていないのかと。

C#

WPFのサンプルです。
コードビハインドですが、気にしません。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using System.Windows;
using System.Windows.Interop;

namespace Wpf
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{

public MainWindow()
{
InitializeComponent();
this.Loaded += this.WindowLoaded;
}

private void WindowLoaded(object sender, RoutedEventArgs e)
{
var source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source?.AddHook(WndProc);
}

private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
const int WM_CLOSE = 0x0010;
if (msg == WM_CLOSE)
{
MessageBox.Show("Get WM_CLOSE");
//handled = true;
}

return IntPtr.Zero;
}

}
}

Source Code

https://github.com/takuya-takeuchi/Demo/tree/master/Misc/03_KillProcessSafetyByHWND