Will loading a DLL dynamically reconcile its stder

2020-03-05 02:49发布

问题:

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.

回答1:

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.



回答2:

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.