bool CreateServiceTerminationThread()

in minifi_main/MiNiFiWindowsService.cpp [258:374]


bool CreateServiceTerminationThread(std::shared_ptr<minifi::core::logging::Logger> logger, HANDLE terminationEventHandle) {
  // Get hService and monitor it - if service is terminated, then terminate current exe, otherwise the exe becomes unmanageable when service is restarted.
  auto hService = [&logger]() -> HANDLE {
    auto hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hSnapShot) {
      logger->log_error("!CreateToolhelp32Snapshot lastError {:#x}", GetLastError());
      return 0;
    }

    auto getProcessInfo = [&logger, &hSnapShot](DWORD processId, DWORD& parentProcessId, std::string& parentProcessName) {
      parentProcessId = 0;
      parentProcessName.clear();

      PROCESSENTRY32 procentry{};
      procentry.dwSize = sizeof(procentry);

      if (!Process32First(hSnapShot, &procentry)) {
        logger->log_error("!Process32First lastError {:#x}", GetLastError());
        return;
      }

      do {
        if (processId == procentry.th32ProcessID) {
          parentProcessId = procentry.th32ParentProcessID;
          parentProcessName = procentry.szExeFile;
          return;
        }
      } while (Process32Next(hSnapShot, &procentry));
    };

    // Find current process info, which contains parentProcessId.
    DWORD parentProcessId{};
    std::string parentProcessName;
    getProcessInfo(GetCurrentProcessId(), parentProcessId, parentProcessName);

    // Find parent process info (the service which started current process), which contains service name.
    DWORD parentParentProcessId{};
    getProcessInfo(parentProcessId, parentParentProcessId, parentProcessName);

    CloseHandle(hSnapShot);

    // Just in case check that service name == current process name.
    char filePath[MAX_PATH];
    if (!GetModuleFileName(0, filePath, _countof(filePath))) {
      logger->log_error("!GetModuleFileName lastError {:#x}", GetLastError());
      return 0;
    }

    const auto pSlash = strrchr(filePath, '\\');
    if (!pSlash) {
      logger->log_error("Invalid filePath {}", filePath);
      return 0;
    }
    const std::string fileName = pSlash + 1;

    if (_stricmp(fileName.c_str(), parentProcessName.c_str())) {
      logger->log_error("Parent process {} != current process {}", parentProcessName.c_str(), fileName.c_str());
      return 0;
    }

    const auto hParentProcess = OpenProcess(SYNCHRONIZE, FALSE, parentProcessId);
    if (!hParentProcess) {
      logger->log_error("!OpenProcess lastError {:#x}", GetLastError());
      return 0;
    }

    return hParentProcess;
  }();
  if (!hService)
    return false;

  using ThreadInfo = std::tuple<std::shared_ptr<minifi::core::logging::Logger>, HANDLE, HANDLE>;
  auto pThreadInfo = new ThreadInfo(logger, terminationEventHandle, hService);

  HANDLE hThread = (HANDLE)_beginthreadex(
    0, 0,
    [](void* pPar) {
      const auto pThreadInfo = static_cast<ThreadInfo*>(pPar);
      const auto logger = std::get<0>(*pThreadInfo);
      const auto terminationEventHandle = std::get<1>(*pThreadInfo);
      const auto hService = std::get<2>(*pThreadInfo);
      delete pThreadInfo;

      HANDLE arHandle[] = { terminationEventHandle, hService };
      switch (auto res = WaitForMultipleObjects(_countof(arHandle), arHandle, FALSE, INFINITE)) {
        case WAIT_FAILED:
          logger->log_error("!WaitForSingleObject lastError {:#x}", GetLastError());
        break;

        case WAIT_OBJECT_0:
          logger->log_info("Service event received");
        break;

        case WAIT_OBJECT_0 + 1:
          logger->log_info("Service is terminated");
        break;

        default:
          logger->log_info("WaitForMultipleObjects return {}", res);
      }

      SignalExitProcess();

      return 0U;
    },
    pThreadInfo,
    0, 0);
  if (!hThread) {
    logger->log_error("!_beginthreadex lastError {:#x}", GetLastError());

    delete pThreadInfo;

    return false;
  }

  return true;
}