Робота з пристроями в windows borland delphi

Отримання списку пристроїв

Включення і відключення пристроїв.

Станом пристрою управляє функція SetupDiSetClassInstallParams. Її опис:
WINSETUPAPI BOOL WINAPI
SetupDiSetClassInstallParams (
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
IN PSP_CLASSINSTALL_HEADER ClassInstallParams, OPTIONAL
IN DWORD ClassInstallParamsSize
);
З першими двома параметрами я думаю все ясно. Третій параметр задає покажчик на структуру SP_CLASSINSTALL_HEADER. Четвертий параметр задає розмір третього параметра. За допомогою цієї функції можна виробляти різні дії з пристроями і, зрозуміло, для кожної дії використовуються різні структури. Але у кожної зі структур перша складова однакова - структура SP_CLASSINSTALL_HEADER, ось вона:
typedef struct _SP_CLASSINSTALL_HEADER DWORD cbSize;
DI_FUNCTION InstallFunction;
> SP_CLASSINSTALL_HEADER, * PSP_CLASSINSTALL_HEADER;
Поле InstallFunction задає вироблену над пристроєм операцію. Для включення / відключення це поле дорівнюватиме константі DIF_PROPERTYCHANGE. Для включення / відключення пристрою використовується наступна структура:
typedef struct _SP_PROPCHANGE_PARAMS SP_CLASSINSTALL_HEADER ClassInstallHeader;
DWORD StateChange;
DWORD Scope;
DWORD HwProfile;
> SP_PROPCHANGE_PARAMS, * PSP_PROPCHANGE_PARAMS;
Якщо поле StateChange дорівнюватиме DICS_ENABLE, то пристрій буде включено інакше DICS_DISABLE. Якщо поле Scope одно DICS_FLAG_GLOBAL, то зміни вступлять в силу для всіх апаратних профілів, якщо DICS_FLAG_CONFIGSPECIFIC, то зміни вступлять в силу тільки для зазначеного апаратного профілю. Поле HwProfile задає ID апаратного профілю, до якого будуть застосовуватися зміни, якщо він дорівнює нулю, то поточний апаратний профіль. Всі параметри потребують «затвердження» перед будь-якими змінами. Тому функцію треба викликати два рази. Якщо після першого виклику функція повернула справжнє значення, значить можна викликати функцію вдруге.
Після зміни стану пристрою треба викликати установник класу, тому що після зміни стану пристрою може знадобитися перезавантаження системи або інші дії, для того щоб зміни вступили в силу. Це здійснюється функцією SetupDiCallClassInstaller
WINSETUPAPI BOOL WINAPI
SetupDiCallClassInstaller (
IN DI_FUNCTION InstallFunction,
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
);
Перший параметр задає код виробленої операції. Два інших параметра я думаю, проблем не викличуть.
Як приклад можна привести код включення і відключення мережевого підключення. Для того щоб включити / вимкнути підключення до мережі досить включити / вимкнути мережевий пристрій, через яке здійснюється підключення до мережі. Це виробляє наступна функція:
procedure EnableNetDevice (aState: boolean; index: integer);
var
NetPnPHandle: HDEVINFO;
PCHP: TSPPropChangeParams;
DeviceData: TSPDevInfoData;
begin
NetPnPHandle: = SetupDiGetClassDevs (@GUID_DEVCLASS_NET, 0, 0, DIGCF_PRESENT);
if NetPnPHandle = INVALID_HANDLE_VALUE then exit;
DeviceData.cbSize: = sizeof (TSPDevInfoData);
SetupDiEnumDeviceInfo (NetPnPHandle, index, DeviceData);
PCHP.ClassInstallHeader.cbSize: = sizeof (TSPClassInstallHeader);
if SetupDiSetClassInstallParams (NetPnPHandle, @ DeviceData, @ PCHP, sizeof (TSPPropChangeParams)) then
begin
PCHP.ClassInstallHeader.cbSize: = sizeof (TSPClassInstallHeader);
PCHP.ClassInstallHeader.InstallFunction: = DIF_PROPERTYCHANGE;
PCHP.Scope: = DICS_FLAG_CONFIGSPECIFIC;
if aState then
PCHP.StateChange: = DICS_ENABLE
else
PCHP.StateChange: = DICS_DISABLE;
SetupDiSetClassInstallParams (NetPnPHandle, @ DeviceData, @ PCHP, sizeof (TSPPropChangeParams));
SetupDiCallClassInstaller (DIF_PROPERTYCHANGE, NetPnPHandle, @ DeviceData);
end;
DeviceData.cbSize: = sizeof (TSPDevInfoData);
SetupDiDestroyDeviceInfoList (NetPnPHandle);
end;
Параметр index задає індекс мережевого пристрою в списку мережевих пристроїв.

Безпечне вилучення пристрою

Отже, з включенням / відключенням пристроїв ми розібралися. А як безпечно видобувати пристрій? Безпечне вилучення пристрою здійснює функція CM_Request_Device_Eject. Ось її опис:
CMAPI CONFIGRET WINAPI
CM_Request_Device_Eject (
IN DEVINST dnDevInst,
OUT PPNP_VETO_TYPE pVetoType,
OUT LPTSTR pszVetoName,
IN ULONG ulNameLength,
IN ULONG ulFlags
);
Перший параметр це хендл пристрою. Другий параметр це покажчик на змінну, в яку буде збережений код причини при невдачі. Третій параметр це покажчик на рядок, в яку буде збережена причина невдачі при невдалої спроби здійснити дзвінок. Обидва цих параметра опційні і можуть бути рівні нулю. П'ятий параметр це максимальна довжина рядка. Шостий не використовується. Якщо pszVetoName дорівнює нулю, то при невдачі повідомлення виведе сама система. Ось і сама функція, яка здійснює безпечне вилучення пристрою.
procedure RemoveDrive (index: integer);
var
DrivesPnPHandle: HDEVINFO;
DevInfo: SP_DEVINFO_DATA;
i: Integer;
Parent: DWORD;
VetoName: array [0..MAX_PATH] of char;
begin
DevInfo.cbSize: = sizeof (SP_DEVINFO_DATA);
DrivesPnPHandle: = SetupDiGetClassDevsA (@GUID_DEVCLASS_DISKDRIVE, nil, 0, 2);
if DrivesPnPHandle = INVALID_HANDLE_VALUE then exit;
if SetupDiEnumDeviceInfo (DrivesPnPHandle, index, DevInfo) then
begin
if (IsUSBDevice (DevInfo.DevInst)) and (CM_Get_Parent (Parent, DevInfo.DevInst, 0) = CR_SUCCESS)
then
begin
CM_Request_Device_Eject (Parent, nil, @VetoName, MAX_PATH, 0);
if VetoName = '' then
ShowMessage ( 'пристрій можна витягти')
else
ShowMessage ( 'пристрій не можна витягти');
end
else
ShowMessage ( 'Чи не USB пристрій');
end;
SetupDiDestroyDeviceInfoList (DrivesPnPHandle);
end;
Єдиний параметр передається функції це індекс дискового пристрою в списку дискових пристроїв. Єдине що модет бути тут незрозуміло - це функція CM_Get_Parent. Вона отримує батько пристрою. Адже будь-яка «флешка» або зовнішній дисковий накопичувач це складене пристрій і відключати треба саме батьківський пристрій. Код функції IsUSBDevice є в исходнике, який додається до статті.

SetupAPI не дозволяє отримувати інформацію (звук, зображення) з різних пристроїв. SetupAPI призначене для установки і видалення пристроїв, для отримання характеристик пристроїв і т.д.

Спасибі величезне, подібну статтю шукав два тижні, але нічого навіть близько лежачого не знайшов. Щиро Дякую!

Схожі статті