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

对象名称 对象类型
DBWinMutex Mutex
DBWIN_BUFFER Section (共享内存)
DBWIN_BUFFER_READY Event
DBWIN_DATA_READY Event

互斥量通常一直保留在系统中,其他三个对象仅当调试器要接收信息才出现。事实上 - 如果一个调试器发现后三个对象已经存在,它会拒绝运行。

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

struct dbwin_buffer {
DWORD dwProcessId;
char data[4096-sizeof(DWORD)];
};

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

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

在调试器端会简单一点。互斥量根本不需要,如果事件对象和/或共享内存对象已经存在,则假定其他调试器已经在运行。系统中任意时刻只能存在一个调试器。

  1. 创建共享内存段以及两个事件对象。如果失败,退出。
  2. 设置 DBWIN_BUFFER_READY 事件对象,由此应用程序得知缓冲区可用。
  3. 等待 DBWIN_DATA_READY 事件对象变为有信号状态。
  4. 从内存缓冲区中提取进程 ID 和 NULL 结尾的字符串。
  5. 转到步骤 2。

这使我们认为这决不是一种低消耗的发送信息的方法,应用程序的运行速度会受到调试器的左右。

#define WIN32_LEAN_AND_MEAN

#include <Windows.h>
#include <stdio.h> #define IfFalseRet(c) do{if(!(c)){return dwLastError = ::GetLastError();}}while(false) class CHandle
{
public:
  CHandle(HANDLE h = NULL): m_h(h)
  {
  }
  ~CHandle()
  {
    Release();
  }
  void Release()
  {
    if(*this)
    {
      ::CloseHandle(m_h);
    }
    m_h = NULL;
  }
  operator bool() const
  {
    return m_h != INVALID_HANDLE_VALUE && m_h != NULL;
  }
  operator HANDLE() const
  {
    return m_h;
  }
  CHandle& operator= (const HANDLE& h)
  {
    Release();
    m_h = h;
    return *this;
  }
  CHandle& operator= (CHandle& h)
  {
   if(this != &h)
   {
      HANDLE hSwap = m_h;
      m_h = h.m_h;
      h.m_h = hSwap;
      h.Release();
    }
    return *this;
  }
private:
  HANDLE m_h;
}; LPCTSTR DBWIN_BUFFER = TEXT("DBWIN_BUFFER");
LPCTSTR DBWIN_BUFFER_READY = TEXT("DBWIN_BUFFER_READY");
LPCTSTR DBWIN_DATA_READY = TEXT("DBWIN_DATA_READY");
LPCTSTR DBWIN_MUTEX = TEXT("DBWinMutex"); #pragma pack(push, 1)
struct CDBWinBuffer
{
  DWORD dwProcessId;
  BYTE  abData[4096 - sizeof(DWORD)];
};
#pragma pack(pop) bool g_fContinue = true; BOOL CtrlHandler(DWORD fdwCtrlType)
{
  switch(fdwCtrlType)
  {
  case CTRL_C_EVENT:
  case CTRL_CLOSE_EVENT:
  case CTRL_LOGOFF_EVENT:
  case CTRL_SHUTDOWN_EVENT:
    g_fContinue = false;
    return TRUE;
  }
  return FALSE;
} int __cdecl main()
{
  DWORD dwLastError = ERROR_SUCCESS;   IfFalseRet(SetConsoleCtrlHandler((PHANDLER_ROUTINE)(CtrlHandler), TRUE) == TRUE);   CHandle hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, DBWIN_MUTEX);
  if(!hMutex)
  {
    IfFalseRet(GetLastError() == ERROR_FILE_NOT_FOUND);
    IfFalseRet(hMutex = CreateMutex(NULL, FALSE, DBWIN_MUTEX));
  }
  
  CHandle hEventBufferReady = OpenEvent(EVENT_MODIFY_STATE, FALSE, DBWIN_BUFFER_READY);
  if(!hEventBufferReady)
  {
    IfFalseRet(GetLastError() == ERROR_FILE_NOT_FOUND);
    IfFalseRet(hEventBufferReady = CreateEvent(NULL, FALSE, TRUE, DBWIN_BUFFER_READY));
  }
  
  CHandle hEventDataReady = OpenEvent(EVENT_MODIFY_STATE, FALSE, DBWIN_DATA_READY);
  if(!hEventDataReady)
  {
    IfFalseRet(GetLastError() == ERROR_FILE_NOT_FOUND);
    IfFalseRet(hEventDataReady = CreateEvent(NULL, FALSE, FALSE, DBWIN_DATA_READY));
  }   CHandle hFileMappingBuffer = OpenFileMapping(FILE_MAP_READ, FALSE, DBWIN_BUFFER);
  if(!hFileMappingBuffer)
  {
    IfFalseRet(GetLastError() == ERROR_FILE_NOT_FOUND);
    IfFalseRet(hFileMappingBuffer = CreateFileMapping(
      INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
      0, sizeof(CDBWinBuffer), DBWIN_BUFFER));
  }   CDBWinBuffer* pDbgBuffer = (CDBWinBuffer*)(MapViewOfFile(
    hFileMappingBuffer, SECTION_MAP_READ, 0, 0, 0));
  IfFalseRet(pDbgBuffer);   while(g_fContinue)
  {
    if(WaitForSingleObject(hEventDataReady, 100) == WAIT_OBJECT_0)
    {
      printf("%s", pDbgBuffer->abData);
      SetEvent(hEventBufferReady);
    }
  }   UnmapViewOfFile(pDbgBuffer);   return dwLastError;
}

win32调试工具原理OutputDebugString以及如何获取输出信息的更多相关文章

  1. Java实践-远程调用Shell脚本并获取输出信息

    1.添加依赖 <dependency> <groupId>ch.ethz.ganymed</groupId> <artifactId>ganymed-s ...

  2. vc++调用exe获取输出信息

    目的 调用命令行程序,返回结果. 思路 把命令行结果输入到管道中,exe的输出信息都存在了strOutput这个变量里. 实现代码 CString strCmd = L"yara64.exe ...

  3. win32程序调试OutputDebugString 类似printf格式化输出

    有没有win32编程因为打印变量调试程序而头疼呢.方法二的函数完全类似printf.非常完美.方法一:不带参数输出如printf("hello world"); OutputDeb ...

  4. .net 代码调用cmd执行.exe程序,获取控制台输出信息

    使用.net core 对老项目升级, .net core 使用TripleDES.Create() 加密众iv字节限制 与 framework中的不同, 新项目还需要兼容老项目版本,还不想通过web ...

  5. C#使用cmd运行命令并返回控制台输出信息

    public static string RunCmd(string cmd){ cmd = cmd.Trim().TrimEnd('&') + "&exit";/ ...

  6. C语言使用cmd命令并获取输出方法

    转自http://blog.csdn.net/hxh129/article/details/8000205 C语言使用cmd命令并获取输出方法 在实践中,我们有时候需要用C语言来调用cmd的命令,并得 ...

  7. 老李推荐:第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用

    老李推荐:第5章5节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 获取系统服务引用   上一节我们描述了monkey的命令处理入口函数run是如何调用optionP ...

  8. 从BIRT报表文件中获取页面设置信息(页边距、纸张大小、输出方向)的方法

     从BIRT报表文件中获取页面设置信息(页边距.纸张大小.输出方向)的方法    报表打印时,尤其是套打的报表,页面设置信息非常重要,比如页边距,纸张大小,输出方向等,而且每个报表的相关参数有可能不同 ...

  9. java执行cmd命令并获取输出结果

    1.java执行cmd命令并获取输出结果 import java.io.BufferedReader; import java.io.InputStreamReader; import org.apa ...

随机推荐

  1. poj3067 Japan(树状数组)

    转载请注明出处:http://blog.csdn.net/u012860063 题目链接:id=3067">http://poj.org/problem? id=3067 Descri ...

  2. centos7系统根目录扩容

    比如 点击了后 点击创建虚拟磁盘 选择一个 20G  然后启动虚拟机使用fdisk查看所有的磁盘 看是否新增了一个20G的硬盘 [root@localhost ~]# fdisk -l 磁盘 /dev ...

  3. epplus excel数据导出(数据量有点大的情况) Web和Client

    Asp.net MVC后台代码 public ActionResult Export() { OfficeOpenXml.ExcelPackage ep = new OfficeOpenXml.Exc ...

  4. ie设置ActiveX控件不提示

    ie设置自动允许activex: 对安全设置-受信任的站点区域-对未标记为可安全执行脚本的ActiveX控件初始化并执形脚本(启用)

  5. php 文件下载代码

    1.对于一般 $file = "/tmp/dummy.tar.gz"; header("Content-type: application/octet-stream&qu ...

  6. MD5的各种实现——也是醉了

    MD5即Message-Digest Algorithm 5(信息-摘要算法5).用于确保信息传输完整一致. 是计算机广泛使用的杂凑算法之中的一个(又译摘要算法.哈希算法),主流编程语言普遍已有MD5 ...

  7. jQuery功能函数详解

    jQuery通过$.browser对象获取浏览器信息. 属性 说明msie 如果是ie为true,否则为falsemozilla 如果是mozilla相关的浏览器为true,否则为falsesafar ...

  8. gradlew assembleRelease打包之前的配置

    http://blog.csdn.net/qq_15527709/article/details/70146061

  9. Scilab 的画图函数(3)

    我们在做数据画图或函数图像时常常须要使用对数坐标系.尤其是数据的范围跨越非常多个数量级时.通常的线性坐标系下无法表现出数据特征. Scilab 中Plot函数无法画出对数坐标.须要使用 plot2d  ...

  10. 编程之美 set 5 寻找数组中最大值和最小值

    解法 1. 设置 min, max 两个变量, 然后遍历一遍数组, 比较次数为 2*N 2. 依然设置 min, max 两个变量并遍历数组, 但将遍历的 step 设置为 2, 比较次数为 1.5 ...