std::shared_ptr<FileWatch> FileMonitorWin::Watch(const std::filesystem::path& path, const t_callbackFunc& callback, FileWatch::State states) {
  try {
    if(!std::filesystem::exists(path))
      return nullptr;
  } catch (...) {
    return nullptr;
  }
  std::filesystem::path directory = (std::filesystem::is_directory(path) || !path.has_parent_path()) ? path : path.parent_path();

  // Ugh.
  std::wstring wpath;
#if _MSC_VER >= 1900
  wpath = directory.wstring();
#else
  auto str = directory.string();
  wpath.assign(str.begin(), str.end());
#endif

  // Open the handle to the directory we were asked to watch with the
  // correct permissions so that we can actually conduct asynchronous
  // read operations.
  HANDLE hFile = CreateFileW(
    wpath.c_str(),
    GENERIC_READ,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    nullptr,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS,
    nullptr
  );
  if (hFile == INVALID_HANDLE_VALUE)
    // No such file, what else can we do?
    return nullptr;

  // Compose the notification filter based on the user's request
  DWORD dwNotifyFilter = 0;
  if(states & FileWatch::State::RENAMED || states & FileWatch::State::DELETED)
    dwNotifyFilter |= FILE_NOTIFY_CHANGE_FILE_NAME;
  if(states & FileWatch::State::MODIFIED)
    dwNotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;

  // Need a second-order shared pointer so we can preserve references
  Entry* pEntry;
  auto key = m_nextKey++;
  {
    std::lock_guard<std::mutex> lk(GetLock());
    pEntry = &m_outstanding[key];
    pEntry->key = key;
  }

  // Need an overlapped structure to track this operation.  We'll also be using this to decide
  // how to notify the true caller.
  auto watcher = std::shared_ptr<FileWatchWin>(
    new FileWatchWin(path, callback, dwNotifyFilter, hFile),
    [this, key](FileWatchWin* fileWatch) {
      --m_numWatchers;
      delete fileWatch;
      m_outstanding.erase(key);
    }
  );
  pEntry->watcherWeak = watcher;

  // Attach to the completion port with the FileWatchWin we just constructed
  if (!CreateIoCompletionPort(hFile, m_hCompletion, key, 0)) {
    // Something went wrong, can't attach a watcher at this point
    m_outstanding.erase(key);
    return nullptr;
  }

  // Initial pend, and then return to the controller
  watcher->ReadDirectoryChanges();
  ++m_numWatchers;
  return watcher;
}
// The processPath ends with dotnet.exe or dotnet
// like: C:\Program Files\dotnet\dotnet.exe, C:\Program Files\dotnet\dotnet, dotnet.exe, or dotnet.
// Get the absolute path to dotnet. If the path is already an absolute path, it will return that path
fs::path
HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
     const fs::path & applicationPath,
     const fs::path & requestedPath
)
{
    LOG_INFOF(L"Resolving absolute path to dotnet.exe from '%ls'", requestedPath.c_str());

    auto processPath = requestedPath;
    if (processPath.is_relative())
    {
        processPath = applicationPath / processPath;
    }

    //
    // If we are given an absolute path to dotnet.exe, we are done
    //
    if (is_regular_file(processPath))
    {
        LOG_INFOF(L"Found dotnet.exe at '%ls'", processPath.c_str());

        return processPath;
    }

    // At this point, we are calling where.exe to find dotnet.
    // If we encounter any failures, try getting dotnet.exe from the
    // backup location.
    // Only do it if no path is specified
    if (requestedPath.has_parent_path())
    {
        LOG_INFOF(L"Absolute path to dotnet.exe was not found at '%ls'", requestedPath.c_str());

        throw InvalidOperationException(format(L"Could not find dotnet.exe at '%s'", processPath.c_str()));
    }

    const auto dotnetViaWhere = InvokeWhereToFindDotnet();
    if (dotnetViaWhere.has_value())
    {
        LOG_INFOF(L"Found dotnet.exe via where.exe invocation at '%ls'", dotnetViaWhere.value().c_str());

        return dotnetViaWhere.value();
    }

    const auto programFilesLocation = GetAbsolutePathToDotnetFromProgramFiles();
    if (programFilesLocation.has_value())
    {
        LOG_INFOF(L"Found dotnet.exe in Program Files at '%ls'", programFilesLocation.value().c_str());

        return programFilesLocation.value();
    }

    LOG_INFOF(L"dotnet.exe not found");
    throw InvalidOperationException(format(
        L"Could not find dotnet.exe at '%s' or using the system PATH environment variable."
        " Check that a valid path to dotnet is on the PATH and the bitness of dotnet matches the bitness of the IIS worker process.",
        processPath.c_str()));
}