Иногда в программах необходимо использовать возможность проверки типа носителя или его серийный номер. Причины могут быть разными от банального контроля устройств в системе до средств защит использующих привязку к железу. Сейчас я вам покажу как с помощью API реализовать эту простую задачу.
Нам понадобится всего 2 API — функции:
- GetDriveType — определяет и возвращает тип носителя;
- GetVolumeInformation — определяет информацию о носителе, среди которой содержится серийный номер.
Рассмотрим описание этих функций для С++ и Delphi. Первой будет функция GetDriveType, она очень простая и использует всего один параметр — указатель на том. Например «c:\»,»a:\» и т.д. Функция возвращает одно из следующих значений:
DRIVE_UNKNOWN — 0 : диск неопределен/не существует
DRIVE_NO_ROOT_DIR — 1 : неверный путь/ путь не указывает на том
DRIVE_REMOVABLE — 2 : тип устройства определяется как съемный (дискета, флешка и т.д.)
DRIVE_FIXED — 3 : тип устройства — фиксированный диск (жесткий диск)
DRIVE_REMOTE — 4 : тип устройства — удаленный(сетевой) диск
DRIVE_CDROM — 5 : это устройство CD-ROM
DRIVE_RAMDISK — 6 : виртуальный диск, созданный в оперативной памяти
C/C++
1 2 3 4 5 | UINT WINAPI GetDriveType( LPCTSTR lpRootPathName //путь к диску ); |
1 2 3 4 5 | function GetDriveType( lpRootPathName: PChar //путь к диску ): UINT; stdcall; |
Замечание: Если в качестве параметра указать для С/С++ NULL, а для Delphi — nil то тип устройства будет определяться для текущего диска (с которого была запущена программа).
А теперь взглянем на функцию GetVolumeInformation. Тоже достаточно простая функция, однако использует параметров значительно больше.
C/C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | BOOL WINAPI GetVolumeInformation( LPCTSTR lpRootPathName, //путь к сетевому или локальному // тому (пример: "\\MyServer\MyShare\" или "C:\". LPTSTR lpVolumeNameBuffer, //буфер - в котором будет храниться // имя тома DWORD nVolumeNameSize, //размер буфера LPDWORD lpVolumeSerialNumber, //серийный номер тома LPDWORD lpMaximumComponentLength, //размер тома LPDWORD lpFileSystemFlags, //тип файловой системы LPTSTR lpFileSystemNameBuffer, //название файловой системы DWORD nFileSystemNameSize //размер буфера под название ФС ); |
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | function GetVolumeInformation( lpRootPathName: PChar; //путь к сетевому или локальному //тому (пример: "\\MyServer\MyShare\" или "C:\". lpVolumeNameBuffer: PChar; //буфер - в котором будет храниться // имя тома nVolumeNameSize: DWORD; //размер буфера lpVolumeSerialNumber: PDWORD; //серийный номер тома var lpMaximumComponentLength, lpFileSystemFlags: DWORD; //размер // тома и тип файловой системы lpFileSystemNameBuffer: PChar; //название файловой системы nFileSystemNameSize: DWORD //размер буфера под название ФС ): BOOL; stdcall; |
Замечание: Если в качестве первого параметра указать для С/С++ NULL, а для Delphi — nil то функция будет выполняется для текущего диска (с которого была запущена программа).
Ну а теперь собственно для пущего интересу приведу пример, как привязать программу к устройству. В данном примере будем привязывать программу к флешке. Смотрим пример:
C/C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #include #include #include #include using namespace std; int main() { // Получаем тип носителя с которого запущена программа unsigned int drive_type = GetDriveType( NULL ); char VolumeNameBuffer[100]; char FileSystemNameBuffer[100]; DWORD sz,fs; unsigned long drive_sn; GetVolumeInformationA( NULL, VolumeNameBuffer, 100, &drive_sn, sz, fs, FileSystemNameBuffer, 100 ); cout << "Volume serial number:\t"; if(drive_sn == 1018821877) //сравниваем серийный номер cout << "correct" << endl; else cout << "invalid" << endl; cout << "Drive type:\t"; if(drive_type == DRIVE_REMOVABLE) cout << "correct" << endl; else cout << "invalid" << endl; getch(); } |
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | program Project1; {$APPTYPE CONSOLE} uses SysUtils,windows; var SerialNum,dtyp:DWORD; a,b:DWORD; Buffer,disk :Array[0..255]of char; begin dtyp:=GetDriveType(nil); if dtyp = DRIVE_REMOVABLE then writeln('Disk(type): Yes') else writeln('Disk(type): No'); GetVolumeInformation( nil, Buffer, sizeof(Buffer), @SerialNum, a, b, nil, 0); if SerialNum = 1018821877 then //сравниваем серийный номер writeln('S\N: Yes') else writeln('S\N: No'); readln; end. |
Замечание: Может возникнуть вопрос, а как узнать серийник диска, чтобы знать с чем сравнивать? Очень просто, для этого пишем тестовую прогу, в которой пишем следующий код:
C/C++
1 2 3 4 5 6 7 | ... GetVolumeInformationA(NULL, VolumeNameBuffer,100, &drive_sn,sz,fs,FileSystemNameBuffer,100); ... |
Delphi
1 2 3 4 5 6 7 8 9 10 11 | ... GetVolumeInformation(nil,Buffer,sizeof(Buffer), @SerialNum, a,b, nil, 0); writeln('S/N drive: ',SerialNum); readln; ... |
Очень нужная вещь эта привязка. Спасибо коффи
Автор, за статью респект. В частности за реализацию на Делфи
Не слишком поднодит для привязки. Поскольку этот серийник меняется при форматировании.
Из описания GetVolumeInformation Function в MSDN :
This function returns the volume serial number that the operating system assigns when a hard disk is formatted. To programmatically obtain the hard disk’s serial number that the manufacturer assigns, use the Windows Management Instrumentation (WMI) Win32_PhysicalMedia property SerialNumber.
Не понимаю Alexey, как вы так пришли к этому выводу… Может вы не понимаете определения привязки? … привязка представляет собой некоторую зависимость, при которой в данном случае будет запускаться/не запускаться программа. А от того что при форматировании сменился номер, фигово то тому кто отформатировал, потому что прога уже работать не будет(пока номер не сменить обратно)
… но соглашусь в одном что данный пример привязки очень прост, и достаточно быстро обходиться =)
А что тут непонятного? «Поскольку этот серийник меняется при форматировании.» — по моему, всё очевидно.