qt - Sådan får du en Windows HANDLE fra en QFile eller QSaveFile til ReOpenFile?

Indlæg af Hanne Mølgaard Plasc

Problem



Givet en QFileDevice instans (a QFile eller QSaveFile) - hvordan ville man få den oprindelige Windows HANDLE af filen? Og kan dette håndtag bruges sammen med ReOpenFile?

Bedste reference


QFileDevice::handle() returnerer en C-filbeskrivelse (fd - et lille heltal) opnået fra QFSFileEnginePrivate::nativeHandle. Denne filbeskrivelse er hvad du får fra \_open\_osfhandle. Du skal bruge \_get\_osfhandle til at gå tilbage til en HANDLE. [19] [20] [21]]] [22]


struct FdHandle {
  int fd = -1;
  HANDLE handle = INVALID\_HANDLE\_VALUE;
  operator HANDLE() const { return handle; }
  bool isFdValid() const { return fd != -1; }
  bool isHandleValid() const { return handle != INVALID\_HANDLE\_VALUE; }
};

FdHandle windowsHandleOf(QFileDevice* dev) {
  auto fd = dev->handle(); 
  return {fd, (HANDLE)\_get\_osfhandle(fd)};
}


ReOpenFile kan returnere et andet filhåndtag, og dermed skal du muligvis genåbne filen for den. En overbelastet ReOpenFile håndterer den: [23]


struct ReOpenResult {
  enum { OK = 0, FileClosed = 10, NoHandle = 20, 
         OldHandle = 30, OpenFailed = 40 } rc;
  HANDLE handle = INVALID\_HANDLE\_VALUE;
  operator HANDLE() const { return handle; }
  explicit operator bool() const { return rc == OK; }
  bool isHandleValid() const { return handle != INVALID\_HANDLE\_VALUE; }
};

ReOpenResult ReOpenFile(QFile *file, DWORD access, DWORD shareMode, DWORD flags) {
  if (!file->isOpen())
    return {ReOpenResult::FileClosed};
  auto oldHandle = windowsHandleOf(file);
  if (oldHandle == INVALID\_HANDLE\_VALUE)
    return {ReOpenResult::NoHandle};
  auto newHandle = ReOpenFile(oldHandle, access, shareMode, flags);
  if (newHandle == INVALID\_HANDLE\_VALUE)
    return {ReOpenResult::OldHandle, oldHandle};
  if (!open(file, file->openMode(), newHandle))
    return {ReOpenResult::OpenFailed, oldHandle};
  return {ReOpenResult::OK, newHandle};
}


Den 'manglende' open, der tager en HANDLE, er:


struct OpenResult {
  enum { OK = 0, SameHandleOK = 1, InvalidHandle = 10, CCloseFailed = 20,
         InvalidFd = 30, OpenFailed = 40 } rc = OK;
  explicit operator bool() const { return rc < InvalidHandle; };
};

OpenResult open(QFile* file, QIODevice::OpenMode mode, HANDLE handle) {
  if (handle == INVALID\_HANDLE\_VALUE)
    return {OpenResult::InvalidHandle};
  if (file->isOpen() && windowsHandleOf(file) == handle)
    return {OpenResult::SameHandleOK};
  file->close();
  if (auto fd = file->handle() != -1)
    if (\_close(fd)) // the C handle is still open, don't leak it
      return {OpenResult::CCloseFailed};
  int flags = 0;
  if (mode & QIODevice::Append)
    flags |= \_O\_APPEND;
  if (!(mode & QIODevice::WriteOnly))
    flags |= \_O\_RDONLY;
  auto fd = \_open\_osfhandle((intptr\_t)handle, flags);
  if (fd == -1)
    return {OpenResult::InvalidFd};
  if (!file->open(fd, mode, QFileDevice::AutoCloseHandle))
    return {OpenResult::OpenFailed};
  return {};
}