使用 ReadFile 函数从进程中读取数据

Posted

技术标签:

【中文标题】使用 ReadFile 函数从进程中读取数据【英文标题】:Using ReadFile function to read data from a process 【发布时间】:2020-04-06 19:43:37 【问题描述】:

我正在尝试使用 Win32 API 函数 ReadFile() 从进程中读取数据。我没有使用标准库函数,因为我使用的是来自公共仓库的源代码。在这里,我提供了我遇到问题的具体 sn-p..

//Those are Globals
HANDLE pipin_w, pipin_r, pipout_w, pipout_r;
DWORD write, read, available;
BYTE buffer[2048];

std::string myFunction()
    std::string out="";
    PeekNamedPipe ( pipout_r, buffer, sizeof(buffer), &read, &available, NULL);
    sleep(300);
    do 
        ZeroMemory(*buffer, sizeof(buffer));
        ReadFile( pipout_r, buffer, sizeof(buffer), &read, NULL);

        if (!read)  return std::string("error"); 
        buffer[read]=0;
        out += (char*)buffer;
     while(read >= sizeof(buffer));
    return out;

前两个调用正确响应,然后返回 "error"

我无法使用和理解标准库函数,因为我没有 C++ 编程经验。我要做的是填充大小为 2048 的 BYTE 缓冲区。

此代码适用于前两个调用,然后read 变量被破坏。我知道我的初始化是正确的,因为我能够连接在任务管理器中打开自身的进程,并且我能够从中检索数据,但是在两次调用之后,read 变量被损坏。

这会导致我的代码挂起或崩溃。这段代码有一些我不喜欢的地方。例如,我无法使用像fstream 这样的标准库函数,所以我决定暂时坚持这个设置。另外,我不知道使用ReadFile() 函数从进程中读取是否正确,但这种方法有效。 while 循环让我感觉很不舒服,我不明白我为什么需要它。

任何想知道我如何初始化此代码的人,请随时询问,我会提供所有缺失的信息,但我认为这不是问题,因为我能够从该过程中获得一些读数本身。我可以提供我正在使用的源代码的链接,因为那是一个公共源代码存储库。

也许问的问题(来自你):

    您的流程是否支持阅读?

    是的

    您是否有足够的权限来运行和读取该进程?

    是的,我有

    也许你需要写一些数据来告诉进程开始通信..

    不是这样的,我还有一个写数据的函数,我在这个调用之前用过,但是不写还是收到数据

    您如何创建流程?

    我可以提供那个 sn-p,只要问我,但本质上我创建了两个管道,一个用于读取,另一个用于写入,然后我通过调用 CreateProcess 来启动该过程,然后就可以了。

    你想用你的代码做什么?

    我正在尝试构建一个类,它可以使用 c++ 在 Windows 平台环境中处理进程的创建、写入和读取,仅此而已..

什么可能导致read 变量损坏?我很难理解这一点,以及如何避免它。另外,如果有人可以向我解释这种情况下的良好做法,我真的很感激,或者至少有一个我可以查看的资源的链接,或者研究它是如何工作的。

【问题讨论】:

您可能想更频繁地致电GetLastError() w32 与 winapi 有何不同? 【参考方案1】: ZeroMemory(*buffer, sizeof(buffer)); 应该是 ZeroMemory(buffer, sizeof(buffer)); 在当前状态下,您读取buffer 中的第一个BYTE 并将其用作开始归零的地址。这会导致未定义的行为。 buffer[read]=0; 可能写入越界。如果你读过2048BYTEs,你有未定义的行为。 out += (char*)buffer; 读取 buffer 直到找到 \0 - 但没有必要这样做,因为您已经知道您已经阅读了多少 BYTEs。

修复:

删除 ZeroMemory - 您只会读取由 ReadFile 填充的内存,因此先将其清零只是浪费时间。 更换
buffer[read]=0;
out += (char*)buffer;
out.append(reinterpret_cast<char*>(buffer), read);

另外,检查您调用的函数的返回值。例如,ReadFile 可能会失败。

【讨论】:

非常感谢您的回答!我不能 sto static_cast 因为编译器说“无效的转换类型”,所以它是这样的:out.append((char*)buffer);但我已经删除了 ZeroMemory 调用,并且 buffer[read]=0。另外,我将 GetLastError() 直接放在 PeekNamedPipe 和 ReadFile 之后,但它总是返回 0,无论如何读取变量不断损坏 我的错误。我修复了答案中的演员表。 out.append((char*)buffer) 是不对的。你需要out.append(reinterpret_cast&lt;char*&gt;(buffer), read);顺便说一句,你为什么要PeekNamedPipe 这就是我创建两个管道 CreatePipe(&pipout_r, &pipout_w, &sats, 0); CreatePipe(&pipin_r, &pipin_w, &sats, 0);这就是我如何查看输出管道 PeekNamedPipe(pipout_r, buffer, sizeof(buffer), &read, &available, NULL);还有那些是我如何创建进程 sti.dwFlags = STARTF_USESHOWWINDOW | 的 STARTUPINFO sti STARTF_USESTDHANDLES; sti.wShowWindow = SW_HIDE; sti.hStdInput = pipin_r; sti.hStdOutput = pipout_w; sti.hStdError = pipout_w; 我看到你PeekNamedPipe 的情况,但我没有看到你对结果做了任何事情,所以我想知道你为什么这样做? 它在原始源代码中,我没有删除它。使用renterpreted_cast 给我在地址上的访问冲突,甚至在我第一次调用它时崩溃:(。你说最好只调用一次,在创建进程之后,或者根本不调用它?【参考方案2】:

崩溃都消失了,在我将转换恢复为 char 指针之后,我不太了解转换转换,但是由于我首先通过将 char 指针初始化为 nullptr 来分配内存,所以我使用 memset 分配了一个更大的缓冲区,不知何故,我的方法适用于这种情况,因为我没有在我的代码中的任何其他地方使用它,如果我需要它,我只需使用 std::string 将它复制到一个新的初始化字符串,这样就解决了持续崩溃问题,只有我想不出更好的方法来解决这个问题。

接下来我预想的也是crash,真的是ReadFile函数造成的死循环,有两种情况。

    当我传递一个“错误”的字符串与它通信时,某些特殊字符(如 \ 或 /)

    当缓冲区没有从应用程序读取的内容,并且应用程序返回空输出时

仍然不确定如何处理第二种情况:(但至少我可以继续调试... :)

【讨论】:

以上是关于使用 ReadFile 函数从进程中读取数据的主要内容,如果未能解决你的问题,请参考以下文章

“ReadFile 函数”有没有办法让我不再声明要读取的特定字节数?

匿名管道的 ReadFile 函数

如何更改通过 ReadFile 函数读取的文本

php读取文件内容的4钟常用方法函数

使用 ReadFile 异步读取管道

无法让 ReadFile 从我刚刚写入的文件中读取