使用 fprintf 调试调用 C DLL 的 MFC 程序

Posted

技术标签:

【中文标题】使用 fprintf 调试调用 C DLL 的 MFC 程序【英文标题】:Debugging MFC program calling C DLL using fprintf 【发布时间】:2012-07-31 13:23:10 【问题描述】:

我有一个 MFC 程序,它调用了一个 C dll,它在发布时崩溃,但在调试时没有。我尝试使用 fprintf 将消息写入文件,这适用于第一次通过程序。但是,程序在第二次运行计算时会崩溃。

在将调试日志文件作为 C 例程顶部的第一个可执行语句写入后,我立即关闭它。信息在第一次计算后写入日志文件,而不是在第二次计算期间。

有什么建议吗?

【问题讨论】:

可能有一些代码示例? 我发现可以添加如下代码:FILE *fp; fp = fopen("c:\\temp\\debugP2.txt","w");到有问题的例程的顶部打开一个文件,然后移动这个代码: fprintf(fp, "check P22\r\n"); fclose(fp);通过例程逐行向下调试。如果“检查 P22”没有写入文件,那么我已经通过了存在错误的点。这很麻烦,因为必须在遇到错误之前关闭文件,否则不会将任何内容写入文件。 FORTRAN 并非如此。 这并不奇怪,您必须在关闭文件之前执行“fflush(fp)”以确保缓冲区中没有任何内容。还要检查“fp”是否为 NULL。 谢谢维克多。通过在每个“fprintf”语句之后放置一个“fflush(fp)”,我可以确保在崩溃之前将所有信息写入日志文件。迈克 【参考方案1】:

在处理此类需要日志记录工具的问题时,我通常会编写一个调用日志记录的函数。然后我将该函数包装在一个允许我启用或禁用日志记录的宏中。作为其中的一部分,我有时也会使用调试级别指示器,这样就不会将所有日志都放入日志文件中。

日志记录函数将作为一个单独的小型库单独存在于一个文件中。将有一个包含文件,其中包含日志记录库函数原型和一个用于启用或禁用日志记录的宏。

我发现根据应用程序的类型,有些事情可能对日志有所帮助,例如将每个日志的时间戳记到秒或毫秒。我还发现,使用标准行长可以更轻松地在文本编辑器中查看日志。了解源文件的路径以及源代码行号会很有帮助。

以下代码是徒手编写的,未经测试甚至编译,因此可能存在一两个错误。

实际的日志记录函数,我们称之为LogWriteLog() 将被包装在一个宏中,如下所示:

#define LOGISSUELOG(level,logtext)  LogWriteLog(level, logtext, __FILE__, __LINE__);

通过使用宏,您可以通过更改宏定义来启用或禁用日志记录。这个宏还会自动生成源文件的路径和日志的源行号。

为此,当我想将变量作为日志的一部分时,有时我会将其包装到以下预处理器代码序列中。大括号允许在源中插入多行并具有局部范围的 char 变量。

#if defined(LOGISSUELOG)

    char  xBuff[128];
    sprintf (xBuff, "value 1 %d, error %d", iValue, iErrorStatus);
    LOGISSUELOG(4, xBuff);

#endif

简单的日志库只有几个函数,一个是打开日志文件并指定调试级别,第二个是实际执行日志,第三个是在完成后关闭日志文件。

使用标准的 C I/O 例程,实际的日志记录函数如下所示。为此,您还需要 string.hstdio.h。此外,您可能需要考虑是否在每次重新启动时截断文件,或者是否要对文件进行追加。

我还检查了文件大小,当它达到一定大小时,fseek () 到文件的开头进行环绕。如果你正在做环绕,时间戳几乎成为必需品。如果做一个环绕真的使所有日志行的标准行宽是必要的,那么事情就会排成一行。

    // allocate the file scope globals used by the logging library
    static FILE  *fpLogWriteLog = 0;
    static int   iLogDebugLevel = 0;

    int LogOpenLog (int iDebugLevel, char *aszLogFilePath)
    
        // open the log file for writing, truncating what is there already
        fpLogWriteLog = fopen (aszLogFilePath, "w");
        iLogDebugLevel = iDebugLevel;

        return (fpLogWriteLog == 0) ? 0 : 1;
    

    void LogCloseLog (void)
    
        if (fpLogWriteLog)
            fclose (fpLogWriteLog);
    

    // allow the debug logging level to be changed.
    //return the current value so that it can be used in another call to set it back.
    int LogChangeLogLevel (int iDebugLevelNew)
    
        int iDebugLevelSave = iLogDebugLevel;

        iLogDebugLevel = iDebugLevelNew;
        return iDebugLevelSave;
    

    // write a log to the log file
    int LogWriteLog (int iDebugLevel, char *aszLogText, char *aszFileName, int iLineNo)
    
          int iRetStatus = 0;

          if (fpLogWriteLog && iDebugLevel < iLogDebugLevel) 
            int iLen = strlen (aszFileName);

            // we will keep only the last 30 characters of the source file path
            if (iLen > 30)
                iLen = 30;
            else
                iLen = 0;

            // use print specifiers to provide the same line width for the output log
            // use %-60.60s to print log text in case of print format specifiers in it
            fprintf (fpLogWriteLog, "%3.3d %-30.30s - %6.6d: %-60.60s", iDebugLevel, aszFileName + iLen, iLineNo, aszLogText);
            fflush (fpLogWriteLog);
            iRetStatus = 1;
          
          return iRetStatus;
    

【讨论】:

以上是关于使用 fprintf 调试调用 C DLL 的 MFC 程序的主要内容,如果未能解决你的问题,请参考以下文章

有啥方法可以调试从 C# DllImport 调用的 c++ dll?

EXE执行文件如何调用DLL?

Delphi:使用调试器调用 C dll 函数需要 15 秒,而没有调试器需要 0.16 秒。为啥?

C#调用C++DLL的小总结5---和C++的DLL的联合调试

如何从 Microsoft Access 调试 dll

c#2005调试程序正常,生成了引用的dll文件,但是生成程序后在其他机器上使用,总是出现缺少程序集的错误