zoukankan      html  css  js  c++  java
  • win32调试工具原理OutputDebugString以及如何获取输出信息

    在应用程序和调试器之间传递数据是通过一个 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;
    
    }
  • 相关阅读:
    nginx 开机自动启动
    Linux 常用命令
    php中数组操作函数
    Windows系统下GitBash显示的中文乱码解决方案
    更改Git默认编辑器为notepad++
    【js】函数问题
    【JS】JavaScript中的参数传递
    Android基础整理之四大组件Activity
    关于BaseAdapter的使用及优化心得(一)
    使用MySQL Workbench建立数据库,建立新的表,向表中添加数据
  • 原文地址:https://www.cnblogs.com/UnGeek/p/3443934.html
Copyright © 2011-2022 走看看