Для записи в файл мы можем использовать ранее рассмотренную функцию WriteFile, передавая ей дескриптор открытого файла. Например:
includelib kernel32.lib ; подключаем библиотеку kernel32.lib extrn WriteFile: proc extrn CreateFileA: proc extrn CloseHandle: proc .data text byte "Hello METANIT.COM" ; тест для записи textLen = $ - text ; длина текста filename byte "test.txt",0 ; имя файла .code ; Параметры: ; RSI - адрес строки ; RCX - длина строки ; RAX - дескриптор файла ; Результат - через RAX возвращаем количество записанных байтов write proc sub rsp, 56 mov rdx, rsi ; Второй параметр - строка mov r8, rcx ; Третий параметр - длина строки mov rcx, rax ; Первый параметр WriteFile - в регистр RCX помещаем дескриптор файла - консольного вывода lea r9, bytesWritten ; Четвертый параметр WriteFile - адрес для получения записанных байтов mov qword ptr [rsp + 32], 0 ; Пятый параметр WriteFile call WriteFile test rax, rax ; проверяем на наличие ошибки mov eax, bytesWritten ; если все нормально, помещаем в RAX количество записанных байтов jnz exit mov rax, -1 ; Возвращаем через RAX код ошибки exit: add rsp, 56 ret bytesWritten equ [rsp+40] write endp ; Процедура открытия существующего файла ; Параметр - в RSI - имя файла ; Результат - в RAX - дескриптор открытого файла openFile proc sub rsp, 56 mov rcx, rsi ; имя файла mov rdx, 080000004h ; доступ на запись и дозапись xor r8, r8 ; Эксклюзивный доступ xor r9, r9 ; атрибуты безопасности отсутствуют mov r10, 1 ; открыть существующий файл, если он не существует - создать mov [rsp + 32], r10 mov r10, 128 ; обычный файл без атрибутов mov [rsp + 40], r10 mov [rsp + 48], r9 ; NULL - шаблон не используется call CreateFileA add rsp, 56 ret openFile endp ; Процедура закрытия существующего файла ; Параметр - в RAX - дескриптор открытого файла ; Результат - в RAX - состояние операции (ошибка или нет) closeFile proc sub rsp, 40 mov rcx, rax ; В RCX помещаем дескриптор файла call CloseHandle add rsp, 40 ret closeFile endp main proc lea rsi, filename call openFile ; открываем файл push rax ; сохраняем дескриптор файла lea rsi, text ; записываемая строка mov rcx, textLen ; длина записываемой строки call write ; записываем строку pop rax ; восстанавливаем дескриптор файла для закрытия файла call closeFile ; закрываем файл ret main endp end
Здесь запись в файл производится в процедуре write, которая через регистр RSI получает адрес записываемого текста, через RCX - количество записываемых символов, а через RAX - дескриптор файла. Если файл уже существует, то выполняется дозапись.
При записи могут возникнуть ошибки. Так, в программе выше у нас несколько потенциальных ситуации возникновения ошибки - файл не будет открыт, запись не будет произведена, закрытие файла пройдет неудачно. В качестве примера рассмотрим обработку ошибки при открытии файла:
includelib kernel32.lib extrn GetStdHandle: proc extrn WriteFile: proc extrn CreateFileA: proc extrn CloseHandle: proc .data text byte "Hello METANIT.COM", 10 ; тест для записи textLen = $ - text ; длина текста filename byte "test.txt",0 ; имя файла badOpenMsg byte "Unable to open file", 10 badOpenMsgLen = $ - badOpenMsg goodWriteMsg byte "File has been written", 10 goodWriteMsgLen = $ - goodWriteMsg stdout qword 0 ; дескриптор консольного вывода stdoutSet byte 0 ; установлен ли дескриптор консольного вывода .code ; Параметры: ; RSI - адрес строки ; RCX - длина строки ; RAX - дескриптор файла ; Результат - через RAX возвращаем количество записанных байтов write proc sub rsp, 56 mov rdx, rsi ; Второй параметр - строка mov r8, rcx ; Третий параметр - длина строки mov rcx, rax ; Первый параметр WriteFile - в регистр RCX помещаем дескриптор файла - консольного вывода lea r9, bytesWritten ; Четвертый параметр WriteFile - адрес для получения записанных байтов mov qword ptr [rsp + 32], 0 ; Пятый параметр WriteFile call WriteFile test rax, rax ; проверяем на наличие ошибки mov eax, bytesWritten ; если все нормально, помещаем в RAX количество записанных байтов jnz exit mov rax, -1 ; Возвращаем через RAX код ошибки exit: add rsp, 56 ret bytesWritten equ [rsp+40] write endp ; Процедура вывода произвольной строки на консоль ; Параметры ; RSI - адрес строки ; RCX - количество символов ; Результат - в RAX количество записанных байтов или -1, если произошла ошибка writeToConsole proc cmp stdoutSet, 1 jz writeData ; если дескриптор консольного вывода установлен sub rsp, 32 push rcx ; сохраняем количество символов mov rcx, -11 ; Аргумент для GetStdHandle - STD_OUTPUT call GetStdHandle ; вызываем функцию GetStdHandle pop rcx ; восстанавливаем RCX - количество символов add rsp, 32 mov stdout, rax ; помещаем в stdout дескриптор консольного вывода mov stdoutSet, 1 ; дескриптор консольного вывода установлен writeData: mov rax, stdout call write ret writeToConsole endp ; Процедура открытия существующего файла ; Параметр - в RSI - имя файла ; Результат - в RAX - дескриптор открытого файла openFile proc sub rsp, 56 mov rcx, rsi ; имя файла mov rdx, 0B0000004h ; доступ на запись и чтение xor r8, r8 ; Эксклюзивный доступ xor r9, r9 ; атрибуты безопасности отсутствуют mov r10, 4 ; открыть существующий файл, если он не существует - создать mov [rsp + 32], r10 mov r10, 128 ; обычный файл без атрибутов mov [rsp + 40], r10 mov [rsp + 48], r9 ; NULL - шаблон не используется call CreateFileA add rsp, 56 ret openFile endp ; Процедура закрытия существующего файла ; Параметр - в RAX - дескриптор открытого файла ; Результат - в RAX - состояние операции (ошибка или нет) closeFile proc sub rsp, 40 mov rcx, rax ; В RCX помещаем дескриптор файла call CloseHandle add rsp, 40 ret closeFile endp main proc lea rsi, filename call openFile ; открываем файл cmp eax, -1 ; если произошла ошибка открытия je badOpen push rax ; сохраняем дескриптор файла lea rsi, text ; записываемая строка mov rcx, textLen ; длина записываемой строки call write ; записываем строку pop rax ; восстанавливаем дескриптор файла для закрытия файла call closeFile ; закрываем файл lea rsi, goodWriteMsg mov rcx, goodWriteMsgLen call writeToConsole jmp exit badOpen: ; если не удалось открыть файл lea rsi, badOpenMsg mov rcx, badOpenMsgLen call writeToConsole exit: ret main endp end
Здесь добавлена процедура writeToConsole для вывода диагностического сообщения на консоль. Если при открытии файла возникает проблема, соответственно в регистре EAX отказывается число -1, то переходим к метке badOpen для обработки этой ситуации. В коде выше в этом случае просто выводим сообщение. Если же запись в файл прошла успешно, выводим другое сообщение на консоль.