【问题标题】:Segmentation fault on EnumMonitors WinAPIEnumMonitors WinAPI 上的分段错误
【发布时间】:2013-03-04 14:24:42
【问题描述】:

我正在编写代码来检查是否安装了我的虚拟打印机,为此我使用 EnumMonitors winapi,代码编译,但是当我尝试运行我的程序时,它崩溃了。 调试我的程序时,我在这一行遇到了 Segmentation fault 错误:EnumMonitors(NULL, 0, (LPBYTE)buffer, sizeof(buffer), &capacity, &returned); 在 .pro 文件中我添加了LIBS += "C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\WinSpool.Lib" 我可以做些什么来使 EnumMonitors WinAPI 工作?

我的代码:

#include <windows.h>
#include <winspool.h>

void Enum()
{
    char buffer[4096];
    DWORD capacity;
    DWORD returned;
    QString monitorname = "Redirected Port";

    /*Program crashes here*/ EnumMonitors(NULL, 1, (LPBYTE)buffer, sizeof(buffer), &capacity, &returned);

    MONITOR_INFO_1 *mi = (MONITOR_INFO_1*)buffer;
    for (uint i = 0; i < returned; i++)
    {
        if (QString::fromWCharArray(mi[i].pName) == monitorname)
        {
            //Do something
        }
    }
}

编辑:我已将我的代码从 0 更新为 1 作为第二个参数

【问题讨论】:

  • 您是否有意不按照有关如何使用EnumMonitors 的说明进行操作? (即,将 0 传递给 Level,而不是在最初将 cbBuf 设置为 0 以确定所需的缓冲区大小时调用它)
  • 调用一次以确定所需的大小是可选的,不是必需的。但是,是的,级别确实需要正确设置。
  • 您可能希望将capacityreturned 的初始值设置为0。没有不确定的值总是一件好事。

标签: c++ qt winapi segmentation-fault


【解决方案1】:

您正在为第二个参数传递“0”。它应该是 1(或 2)。

根据MSDN

Level [in] pMonitors 指向的结构的版本。

这个值可以是 1 或 2。

【讨论】:

    【解决方案2】:

    以下注释代码似乎适用于 VS2010 SP1 (VC10)。

    关注 cmets,了解如何调用 EnumMonitors() API。

    基本上,在第一次调用中,您会询问输出缓冲区大小,calling EnumMonitors() with cbBuf set to zero.

    然后你用适当的输出缓冲区大小正确调整std::vector的大小,并将向量中第一个字节的地址(使用std::vector::data())传递给EnumMonitors()的第二次调用,用@填充输出缓冲区987654328@ 结构。

    (请注意,std::vector 将在函数退出时自动释放它自己分配的内存,无论是在成功路径还是在失败异常抛出路径上。)

    #include <exception>        // for std::exception
    #include <iostream>         // for std::wcout, std::wcerr, std::endl
    #include <sstream>          // for std::ostringstream
    #include <stdexcept>        // for std::runtime_error
    #include <vector>           // for std::vector
    #include <windows.h>        // Win32 SDK main header
    #include <winspool.h>       // for EnumMonitors()
    using namespace std;
    
    void ThrowOnApiFailure(const char* apiName, DWORD errorCode)
    {
        ostringstream errorMessage;
        errorMessage << apiName << "() failed with error code " << errorCode;
        throw runtime_error(errorMessage.str());    
    }
    
    void PrintMonitors()
    {
        static const int kMonitorInfoLevel = 1; // for MONITOR_INFO_1
    
        // Ask output buffer size
        DWORD bufferSize = 0;
        DWORD infoCount = 0;
        ::EnumMonitors(
            nullptr, 
            kMonitorInfoLevel,
            nullptr,
            0,          // ask buffer size
            &bufferSize,
            &infoCount);
        DWORD error = ::GetLastError();
        if (error != ERROR_INSUFFICIENT_BUFFER)
        {
            ThrowOnApiFailure("EnumMonitors", error);
        }   
    
        // Size output buffer
        vector<BYTE> buffer(bufferSize);
    
        // Fill buffer with monitor info
        if ( ! ::EnumMonitors(
            nullptr,
            kMonitorInfoLevel,
            buffer.data(),
            buffer.size(),
            &bufferSize,
            &infoCount       
            ) )    
        {
            error = ::GetLastError();
            ThrowOnApiFailure("EnumMonitors", error);
        }
    
        // Print monitor info
        const MONITOR_INFO_1 * monitorInfo = 
            reinterpret_cast<const MONITOR_INFO_1*>(buffer.data());    
        for (DWORD i = 0; i < infoCount; i++)
        {
            wcout << monitorInfo[i].pName << endl;
        }
    }
    
    int main()
    {
        try
        {
            PrintMonitors();    
        }
        catch(const exception& e)
        {
            wcerr << "\n*** ERROR: " << e.what() << endl;
        }
    }
    

    【讨论】:

    • 在使用返回的缓冲区大小之前,您需要检查 EnumMonitors() 是否确实返回了 ERROR_INSUFFICIENT_BUFFER 错误。它可能由于其他原因而失败。
    • 不,您需要确保它确实是 ERROR_INSUFFICIENT_BUFFER。
    • 是的,在第一次通话时,不是第二次。如果第一次调用失败并出现ERROR_INSUFFICIENT_BUFFER 以外的任何错误,则缓冲区大小将无效。
    【解决方案3】:

    您使用的是哪个编译器?当我在 C++Builder 中按原样尝试您的代码时,EnumMonitors() 不会崩溃并按预期返回ERROR_INVALID_LEVEL 错误。这让我觉得你的编译器可能没有正确声明EnumMonitors(),例如调用堆栈管理不善。

    【讨论】:

    • 我正在使用 Qt 4.8.1 和 MinGW 作为编译器来开发 Qt Creator
    • 所以Qt/MinGW在运行时与EnumMonitors()交互的方式有问题。这仍然向我表明,可能 MinGW 的 EnumMonitors() 声明是错误的,例如它缺少调用约定。
    • MinGW 的头文件和库非常不完整,可能已经过时。我发现 Cygwin 对 WinAPI 更友好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-23
    • 2017-09-17
    • 2010-10-30
    相关资源
    最近更新 更多