OutputDebugString输出调试信息

来源:互联网 发布:淘宝购物客户手机号 编辑:程序博客网 时间:2024/05/24 03:21

OutputDebugString输出调试信息

声明

<windows.h>文件声明了 OutputDebugString() 函数的两个版本:一个用于 ASCII,一个用于 Unicode 。与绝大多数 Win32 API 不同,该函数的原始版本是 ASCII。而大多数的 Win32 API 的原始版本是 Unicode。使用一个 NULL 结尾的字符串缓冲区简单调用 OutputDebugString() 将导致信息出现在调试器中,如果有调试器的话。

开发

Win32开发人员可能对 OutputDebugString()API 函数比较熟悉,它可以使你的程序和调试器进行交谈。它要比创建日志文件容易,而且所有“真正的”调试器都能使用它。应用程序和调试器交谈的机制相当简单,而本文将揭示整件事情是如何工作的。  

本文首先是由以下事件促使的,我们观察到 OutputDebugString()在管理员和非管理员用户试图一起工作或游戏时并不总是能可靠地工作(至少在 Win2000 上)。我们怀疑是一些相关的内核对象的权限问题,此间涉略了相当多不得不写下来的信息。

请注意,尽管我们使用了“调试器”这一术语,但不是从调试 API 的意义上来使用的:并没有“单步执行”、“断点”或者“附着到进程”等可以在 MS Visual C 或者一些真正的交互开发环境中找到的东西。从某种意义上来说,任何实现了协议的程序都是“调试器”。可能是一个非常小的命令行工具,或者像来自于 SysInternals那帮聪明的家伙们的 DebugView 那样的高级货。 应用程序用法 构建一条信息并发送之的通常用法是:

sprintf(msgbuf, "Cannot open file %s[err=%ld]\n", fname, GetLastError());

OutputDebugString(msgbuf);

不过在实际环境中我们中的不少人会创建一个前端函数,以允许我们使用 printf 风格的格式化。下面的 odprintf() 函数格式化字符串,确保结尾有一个合适的回车换行(删除原来的行结尾),并且发送信息到调试器。

2应用

代码

#include <stdio.h>

#include <stdarg.h>

#include <ctype.h>

void __cdecl odprintf(const char *format, ...)

{char buf[4096], *p = buf;

va_list args;

va_start(args, format);

p += _vsnprintf(p, sizeof buf - 1, format, args);

va_end(args);

while ( p > buf && isspace(p[-1]) )

{

*--p = '\0';

*p++ = '\r';

*p++ = '\n';*p = '\0';

}

OutputDebugString(buf)

;}

于是在代码中使用它就很简单:  

odprintf("Cannot open file %s [err=%ld]",fname, GetLastError());

我们已经这样使用多年了。 

协议

在应用程序和调试器之间传递数据是通过一个 4KB 大小的共享内存块完成的,并有一个互斥量和两个事件对象用来保护对他的访问。

下面就是相关的四个内核对象对象名称对象类型DBWinMutexMutexDBWIN_BUFFERSection(共享内存)DBWIN_BUFFER_READYEvent、DBWIN_DATA_READYEvent互斥量通常一直保留在系统中,其他三个对象仅当调试器要接收信息才出现。事实上,如果一个调试器发现后三个对象已经存在,它会拒绝运行。

当DBWIN_BUFFER出现时,会被组织成以下结构。进程ID显示信息的来源,字符串数据填充这4K的剩余部分。按照约定,信息的末尾总是包括一个 NULL 字节。

struct dbwin_buffer { DWORD dwProcessId;

char data[4096-sizeof(DWORD)]; };

当 OutputDebugString()被应用调用时,它执行以下步骤。注意在任意位置的错误都将放弃整个事情,调试请求被认为是什么也不做(不会发送字符串)。

打开 DBWinMutex 并且等待,直到我们取得了独占访问。映射 DBWIN_BUFFER 段到内存中:如果没有发现,则没有调试器在运行,将忽略整个请求。打开 DBWIN_BUFFER_READY 和 DBWIN_DATA_READY 事件对象。就像共享内存段一样,缺少对象意味着没有可用的调试器。等待 DBWIN_BUFFER_READY 事件对象为有信号状态:表示内存缓冲区不再被占用。大部分时候,这一事件对象一被检查就处于有信号状态,但等待缓冲区就绪不会超过 10 秒(超时将放弃请求)。复制数据直到内存缓冲区中接近 4KB,再保存当前进程 ID。总是放置一个 NULL 字节到字符串结尾。通过设置 DBWIN_DATA_READY 事件对象告诉调试器缓冲区就绪。调试器从那儿取走它。释放互斥量。关闭事件对象和段对象,但保留互斥量的句柄以备后用。在调试器端会简单一点。互斥量根本不需要,如果事件对象和/或共享内存对象已经存在,则假定其他调试器已经在运行。系统中任意时刻只能存在一个调试器。  

创建共享内存段以及两个事件对象。如果失败,退出。设置 DBWIN_BUFFER_READY 事件对象,由此应用程序得知缓冲区可用。等待 DBWIN_DATA_READY 事件对象变为有信号状态。从内存缓冲区中提取进程 ID 和 NULL 结尾的字符串。转到步骤 2。这使我们认为这决不是一种低消耗的发送信息的方法,应用程序的运行速度会受到调试器的左右。

 

在编写控制台程序的时候我们经常会使用printf输出调试信息,使我们了解程序的状态,方便调试,但是当编写非控制台程序的时候这种方法就行不通了,那我们应该怎么办?上网查了一些方法,大致就如下几种

1.       使用Log机制

2.       用TRACE宏

3.       其他

   首先,使用Log机制的话要先写一个Log系统,麻烦。而关于TRACE宏,查了资料后才发现原来是MFC里的东西,那对于非MFC程序,就用不了了。    后来发现了OutputDebugString这玩意儿,发现不错。他是属于windows API的,所以只要是包含了window.h这个头文件后就可以使用了,很方便。他可以把调试信息输出到编译器的输出窗口,如下:还可以用DbgView这样的工具查看,这样就可以脱离编译器了。    下面说说如何使用OutputDebugString,他的函数原型是:

voidWINAPI OutputDebugString( __in_opt  LPCTSTR lpOutputString);

就一个参数,是LPCTSTR 类型的。

   这里我对这个API做了一些包装,当然,参考了一些文章:

//木杉的博客  http://blog.csdn.net/mazhibinit  

//2012610         ------转载请注明出处  

 

#ifndef _DEBUGPRINTF_H_ 

#define _DEBUGPRINTF_H_ 

 

#include<Windows.h> 

#include <tchar.h> 

 

//用于输出信息到编译器输出窗口的宏定义 

//使用win APIDEBUG版本会执行,RELEASE版本则不会 

//还可以使用DebugViewWinDbg等工具查看输出 

 

#ifdef _DEBUG 

 

#define DP0(fmt) {TCHAR sOut[256];_stprintf_s(sOut,_T(fmt));OutputDebugString(sOut);} 

#define DP1(fmt,var) {TCHAR sOut[256];_stprintf_s(sOut,_T(fmt),var);OutputDebugString(sOut);} 

#define DP2(fmt,var1,var2) {TCHAR sOut[256];_stprintf_s(sOut,_T(fmt),var1,var2);OutputDebugString(sOut);} 

#define DP3(fmt,var1,var2,var3) {TCHAR sOut[256];_stprintf_s(sOut,_T(fmt),var1,var2,var3);OutputDebugString(sOut);} 

 

#endif 

 

#ifndef _DEBUG 

 

#define DP0(fmt) ; 

#define DP1(fmt, var) ; 

#define DP2(fmt,var1,var2) ; 

#define DP3(fmt,var1,var2,var3) ; 

 

#endif 

 

#endif 

其中的DP就是表示BebugPrint。而且这些调试输出只会在BEBUG版本中有效,在Release版本中就不会有效。

这样使用:

//木杉的博客  http://blog.csdn.net/mazhibinit  

//2012610         ------转载请注明出处  

 

#include<stdio.h> 

#include"debugPrint.h" 

void main() 

    for(int i=0;i<10;i++) 

    { 

        printf("hello!\n"); 

        DP0("这是调试信息!\n"); 

        DP1("这是调试信息%d\n",i); 

        DP2("这是调试信息%d--%d\n",i,i+1); 

        DP3("这是调试信息%d--%d--%d\n",i,i+1,i+2); 

    } 

    getc(stdin); 

   不过,输出这些信息对程序还是有拖慢作用的,像我在写游戏是使用了这个输出调试信息就使帧率下降了不少,不过对于非游戏程序应该还是没有什么影响的。

 

0 0
原创粉丝点击