C++判断QMetaObject::invokeMethod()里的函数是否调用成功

 更新时间:2025年07月16日 08:49:36   作者:sanqima  
本文详细介绍里的invokeMethod()函数的2种调用方式,包括同步调用、异步调用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

今天,在Qt编程,碰到一个需要使用invokeMethod方式来获取函数是否执行成功的情况。

invokeMethod()即可以同步调用,也可以异步调用。若调用者、被调用者,都在同一个线程,则是同步调用;若调用者、被调用者,在不同线程,则是异步调用。

注意:只有同步调用,才能通过invokeMethod()的返回值,来判断函数是否执行成功

比如,有如下精简代码:

//1)业务代码
class ComWork : public QObject {
 Q_OBJECT
public:
	ComWork();
	
signals:
   void sigSendResult(bool bOK);

public slots:
    void SendCommand(QByteArray by) {
    	qint64 nRet = m_pSerial->write(by);
    	bool bOK = (nRet != -1);
    	if(bOK)
    	   m_pSerial->flush();
    	emit sigSendResult(bOK);
    } 

private:
   QSerialPort* m_pSerial;
};

//2) 界面逻辑代码
class ZoomWidget : public QWidget {
public:
	ZoomWidget (QWidget *parent= NULL);

protected slots:
    void OnRecvCmdResult(bool bOK);

private:
    QThread* m_pThread; //子线程
    ComWork* m_pWork;
    bool     m_bResult;
};


ZoomWidget ::ZoomWidget (QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);

    m_pThread = new QThread(this);
    m_pWork   = new ComWork();
    m_pWork->moveToThread(m_pThread); //m_pWork移动到子线程里,语句(a)

    //连接信号,语句(b)
    connect(m_pWork,&m_pWork::sigSendResult,this,ZoomWidget::OnRecvCmdResult);
}

ZoomWidget::OnRecvCmdResult(bool bOK)
{
	m_bResult = bOK; //语句(c)
	qDebug()<<"recv status:"<<bOK;
}

// 3)核心调用
ZoomWidget::DoSend()
{
    //进行调用,语句(d)
	QMetaObject::invokeMethod(m_pWork, "SendCommand", Qt::QueuedConnection, Q_ARG(QByteArray, sendData));
}

在Qt中,UI对象必须在主线程。而ZoomWidget是一个QWidtget,属于UI对象,即ZoomWidget在主线程。

由语句(a)可知,m_pWork移动到了子线程里,即ZoomWidget与m_pWork不在同一个线程,
则在调用DoSend()函数时,QMetaObject::invokeMethod(m_pWork,…)其实是跨线程调用,也就是异步调用。而异步调用,由于"它不会等待函数是否执行,就直接返回了",即invokeMethod(,…Qt::QueuedConnection ,)函数,不会等待m_pWork的SendCommand()是否执行,就直接返回,并给出一个返回值,该返回值是无效的。

如果强制返回,Qt编译也会报"QMetaMethod::invoke: Unable to invoke methods with return values in queued connections"错误。

但可以通过信号槽的方式,把异步调用的结果(比如语句(b)、语句©),返回给调用者(比如本例的ZoomWidget)。

这个 "Unable to invoke methods"的Qt 编译报错:

QMetaMethod::invoke: Unable to invoke methods with return values in queued connections

是一个非常常见的运行时错误,它的含义是:

你试图使用 Qt::QueuedConnection(即异步方式)调用一个带有返回值的方法,而这是 Qt 不支持的。

错误原因详解

什么是Qt::QueuedConnection?

  • 它表示方法调用会在目标对象所在的线程中排队执行(异步调用),而不是立即执行。
  • 常用于跨线程通信,比如从子线程调用主线程的 UI 方法。

为什么不能在Qt::QueuedConnection中使用带返回值的函数?

  • 因为它是异步调用,调用者不会等待函数执行完成。
  • 所以无法通过 Q_RETURN_ARG(...) 拿到函数的返回值。
  • Qt 在底层会检测这一点,并抛出警告或导致返回值无效。

示例代码:错误写法

int result = 0;
bool success = QMetaObject::invokeMethod(obj, "addNumbers",
                                         Qt::QueuedConnection, // ❌ 异步调用
                                         Q_RETURN_ARG(int, result),
                                         Q_ARG(int, 3),
                                         Q_ARG(int, 5));

上面这段代码会导致报错:

QMetaMethod::invoke: Unable to invoke methods with return values in queued connections

正确做法

✅ 方法一:使用Qt::DirectConnection(同步调用)

如果你需要获取返回值,请使用同步连接方式:

int result = 0;
bool success = QMetaObject::invokeMethod(obj, "addNumbers",
                                         Qt::DirectConnection, // ✅ 同步调用
                                         Q_RETURN_ARG(int, result),
                                         Q_ARG(int, 3),
                                         Q_ARG(int, 5));

if (success) {
    qDebug() << "Result:" << result;
}

⚠️ 注意:Qt::DirectConnection 要求调用线程和目标对象处于同一个线程,否则行为未定义。

✅ 方法二:避免返回值 + 使用信号传递结果(适合异步场景)

如果你确实需要跨线程调用并想获取结果,可以这样做:

步骤如下:

  1. 将原函数改为无返回值;
  2. 使用 QMetaObject::invokeMethod() 调用它;
  3. 函数内部处理完后,发出一个信号把结果传回来。

示例代码:

class Worker : public QObject {
    Q_OBJECT

signals:
    void resultReady(int result); // 用于返回结果

public slots:
    void addNumbersAsync(int a, int b) {
        int result = a + b;
        emit resultReady(result); // 发送结果
    }
};
调用方式:
Worker* worker = new Worker();
worker->moveToThread(thread);

// 连接信号与槽来接收结果
connect(worker, &Worker::resultReady, this, [](int res) {
    qDebug() << "异步返回结果:" << res;
});

// 异步调用
QMetaObject::invokeMethod(worker, "addNumbersAsync",
                          Qt::QueuedConnection,
                          Q_ARG(int, 3),
                          Q_ARG(int, 5));

总结1

场景是否允许返回值推荐连接方式备注
同一线程调用,需返回值✅ 是Qt::DirectConnection可使用 Q_RETURN_ARG
跨线程调用,需返回值❌ 否❌ 不可用必须用信号传递结果
跨线程调用,不需要返回值✅ 是Qt::QueuedConnection正常使用

QMetaObject::invokeMethod()的返回值类型

bool QMetaObject::invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, ...)

返回值说明:

  • 返回类型:bool
  • 返回值含义
    • true:表示方法成功调用。
    • false:表示方法调用失败,可能原因包括:
      • 没有找到名字匹配的方法(函数名错误或未声明为 Q_INVOKABLE 或 slot)。
      • 参数类型不匹配。
      • 对象已经被删除(悬空指针)。
      • 使用了 Qt::QueuedConnection 但目标对象没有运行事件循环。

如何进一步排查失败原因?

虽然返回值只能告诉你是否成功,但你可以通过以下方式定位问题:

1. 检查函数是否被正确声明为Q_INVOKABLE或slot

class Worker : public QObject {
    Q_OBJECT

public slots:
    void OpenPort(const QString &portName, int baudRate); // 必须匹配参数类型
};

或者:

Q_INVOKABLE void OpenPort(const QString &portName, int baudRate);

2. 确保对象没有被释放(避免悬空指针)

确保 m_pWork 是一个有效的 QObject* 指针,且对象尚未被 delete

3. 参数类型必须一致(支持元对象系统)

确保你使用的参数类型是 Qt 元对象系统支持的类型(如 int, QString, double 等),或者自定义类型已注册:

Q_DECLARE_METATYPE(MyCustomType)
qRegisterMetaType<MyCustomType>();

4. 调试输出所有可用方法(用于排查函数名/参数是否正确)

const QMetaObject* metaObj = m_pWork->metaObject();
for (int i = 0; i < metaObj->methodCount(); ++i) {
    qDebug() << metaObj->method(i).signature();
}

总结2

内容说明
invokeMethod() 是否有返回值?✅ 有,返回 bool 类型
true 表示什么?方法调用成功
false 表示什么?方法调用失败(函数名错误、参数不匹配、对象无效、异步调用等)
可以用来做什么?判断函数是否被成功调用,用于调试和错误处理

到此这篇关于判断QMetaObject::invokeMethod()里的函数是否调用成功的文章就介绍到这了,更多相关QMetaObject::invokeMethod()调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • C语言实现Fibonacci数列递归

    C语言实现Fibonacci数列递归

    这篇文章主要介绍了C语言实现Fibonacci数列递归,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • C语言sizeof和strlen区别小结

    C语言sizeof和strlen区别小结

    C语言中的sizeof和strlen是两个常用的操作符/函数,但它们的功能和用途有很大的区别,本文就详细的来介绍一下C语言sizeof和strlen区别,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • C++变量存储的生命周期与作用域实例代码精讲

    C++变量存储的生命周期与作用域实例代码精讲

    这篇文章主要介绍了C++变量存储的生命周期与作用域,从创建到消亡的完整过程,例如人从出生到死亡的整个过程就是一个生命周期。本文将通过示例为大家详细讲讲,感兴趣的可以学习一下
    2022-10-10
  • C语言在头文件中定义const变量详解

    C语言在头文件中定义const变量详解

    这篇文章主要介绍了C语言在头文件中定义const变量详解的相关资料,需要的朋友可以参考下
    2017-05-05
  • C语言图书借阅系统源码

    C语言图书借阅系统源码

    这篇文章主要为大家分享了C语言图书借阅系统源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • VC++ 自定义控件的建立及使用方法

    VC++ 自定义控件的建立及使用方法

    这篇文章主要介绍了VC++ 自定义控件的建立及使用方法的相关资料,十分的详细,需要的朋友可以参考下
    2015-06-06
  • c语言中assert断言用法实例详解

    c语言中assert断言用法实例详解

    断言是C语言中一种用于检查程序中假设语句正确性的方法,通过使用断言,开发人员可以在程序中插入一些条件,以确保程序的执行满足特定的预期,这篇文章主要给大家介绍了关于c语言中assert断言用法的相关资料,需要的朋友可以参考下
    2024-02-02
  • Qt的Qss用法小结

    Qt的Qss用法小结

    Qt的Qss是一种用于定义用户界面的样式表语言,本文主要介绍了Qt的Qss用法小结,非常具有实用价值,需要的朋友可以参考下
    2023-06-06
  • VC++ 2019 "const char*"类型的实参与"LPCTSTR"类型的形参不兼容解决

    VC++ 2019 "const char*"类型的实参与"LPCTSTR"

    这篇文章主要给大家介绍了关于VC++ 2019 "const char*"类型的实参与"LPCTSTR"类型的形参不兼容的解决方法,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2023-03-03
  • c++输出斐波那契数列示例分享

    c++输出斐波那契数列示例分享

    这篇文章主要介绍了c++输出斐波那契数列示例,需要的朋友可以参考下
    2014-03-03

最新评论