【问题标题】:Different disk serial number result from GetVolumeInformation()GetVolumeInformation() 产生不同的磁盘序列号
【发布时间】:2015-03-27 10:26:32
【问题描述】:
DWORD disk_serialINT[MAX_PATH + 1];
GetVolumeInformationA(NULL, NULL, NULL, disk_serialINT, NULL, NULL, NULL, NULL);
char* disk_serialANSI;
sprintf(disk_serialANSI, "%d", disk_serialINT);
std::string HDDserial = disk_serialANSI;

这是我获取硬盘序列号的一段代码,但问题是每次程序执行时值都不一样。谁能解释一下?

已解决:

DWORD disk_serialINT;
GetVolumeInformationA(NULL, NULL, NULL, &disk_serialINT, NULL, NULL, NULL, NULL);
std::string HDDserial = std::to_string(disk_serialINT);

谢谢。

【问题讨论】:

  • 这段代码中有三个错误,是时候更新你的工具了。
  • 您使用的是哪本 C++ 书籍?它在教你糟糕的做法。
  • 这是我的私人项目。不幸的是,我找不到任何可以学习 winapi 的书。

标签: c++ winapi disk hardware-id


【解决方案1】:

这两行会给你undefined behavior:

char* disk_serialANSI;
sprintf(disk_serialANSI, "%d", disk_serialINT);

你声明了一个指针变量,但实际上并没有让它指向任何地方。未初始化的局部变量有一个不确定的值(实际上它看起来是随机的),并且通过使用未初始化的指针,您不知道sprintf 调用将写入何处


由于您使用 C++ 编程,因此有几个解决方案。

  • 老式的做法是让disk_serialANSI 成为一个字符数组,大到足以容纳数字(包括字符串终止符)。另一种方法是手动为指针分配内存,然后在完成后再次释放该内存。

  • 使用std::ostringstream格式化数据,得到std::string

  • 使用std::to_string直接转成字符串。

  • 使用Boost Lexical cast

【讨论】:

  • 使用 Boost 将 DWORD 转换为十六进制完全是矫枉过正。使用任何其他 std 方法都是低效的。他需要的是一个简单的char sn[9];sprintf(sn, "%08lX", disk_serialINT);
  • 不管是第一个点,当任何人阅读答案时都可以更轻松地对您的其他三个点采取行动(选择函数名称并谷歌它以查看它的使用方式,或单击 Boost链接)。
【解决方案2】:

除了 Joachim 所说,您没有正确传递序列号。您应该传递一个指向单个值的指针。

DWORD disk_serialINT;
GetVolumeInformationA(NULL, NULL, NULL, &disk_serialINT, NULL, NULL, NULL, NULL);

在您的代码中,这与您在此处所做的事情有关:

sprintf(disk_serialANSI, "%d", disk_serialINT);

忽略 Joachim 的回答所涵盖的未初始化变量 disk_serialANSI,您正在传递一个指向 "%d" 格式字符串的指针。一旦您将 disk_serialINT 更改为单一值,情况会更好。但是,您将无符号值传递给单个格式字符串。

是时候放弃这些粗糙的 C 格式化函数,使用 C++ 标准库在整数值和文本之间进行转换了。

最后一点要强调的是,您必须检查对 Win32 API 的调用的返回值。你不知道函数调用是否成功。你不能假设它确实如此。这一切都包含在文档中:https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993.aspx

此程序打印包含当前目录的卷的序列号:

#include <Windows.h>
#include <iostream>

int main()
{
    DWORD disk_serialINT;
    if (!GetVolumeInformationA(NULL, NULL, NULL, &disk_serialINT, NULL,
        NULL, NULL, NULL))
    {
        std::cout << "Failed: " << GetLastError() << std::endl;
        return 1;
    }
    std::cout << "Current directory volume serial numnber: " << std::hex 
        << disk_serialINT << std::endl;

    return 0;
}

【讨论】:

  • 粗糙的 C 格式?我认为您实际上从未尝试过使用 std 格式化比单个数字更复杂的东西。
【解决方案3】:

如果有人来看这段代码,这里的解决方案就是我的看法:

问题 #1

DWORD disk_serialINT[MAX_PATH + 1];
  • 当您只需要一个 DWORD 值时声明一个数组。
  • 在不了解其用途的情况下使用 MAX_PATH 宏(并将其加 1)。
  • 不知道类型(当DWORD 类型为无符号时命名变量INT)。
  • 不理解问题域(将变量命名为disk_serial,而它实际上是一个序列号)。

问题 #2

GetVolumeInformationA(NULL, NULL, NULL, disk_serialINT, NULL, NULL, NULL, NULL);
  • 没有错误处理(不检查函数返回值是否失败)。
  • 不明白按值传递参数和按引用传递参数的区别(不传递disk_serialINT变量的地址)。

问题 #3

char* disk_serialANSI;
  • 不了解数组和指针之间的区别。
  • 不了解静态分配和动态分配之间的区别。

问题 #4

sprintf(disk_serialANSI, "%d", disk_serialINT);
  • 使用未初始化的变量disk_serialANSI
  • 使用了错误的变量类型(要么使用char 的数组,要么保留指针但分配内存然后释放它)。
  • 不了解问题域(卷序列号格式错误,应为%08lX)。
  • 未使用sprintf 的安全版本。

问题 #5

std::string HDDserial = disk_serialANSI;
  • 无缘无故混用 C 和 C++。

换句话说,所有典型的初学者程序员错误。希望他们在此期间学到更多,而不仅仅是在 Stack Overflow 上找到问题的答案。

上面已经有 David Heffeman 提供的 C++ 解决方案,所以为了回答的完整性,这里是等效的 C 解决方案:

#include <stdio.h>
#include <windows.h>

int main()
{
    const int VolumeSerialLength = 9; // 8 hex digits + zero termination
    char VolumeSerial[VolumeSerialLength] = { 0 };
    DWORD VolumeSerialNumber;

    if (GetVolumeInformationA(NULL, NULL, NULL, &VolumeSerialNumber, NULL, NULL, NULL, NULL) {
        sprintf_s(VolumeSerial, VolumeSerialLength, "%08lX", VolumeSerialNumber);
        printf("Volume serial number: %8.8s.\n", VolumeSerial);
        return 0;
    } else {
        printf("GetVolumeInformationA() error: %08lX\n", GetLastError());
        return 1;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 2014-04-22
    • 1970-01-01
    • 2022-10-24
    • 2019-07-21
    • 2013-10-12
    相关资源
    最近更新 更多