Qt 中带有进度回调的 CopyFileEx

2024-01-14

有人能给我一个在 Qt 中使用 CopyFileEx 和进度回调的工作示例吗?

我发现了一些划痕并尝试合并它但没有成功。我什至无法将 CopyProgressRoutine 函数作为 CopyFileEx 的参数传递,因为我无法声明指向该函数的指针。

我不太擅长从其他 IDE 移植代码,所以我需要你的帮助。


下面的代码是一个完整的、独立的示例。它可以在 Qt 5 和 Qt 4 下运行,并使用 C++11(例如 Visual Studio 2015 及更高版本)。

main.cpp

// https://github.com/KubaO/stackoverflown/tree/master/questions/copyfileex-19136936
#include <QtGui>
#include <QtConcurrent>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
#include <windows.h>
#include <comdef.h>
//#define _WIN32_WINNT _WIN32_WINNT_WIN7

static QString toString(HRESULT hr) {
   _com_error err{hr};
   return QStringLiteral("Error 0x%1: %2").arg((quint32)hr, 8, 16, QLatin1Char('0'))
         .arg(err.ErrorMessage());
}

static QString getLastErrorMsg() {
   return toString(HRESULT_FROM_WIN32(GetLastError()));
}

static QString progressMessage(ULONGLONG part, ULONGLONG whole) {
   return QStringLiteral("Transferred %1 of %2 bytes.")
         .arg(part).arg(whole);
}

class Copier : public QObject {
   Q_OBJECT

   BOOL m_stop;
   QMutex m_pauseMutex;
   QAtomicInt m_pause;
   QWaitCondition m_pauseWait;

   QString m_src, m_dst;
   ULONGLONG m_lastPart, m_lastWhole;
   void newStatus(ULONGLONG part, ULONGLONG whole) {
      if (part != m_lastPart || whole != m_lastWhole) {
         m_lastPart = part;
         m_lastWhole = whole;
         emit newStatus(progressMessage(part, whole));
      }
   }
#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
   static COPYFILE2_MESSAGE_ACTION CALLBACK copyProgress2(
         const COPYFILE2_MESSAGE *message, PVOID context);
#else
   static DWORD CALLBACK copyProgress(
         LARGE_INTEGER totalSize, LARGE_INTEGER totalTransferred,
         LARGE_INTEGER streamSize, LARGE_INTEGER streamTransferred,
         DWORD streamNo, DWORD callbackReason, HANDLE src, HANDLE dst,
         LPVOID data);
#endif
public:
   Copier(const QString & src, const QString & dst, QObject * parent = nullptr) :
      QObject{parent}, m_src{src}, m_dst{dst} {}
   Q_SIGNAL void newStatus(const QString &);
   Q_SIGNAL void finished();
   /// This method is thread-safe
   Q_SLOT void copy();
   /// This method is thread-safe
   Q_SLOT void stop() {
      resume();
      m_stop = TRUE;
   }
   /// This method is thread-safe
   Q_SLOT void pause() {
      m_pause = true;
   }
   /// This method is thread-safe
   Q_SLOT void resume() {
      if (m_pause)
         m_pauseWait.notify_one();
      m_pause = false;
   }
   ~Copier() override { stop(); }
};

#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
void Copier::copy() {
   m_lastPart = m_lastWhole = {};
   m_stop = FALSE;
   m_pause = false;
   QtConcurrent::run([this]{
      COPYFILE2_EXTENDED_PARAMETERS params{
         sizeof(COPYFILE2_EXTENDED_PARAMETERS), 0, &m_stop,
               Copier::copyProgress2, this
      };
      auto rc = CopyFile2((PCWSTR)m_src.utf16(), (PCWSTR)m_dst.utf16(), &params);
      if (!SUCCEEDED(rc))
         emit newStatus(toString(rc));
      emit finished();
   });
}
COPYFILE2_MESSAGE_ACTION CALLBACK Copier::copyProgress2(
      const COPYFILE2_MESSAGE *message, PVOID context)
{
   COPYFILE2_MESSAGE_ACTION action = COPYFILE2_PROGRESS_CONTINUE;
   auto self = static_cast<Copier*>(context);
   if (message->Type == COPYFILE2_CALLBACK_CHUNK_FINISHED) {
      auto &info = message->Info.ChunkFinished;
      self->newStatus(info.uliTotalBytesTransferred.QuadPart, info.uliTotalFileSize.QuadPart);
   }
   else if (message->Type == COPYFILE2_CALLBACK_ERROR) {
      auto &info = message->Info.Error;
      self->newStatus(info.uliTotalBytesTransferred.QuadPart, info.uliTotalFileSize.QuadPart);
      emit self->newStatus(toString(info.hrFailure));
      action = COPYFILE2_PROGRESS_CANCEL;
   }
   if (self->m_pause) {
      QMutexLocker lock{&self->m_pauseMutex};
      self->m_pauseWait.wait(&self->m_pauseMutex);
   }
   return action;
}
#else
void Copier::copy() {
   m_lastPart = m_lastWhole = {};
   m_stop = FALSE;
   m_pause = false;
   QtConcurrent::run([this]{
      auto rc = CopyFileExW((LPCWSTR)m_src.utf16(), (LPCWSTR)m_dst.utf16(),
                            &copyProgress, this, &m_stop, 0);
      if (!rc)
         emit newStatus(getLastErrorMsg());
      emit finished();
   });
}
DWORD CALLBACK Copier::copyProgress(
      const LARGE_INTEGER totalSize, const LARGE_INTEGER totalTransferred,
      LARGE_INTEGER, LARGE_INTEGER, DWORD,
      DWORD, HANDLE, HANDLE,
      LPVOID data)
{
   auto self = static_cast<Copier*>(data);
   self->newStatus(totalTransferred.QuadPart, totalSize.QuadPart);
   if (self->m_pause) {
      QMutexLocker lock{&self->m_pauseMutex};
      self->m_pauseWait.wait(&self->m_pauseMutex);
   }
   return PROGRESS_CONTINUE;
}
#endif

struct PathWidget : public QWidget {
   QHBoxLayout layout{this};
   QLineEdit edit;
   QPushButton select{"..."};
   QFileDialog dialog;
   explicit PathWidget(const QString & caption) : dialog{this, caption} {
      layout.setMargin(0);
      layout.addWidget(&edit);
      layout.addWidget(&select);
      connect(&select, SIGNAL(clicked()), &dialog, SLOT(show()));
      connect(&dialog, SIGNAL(fileSelected(QString)), &edit, SLOT(setText(QString)));
   }
};

class Ui : public QWidget {
   Q_OBJECT
   QFormLayout m_layout{this};
   QPlainTextEdit m_status;
   PathWidget m_src{"Source File"}, m_dst{"Destination File"};
   QPushButton m_copy{"Copy"};
   QPushButton m_cancel{"Cancel"};

   QStateMachine m_machine{this};
   QState s_stopped{&m_machine};
   QState s_copying{&m_machine};

   Q_SIGNAL void stopCopy();
   Q_SLOT void startCopy() {
      auto copier = new Copier(m_src.edit.text(), m_dst.edit.text(), this);
      connect(copier, SIGNAL(newStatus(QString)), &m_status, SLOT(appendPlainText(QString)));
      connect(copier, SIGNAL(finished()), SIGNAL(copyFinished()));
      connect(copier, SIGNAL(finished()), copier, SLOT(deleteLater()));
      connect(this, SIGNAL(stopCopy()), copier, SLOT(stop()));
      copier->copy();
   }
   Q_SIGNAL void copyFinished();
public:
   Ui() {
      m_layout.addRow("From:", &m_src);
      m_layout.addRow("To:", &m_dst);
      m_layout.addRow(&m_status);
      m_layout.addRow(&m_copy);
      m_layout.addRow(&m_cancel);

      m_src.dialog.setFileMode(QFileDialog::ExistingFile);
      m_dst.dialog.setAcceptMode(QFileDialog::AcceptSave);
      m_status.setReadOnly(true);
      m_status.setMaximumBlockCount(5);

      m_machine.setInitialState(&s_stopped);
      s_stopped.addTransition(&m_copy, SIGNAL(clicked()), &s_copying);
      s_stopped.assignProperty(&m_copy, "enabled", true);
      s_stopped.assignProperty(&m_cancel, "enabled", false);
      s_copying.addTransition(&m_cancel, SIGNAL(clicked()), &s_stopped);
      s_copying.addTransition(this, SIGNAL(copyFinished()), &s_stopped);
      connect(&s_copying, SIGNAL(entered()), SLOT(startCopy()));
      connect(&s_copying, SIGNAL(exited()), SIGNAL(stopCopy()));
      s_copying.assignProperty(&m_copy, "enabled", false);
      s_copying.assignProperty(&m_cancel, "enabled", true);
      m_machine.start();
   }
};

int main(int argc, char *argv[])
{
   QApplication a{argc, argv};
   Ui ui;
   ui.show();
   return a.exec();
}
#include "main.moc"
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt 中带有进度回调的 CopyFileEx 的相关文章

随机推荐

  • 从关系型 OLTP 数据库到 OLAP 多维数据集的最佳方法是什么?

    我有一个相当标准的 OLTP 标准化数据库 并且我意识到我需要在数据的不同维度上执行一些复杂的查询 平均值 标准差 所以我转向了 SSAS 和 OLAP 多维数据集的创建 然而 要创建多维数据集 我相信我的数据源结构需要采用 星形 或 雪花
  • 在 Outlook 中生成新消息并显示为模式

    我需要从应用程序使用 Outlook 生成并发送消息 邮件表单应显示为模式 主要是因为我生成附件 并且当用户发送电子邮件 或丢弃它 时应将其删除 我的问题是当我将 Outlook 对话框设置为模式 MailIt Display True 时
  • 如何在Javascript文件(.js)中使用Python Flask? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 当 Javascript 代码位于 HTML 文件中时 这不是问题 但是 当我将 script 标签的内容移至 js 文件以便在其他
  • 安装路径不可写R,无法更新包

    我正在尝试使用他们网站上的代码将 Bioconductor 安装到 R 中 当我输入代码 见下文 时 我收到一条错误消息 指出某些软件包无法更新 安装路径不可写 gt try http if https URLs are not suppo
  • 重命名 Kubernetes 中的部署

    If I do kubectl get deployments I get kubectl get deployments NAME DESIRED CURRENT UP TO DATE AVAILABLE AGE analytics re
  • 如何在 Django-rest-framework 序列化器中的关系模型中获取额外的列?

    I have Category and Article model Article有外键引用Category in my serializer由于 我可以在类别模型中获取名称列 str 方法 但我怎样才能得到其他列Category mode
  • 使用 Symfony2 / Symfony3 中的 FOSUserBundle 删除/替换用户名字段为电子邮件

    我只想使用电子邮件作为登录方式 我不想使用用户名 symfony2 symfony3 和 FOSUserbundle 可以吗 我在这里读到http groups google com group symfony2 browse thread
  • 不要扩展 React.Component

    创建 React 组件的 es6 语法是export default class ExampleComponent extends React Component 但是它仍然有效export default class ExampleCom
  • 如何为pytest命令指定几个标记

    Reading http doc pytest org en latest example markers html http doc pytest org en latest example markers html我看到了基于标记包含或
  • 限制 Java 中 HashMap 的最大大小

    我想限制 a 的最大尺寸HashMap对我正在实现的各种哈希算法进行度量 我查看了其中之一的负载因子HashMap的重载构造函数 HashMap int initialCapacity float loadFactor 我尝试在构造函数中将
  • 无法在 logback.xml 中使用 Spring 属性占位符

    我有一个使用 Logback 的 Spring Boot 控制台应用程序 所有属性 对于应用程序以及 Logback 都被外部化到类路径中的标准 application properties 文件中 这些属性可以在应用程序本身中很好地获取
  • 如何导入和使用.so文件

    请给我示例 代码 如何将 gccgo 生成的 so 文件导入到 main go 中 Go 尚不支持动态代码加载 即插件 有计划在未来添加支持 但目前尚未确定时间表 https docs google com document d 1nr T
  • 如何查看 numpy 广播对象?

    根据文档 http docs scipy org doc numpy user basics broadcasting html gt gt gt a np array 1 0 2 0 3 0 gt gt gt b np array 2 0
  • 正则表达式用于识别 Guid 后跟数字

    我有一个可以识别 Guid 的正则表达式 string pattern 0 9a fA F 8 0 9a fA F 4 0 9a fA F 4 0 9a fA F 4 0 9a fA F 12 我有一个可以识别数字的正则表达式 string
  • 如何在 Xamarin.Forms 中获取客户端设备的 MAC 地址?

    在我的客户端服务器解决方案中 我需要运行 Xamarin Forms 应用程序的前端设备的 MAC 地址 目前我只有一个条目 用户应该在其中输入运行应用程序的设备的 MAC 地址 主界面 我已经搜索过这个主题 发现iOS和Android对于
  • 如何将 AngularJS 变量传递给 Javascript?

    我正在构建一个带有模态窗口的 AngularJS Web 应用程序 在模态窗口中 我可以显示 JQuery Flot 实时图表 类似于 http people iola dk olau flot examples realtime html
  • 获取两个 NSDate 之间的准确时差?

    有什么办法可以找出两者之间的准确差异NSDate 我已经找到了解决方案 但它们不够准确 我需要考虑夏令时 不同月份有不同天数等事实 诸如 60 60 24 等计算分钟 小时和天的简单计算不会将它们考虑在内 假设我需要计算出现在时间之间的差异
  • 使用字典的 Protobuf-net 对象引用反序列化:引用跟踪对象在反序列化期间更改了引用

    我在尝试使用 protobuf net 序列化 反序列化复杂对象图时遇到一些问题 我正在开发一个遗留应用程序 我们正在使用 Net Remoting 将 GUI 客户端连接到 C 服务 由于使用默认值的对象图的序列化大小 我们发现海外用户的
  • 上传1GB或以上的文件

    上传大于 1 GB 的文件的最佳方式是什么 当前情况 我们正在使用 Flask 来部署网络服务器 我们必须上传大于 1 GB 的文件 以前我们只上传了几MB的文件 所以 这不是问题 但现在对于大于 1 GB 的文件 旧的上传方法不再可行 服
  • Qt 中带有进度回调的 CopyFileEx

    有人能给我一个在 Qt 中使用 CopyFileEx 和进度回调的工作示例吗 我发现了一些划痕并尝试合并它但没有成功 我什至无法将 CopyProgressRoutine 函数作为 CopyFileEx 的参数传递 因为我无法声明指向该函数