【问题标题】:Program crash on printing wstringstream打印 wstringstream 时程序崩溃
【发布时间】:2021-11-07 02:06:11
【问题描述】:

下面的代码在我用来挂钩CreateWindowExA() 和其他函数的DLL 中。

当我使用std::wstringstream 将变量的值打印到DebugView 时,当前挂钩的应用程序崩溃。我确认当我评论 std::wstringstream 时它不会崩溃。

我可以使用哪些其他选项来打印不需要指定每个变量类型的值,就像您在 wsprintf() 上所做的那样?

HWND __stdcall CreateWindowExA_Hook(
    DWORD     dwExStyle,
    LPCSTR    lpClassName,
    LPCSTR    lpWindowName,
    DWORD     dwStyle,
    int       X,
    int       Y,
    int       nWidth,
    int       nHeight,
    HWND      hWndParent,
    HMENU     hMenu,
    HINSTANCE hInstance,
    LPVOID    lpParam
)
{
    std::wstringstream text;
    text << L"dwExStyle: " << dwExStyle << L" lpClassName: " << lpClassName << L" lpWindowName: " << lpWindowName
    << L" dwStyle: " << dwStyle << L" X: " << X << L" Y: " << Y << L" nWidth: " << nWidth << L" nHeight: " << nHeight
    << L" hWndParent: " << hWndParent << L" hMenu" << hMenu << L" hInstance: " << hInstance << L" lpParam: " << lpParam;

    OutputDebugString(L"CreateWindowExA:");
    OutputDebugString(text.str().c_str());
    OutputDebugString(L" ");

    return CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
}

我想知道是否有比这样做“更好”的方法:

std::wstringstream ss;
( dwExStyle ? ss << L"dwExStyle: " << dwExStyle : ss << 0 );
( lpClassName ? ss << L" lpClassName: " << lpClassName : ss << 0 );
( lpWindowName ? ss << L" lpWindowName: " << lpWindowName : ss << 0 );
( dwStyle ? ss << L" dwStyle: " << dwStyle : ss << 0 );
....

OutputDebugString(L"CreateWindowExA:");
OutputDebugString(text.str().c_str());
OutputDebugString(L" ");

【问题讨论】:

  • 是不是因为其中一个字符串为NULL?
  • 可怕的想法:如果 bug 距离打印 5000 行代码怎么办?未知且遥远的错误对程序造成了致命的伤害,但程序在最终跌倒并死在无辜的某个地方之前蹒跚了一段时间?
  • 尝试将打印出来的项目一一注释掉,看看是哪一个(或更多)导致它崩溃。
  • @user253751 很可能,我可以做些什么来避免在这个用例中打印一个 NULL 字符串? dratenik 我一直在使用它,不会停止检查每个值,因为它总是会有不同的情况。
  • (the_string == NULL ? L"(null)" : the_string)

标签: c++ winapi dll


【解决方案1】:

wstringstream 打印非字符串NULL 指针没有问题,它们按原样打印为数值(即0x00000000)。只有NULL 字符串指针可能有问题,并且这段代码中唯一的字符串指针是lpClassNamelpWindowName,所以只需明确检查它们是否有NULL,例如:

std::wstringstream text;
text << ...
     << L" lpClassName: " << (lpClassName ? lpClassName : L"")
     << L" lpWindowName: " << (lpWindowName ? lpWindowName : L"")
     << ...;

您不需要将?: 用于其他参数值,因为当0/NULL 时按原样打印它们是完全安全的。

附带说明一下,我还建议在挂钩中尽可能少地调用OutputDebugString(),因此您应该在wstringstream 文本中包含L"CreateWindowExA:" 前缀。

试试这个:

HWND __stdcall CreateWindowExA_Hook(
    DWORD     dwExStyle,
    LPCSTR    lpClassName,
    LPCSTR    lpWindowName,
    DWORD     dwStyle,
    int       X,
    int       Y,
    int       nWidth,
    int       nHeight,
    HWND      hWndParent,
    HMENU     hMenu,
    HINSTANCE hInstance,
    LPVOID    lpParam
)
{
    std::wstringstream text;
    text << L"CreateWindowExA:"
         << L" dwExStyle: " << dwExStyle
         << L" lpClassName: " << (!lpClassName ? lpClassName : L"")
         << L" lpWindowName: " << (!lpWindowName ? lpWindowName : L"")
         << L" dwStyle: " << dwStyle
         << L" X: " << X
         << L" Y: " << Y
         << L" nWidth: " << nWidth
         << L" nHeight: " << nHeight
         << L" hWndParent: " << hWndParent
         << L" hMenu" << hMenu
         << L" hInstance: " << hInstance
         << L" lpParam: " << lpParam
         << L"\n \n";

    OutputDebugStringW(text.str().c_str());

    return CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
}

更新:根据 Easyhook 的文档:

注意:EasyHook 实现挂钩蹦床代码的方式是,在挂钩处理程序中直接(或间接)调用原始方法将绕过挂钩处理程序并调用原始方法。

【讨论】:

  • 我添加了一个额外的OutputDebugString(L" "); 以在DebugView 中添加一个新的空行,因为当我打印code...\n 时它不会创建一个新行。为什么建议创建一个新函数,而不是在 CreateWindowExA 函数本身内部编辑 CreateWindowExA 的值,然后再将其返回编辑? (我问是因为我是 hooking 的新话题,我 appreciate 任何“提示”)。
  • "你为什么建议创建一个新函数..." - 我没说过这样的话。我认为你误读了我让你做的事情。 “...不是在 _Hook 函数本身内部编辑 CreateWindowExA 的值,而是在返回之前编辑它?” - 嗯?我不知道你现在在说什么。这里没有“编辑”任何内容。
  • 我的意思是,有任何理由创建CreateWindowExA_Orig,而不是在CreateWindowExA_Hook 中编辑任何需要的值并将其返回为return CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);,例如:codeshare.io/8pX8r4
  • 钩子可以对参数做任何它想做的事,并将它想要的任何值传递给原始的未挂钩函数,但你似乎完全错过了我所说的为什么CreateWindowExA_Orig变量首先是需要的。请再仔细阅读我的回答。
  • 雷米,你介意跟我聊聊吗?重读了好几遍,最能理解,但还是有些疑惑,关于_orig函数
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多