【发布时间】:2017-10-25 08:04:06
【问题描述】:
目标是将 QtCreator 中显示的所有应用程序输出显示为 QTextEdit 作为调试控制台窗口,因此对于相同的应用程序,只有拥有密码的人才能看到控制台窗口,而普通用户则不能。有一个带有几个 dll 的 exe。所有来自 DLL 和 qDebug 的 std::cout 都需要显示在调试控制台窗口中。
为了实现这一点,我遵循了 http://www.qtforum.org/article/39768/redirecting-std-cout-std-cerf-qdebug-to-qtextedit.html
该代码对单线程非常有效,但在启动线程调用 DLL 中的函数时会挂起。我想知道如何解决这个问题。
代码
在 mainwindow.cpp 中
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QObject>
#include <QThread>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_qd = new Q_DebugStream(std::cout,ui->textEdit); //Redirect Console output to QTextEdit
m_qd->Q_DebugStream::registerQDebugMessageHandler(); //Redirect qDebug() output to QTextEdit
connect(ui->pushButtonSingleThreadTest, SIGNAL(clicked()),this,SLOT(SingleThreadTest()));
connect(ui->pushButtonMultiThreadTest, SIGNAL(clicked()),this,SLOT(MultiThreadTest()));
}
void MainWindow::SingleThreadTest()
{
run();
}
void MainWindow::MultiThreadTest()
{
QThread *workerThread= new QThread;
ThreadWorker *worker = new ThreadWorker();
worker->moveToThread(workerThread);
connect(workerThread, SIGNAL(started()), worker, SLOT(doWork()));
connect(worker, SIGNAL(finished()), workerThread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
workerThread->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
在 mainwindow.h 中
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "q_debugstream.h"
#include "../ToyProj1/Header.h"
namespace Ui {
class MainWindow;
}
class ThreadWorker : public QObject
{
Q_OBJECT
public:
ThreadWorker()
{
}
private:
signals:
void finished();
public slots:
void doWork()
{
run();
emit finished();
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void SingleThreadTest();
void MultiThreadTest();
private:
Ui::MainWindow *ui;
Q_DebugStream* m_qd;
};
#endif // MAINWINDOW_H
在 q_debugstream.h 中
#ifndef Q_DEBUGSTREAM_H
#define Q_DEBUGSTREAM_H
#include <iostream>
#include <streambuf>
#include <string>
#include "QTextEdit.h"
class Q_DebugStream : public std::basic_streambuf<char>
{
public:
Q_DebugStream(std::ostream &stream, QTextEdit* text_edit) : m_stream(stream)
{
log_window = text_edit;
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
}
~Q_DebugStream()
{
m_stream.rdbuf(m_old_buf);
}
static void registerQDebugMessageHandler(){
qInstallMessageHandler(myQDebugMessageHandler);
}
private:
static void myQDebugMessageHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
{
std::cout << msg.toStdString().c_str();
}
protected:
//This is called when a std::endl has been inserted into the stream
virtual int_type overflow(int_type v)
{
if (v == '\n')
{
log_window->append("");
}
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n)
{
QString str(p);
if(str.contains("\n")){
QStringList strSplitted = str.split("\n");
log_window->moveCursor (QTextCursor::End);
log_window->insertPlainText (strSplitted.at(0)); //Index 0 is still on the same old line
for(int i = 1; i < strSplitted.size(); i++){
log_window->append(strSplitted.at(i));
}
}else{
log_window->moveCursor (QTextCursor::End);
log_window->insertPlainText (str);
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
QTextEdit* log_window;
};
#endif // Q_DEBUGSTREAM_H
在 DLL 中,
int run()
{
std::cout << "Hello World" << std::endl;
return 0;
}
代码示例上传到github供参考。重复问题时构建 ToyProj1 和 ToyProj1GUI。
【问题讨论】:
-
exe 没有“连接”到 DLL。一旦加载,DLL 代码实际上就是可执行代码的一部分。您通常无法检测调用是来自 DLL 函数还是来自主程序函数。
-
感谢您的澄清。我会修改我的措辞。你的意思是问题不在于 dll 而是多线程?
-
@Cheersandhth.-Alf 你是对的。我认为DLL是问题的一部分。事实上并非如此。
-
这里的问题是你的 QTextEdit,Qt 应用程序 GUI 在主线程中运行,但是如果每个线程都用 std::cout 写一些东西,每个线程都会尝试在你的 QTextEdit 中写入你的主线程线程。
-
您有多种选择,一种是使用信号槽在主线程内重定向 std::out 的每次调用,另一种是使用互斥锁通过 std::out 阻止和释放您的写入