加载 DLL 是否会动态地将其 stderr 与主应用程序协调一致?如果是这样,那么如何...?

发布于 2024-09-08 07:37:01 字数 3361 浏览 5 评论 0原文

我正在使用 Qt 编写一个 GUI 应用程序,它链接到有时会向 stderr 发送错误消息的第三方 DLL。我希望这些错误消息显示在 GUI 内的窗口中。

即使经过多次搜索,我也找不到重定向 stderr (而不是 std::cerr)的既定方法,因此我自己编写了以下类:

class StdErrRedirect : public QObject
{
    Q_OBJECT

public:
    // Constructor
    StdErrRedirect(QTextEdit *errorLog,
                   QObject   *parent = NULL);

    // Destructor
    ~StdErrRedirect();

private slots:
    void fileChanged(const QString &filename);

private:    
    QFile              tmp;
    QFileSystemWatcher watcher;
    QString            tmpFileNameQtFormat;
    QString            tmpFileNameNativeFormat;

    QTextEdit *m_errorLog;
    QString   oldContent;
};

StdErrRedirect::StdErrRedirect(QTextEdit *errorLog,
                               QObject   *parent)
    : QObject(parent)
{
    // Store the pointer to the error log window
    m_errorLog = errorLog;

    // Create a temporary filename: first find the path:
    tmpFileNameQtFormat = QDir::tempPath();

    // Make sure the closing slash is present:
    if (!tmpFileNameQtFormat.endsWith(QChar('/')))
        tmpFileNameQtFormat.append(QChar('/'));

    // Add the file name itself:
    tmpFileNameQtFormat.append("nb_stderrlog");

    // Obtain a version of the filename in the operating system's native format:
    tmpFileNameNativeFormat = QDir::toNativeSeparators(tmpFileNameQtFormat);

    // Set up redirection to this file:
    freopen(tmpFileNameNativeFormat.toAscii().constData(), "a+", stderr);

    // Initialise the QFileSystemWatcher:
    connect(&watcher, SIGNAL(fileChanged(const QString &)),
            this,     SLOT(fileChanged(const QString &)));
    watcher.addPath(tmpFileNameQtFormat);

    tmp.setFileName(tmpFileNameQtFormat);
}

StdErrRedirect::~StdErrRedirect()
{
    // Ensure the temporary file is properly deleted:
    fclose(stderr);
    tmp.close();
    tmp.open(QIODevice::ReadWrite);
    tmp.remove();
}

void StdErrRedirect::fileChanged(const QString &filename)
{
    tmp.open(QIODevice::ReadOnly);
    QTextStream stream(&tmp);
    QString content = stream.readAll();
    tmp.close();

    // Identify what's new, and just send this to the window:
    int newchars = content.size() - oldContent.size();
    if (newchars)
    {
        m_errorLog -> append(content.right(newchars));
        oldContent = content;
    }
}

如果我使用以下命令从主窗口实例化它:

errorLog = new QTextEdit;
redirector = new StdErrRedirect(errorLog);

... 那么我写的所有内容to stderr 出现在窗口中。

到目前为止,一切都很好。问题是 DLL 的输出仍然没有。在调用发出错误的 DLL 函数时,如果我输入以下代码:

if (error != _OK)
{
    error.PrintErrorTrace();
    fprintf(stderr, "Should have printed an error \r\n");
    fflush(stderr);
    //fsync(_fileno(stderr));    Linux version
    _commit(_fileno(stderr));
    return;
}

...则出现文本“应该打印错误”,但错误消息本身不会出现。

现在,我在某处读到这可能是因为在应用程序开始加载 DLL 之后设置了重定向,因此它自己的 stderr 通道不受影响。因此,我应该能够通过动态加载DLL来解决这个问题,设置重定向之后。

那么我的问题是:我该怎么做?我可以尝试将以下代码放在应用程序的开头:

QLibrary extlib;
extlib.setFileName("libname");
extlib.setLoadHints(QLibrary::ResolveAllSymbolsHint);
extlib.load();

...但它本身没有任何效果。我认为这是因为链接器仍在将库设置为自动打开。但是,如果我从链接器中删除 DLL(我使用的是 VS2008,因此我从依赖项列表中删除 extlib.lib),则应用程序将无法编译,因为编译器无法从 DLL 中找到符号。

因此,我在这里尝试做的事情显然存在严重错误。有人可以帮忙吗?

谢谢, 史蒂芬.

I'm writing a GUI application, using Qt, which links to a third-party DLL that sometimes sends error messages to stderr. I'd like these error messages to be displayed in a window within my GUI.

I couldn't find an established way to redirect stderr (as opposed to std::cerr) even after much searching, so I wrote the following class myself:

class StdErrRedirect : public QObject
{
    Q_OBJECT

public:
    // Constructor
    StdErrRedirect(QTextEdit *errorLog,
                   QObject   *parent = NULL);

    // Destructor
    ~StdErrRedirect();

private slots:
    void fileChanged(const QString &filename);

private:    
    QFile              tmp;
    QFileSystemWatcher watcher;
    QString            tmpFileNameQtFormat;
    QString            tmpFileNameNativeFormat;

    QTextEdit *m_errorLog;
    QString   oldContent;
};

StdErrRedirect::StdErrRedirect(QTextEdit *errorLog,
                               QObject   *parent)
    : QObject(parent)
{
    // Store the pointer to the error log window
    m_errorLog = errorLog;

    // Create a temporary filename: first find the path:
    tmpFileNameQtFormat = QDir::tempPath();

    // Make sure the closing slash is present:
    if (!tmpFileNameQtFormat.endsWith(QChar('/')))
        tmpFileNameQtFormat.append(QChar('/'));

    // Add the file name itself:
    tmpFileNameQtFormat.append("nb_stderrlog");

    // Obtain a version of the filename in the operating system's native format:
    tmpFileNameNativeFormat = QDir::toNativeSeparators(tmpFileNameQtFormat);

    // Set up redirection to this file:
    freopen(tmpFileNameNativeFormat.toAscii().constData(), "a+", stderr);

    // Initialise the QFileSystemWatcher:
    connect(&watcher, SIGNAL(fileChanged(const QString &)),
            this,     SLOT(fileChanged(const QString &)));
    watcher.addPath(tmpFileNameQtFormat);

    tmp.setFileName(tmpFileNameQtFormat);
}

StdErrRedirect::~StdErrRedirect()
{
    // Ensure the temporary file is properly deleted:
    fclose(stderr);
    tmp.close();
    tmp.open(QIODevice::ReadWrite);
    tmp.remove();
}

void StdErrRedirect::fileChanged(const QString &filename)
{
    tmp.open(QIODevice::ReadOnly);
    QTextStream stream(&tmp);
    QString content = stream.readAll();
    tmp.close();

    // Identify what's new, and just send this to the window:
    int newchars = content.size() - oldContent.size();
    if (newchars)
    {
        m_errorLog -> append(content.right(newchars));
        oldContent = content;
    }
}

If I instantiate this from my main window using:

errorLog = new QTextEdit;
redirector = new StdErrRedirect(errorLog);

... then everything I write to stderr appears in the window.

So far, so good. The problem is that the DLL's output still does not. In a call to a DLL function which emits an error, if I put the code:

if (error != _OK)
{
    error.PrintErrorTrace();
    fprintf(stderr, "Should have printed an error \r\n");
    fflush(stderr);
    //fsync(_fileno(stderr));    Linux version
    _commit(_fileno(stderr));
    return;
}

...then the text "Should have printed an error" appears but the error message itself does not.

Now, I've read somewhere that this is probably because the redirection is being set up after the DLL was loaded at the beginning of the application, and so it's own stderr channel is unaffected. Therefore, I should be able to fix this by loading the DLL dynamically, after setting up the redirection, instead.

Here is my question, then: how do I do this? I can try putting the following code at the beginning of my application:

QLibrary extlib;
extlib.setFileName("libname");
extlib.setLoadHints(QLibrary::ResolveAllSymbolsHint);
extlib.load();

...but on its own it has no effect. I think this is because the linker is still setting the library up to be opened automatically. However, if I remove the DLL from the linker (I'm using VS2008, so I remove extlib.lib from the dependency list) then the application won't compile because the compiler can't find the symbols from the DLL.

So there's obviously something deeply wrong with what I'm trying to do here. Can anybody help?

Thanks,
Stephen.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

燕归巢 2024-09-15 07:37:01

DLL 真的会写入 stderr 吗?或者它是否写入 GetStdHandle(STD_ERROR_HANDLE) ?第一个最初映射到第二个。但使用freopen(),您只需更改映射即可。任何写入 STD_ERROR_HANDLE 的内容仍然会到达那里。

要重定向每个人的错误输出,您需要 SetStdHandle< /a>.

Does the DLL really write to stderr? Or does it write to GetStdHandle(STD_ERROR_HANDLE) ? The first maps to the second, initially. But with freopen() you merely change the mapping. Anything written to STD_ERROR_HANDLE will still go there.

To redirect everyones error output, you would need SetStdHandle.

李不 2024-09-15 07:37:01

只有一个 stderr,所以我的猜测是问题不是您需要动态加载 dll,而是您的重定向代码中的某处。您的重定向代码是 Linux 风格编写的,在 Windows 中工作方式有所不同。

如果您可以在 Linux 上测试您的应用程序,这将有助于查明问题。如果它在 Linux 上运行,那么它肯定是重定向代码。

无论如何,您应该阅读更多有关重定向和窗口的内容,因为我认为您现在尝试做的事情不会对您有帮助。

There is only one stderr, so my guess is that the problem is not that you need to load the dll dynamically, but somewhere in your redirection code. Your redirection code is written Linux style, where in windows things work differently.

if you could test your application on Linux, It would help to pin point the problem. If it works on Linux, that it is surly the redirection code.

Anyway, you should read some more about redirection and windows, as I don't think that what you are trying to do now will help you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文