본문 바로가기
개발/Windows

[Windows] DeviceIoControl을 이용하여 하드디스크 시리얼 구하기

by lucidmaj7 2025. 2. 12.
728x90
반응형

윈도우 클라이언트를 개발하다 보면 특정 PC의 고유 값을 얻어야 할 때가 있다. 보통 하드디스크 시리얼, Mac 주소 등의 조합으로 하드웨어를 특정 짓는다.

이번에 알아본 방법은 DeviceIoControl를 이용하여 하드디스크 시리얼을 구하는 방법이다.

 

 

BOOL GetDiskSerialNumber(CString &szSerialNumber)
{
	TCHAR szWindowDir[MAX_PATH] = { 0, };
	if (ExpandEnvironmentStrings(_T("%windir%"), szWindowDir, MAX_PATH) == 0)
		return FALSE;

	szSerialNumber.Empty();

	// Format physical drive path (may be '\\.\PhysicalDrive0', '\\.\PhysicalDrive1' and so on or '\\\\.\\\\C:').
	CString strDrivePath;
	strDrivePath.Format(_T("\\\\.\\\\%c:"), szWindowDir[0]);

	// call CreateFile to get a handle to physical drive
	HANDLE hDevice = ::CreateFile(strDrivePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL, OPEN_EXISTING, 0, NULL);

	if (INVALID_HANDLE_VALUE == hDevice)
		return FALSE;

	// set the input STORAGE_PROPERTY_QUERY data structure
	STORAGE_PROPERTY_QUERY storagePropertyQuery;
	ZeroMemory(&storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY));
	storagePropertyQuery.PropertyId = StorageDeviceProperty;
	storagePropertyQuery.QueryType = PropertyStandardQuery;

	// get the necessary output buffer size
	STORAGE_DESCRIPTOR_HEADER storageDescriptorHeader = { 0 };
	DWORD dwBytesReturned = 0;
	if (!::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
		&storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY),
		&storageDescriptorHeader, sizeof(STORAGE_DESCRIPTOR_HEADER),
		&dwBytesReturned, NULL))
	{
		::CloseHandle(hDevice);
		return FALSE;
	}

	// allocate the necessary memory for the output buffer
	const DWORD dwOutBufferSize = storageDescriptorHeader.Size;
	BYTE* pOutBuffer = new BYTE[dwOutBufferSize];
	ZeroMemory(pOutBuffer, dwOutBufferSize);

	// get the storage device descriptor
	if (!::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
		&storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY),
		pOutBuffer, dwOutBufferSize,
		&dwBytesReturned, NULL))
	{
		delete[]pOutBuffer;
		::CloseHandle(hDevice);
		return FALSE;
	}

	// Now, the output buffer points to a STORAGE_DEVICE_DESCRIPTOR structure
	// followed by additional info like vendor ID, product ID, serial number, and so on.
	STORAGE_DEVICE_DESCRIPTOR* pDeviceDescriptor = (STORAGE_DEVICE_DESCRIPTOR*)pOutBuffer;

	const DWORD dwProductIdOffset = pDeviceDescriptor->ProductIdOffset;
	if (dwProductIdOffset != 0) {
		TCHAR *szVMProductName[] = { _T("Virtual HD"), NULL };
		CString szProductName = CString(pOutBuffer + dwProductIdOffset);
		wprintf(L"ProductId : %s\n", szProductName.GetBuffer(0));

		BOOL bVM = FALSE;
		for (INT i = 0; szVMProductName[i] != '\0'; ++i) {
			if (_tcsnicmp(szProductName.GetBuffer(0), szVMProductName[i], _tcslen(szVMProductName[i])) == 0)
				bVM = TRUE;

			/*if (szProductName.CompareNoCase(szVMProductName[i]) == 0)
			bVM = TRUE;*/
		}

		if (bVM == TRUE) {
			delete[]pOutBuffer;
			::CloseHandle(hDevice);
			return FALSE;
		}
	}

	const DWORD dwSerialNumberOffset = pDeviceDescriptor->SerialNumberOffset;
	if (dwSerialNumberOffset != 0)
	{
		// finally, get the serial number
		szSerialNumber = CString(pOutBuffer + dwSerialNumberOffset);
		szSerialNumber.Trim();
	}
	else {
		delete[]pOutBuffer;
		::CloseHandle(hDevice);
		return FALSE;
	}


	// perform cleanup and return
	delete[]pOutBuffer;
	::CloseHandle(hDevice);
	return TRUE;
}

물론 하드디스크 시리얼은 WMI등으로도 구할 수 있다.

참고로 가상머신 같은 일부 PC에서는 정확히 값을 얻어 낼 수 없다.

728x90
반응형

댓글