【问题标题】:GetComputerName() works on win7 but fails on xpGetComputerName() 在 win7 上工作,但在 xp 上失败
【发布时间】:2014-04-24 20:44:06
【问题描述】:

我正在尝试使用 GetComputerName() 来检索框的名称。该代码在 Windows 7 上运行良好,但是,当我在 windows xp 上测试时,代码不起作用(结果未知)。知道为什么吗?

int GetBoxName(BoxInfo &box_info)
{
    int Ret;    
    DWORD dwLen;
    const char* szUnk = "UNKNOWN\0";

    // Get the size of the box name then allocate memory.
    Ret = GetComputerName(NULL, &dwLen);
    box_info.BoxName = new char[dwLen]; 

    // Attempt to retrieve the box name.
    if((Ret = GetComputerName(box_info.BoxName, &dwLen) == 0))
    {       
        delete[] box_info.BoxName;
        box_info.BoxName = new char[strlen(szUnk)];
        box_info.BoxName = (char*)szUnk;
        return 1;       
    }   

    return 0;
}

【问题讨论】:

  • 尝试调用 GetLastError?
  • 如果你使用wsprintf,你可能不应该使用char
  • @Mats:wsprintfA 是 Win32 API 函数的 ASCII 版本。也许您将它与 C 宽字符串函数 swprintf 混淆了?
  • 可能。无法理解 MS 库中的任何函数名称,对吗?
  • @Mats:实际上,它比 CRT 更规律。 ASCII 与 Unicode 总是由AW 后缀表示,没有任何后缀的名称被转发到基于UNICODE 宏的实际函数之一。

标签: c++ winapi windows-xp


【解决方案1】:

@Ben 很好地说明了您所犯的错误。我想向您展示您通常如何调用GetComputerName。你让它变得比它需要的更难。关键信息是文档的摘录:

缓冲区大小应该足够大,可以包含 MAX_COMPUTERNAME_LENGTH + 1 个字符。

您对缓冲区的大小有一个硬性上限。因此,您可以使用固定大小的缓冲区,并且只调用一次 GetComputerName

std::string getComputerName()
{
    char buffer[MAX_COMPUTERNAME_LENGTH + 1];
    DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
    if (GetComputerName(buffer, &len))
        return std::string(buffer, len);
    return "UNKNOWN";
}

如果你为 Unicode 编译它会是:

std::wstring getComputerName()
{
    wchar_t buffer[MAX_COMPUTERNAME_LENGTH + 1];
    DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
    if (GetComputerName(buffer, &len))
        return std::wstring(buffer, len);
    return L"UNKNOWN";
}

如果你想解决计算机名比MAX_COMPUTERNAME_LENGTH长的可能性,那么你可以这样写:

std::string getComputerName()
{
    char buffer[MAX_COMPUTERNAME_LENGTH + 1];
    DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
    if (GetComputerName(buffer, &len))
    {
        return std::string(buffer, len);
    }
    if (GetLastError() == ERROR_BUFFER_OVERFLOW)
    {
        std::vector<char> name(len);
        if (GetComputerName(&name[0], &len))
        {
            return std::string(&name[0], len);
        }
    }
    return "UNKNOWN";
}

我不知道这是否会发生。文档暗示它可以,尽管如果它确实发生了,那么它会使MAX_COMPUTERNAME_LENGTH 有点用词不当。如果我们传递一个小于MAX_COMPUTERNAME_LENGTH+1 的值len,那么如果名称合适,函数就会成功。 ERROR_BUFFER_OVERFLOW 不会自动失败。当然,如果这个函数返回的名字永远不能超过MAX_COMPUTERNAME_LENGTH,那么第二个版本就比较偏执了。


FWIW,您更新的答案中的代码仍然严重损坏。您根本不能将NULL 的第一个参数传递给GetComputerName。文档再清楚不过了。

【讨论】:

  • 感谢您向我展示这一点。我意识到最大尺寸由 MAX_COMPUTERNAME_LENGTH 定义。但是如果计算机名小于15是不是很浪费内存?
  • 不,不是。内存是在栈上分配的。它已经被保留并且很可能已经分配。此外,根据文档,您的代码已损坏。文档告诉您,您必须传递长度至少为MAX_COMPUTERNAME_LENGTH + 1 的缓冲区。您将不得不在某处分配该内存。我的答案中的代码是使用此类函数的规范方法。
  • 也许限制在过去的某个时候增加了,这个古老的 API 会遭受向后兼容的后果。
  • @SnakeByte 我从不使用 ANSI API。
  • NETBIOS 名称不能是 Unicode,因此 ANSI API 没有缺点。
【解决方案2】:

这完全没有意义:

box_info.BoxName = new char[strlen(szUnk)];
box_info.BoxName = (char*)szUnk;

您分配了内存,然后立即忘记了它。而且您将非const 指针指向字符串文字。而且这里分配的内存量不包括终止 NUL 字节的空间,所以你会溢出缓冲区。

也许你希望第二行是

strcpy(box_info.BoxName, szUnk);

为什么不使用智能指针在需要时自动释放内存,例如std::stringstd::unique_ptr&lt;char[]&gt;


最后,文档说

缓冲区大小应该足够大以包含MAX_COMPUTERNAME_LENGTH + 1 字符。

这是一个非常简单的要求。让你好奇

如果缓冲区太小,函数将失败,GetLastError 返回ERROR_BUFFER_OVERFLOWlpnSize 参数指定所需的缓冲区大小,包括终止空字符。

后一种行为似乎只适用于 Unicode 版本。 Stanly Roark 在 MSDN 页面上留言:

ANSI 版本没有返回所需的长度

我必须维护一个使用 ANSI 的旧应用程序。 我注意到,虽然 Unicode 版本返回所需的缓冲区长度,但 ANSI 版本没有。

【讨论】:

  • @SnakeByte:如果您稍后尝试delete[] box_info.BoxName,它可以解释您的崩溃。
  • @SnakeByte:但我也发现了另一个问题。
  • @BenVoigt ANSI 版本也返回长度。如果你传递一个真正的缓冲区。
  • @DavidHeffernan:啊,所以它可能没有返回ERROR_BUFFER_OVERFLOW,而是返回ERROR_INVALID_PARAMETER NULL 指针?
  • @Ben 在我的机器上这是一个段错误
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-04
  • 2011-08-14
  • 1970-01-01
  • 2019-12-28
  • 1970-01-01
  • 2014-02-06
相关资源
最近更新 更多