Как получить серийные номера устройств в системе?
От: vanes Россия  
Дата: 31.07.06 11:12
Оценка:
Подскажите, пожалуйста, как через WIN32 API(или вообще хотя бы как-нибудь) определить серийные номера всех устройств в машине (ИМЕННО ЖЕЛЕЗОК). Я шел двумя путями:
1) Через WMI. Пробовал получить экзмпляры всех классов WMI, производных от CIM_PhysicalElement, но почему-то кроме материнки ни у каких остальных device'ов серийных номеров обнаружено не было (хотя должны были там быть). Вот код:

USES_CONVERSION;


HRESULT hres;

// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------

hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
cout << "Failed to initialize COM library. Error code = 0x"
<< hex << hres << endl;
return 1; // Program has failed.
}

// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
// Note: If you are using Windows 2000, you need to specify —
// the default authentication credentials for a user by using
// a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
// parameter of CoInitializeSecurity ------------------------

hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);


if (FAILED(hres))
{
cout << "Failed to initialize security. Error code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}

// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------

IWbemLocator *pLoc = NULL;

hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);

if (FAILED(hres))
{
cout << "Failed to create IWbemLocator object."
<< " Err code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}

// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method

IWbemServices *pSvc = NULL;

// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (e.g. Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);

if (FAILED(hres))
{
cout << "Could not connect. Error code = 0x"
<< hex << hres << endl;
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}

cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;


// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------

hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);

if (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}

// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----

// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM CIM_Phisical"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);

if (FAILED(hres))
{
cout << "Query for operating system name failed."
<< " Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}

// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------

IWbemClassObject *pclsObj;
ULONG uReturn = 0;
INT i = 0;

while (pEnumerator)
{
cout << ++i << ")" << endl;

HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if(0 == uReturn)
{
break;
}

SAFEARRAY *buffer;
hr = pclsObj->GetNames(NULL, WBEM_FLAG_ALWAYS, NULL, &buffer);

CComSafeArray<BSTR> aPropertyNames;
aPropertyNames.Attach(buffer);

ULONG nCount = aPropertyNames.GetCount();
for (ULONG i = 0; i < nCount; ++i)
{
CString strPropertyName = _bstr_t(aPropertyNames.GetAt(i));

CComVariant vtProp;
hr = pclsObj->Get(A2W(strPropertyName), 0, &vtProp, 0, 0);
if (VT_BSTR == vtProp.vt)
{
CComBSTR bstrPropertyName = aPropertyNames.GetAt(i);
cout << strPropertyName << ": ";
wcout << vtProp.bstrVal << endl;
}
}

// Cleanup
// ========

pSvc->Release();

pLoc->Release();
pEnumerator->Release();
pclsObj->Release();
CoUninitialize();

return 0;

2) Через непосредственно WINAPI. Но результатом было только GetLastError = ERROR_INVALID_FUNCTION (Incorrect function) после вызова функции DeviceIoControl. Вот главный кусок кода:

VOID EnumerateExistingDevices(const GUID* guid)
{
HDEVINFO info = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
SP_INTERFACE_DEVICE_DATA ifdata;
ifdata.cbSize = sizeof(ifdata);
DWORD devindex;
for (devindex = 0; SetupDiEnumDeviceInterfaces(info, NULL, guid, devindex, &ifdata); ++devindex)
{
DWORD needed;
SetupDiGetDeviceInterfaceDetail(info, &ifdata, NULL, 0, &needed, NULL);
PSP_INTERFACE_DEVICE_DETAIL_DATA detail = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc(needed);
detail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
SP_DEVINFO_DATA devdata = {sizeof(SP_DEVINFO_DATA)};
SetupDiGetDeviceInterfaceDetail(info, &ifdata, detail, needed, NULL, &devdata);
CString devname = detail->DevicePath;
HANDLE hDevice = CreateFile (devname,
GENERIC_READ, // no access to the drive
FILE_SHARE_READ // share mode
,
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL); // do not copy file attributes

if(hDevice != INVALID_HANDLE_VALUE)
{
CHANGER_PRODUCT_DATA * product = new CHANGER_PRODUCT_DATA;
DWORD dwReturn = 0;
DWORD dwError = GetLastError();
OVERLAPPED aOverlapped;
BOOL bOk =DeviceIoControl ((HANDLE)hDevice, IOCTL_CHANGER_GET_PRODUCT_DATA, NULL, 0, product,
sizeof(CHANGER_PRODUCT_DATA), &dwReturn, &aOverlapped);
dwError = GetLastError();
if(bOk)
{
cout << "SerialNumber: " << product->SerialNumber << "n";
}
else
{
cout << "Error: " << GetLastError() << "n";
}
delete product;
CloseHandle(hDevice);
}

free((PVOID) detail);
}
}

Очень прошу при возможности привести РАБОТОСПОСОБНЫЙ код в ответе, если можно.
Re: Как получить серийные номера устройств в системе?
От: Alexey Frolov Беларусь  
Дата: 01.08.06 08:22
Оценка:
Здравствуйте, vanes, Вы писали:

V>2) Через непосредственно WINAPI. Но результатом было только GetLastError = ERROR_INVALID_FUNCTION (Incorrect function) после вызова функции DeviceIoControl. Вот главный кусок кода:


V>VOID EnumerateExistingDevices(const GUID* guid)

V>{
V> HDEVINFO info = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
V> SP_INTERFACE_DEVICE_DATA ifdata;
V> ifdata.cbSize = sizeof(ifdata);
V> DWORD devindex;
V> for (devindex = 0; SetupDiEnumDeviceInterfaces(info, NULL, guid, devindex, &ifdata); ++devindex)
V> {
V> DWORD needed;
V> SetupDiGetDeviceInterfaceDetail(info, &ifdata, NULL, 0, &needed, NULL);
V> PSP_INTERFACE_DEVICE_DETAIL_DATA detail = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc(needed);
V> detail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
V> SP_DEVINFO_DATA devdata = {sizeof(SP_DEVINFO_DATA)};
V> SetupDiGetDeviceInterfaceDetail(info, &ifdata, detail, needed, NULL, &devdata);
V> CString devname = detail->DevicePath;
V> HANDLE hDevice = CreateFile (devname,
V> GENERIC_READ, // no access to the drive
V> FILE_SHARE_READ // share mode
V> ,
V> NULL, // default security attributes
V> OPEN_EXISTING, // disposition
V> 0, // file attributes
V> NULL); // do not copy file attributes

V> if(hDevice != INVALID_HANDLE_VALUE)

V> {
V> CHANGER_PRODUCT_DATA * product = new CHANGER_PRODUCT_DATA;
V> DWORD dwReturn = 0;
V> DWORD dwError = GetLastError();
V> OVERLAPPED aOverlapped;
V> BOOL bOk =DeviceIoControl ((HANDLE)hDevice, IOCTL_CHANGER_GET_PRODUCT_DATA, NULL, 0, product,
V> sizeof(CHANGER_PRODUCT_DATA), &dwReturn, &aOverlapped);
V> dwError = GetLastError();
V> if(bOk)
V> {
V> cout << "SerialNumber: " << product->SerialNumber << "n";
V> }
V> else
V> {
V> cout << "Error: " << GetLastError() << "n";
V> }
V> delete product;
V> CloseHandle(hDevice);
V> }

V> free((PVOID) detail);

V> }
V> }

V>Очень прошу при возможности привести РАБОТОСПОСОБНЫЙ код в ответе, если можно.


Насколько я понял вы делаете enumerate всех устройств, а запрос им отсылаете, как будто все они FILE_DEVICE_CHANGER. Так нельзя, это специфический запрос. Вам надо либо различать устройства и каждому посылать специфический запрос, либо попробовать вытащить serial number из Device Instance ID который можно получить с помощью функции
CM_Get_Device_ID(devdata.DevInst, strData, sizeof(data), 0);
Но не всегда там присутствует уникальный серийник, а только в том случае если железка имеет этот самый серийник, это можно проверить с помощью

            if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &devInfoData, SPDRP_CAPABILITIES, &dwRegDataType, data, sizeof(data), &dwRequired))
            {
                DWORD capabilities = * ((DWORD *) data);
                devNode.UniqueSerialNumber = (capabilities & CM_DEVCAP_UNIQUEID) != 0;
            }
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.