Ну или DVD-ROM’ом - какая разница… Я ж не диски собираюсь записывать из собственной программы, а просто поприкалываться:)
Сегодня мы будем творить всякую фигню с оптическим приводом. Я думаю, читатели уже видели в примерах к FASM’у программку BEER, которая открывает лоток (трей) CD/DVD-привода. Сегодня мы возьмём за основу эту идею и доработаем:)

За помощь в написании этой статьи я благодарен Monah’у - именно он, не пожалев времени и денег, прислал мне по почте MSDN, без которого мне было бы негде искать описания жизненно важных функций и параметров

Закроем и откроем

Программка BEER основана на использовании функции mciSendStringA из библиотеки winmm.dll, позволяющей отсылать мультимедиа-устройствам строковые команды (для CD-ROM’а - через драйвер mcicda.dll). В примере устройству отсылаются команды ‘open cdaudio’, ’set cdaudio door open’, ‘close cdaudio’, т.е. открыли устройство, приказали ему выбросить трей и закрыли устройство. Логично… На личном опыте я убедился, что без ‘open cdaudio’ и ‘close cdaudio’ вполне можно обойтись. А вместо ’set cdaudio door open’ знающие люди советуют применять ’set cdaudio door open wait’ и парную ей ’set cdaudio door closed wait’, хотя, вроде, и без ‘wait’ всё рабоотает…
Прошу читателей учесть, что на моём компе всего один оптический привод. Владельцы двух и более CD/DVD-приводов могут столкнуться с тем, что использование mciSendString приведёт к реакции со стороны сразу всех приводов
Другая проблема заключается в том, что в инклуде win32axp.inc нет импорта из winmm.dll. Я всячески пытался прикрутить его туда, даже собственную инклуду писал - не получилось:( Сами понимаете, что глупо инклудить win32a.inc и писать весь импорт вручную только из-за одной функции, поэтому мы воспользуемся поистине тру-хакерским способом:) - вызвав LoadLibrary и GetProcAddress
LoadLibrary принимает в качестве параметра указатель на строку с именем подгружаемой .dll-ки и возвращает её хэндл. Затем мы вызовем GetProcAddress и передадим ей этот хэндл и указатель на строку с именем искомой функции. Результатом этих страданий станет адрес функции, которую мы теперь вправе свободно вызывать
Можно хоть весь импорт через LoadLibrary+GetProcAddress обустроить… Как? Спросите у вирмейкеров:)

По поводу интерфейса я особо не запаривался: решил создать диалоговое окошко с тремя кнопками: “Открыть”, “Закрыть” и “Выход”. В одной из статей мы уже рассматривали основы создания диалоговых окон и обработки сообщений от контролов, так что, думаю, проблем с этим возникнуть не должно
Секцию ресурсов я буду оформлять в отдельном .rc-скрипте. О написании и компиляции .rc-скриптов я уже писал. Единственное, что в этом деле изрядно напрягает - это тупость используемого для компиляции масмовского rc.exe, вечно ругающегося на символьные имена стилей диалога… Хорошо хоть, что их числовые коды всегда можно подсмотреть в масмовском же windows.inc

.rc-скрипт (я обозвал его CDzp.rc) выглядит так:

//из-за того, что масмовский rc.exe
//не распознаёт используемые символьные имена,
//я вынужден писать кучи дефайнов.
//читатель может не геморроиться,
//а сразу использовать их числовые значения
#define LANG_ENGLISH 0x9
#define LANG_RUSSIAN 0x19

#define DS_MODALFRAME 0x0080L
#define WS_POPUP 0x80000000L
#define WS_CAPTION 0x0C00000L
#define WS_SYSMENU 0x80000L
#define BS_DEFPUSHBUTTON 0x1
#define WS_CHILD 0x40000000L
#define WS_VISIBLE 0x10000000L
#define WS_TABSTOP 0x10000L
#define BS_PUSHBUTTON 0x0

//следующие три дефайна -
//ID'ы кнопок
//вы вправе давать им любые имена
//и почти любые числовые значения в пределах dword,
//главное - чтобы в .rc и .asm эти имена
//и числа были одинаковы
#define ID_OPEN 0x101L
#define ID_CLOSE 0x102L
#define ID_EXIT 0x103L

//описываем диалоговое окно с именем 69
//имя взято от балды
//возбуждает меня это число:)
69 DIALOG 70, 70, 300, 40
STYLE  DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Владыка CD-ROM'
а  ver. 0.0.0.1"
LANGUAGE LANG_ENGLISH, LANG_RUSSIAN
FONT 8, "
MS Sans Serif"
{
CONTROL "
Открыть",ID_OPEN, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 85, 20, 45, 15
CONTROL "
Закрыть",ID_CLOSE, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 135, 20, 45, 15
CONTROL "
Выход",ID_EXIT,BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP,185, 20, 45, 15
}

//у нас всё должно быть по-взрослому
//сделаем в свойствах файла вкладку "
Версия"
1 VERSIONINFO
FILEVERSION 0,0,0,1
PRODUCTVERSION 0,0,0,1
FILEOS 0x4
FILETYPE 0x1
{BLOCK "
StringFileInfo"
{BLOCK "
06660666"//типа, я - сатанист:)
{
VALUE "
CompanyName", "RussianFuckersTeam"
VALUE "
FileDescription", "Владыка CD-ROM'а"
VALUE "FileVersion", "0.0.0.1"
VALUE "InternalName", "Прога для сидюка №0"
VALUE "LegalCopyright", "Copyright © 2007 Adrax"
VALUE "LegalTrademarks", "Владыка CD-ROM'
а - unregistered trademark of RFTeam"
VALUE "
OriginalFilename", "CDzp.exe"
VALUE "
ProductName", "Владыка CD-ROM'а"
VALUE "ProductVersion", "0.0.0.1"
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0666 0x0666
//подтверждаем сатанизм:)
}
}
}

Сам же исходник, коий я нарёк CDzp.asm представляет собой следующее:

format PE GUI 4.0

include 'win32axp.inc'

;определяем ID'ы кнопок
ID_OPEN = 101h
ID_CLOSE = 102h
ID_EXIT = 103h

.data
;разместим в секции данных команды для CD-ROM'а
eject db 'set cdaudio door open wait',0
closed db 'set cdaudio door closed wait',0
;выделим dword под адрес функции
NashaFunka dd ?
;укажем, что и где искать
name db 'mciSendStringA',0
dllka db 'winmm.dll',0

.code

fuck:

invoke LoadLibrary,dllka
invoke GetProcAddress,eax,name
mov [NashaFunka],eax
;получили адрес mciSendStringA
;и сохранили его в переменной NashaFunka
invoke GetModuleHandle,0
invoke DialogBoxParam,eax,69,HWND_DESKTOP,ProceduraDialoga,0
;создали диалоговое окно с именем 69 из секции ресурсов
invoke ExitProcess,0

;процедура диалога - самая обычная
proc ProceduraDialoga,hwnddlg,msg,wparam,lparam
push ebx
push esi
push edi
cmp [msg],WM_INITDIALOG
je Initialization
cmp [msg],WM_COMMAND
je Command
cmp [msg],WM_CLOSE
je Exit
xor eax,eax
jmp Finish

Command:
cmp [wparam],BN_CLICKED shl 16 + ID_OPEN
je Open;если нажали "Открыть"
cmp [wparam],BN_CLICKED shl 16 + ID_CLOSE
je Close;если нажали "Закрыть"

invoke SendMessage,[hwnddlg],WM_CLOSE,0,0
;если нажали не "Открыть" и не "Закрыть",
;значит, нажали "Выход",
;и мы пошлём сами себе WM_CLOSE

Initialization:
xor eax,eax
inc eax
;помещаем в eax единицу перед выходом
;это знак успешной работы

Finish:
pop edi
pop esi
pop ebx
ret

Exit:
invoke EndDialog,[hwnddlg],-1
jmp Finish

;в обработчиках Open и Close
;мы шлём команды CD-ROM'у,
;после чего прыгаем
;на метку Initialization

Open:
invoke NashaFunka,eject,0,0,0
jmp Initialization

Close:
invoke NashaFunka,closed,0,0,0
jmp Initialization

endp
.end fuck

section '.rsrc' resource from 'CDzp.res' data readable discardable
;секцию ресурсов построим из файла

Теперь компилируем CDzp.rc в CDzp.res, после чего компилим и .asm-исходник

Если CD-ROM не один

Но что делать, если у пользователя несколько CD-ROM’ов? Надо же как-то выбрать, какому именно из них послать команду?
Логика подсказывает, что различать приводы между собой проще всего по присвоенным ими буквам. В принципе, можно продолжить юзать семейство функций mci, но я решил пойти другим путём. Всё-таки, mci-функции создавались не для открытия/закрытия трея CD-ROM’а, а для управления мультимедиа-устройствами в плане воспроизведения музыки и видео
Мы заюзаем DeviceIoControll
DeviceIoControl позволяет отсылать драйверу устройства особые FSCTL/IOCTL-команды. Первые относятся к файловой системе, вторые - к вводу/выводу. Прототип её в MSDN таков:

BOOL DeviceIoControl(
HANDLE hDevice,//хэндл устройства
DWORD dwIoControlCode,//команда
LPVOID lpInBuffer,//указатель на буфер ввода
DWORD nInBufferSize,//его размер
LPVOID lpOutBuffer,//указатель на буфер вывода
DWORD nOutBufferSize,//его размер
LPDWORD lpBytesReturned,//указатель на переменную-счётчик
(сколько байт в буфере вывода)
LPOVERLAPPED lpOverlapped//указатель на структуру OVERLAPPED
);

Искать в описании DeviceIoControl перечень доступных IOCTL-команд бессмысленно - эти команды зависят не от функции, а от драйвера устройства. Поэтому приходится лезть в DDK, искать там доку “Storage Devices” и разбираться, какие же команды переваривают оптические приводы. Я вот залез и нашёл IOCTL_STORAGE_EJECT_MEDIA и IOCTL_STORAGE_LOAD_MEDIA - думаю, их имена говорят сами за себя. Хуже другое: FASM, разумеется, не содержит этих команд в своих инклудах, поэтому придётся лезть за ними в хидеры от DDK (эти две команды описаны в ntddstor.h), но и там вас ждёт шок. Описуха IOCTL_STORAGE_EJECT_MEDIA, например, выглядит так: #define IOCTL_STORAGE_EJECT_MEDIA CTL_CODE(IOCTL_STORAGE_BASE, 0×0202, METHOD_BUFFERED, FILE_READ_ACCESS).  Эта китайская грамота переводится следующим образом: чтобы узнать числовое значение этой команды, нужно взять DDK’шный макрос CTL_CODE и передать ему данные в скобках. В общем, геморройно всё это…
Другой вопрос - как узнать от пользователя, какой именно из CD-ROM’ов нужно открыть/закрыть? С этой целью я решил впихнуть в окно приложения Edit, куда бы юзер вводил букву диска. Эту букву надо будет считать и скомпоновать строку вида \\.\X:, где X - принятая буква. Именно такую строку нужно передать функции CreateFile, чтобы открыть диск как файл и получить его хэндл

Что ж, основные моменты обсудили, пора приступать к кодингу. Скрипт ресурсов я написал такой:

#define LANG_ENGLISH 0x9
#define LANG_RUSSIAN 0x19
#define DS_MODALFRAME 0x0080L
#define WS_POPUP 0x80000000L
#define WS_CAPTION 0x0C00000L
#define WS_SYSMENU 0x80000L
#define BS_DEFPUSHBUTTON 0x1
#define WS_CHILD 0x40000000L
#define WS_VISIBLE 0x10000000L
#define WS_TABSTOP 0x10000L
#define BS_PUSHBUTTON 0x0
#define SS_LEFT 0x0
#define ES_LEFT 0x0
#define WS_BORDER 0x800000L

#define ID_OPEN 0x101L
#define ID_CLOSE 0x102L
#define ID_EXIT 0x103L
#define ID_EDIT 0x104L

69 DIALOG 70, 70, 160, 50
STYLE  DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Владыка CD-ROM'а  ver. 0.0.1.15"
LANGUAGE LANG_ENGLISH, LANG_RUSSIAN
FONT 8, "MS Sans Serif"
{
CONTROL "Введите букву CD-ROM'а:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE, 10, 10, 100, 8
CONTROL "", ID_EDIT, EDIT, ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 110, 10, 30, 13
CONTROL "Открыть",ID_OPEN, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 10, 30, 45, 15
CONTROL "Закрыть",ID_CLOSE, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 60, 30, 45, 15
CONTROL "Выход",ID_EXIT,BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP,110, 30, 45, 15
}

1 VERSIONINFO
FILEVERSION 0,0,1,15
PRODUCTVERSION 0,0,1,15
FILEOS 0x4
FILETYPE 0x1
{BLOCK "StringFileInfo"
{BLOCK "06660666"
{
VALUE "CompanyName", "RussianFuckersTeam"
VALUE "FileDescription", "Владыка CD-ROM'а"
VALUE "FileVersion", "0.0.1.15"
VALUE "InternalName", "Прога для сидюка №0"
VALUE "LegalCopyright", "Copyright © 2007 Adrax"
VALUE "LegalTrademarks", "Владыка CD-ROM'а - unregistered trademark of RFTeam"
VALUE "OriginalFilename", "CDzap.exe"
VALUE "ProductName", "Владыка CD-ROM'а"
VALUE "ProductVersion", "0.0.1.15"
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0666 0x0666
}
}
}

Думаю, в .rc-скрипте читатали разберутся самостоятельно. А вот сам исходник получился немного запутанный, поэтому я постараюсь его подробно прокомментировать:

format PE GUI 4.0
include 'win32axp.inc'

ID_OPEN = 101h
ID_CLOSE = 102h
ID_EXIT = 103h
ID_EDIT = 104h
;ID'ы контролов

FILE_GENERIC_READ = 1
;эту константу мне порекомендовал RamMerLabs,
;и она работает!

FILE_DEVICE_MASS_STORAGE = 2Dh
METHOD_BUFFERED = 0
FILE_ANY_ACCESS = 0
FILE_READ_ACCESS = 1
FILE_WRITE_ACCESS = 2
;вся вышеперечисленная хрень нужна нам, чтобы вычислить
;числовые значения IOCTL-команд
;а вот, собственно, сами вычисления

IOCTL_STORAGE_EJECT_MEDIA = (FILE_DEVICE_MASS_STORAGE shl 16) or (FILE_READ_ACCESS shl 14) or (202h shl 2) or METHOD_BUFFERED
IOCTL_STORAGE_LOAD_MEDIA = (FILE_DEVICE_MASS_STORAGE shl 16) or (FILE_READ_ACCESS shl 14) or (203h shl 2) or METHOD_BUFFERED
.data
bukva rb 1;байт под букву
handl dd ?;dword под хэндл привода
xren rb 7;7 байт под строку
dvoet db ':',0;символ двоеточия
byr dd ?;вспомогательная переменная для DeviceIoControl

.code
fuck:
invoke GetModuleHandle,0
invoke DialogBoxParam,eax,69,HWND_DESKTOP,ProceduraDialoga,0
invoke ExitProcess,0
;нижележащий код прост до ужаса
;обработчики нажатий на кнопки, процедура диалога -
;всё это давно пройдено нами
;весь сакральный смысл заключён в функции Edit

proc ProceduraDialoga,hwnddlg,msg,wparam,lparam
push ebx
push esi
push edi
cmp [msg],WM_INITDIALOG
je Initialization
cmp [msg],WM_COMMAND
je Command
cmp [msg],WM_CLOSE
je Exit
xor eax,eax
jmp Finish

Command:
cmp [wparam],BN_CLICKED shl 16 + ID_EXIT
je Exit
cmp [wparam],BN_CLICKED shl 16 + ID_CLOSE
je Close
cmp [wparam],BN_CLICKED shl 16 + ID_OPEN
je Open

Initialization:
xor eax,eax
inc eax

Finish:
pop edi
pop esi
pop ebx
ret

Exit:
invoke EndDialog,[hwnddlg],-1
jmp Finish

Open:
call Edit
invoke DeviceIoControl,[handl],IOCTL_STORAGE_EJECT_MEDIA,0,0,0,0,byr,0
jmp Initialization

Close:
call Edit
invoke DeviceIoControl,[handl],IOCTL_STORAGE_LOAD_MEDIA,0,0,0,0,byr,0
jmp Initialization

Edit:
invoke RtlZeroMemory,xren,7
;вычищаем буфер xren
invoke lstrcpy,xren,'\\.\'
;копируем туда вот такую строчку: \\.\
invoke CloseHandle,[handl]
;закрываем предыдущий хэндл привода
invoke GetDlgItemText,[hwnddlg],ID_EDIT,bukva,2
;копируем букву в переменную bukva
invoke lstrcat,xren,bukva
;сшиваем \\.\ и букву
invoke lstrcat,xren,dvoet
;добавляем двоеточие
invoke CreateFile,xren,FILE_GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0
mov [handl],eax
;получаем хэндл оптического привода
retn

endp

.end fuck

section '.rsrc' resource from 'CDzap.res' data readable discardable

Странноват, конечно, исходничек… Длины буферов и число копирумых байт я выбирал почти наугад, прогоняя прогу под отладчиком и контролируя формирование строк. Я просил помощи на форуме WASM.RU, но никто не захотел мне помочь, поэтому пришлось до всего доходить самому

Кстати, при написании этой программы произошёл вполне забавный случай. Функция CreateFile раз за разом возвращала INVALID_HANDLE_VALUE (-1) при попытке обращения к DVD-приводу. Я раз пять переписывал код, гонял его отладчиком в поисках ошибок, просил помощи на форуме (кстати, вот про CreateFile мне на форуме рассказали многое - спасибо RamMerLabs и q_q), но всё было тщетно…
Причина моих злоключений была банальна - работающая виртуальная машина VmWare, блокирующая доступ к DVD-приводу. Как только я закрыл VmWare, CreateFile вернула нужный хэндл!

Блокировка CD

Каждый оптический привод обладает замечательной фичей - счётчиком блокировок, изменяющимся от нуля до… не знаю, в общем, до куда:) Фишка в том, что если значение счётчика не равно нулю, то в этот момент привод не реагирует на нажатие кнопки извлечения диска, что может быть полезно, например, в программе прожига болванок - чтобы юзер сгоряча не вытащил диск в момент записи. В такой момент освободить диск можно либо ткнув спицей-эжектором в отверстие для аварийного извлечения диска, либо сбросив счётчик (например, перезагрузив компьютер). Сейчас мы добавим к нашей программке пару кнопок для увеличения и уменьшения значения счётчика блокировок
Поможет нам в этом IOCTL-команда IOCTL_CDROM_MEDIA_REMOVAL, числовое значение которой я нашёл у Мыщъха - 24804h. Для работы ей необходим указатель на структуру _PREVENT_MEDIA_REMOVAL, которая не описана в инклудах FASM, но её можно подсмотреть в ntddstor.h из DDK. Эта структура содержит в себе одну логическую (BOOLEAN) переменную PreventMediaRemoval, которая определяет поведение команды IOCTL_CDROM_MEDIA_REMOVAL: если TRUE - заблокировать привод, если FALSE - разблокировать

Обращу ваше внимание на тот факт, что в отличие от предыдущих описанных IOCTL-команд IOCTL_CDROM_MEDIA_REMOVAL срабатывает только, если в приводе присутствует диск. Пустой CD/DVD-ROM заблокировать не удастся

Что ж, приступим к кодингу. Скрипт ресурсов изменился совсем незначительно:

#define LANG_ENGLISH 0x9
#define LANG_RUSSIAN 0x19
#define DS_MODALFRAME 0x0080L
#define WS_POPUP 0x80000000L
#define WS_CAPTION 0x0C00000L
#define WS_SYSMENU 0x80000L
#define BS_DEFPUSHBUTTON 0x1
#define WS_CHILD 0x40000000L
#define WS_VISIBLE 0x10000000L
#define WS_TABSTOP 0x10000L
#define BS_PUSHBUTTON 0x0
#define SS_LEFT 0x0
#define ES_LEFT 0x0
#define WS_BORDER 0x800000L
#define ID_OPEN 0x101L
#define ID_CLOSE 0x102L
#define ID_EXIT 0x103L
#define ID_EDIT 0x104L
#define ID_LOCK 0x105L
#define ID_UNLOCK 0x106L

69 DIALOG 70, 70, 160, 70
STYLE  DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Владыка CD-ROM'а  ver. 0.0.1.15"
LANGUAGE LANG_ENGLISH, LANG_RUSSIAN
FONT 8, "MS Sans Serif"
{
CONTROL "Введите букву CD-ROM'а:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE, 10, 10, 100, 8
CONTROL "", ID_EDIT, EDIT, ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 110, 10, 30, 13
CONTROL "Открыть",ID_OPEN, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 10, 30, 45, 15
CONTROL "Закрыть",ID_CLOSE, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 60, 30, 45, 15
CONTROL "Выход",ID_EXIT,BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP,110, 30, 45, 15
CONTROL "Блокировать",ID_LOCK, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 15,50,65,15
CONTROL "Разблокировать",ID_UNLOCK, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 85,50,65,15
}

1 VERSIONINFO
FILEVERSION 0,1,0,3
PRODUCTVERSION 0,1,0,3
FILEOS 0x4
FILETYPE 0x1
{BLOCK "StringFileInfo"
{BLOCK "06660666"
{
VALUE "CompanyName", "RussianFuckersTeam"
VALUE "FileDescription", "Владыка CD-ROM'а"
VALUE "FileVersion", "0.1.0.3"
VALUE "InternalName", "Прога для сидюка №0"
VALUE "LegalCopyright", "Copyright © 2007 Adrax"
VALUE "LegalTrademarks", "Владыка CD-ROM'а - unregistered trademark of RFTeam"
VALUE "OriginalFilename", "CDzap.exe"
VALUE "ProductName", "Владыка CD-ROM'а"
VALUE "ProductVersion", "0.1.0.3"
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0666 0x0666
}
}
}

В .asm-исходник добавились новые переменные и два обработчика нажатий кнопок:

format PE GUI 4.0
include 'win32axp.inc'

ID_OPEN = 101h
ID_CLOSE = 102h
ID_EXIT = 103h
ID_EDIT = 104h
ID_LOCK = 105h
ID_UNLOCK = 106h

FILE_GENERIC_READ = 1

FILE_DEVICE_MASS_STORAGE = 2Dh
METHOD_BUFFERED = 0
FILE_ANY_ACCESS = 0
FILE_READ_ACCESS = 1
FILE_WRITE_ACCESS = 2

IOCTL_STORAGE_EJECT_MEDIA = (FILE_DEVICE_MASS_STORAGE shl 16) or (FILE_READ_ACCESS shl 14) or (202h shl 2) or METHOD_BUFFERED
IOCTL_STORAGE_LOAD_MEDIA = (FILE_DEVICE_MASS_STORAGE shl 16) or (FILE_READ_ACCESS shl 14) or (203h shl 2) or METHOD_BUFFERED
IOCTL_CDROM_MEDIA_REMOVAL = 24804h

;описываем структуру,
;состоящую из одного 4-байтового поля
struct _PREVENT_MEDIA_REMOVAL
PreventMediaRemoval dd ?
ends

.data
bukva rb 1
handl dd ?
xren rb 7
dvoet db ':',0
byr dd ?
prevent _PREVENT_MEDIA_REMOVAL
;заводим переменную prevent,
;представляющую собой структуру
;_PREVENT_MEDIA_REMOVAL

.code
fuck:
invoke GetModuleHandle,0
invoke DialogBoxParam,eax,69,HWND_DESKTOP,ProceduraDialoga,0
invoke ExitProcess,0

proc ProceduraDialoga,hwnddlg,msg,wparam,lparam
push ebx
push esi
push edi
cmp [msg],WM_INITDIALOG
je Initialization
cmp [msg],WM_COMMAND
je Command
cmp [msg],WM_CLOSE
je Exit
xor eax,eax
jmp Finish

Command:
cmp [wparam],BN_CLICKED shl 16 + ID_EXIT
je Exit
cmp [wparam],BN_CLICKED shl 16 + ID_CLOSE
je Close
cmp [wparam],BN_CLICKED shl 16 + ID_OPEN
je Open
cmp [wparam],BN_CLICKED shr 16 + ID_LOCK
je Block
cmp [wparam],BN_CLICKED shr 16 + ID_UNLOCK
je Unlock

Initialization:
xor eax,eax
inc eax

Finish:
pop edi
pop esi
pop ebx
ret

Exit:
invoke EndDialog,[hwnddlg],-1
jmp Finish

Open:
call Edit
invoke DeviceIoControl,[handl],IOCTL_STORAGE_EJECT_MEDIA,0,0,0,0,byr,0
jmp Initialization

Close:
call Edit
invoke DeviceIoControl,[handl],IOCTL_STORAGE_LOAD_MEDIA,0,0,0,0,byr,0
jmp Initialization

Block:
call Edit
mov [prevent.PreventMediaRemoval],1
;PreventMediaRemoval = TRUE
invoke DeviceIoControl,[handl],IOCTL_CDROM_MEDIA_REMOVAL,prevent,sizeof._PREVENT_MEDIA_REMOVAL,0,0,byr,0
jmp Initialization

Unlock:
call Edit
mov [prevent.PreventMediaRemoval],0
;PreventMediaRemoval = FALSE
invoke DeviceIoControl,[handl],IOCTL_CDROM_MEDIA_REMOVAL,prevent,sizeof._PREVENT_MEDIA_REMOVAL,0,0,byr,0
jmp Initialization

Edit:
invoke RtlZeroMemory,xren,7
invoke lstrcpy,xren,'\\.\'
invoke CloseHandle,[handl]
invoke GetDlgItemText,[hwnddlg],ID_EDIT,bukva,2
invoke lstrcat,xren,bukva
invoke lstrcat,xren,dvoet
invoke CreateFile,xren,FILE_GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0
mov [handl],eax
retn

endp
.end fuck

section '.rsrc' resource from 'CDzip.res' data readable discardable

Компилируем .rc, компилируем .asm - и получаем такую вот прикольную программку. Учитывая, что она весит всего 4 Кб и может запускаться и с дискеты, и с флэшки, можно поиздеваться над друзьями и коллегами по работе - запереть диски в их CD-ROM’ах, пусть перезагружаются:)
Программка немного притормаживает при первом нажатии на кнопку “Блокировать”, но тут моей вины нет - эта задержка вызвана распознаванием диска. Хотя код, конечно, можно бы и пооптимизировать и прикрутить вывод сообщений об ошибке…

Как видите, на Ассемблере можно написать маленькую и прикольную программку, которая доставит много радости вам и вашим друзьям:)Пусть даже ради её аписания пришлось часами рыться в MSDN и DDK…

Автор: Adrax