Пять вариантов программы, которая выполняет следующее: открывает файл, читает его содержимое в память и в поле Edit, Вы что-либо меняете в Edit'e → изменения передаются в память, содержимое памяти сохраняется в файл. Во вложении1 четыре варианта, asm-\rc-файлы и иконка Во вложении2 пятый вариант, asm-\rc-файлы и иконкаGlobalAlloc, GlobalFreeПолучение блока памятиДля получения блока памяти используют функцию GlobalAlloc, Код (C): HGLOBAL WINAPI GlobalAlloc ( UINT fuAlloc, // тип выделяемой памяти DWORD cbAlloc // размер блока памяти в байтах Параметр fuAlloc определяет тип выделяемой памяти. Размер блока памяти в байтах должен передаваться через параметр cbAlloc. Функция возвращает идентификатор глобального блока памяти или NULL, если Windows не может выделить память указанного объема. Параметра fuAlloc должен быть указан как логическая комбинация следующих значений: ЗначениеhexОписаниеGMEM_FIXED0Заказывается фиксированный блок памяти. Этот флаг несовместим с флагом GMEM_MOVEABLE. Фиксированный сегмент, созданный с использованием флага GMEM_FIXED, является перемещаемым. Для такого сегмента в процессе перемещения логический адрес не изменяется, но линейный (и физический) может изменятьсяGMEM_MOVEABLE2Заказывается перемещаемый блок памяти. Логический адрес перемещаемого блока памяти может изменяться. Несовместим с флагом GMEM_FIXEDGMEM_NOCOMPACT10Для удовлетворения запроса памяти не следует выполнять объединение всех свободных участков памяти в один и удалять блоки памяти, отмеченные как удаляемыеGMEM_NODISCARD20Для удовлетворения запроса памяти не следует выполнять объединение всех свободных участков памяти в одинGMEM_ZEROINIT40Во все байты блока необходимо записать нулевые значенияGPTR40Комбинация флагов GMEM_FIXED и GMEM_ZEROINITGHDN42Комбинация флагов GMEM_MOVEABLE и GMEM_ZEROINITGMEM_MODIFY80Выполняется изменение характеристик существующего блока памяти. этот флаг необходимо указывать вместе с флагами GMEM_DISCARDABLE и GMEM_MOVEABLEGMEM_LOCKCOUNTFFGMEM_DISCARDABLE100Заказывается удаляемый блок памяти. Этот флаг должен использоваться совместно с флагом GMEM_MOVEABLE. Если блок памяти был перемещаемый, то теперь дополнительно он будет и удаляемый. Этот флаг должен использоваться совместно с флагом GMEM_MODIFYGMEM_NOT_BANKED1000Получить блок памяти вне фрейма дополнительной памяти EMSGMEM_LOWER1000Синоним GMEM_NOT_BANKEDGMEM_SHARE2000Синоним GMEM_DDESHAREGMEM_DDESHARE2000Блок памяти будет использоваться совместно несколькими приложениями при помощи механизма динамического обмена данными DDEGMEM_NOTIFY4000Если заказанный объект будет удален, требуется вызов процедуры извещения. Процедура извещения назначается функцией GlobalNotify и должна располагаться в фиксированном сегменте кода в библиотеке DLL. С ее помощью приложение может разрешить или запретить Windows удалять блок данныхGMEM_DISCARDED4000GMEM_VALID_FLAGS7F72GMEM_INVALID_HANDLE8000Фиксирование и расфиксирование блока памятиДля получения доступа к полученному блоку памяти его необходимо зафиксировать, вызвав функцию GlobalLock. GlobalLock возвращает указатель на данный объект памяти глобальных данных. Функция GlobalLock наращивает(увеличивает на единицу) счетчик блокировки перемещаемых объектов и фиксирует память. Фиксированная память не будет перемещаться или сбрасывается до тех пор, пока объект памяти не будет перераспределен функцией GlobalReAlloc. Объект остается блокированным (фиксированным) в памяти до тех пор, пока счетчик блокировки не уменьшится до нуля Код (C): GlobalLock( HGLOBAL hglb // дескриптор блокированного объекта памяти Возвращаемое значение указывает на первый байт памяти в глобальном объекте, если функция завершается успешно. Возвращаемое значение=0, если память объекта была сброшена, или произошла ошибка GlobalUnlock разблокирует данный объект памяти глобальных данных. Функция не имеет никакого действия на фиксированную память Код (C): GlobalUnlock( HGLOBAL hglb // дескриптор разблокируемой памяти глобальных данных Возвращаемое значение =0, если счетчик блокировки (фиксации) объекта был уменьшен (на единицу) до нуля. В противном случае, возвращаемое значение ≠ 0Определение идентификатора блока памяти по его адресуС помощью функции GlobalHandle можно, зная селектор блока памяти, определить идентификатор блока памяти Код (C): DWORD WINAPI GlobalHandle( UINT uGlobalSel // селекторная компонента логического адреса блока памяти Параметр uGlobalSel указывает селекторную компоненту логического адреса блока памяти. Младшее слово возвращаемого значения содержит идентификатор блока памяти, старшее - селектор блока памяти. В случае ошибки возвращается нулевое значение.Работа с удаляемыми блоками памятиЧтобы заказать удаляемый блок памяти, нужно указать флаги GMEM_DISCARDABLE и GMEM_MOVEABLE Код (ASM): invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_DISCARDABLE, 200000 При необходимости Windows может удалить полученный таким образом блок из памяти, сохранив только его идентификатор, и использовать ранее распределенную этому блоку памяти для других приложений.Определение состояния блока памятиПри попытке зафиксировать удаленный блок с помощью функции GlobalLock приложение получит от этой функции значение NULL. В этом случае следует вызвать функцию GlobalFlags, предназначенную для определения состояния блока памяти, и проверить, находится ли данный блок в удаленном состоянии. Код (C): UINT WINAPI GlobalFlags( HGLOBAL hglb // идентификатор блока памяти Функция возвращает состояние блока памяти, указанного своим единственным параметром. Младший байт возвращаемого значения содержит содержимое счетчика фиксаций блока памяти. В старшем байте могут быть установлены флаги GMEM_DISCARDABLE и GMEM_DISCARDED. Если установлен флаг GMEM_DISCARDABLE, проверяемый блок памяти может быть удален Windows в процессе дефрагментации свободной области памяти. Если же установлен флаг GMEM_DISCARDED, удаление блока памяти уже произошло.Изменение характеристик блока памятиДля получения доступа к удаленному блоку памяти его необходимо восстановить, вызвав функцию GlobalReAlloc. Эта функция позволяет изменить характеристики существующего блока памяти Если надо изменить размер заказанного ранее блока памяти, сделав его равным 51200 байт, можно для этого использовать следующий фрагмент кода: Код (ASM): invoke GlobalReAlloc,hmemGlobal, 51200, \ GMEM_MODIFY or GMEM_DISCARDABLE or GMEM_MOVEABLE or GMEM_ZEROINIT mov hmemGlobal,rax После вызова функции блок памяти будет перемещаемый и удаляемый, причем если раньше его размер был меньше 51200 байт, во все байты дополнительной памяти будут записаны нулевые значения. При увеличении размера блока может возникнуть ситуация нехватки памяти, поэтому проверяйте значение идентификатора, возвращаемой функцией GlobalReAlloc, на неравенство нулю. Можно инициировать удаление блока памяти при помощи функции GlobalReAlloc, если указать нулевой размер блока и флаг GMEM_MOVEABLE. Определение размера блока памятиС помощью функции GlobalSize можно определить размер блока памяти по его идентификатору: Код (C): DWORD WINAPI GlobalSize( HGLOBAL hglb // идентификатор глобального блока памяти Функция возвращает размер блока памяти, идентификатор которого задан параметром hglb. Если указанный блок памяти не существует или удален, возвращается нулевое значениеДефрагментация памятиПосле вызова GlobalCompact, Windows выполнит объединение всех свободных блоков в один. В качестве параметра функции необходимо указать требуемый размер непрерывного блока свободной памяти: Код (C): DWORD WINAPI GlobalCompact( DWORD dwMinFree // требуемый размер непрерывного блока свободной памяти Функция возвращает размер самого большого доступного непрерывного блока памяти, причем, если параметр не равен 0 или -1, выполняется дефрагментация памяти и удаление блоков, отмеченных как удаляемые. Если параметр функции указан как 0 или -1, функция не выполняет дефрагментацию памяти, но возвращает правильное значение с учетом возможного выполнения дефрагментации.Освобождение блока памятиДля освобождения глобального блока памяти, полученного от функции GlobalAlloc, вы должны использовать функцию GlobalFree Код (C): HGLOBAL WINAPI GlobalFree( HGLOBAL hglb // идентификатор глобального блока памяти Идентификатор освобождаемого блока передается функции в качестве ее единственного параметра. Функция возвращает NULL при успешном завершении или значение hglb при ошибке. Перед освобождением зафиксированных блоков памяти их следует предварительно расфиксировать. Можно узнать содержимое счетчика фиксаций блока при помощи функции GlobalFlagsФиксирование страниц блока памятипри использовании виртуальной памяти вся глобальная область памяти делится на страницы размером 4 Кбайт. Эти страницы могут располагаться в физической оперативной памяти или на диске в специальном файле виртуальной памяти. Если приложение обращается к странице, которая отсутствует в физической оперативной памяти, она загружается туда из файла виртуальной памяти. Однако на загрузку страницы памяти из файла требуется значительное время. В некоторых случаях необходимо обеспечить постоянное присутствие блока памяти в физической оперативной памяти. Фиксирование блока памяти функцией GlobalFix не предотвращает сброс страниц памяти, распределенных блоку, в файл виртуальной памяти, а всего лишь запрещает перемещение блока памяти в линейном адресном пространстве. Для исключения страниц памяти, принадлежащих указанному блоку памяти, из процесса страничного обмена необходимо использовать функцию GlobalPageLock: Код (C): UINT WINAPI GlobalPageLock( HGLOBAL hglb // идентификатор блока памяти Идентификатор блока, для которого необходимо запретить страничный обмен, указывается через параметр hglb. Windows поддерживает счетчик блокирования страничного обмена. Содержимое этого счетчика увеличивается на единицу при каждом вызове функции GlobalPageLock. Функция GlobalPageLock возвращает новое значение счетчика или ноль при ошибке. Как только надобность в блокировке страничного обмена отпадает, следует вызвать функцию GlobalPageUnlock: Код (C): UINT WINAPI GlobalPageUnlock( HGLOBAL hglb // идентификатор блока памяти Эта функция разрешает страничный обмен для блока памяти, заданного параметром hglb. Функция возвращает текущее значение счетчика или ноль при ошибке Принудительное удаление блока памятиGlobalDiscard Для получения блоков памяти используют GlobalAlloc. Вам едва ли потребуется перемещаемая память, так как система управления памятью выполняет операцию перемещения с помощью механизма страничной адресации, не изменяя значение логического адреса. В случае, если вы решили получить блок перемещаемой памяти, перед использованием его необходимо зафиксировать функцией GlobalLock. Если вы заказываете перемещаемый блок памяти, функция GlobalAlloc возвращает не адрес блока памяти, а его идентификатор. Если же вы получаете фиксированный блок памяти, то функции GlobalAlloc вернет вам его адрес, который можно немедленно использовать. Операционная система сможет перемещать этот блок памяти без изменения его логического адреса. asm-файл Код (ASM): ; GUI # include win64a.inc ZZZ_OPEN equ 1 ZZZ_SAVE equ 2 ZZZ_EXIT equ 3 MAXSIZE equ 256 MEM_SIZE equ 65535 EditID equ 1 IDR_MAINMENU equ 30 .code WinMain proc <20> local msg:MSG xor ebx,ebx mov edi,offset ClassName mov esi,IMAGE_BASE invoke LoadCursorFromFileA,"br_Rabbit3.cur" push rax ;hIconSm push rdi ;lpszClassName push IDR_MAINMENU;lpszMenuName push COLOR_WINDOW;hbrBackground push rax ;hCursor push rax ;hIcon push rsi ;hInstance push rbx ;cbClsExtra & cbWndExtra pushaddr WndProc;lpfnWndProc push sizeof WNDCLASSEX;cbSize & style invoke RegisterClassExA,esp ;addr WNDCLASSEX push rbx push rsi shl esi,9 ;rsi=512*400000h=CW_USEDEFAULT push rbx push rbx push rsi push rsi push rsi push rsi sub esp,20h invoke CreateWindowExA,WS_EX_CLIENTEDGE,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE invoke SetFocus,hwndEdit lea edi,msg @@: invoke GetMessageA,edi,0,0,0 invoke TranslateMessage,edi invoke DispatchMessageA,edi jmp @b WinMain endp WndProc proc <12> hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM local szReadWrite:QWORD ;number of bytes actually read or write mov hWnd,rcx mov wParam,r8 mov lParam,r9 cmp edx,WM_DESTROY je wmDESTROY cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_CREATE je wmCREATE cmp edx,WM_SIZE je wmSIZE leave jmp NtdllDefWindowProc_ wmDESTROY:invoke GlobalFree,hMem invoke CloseHandle,hFile invoke RtlExitUserProcess,0 wmSIZE: movzx eax,word ptr lParam+2 movzx r9,r9w;word ptr lParam mov qword ptr[rsp+28h],TRUE mov [rsp+20h],rax invoke MoveWindow,hwndEdit,0,0 jmp wmBYE OPEN: invoke GetOpenFileName,&dlgOpenOfn test eax,eax je wmBYE and qword ptr[rsp+30h],0 mov qword ptr[rsp+28h],FILE_ATTRIBUTE_NORMAL mov qword ptr[rsp+20h],OPEN_EXISTING invoke CreateFile,&Buffer,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ,0 mov hFile,rax;handle to file invoke GetFileSize,eax,0 mov FileSize,rax and qword ptr[rsp+20h],0 invoke ReadFile,hFile,hMem,eax,&szReadWrite ;забрать в окно редактора текст из буфера invoke SetWindowTextA,hwndEdit,hMem ;заголовок окна - имя открытого файла movzx edx,dlgOpenOfn.nFileOffset add edx,offset Buffer invoke SetWindowTextA,hWnd jmp wmBYE SAVE:;узнать длину текст в окне редактора invoke GetWindowTextLengthA,hwndEdit inc eax mov FileSize,rax ;вывести текст из окна редактора в буфер invoke GetWindowTextA,hwndEdit,hMem,FileSize invoke SetFilePointer,hFile,0,0,FILE_BEGIN ;прочитать текст из буфера в файл and qword ptr[rsp+20h],0 invoke WriteFile,hFile,hMem,FileSize,&szReadWrite jmp wmBYE wmCREATE:mov dlgOpenOfn.hwndOwner,rcx push 0 push IMAGE_BASE push EditID push rcx push 0 push 0 push 0 push 0 sub esp,20h invoke CreateWindowExA,0,"edit",0,WS_VISIBLE or WS_CHILD or ES_LEFT \ or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or ES_AUTOHSCROLL or ES_AUTOVSCROLL mov hwndEdit,rax ;выделить память invoke GlobalAlloc,GPTR,MEM_SIZE mov hMem,rax jmp wmBYE wmCOMMAND:movzx r8d,word ptr wParam or r9,r9 ;cmp lParam,0 jnz wmBYE cmp r8d,ZZZ_EXIT ja wmBYE jmp [menu_handlers+r8*8] EXIT: invoke DestroyWindow,hWnd wmBYE: leave retn menu_handlers dq wmBYE,OPEN,SAVE,EXIT WndProc endp ;--------------------------------------- .data ClassName db 'Файловые операции и управление памятью: GlobalAlloc, GlobalFree',0 dlgOpenTitle db 'Открытие файла',0 ;------------------------------------------ align 8 dlgOpenOfn label OPENFILENAME dd sizeof OPENFILENAME,?;lStructSize dq ? ;hwndOwner dq IMAGE_BASE ;hInstance dq FilterString ;lpstrFilter dq ? ;lpstrCustomFilter dd ? ;nMaxCustFilter dd ? ;nFilterIndex dq Buffer ;lpstrFile dd MAXSIZE,? ;nMaxFile dq ? ;lpstrFileTitle dd ?,? ;nMaxFileTitle dq ? ;lpstrInitialDir dq dlgOpenTitle ;lpstrTitle dd OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or \ OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY;Flags dw ? ;nFileOffset dw ? ;nFileExtension dq ? ;lpstrDefExt dd ?,? ;lCustData dq ? ;lpfnHook dq ? ;lpTemplateName ;------------------------------------------- FilterString db 'All Files (*.*)',0,'*.*',0 db 'Text Files (*.txt)',0,'*.txt',0,0 .data? hwndEdit dq ? ;handle for edit control hHeap dq ? hMem dq ? FileSize dq ? hFile dq ?;handle of file Buffer db MAXSIZE dup(?) end rc-файл Код (C): #define ZZZ_OPEN 1 #define ZZZ_SAVE 2 #define ZZZ_EXIT 3 #define IDR_MAINMENU 30 IDR_MAINMENU MENU { POPUP "&File" { MENUITEM "&Open",ZZZ_OPEN MENUITEM "&Save",ZZZ_SAVE MENUITEM SEPARATOR MENUITEM "&Exit",ZZZ_EXIT } MENUITEM "&Exit",ZZZ_EXIT }
GetProcessHeap, RtlAllocateHeap, HeapFreeКуча (heap) — область зарезервированного адресного пространства размером в одну или более страниц, из которой диспетчер куч (heap menager) может динамически выделять память меньшими порциями. Диспетчер куч представляет собой набор функций для выделения и освобождения памяти (в отличие от функции VirtualAlloc они не обязательно соблюдают гранулярность выделения памяти). Функции дисптчера куч реализованы в Ntdll.dll и Ntoskernel.exe. Функции подсистем вызывают функции из Ntdll.dll, компоненты исполнительной системы и драйвера устройств — из Ntoskernel.exe Каждый процесс получает при создании стандартную кучу (default process heap), размер которой по умолчанию равен 1 Мб, если при сборке исполняемого файла не было указано иное. Однако этот объем памяти резервируется только для начала и по мере необходимости автоматически увеличивается (в исполняемом файле можно указать и начальный размер переданной памяти). Стандартную кучу процесса используют не только Win32-приложения, но и некоторые Wm32-функции, которым нужны блоки временной памяти. Процесс может создавать дополнительные закрытые кучи вызовом HeapCreate. Когда куча больше не нужна, занимаемое ею виртуальное адресное пространство можно освободить, вызвав HeapDestroy. В течение своего жизненного цикла процесс имеет право уничтожать лишь те кучи, которые он создал с помощью HeapCreate. Для выделения памяти из стандартной кучи поток должен получить ее дескриптор вызовом GetProcessHeap (эта функция возвращает адрес структуры данных, описывающей кучу, но вызывающая программа не должна полагаться на использование этой структуры). Затем поток передает этот описатель функциям НеарАllос и HeapFree, выделяя и освобождая блоки памяти в данной куче. Диспетчер куч также позволяет упорядочивать операции выделения и освобождения памяти в каждой куче, чтобы несколько потоков могли одновременно вызывать функции управления кучами, не повреждая ее структуры данных. Для стандартной кучи процесса такое упорядочение осуществляется по умолчанию (Вы можете отключать его в рамках каждого вызова). Но создавая дополнительную закрытую кучу, Вы должны указать HeapCreate, следует ли упорядочивать в ней операции выделения и освобождения памяти. Диспетчер куч поддерживает ряд недокументированных флагов, которые можно определить в рамках всей системы или конкретной программы с помощью утилиты Global Flags (Gflags.t-xe) из Windows 2000 Support Tools, Platform SDK или DDK. Действия, выполняемые диспетчером куч при задании большинства флагов, понятны без дополнительных объяснений. После установки этих флагов неправильное использование кучи или ее повреждение обычно вызывает генерацию ошибок или исключений. Основные используемые функции: GetProcessHeap — Извлекает дескриптор в кучу по умолчанию вызывающего процесса. Затем этот дескриптор можно использовать в последующих вызовах функций кучи RtlAllocateHeap — выделяет блок памяти из кучи HeapFree — Освобождает блок памяти, выделенный из кучи функцией RtlAllocateHeap или HeapReAlloc Создание блока памяти из кучи Если вам нужен блок памяти из кучи, вы можете его создать при помощи функции HeapCreate: Код (C): HANDLE HeapCreate( DWORD flOptions, // флаг создания блока DWORD dwInitialSize, // первоначальный размер блока в байтах DWORD dwMaximumSize);// максимальный размер блока в байтах Параметры dwMaximumSize и dwInitialSize определяют размер зарезервированного блока памяти. Параметр flOptions может быть равным 0, HEAP_NO_SERIALIZE и HEAP_GENERATE_EXCEPTIONS. Параметр HEAP_NO_SERIALIZE относится к мультизадачности. Если параметр не указан, работающие параллельно задачи одного процесса не могут одновременно получать доступ к такому блоку. Можно использовать флаг HEAP_NO_SERIALIZE для повышения производительности, если создаваемым блоком будет пользоваться только одна задача процесса. При выделении памяти из блока могут возникать ошибочные ситуации. Если не указан флаг HEAP_GENERATE_EXCEPTIONS, при ошибках соответствующей функции будут возвращать значение NULL. В противном случае в приложении будут генерироваться исключения. Флаг HEAP_GENERATE_EXCEPTIONS удобен в тех случаях, когда в приложении предусмотрена обработка исключений, позволяющая исправлять возникающие ошибки. В случае удачи функция HeapCreate возвращает идентификатор созданного блока памяти из кучи. При ошибке возвращается 0 (либо возникает исключение, если указан флаг HEAP_GENERATE_EXCEPTIONS).Удаление блока памяти из кучиДля удаления блока памяти из кучи, созданного функцией HeapCreate, используют HeapDestroy: Код (C): BOOL HeapDestroy( HANDLE hHeap// идентификатор удаляемого блока памяти Через единственный параметр этой функции передается идентификатор удаляемого блока памяти из кучи. Не следует удалять стандартный блок памяти из кучи, передавая этой функции значение, полученное от функции GetProcessHeap. Функция HeapDestroy выполняет безусловное удаление блока памяти из кучи, даже если из него были получены блоки памяти и на момент удаления блок памяти из кучи они не были возвращены системе.Получение блока памяти из кучиДля получения блока памяти из кучи используют функцию HeapAlloc: Код (C): LPVOID HeapAlloc( HANDLE hHeap, // идентификатор блока DWORD dwFlags, // управляющие флаги DWORD dwBytes); // объем получаемой памяти в байтах В качестве параметра hHeap можно использовать либо идентификатор, полученный от GetProcessHeap, либо идентификатор блока памяти, созданный при помощи HeapCreate. Параметр dwBytes определяет объем памяти в байтах. Параметр dwFlags комбинация следующих значений: ЗначениеhexОписаниеHEAP_NO_SERIALIZE1Если указан этот флаг, не выполняется блокировка одновременного обращения к блоку памяти нескольких задач одного процессаHEAP_GROWABLE2HEAP_GENERATE_EXCEPTIONS4Если при выполнении функции произойдет ошибка, возникнет исключениеHEAP_ZERO_MEMORY8Выделенная память заполняется нулямиHEAP_REALLOC_IN_PLACE_ONLY10HEAP_TAIL_CHECKING_ENABLED20HEAP_FREE_CHECKING_ENABLED40HEAP_DISABLE_COALESCE_ON_FREE80HEAP_MAXIMUM_TAGFFFHEAP_PSEUDO_TAG_FLAG8000HEAP_CREATE_ALIGN_1610000HEAP_CREATE_ENABLE_TRACING20000Изменение размера блока памятиС помощью HeapReAlloc приложение может изменить размер блока памяти, выделенного ранее функцией HeapAlloc, уменьшив или увеличив его. Код (C): LPVOID HeapReAlloc( HANDLE hHeap, // идентификатор кучи DWORD dwFlags, // флаг изменения размера блока памяти LPVOID lpMem, // адрес блока памяти DWORD dwBytes); // новый размер блока памяти в байтах Для кучи hHeap функция изменяет размер блока памяти, расположенного по адресу lpMem. Новый размер составит dwBytes байт. В случае удачи HeapReAlloc возвращает адрес нового блока памяти, который не обязательно будет совпадать с адресом, полученным этой функцией через параметр lpMem. Через параметр dwFlags можно передавать те же параметры, что и через аналогичный параметр для функции HeapAlloc. Дополнительно можно указать параметр HEAP_REALLOC_IN_PLACE_ONLY, определяющий, что при изменении размера блока памяти его нужно оставить на прежнем месте адресного пространства. Если указан этот параметр, в случае успешного завершения функция HeapReAlloc вернет то же значение, что было передано ей через параметр lpMem.Определение размера блока памятиЗная адрес блока памяти, полученного из кучи, можно определить его размер при помощи HeapSize: Код (C): DWORD HeapSize( HANDLE hHeap, // идентификатор кучи DWORD dwFlags, // управляющие флаги LPCVOID lpMem); // адрес проверяемого блока памяти В случае ошибки эта функция возвращает -1. Если блок памяти использует только одна задача процесса, можно передать через параметр dwFlags значение HEAP_NO_SERIALIZE.Освобождение памятиПамять, выделенную с помощью функции HeapAlloc освобождают, как только в ней отпадет надобность. Это делают при помощи HeapFree: Код (C): BOOL HeapFree( HANDLE hHeap, // идентификатор кучи DWORD dwFlags, // флаги освобождения памяти LPVOID lpMem); // адрес освобождаемого блока памяти Если блок памяти использует только одна задача процесса, можно передать через параметр dwFlags значение HEAP_NO_SERIALIZE. Если размер блока памяти, выделенного функцией HeapAlloc, был изменен функцией HeapReAlloc, для освобождения такого блока памяти нужно использовать функцию HeapFreeДругие функции кучиGetProcessHeaps — Получение дескрипторов для всех куч, допустимых для вызывающего процесса HeapCompact — Объединение смежных свободных блоков памяти в куче HeapLock — Получение блокировки, связанной с указанной кучей HeapUnlock — Освобождение блокировки, связанной с указанной кучей. HeapQueryInformation — Извлечение сведений об указанной куче HeapSetInformation — Задает сведения для указанной кучи HeapValidate — Проверка указанной кучи HeapWalk — Перечисление блоков памяти в указанной кучеasm-файл Код (ASM): ; GUI # include win64a.inc ZZZ_OPEN equ 1 ZZZ_SAVE equ 2 ZZZ_EXIT equ 3 MAXSIZE equ 256 MEM_SIZE equ 65535 EditID equ 1 IDR_MAINMENU equ 30 .code WinMain proc <20> local msg:MSG xor ebx,ebx mov edi,offset ClassName mov esi,IMAGE_BASE invoke LoadCursorFromFileA,"br_Rabbit3.cur" push rax ;hIconSm push rdi ;lpszClassName push IDR_MAINMENU;lpszMenuName push COLOR_WINDOW;hbrBackground push rax ;hCursor push rax ;hIcon push rsi ;hInstance push rbx ;cbClsExtra & cbWndExtra pushaddr WndProc;lpfnWndProc push sizeof WNDCLASSEX;cbSize & style invoke RegisterClassExA,esp ;addr WNDCLASSEX push rbx push rsi shl esi,9 ;rsi=512*400000h=CW_USEDEFAULT push rbx push rbx push rsi push rsi push rsi push rsi sub esp,20h invoke CreateWindowExA,WS_EX_CLIENTEDGE,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE invoke SetFocus,hwndEdit lea edi,msg @@: invoke GetMessageA,edi,0,0,0 invoke TranslateMessage,edi invoke DispatchMessageA,edi jmp @b WinMain endp WndProc proc <12> hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM local szReadWrite:QWORD ;number of bytes actually read or write mov hWnd,rcx mov lParam,r9 cmp edx,WM_DESTROY je wmDESTROY cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_CREATE je wmCREATE cmp edx,WM_SIZE je wmSIZE leave jmp NtdllDefWindowProc_ wmDESTROY:invoke HeapFree,hHeap,0,hMem invoke CloseHandle,hFile invoke RtlExitUserProcess,0 wmSIZE: movzx eax,word ptr lParam+2 movzx r9,r9w;word ptr lParam mov qword ptr[rsp+28h],TRUE mov [rsp+20h],rax invoke MoveWindow,hwndEdit,0,0 jmp wmBYE OPEN: invoke GetOpenFileName,&dlgOpenOfn test eax,eax je wmBYE and qword ptr[rsp+30h],0 mov qword ptr[rsp+28h],FILE_ATTRIBUTE_NORMAL mov qword ptr[rsp+20h],OPEN_EXISTING invoke CreateFile,&Buffer,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ,0 mov hFile,rax;handle to file invoke GetFileSize,eax,0 mov FileSize,rax and qword ptr[rsp+20h],0 invoke ReadFile,hFile,hMem,eax,&szReadWrite ;забрать в окно редактора текст из буфера invoke SetWindowTextA,hwndEdit,hMem ;заголовок окна - имя открытого файла movzx edx,dlgOpenOfn.nFileOffset add edx,offset Buffer invoke SetWindowTextA,hWnd jmp wmBYE SAVE:;узнать длину текст в окне редактора invoke GetWindowTextLengthA,hwndEdit inc eax mov FileSize,rax ;вывести текст из окна редактора в буфер invoke GetWindowTextA,hwndEdit,hMem,FileSize invoke SetFilePointer,hFile,0,0,FILE_BEGIN ;прочитать текст из буфера в файл and qword ptr[rsp+20h],0 invoke WriteFile,hFile,hMem,FileSize,&szReadWrite jmp wmBYE wmCREATE:mov dlgOpenOfn.hwndOwner,rcx push 0 push IMAGE_BASE push EditID push rcx push 0 push 0 push 0 push 0 sub esp,20h invoke CreateWindowExA,0,"edit",0,WS_VISIBLE or WS_CHILD or ES_LEFT \ or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or ES_AUTOHSCROLL or ES_AUTOVSCROLL mov hwndEdit,rax ;выделить память invoke GetProcessHeap mov hHeap,rax invoke RtlAllocateHeap,eax,HEAP_ZERO_MEMORY,MEM_SIZE mov hMem,rax jmp wmBYE wmCOMMAND:and r8d,0FFFFh;word ptr wParam or r9,r9 ;cmp lParam,0 jnz wmBYE cmp r8d,ZZZ_EXIT ja wmBYE jmp [menu_handlers+r8*8] EXIT: invoke DestroyWindow,hWnd wmBYE: leave retn menu_handlers dq wmBYE,OPEN,SAVE,EXIT WndProc endp ;--------------------------------------- .data ClassName db 'Файловые операции и управление памятью: GetProcessHeap, RtlAllocateHeap, HeapFree',0 dlgOpenTitle db 'Открытие файла',0 ;------------------------------------------ align 8 dlgOpenOfn label OPENFILENAME dd sizeof OPENFILENAME,?;lStructSize dq ? ;hwndOwner dq IMAGE_BASE ;hInstance dq FilterString ;lpstrFilter dq ? ;lpstrCustomFilter dd ? ;nMaxCustFilter dd ? ;nFilterIndex dq Buffer ;lpstrFile dd MAXSIZE,? ;nMaxFile dq ? ;lpstrFileTitle dd ?,? ;nMaxFileTitle dq ? ;lpstrInitialDir dq dlgOpenTitle ;lpstrTitle dd OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or \ OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY;Flags dw ? ;nFileOffset dw ? ;nFileExtension dq ? ;lpstrDefExt dd ?,? ;lCustData dq ? ;lpfnHook dq ? ;lpTemplateName ;------------------------------------------- FilterString db 'All Files (*.*)',0,'*.*',0 db 'Text Files (*.txt)',0,'*.txt',0,0 .data? hwndEdit dq ? ;handle for edit control hHeap dq ? hMem dq ? FileSize dq ? hFile dq ?;handle of file Buffer db MAXSIZE dup(?) end rc-файл Код (C): #define ZZZ_OPEN 1 #define ZZZ_SAVE 2 #define ZZZ_EXIT 3 #define IDR_MAINMENU 30 IDR_MAINMENU MENU { POPUP "&File" { MENUITEM "&Open",ZZZ_OPEN MENUITEM "&Save",ZZZ_SAVE MENUITEM SEPARATOR MENUITEM "&Exit",ZZZ_EXIT } MENUITEM "&Exit",ZZZ_EXIT }
Асинхронные файловые операцииПолучение виртуальной памятиУ приложения две возможности заказать страницы виртуальной памяти: Зарезервировать заданный диапазон адресов в адресном пространстве приложения фактически получить в пользование страницы виртуальной памяти, к которым можно выполнять обращение Процесс резервирования не отнимает много времени и не приводит к изменениям в файлах страниц. При резервировании приложение только отмечает область памяти, лежащую в заданном диапазоне адресов как зарезервированную. Для чего может потребоваться резервирование диапазона адресов? При отображении файла, имеющего большие размеры, в память, приложение может зарезервировать область виртуальной памяти, имеющую размер, равный размеру файла (даже если файл занимает несколько сотен Мбайт). При этом гарантируется, что для адресации этой области памяти можно будет использовать сплошной диапазон адресов. Это удобно, если вы собираетесь работать с файлом, как с массивом, расположенным в оперативной памяти. Если же приложение не резервирует, а получает страницы памяти для непосредственного использования, эти страницы физически создаются в виртуальной памяти и заполняются нулями. При этом может происходить запись в файлы страниц. Такой процесс отнимает больше времени, чем резервирование. Чтобы зарезервировать или получить в свое распоряжение некоторое количество страниц виртуальной памяти, приложение должно воспользоваться функцией VirtualAlloc, прототип которой представлен ниже: Код (C): LPVOID VirtualAlloc( LPVOID lpvAddress, // адрес области DWORD cbSize, // размер области DWORD fdwAllocationType, // способ получения памяти DWORD fdwProtect); // тип доступа Параметры lpvAddress и cbSize задают, соответственно, начальный адрес и размер резервируемой либо получаемой в пользование области памяти. При резервировании адрес округляется до ближайшей границы блока размером 64 Кбайт. В остальных случаях адрес округляется до границы ближайшей страницы памяти. Параметр lpvAddress можно указать как NULL. При этом операционная система выберет начальный адрес самостоятельно. Параметр cbSize округляется до целого числа страниц. Поэтому если вы пытаетесь с помощью функции VirtualAlloc получить область памяти размером в один байт, вам будет выделена страница размером 4096 байт. При попытке получить блок памяти размером 4097=4096+1 байт вы получите две страницы памяти общим размером 2×4096=8192 байта. Программный интерфейс системы управления виртуальной памятью не предназначен для работы с областями малого размера. Для параметра fdwAllocationType можно использовать комбинацию из следующих значений: ЗначениеhexОписаниеMEM_COMMIT1000Выполняется выделение страниц памяти для непосредственной работы с ними. Выделенные страницы заполняются нулямиMEM_RESERVE2000Функция VirtualAlloc выполняет резервирование диапазона адресов в адресном пространстве приложенияMEM_DECOMMIT4000MEM_RELEASE8000MEM_FREE10000MEM_PRIVATE20000MEM_MAPPED40000MEM_RESET80000MEM_TOP_DOWN100000Память выделяется в области верхних адресов адресного пространства приложенияMEM_IMAGE1000000С помощью параметра fdwProtect приложение может установить желаемый тип доступа для заказанных страниц. Можно использовать комбинацию из следующих значений: ЗначениеhexbinРазрешенный доступPAGE_NOACCESS11Запрещен любой вид доступаPAGE_READONLY210Только чтениеPAGE_READWRITE4100Чтение и записьPAGE_WRITECOPY81000К выделенной области памяти предоставляется доступ для копирования при записи. При создании или открывании файла необходимо указать флаги GENERIC_READ и GENERIC_WRITEPAGE_EXECUTE1010000Только исполнение программного кодаPAGE_EXECUTE_READ20100000Исполнение и чтениеPAGE_EXECUTE_READWRITE401000000Исполнение, чтение и записьPAGE_EXECUTE_WRITECOPY8010000000Любая попытка записи на этой странице заставляет систему выдать процессу закрытую копию данной страницыPAGE_GUARD100100000000Сигнализация доступа к странице. Это значение можно использовать вместе с любыми другими, кроме PAGE_NOACCESSPAGE_NOCACHE2001000000000Отмена кэширования для страницы памяти. Используется драйверами устройств. Это значение можно использовать вместе с любыми другими, кроме PAGE_NOACCESSЕсли страница отмечена как PAGE_READONLY, при попытке записи в нее возникает аппаратное прерывание защиты доступа (access violation). Страница также не может содержать исполнимый код. Попытка выполнения такого кода приведет к возникновению прерывания. У вас есть возможность получения страниц, предназначенных только для хранения исполнимого кода. Если такие страницы отмечены как PAGE_EXECUTE, для них не разрешаются операции чтения и записи. При необходимости зафиксировать обращение к той или иной странице приложение может отметить ее как PAGE_GUARD. Если произойдет попытка обращения к такой странице, возникнет исключение с кодом STATUS_GUARD_PAGE, после чего признак PAGE_GUARD будет автоматически сброшен. В случае успешного завершения VirtualAlloc возвратит адрес зарезервированной или полученной области страниц. При ошибке возвращается 0. Приложение может вначале зарезервировать страницы, вызвав функцию VirtualAlloc с параметром MEM_RESERVE, а затем получить их в пользование, вызвав эту же функцию еще раз для полученной области памяти, но уже с параметром MEM_COMMIT Освобождение виртуальной памятиПосле использования необходимо освободить полученную виртуальную память, вызвав функцию VirtualFree Код (C): BOOL VirtualFree( LPVOID lpvAddress, // адрес области DWORD cbSize, // размер области DWORD fdwFreeType); // выполняемая операция Через параметры lpvAddress и cbSize передаются адрес и размер освобождаемой области. Если область виртуальной памяти зарезервировали функцией VirtualAlloc с параметром MEM_RESERVE для последующего получения страниц, а затем вызвали эту функцию с параметром MEM_COMMIT, можно или освободить область памяти, обозначив соответствующие страницы как свободные, или оставить их зарезервированными, но не используемыми. В первом случае функцию VirtualFree нужно вызвать с параметром fdwFreeType=MEM_RELEASE, во втором - fdwFreeType=MEM_DECOMMIT WriteFile и ReadFile вызываются с параметром ovlp типа OVERLAPPED при организации асинхронных операций Код (ASM): OVERLAPPED struc Internal ULONG_PTR ? InternalHigh ULONG_PTR ? union struc _Offset DWORD ? OffsetHigh DWORD ? ends Pointer PVOID ? ends hEvent HANDLE ? OVERLAPPED ends Первые два элемента (Internal, InternalHigh) предназначены для использования системой, третий и четвертый элемент (_Offset, OffsetHigh) определяют начальное 64-разрядное смещение в файле. Если требуется прочитать не весь файл, а его вторую половину, тогда ovlp._Offset, будет равно (FileSize/2). Если файл достаточно большой то после ReadFile/WriteFile рекомендуют вызвать WailtForSingleObject(hFile, INFINITE)asm-файл Код (ASM): ; GUI # include win64a.inc ZZZ_OPEN equ 1 ZZZ_SAVE equ 2 ZZZ_EXIT equ 3 MAXSIZE equ 256 MEM_SIZE equ 0FFFC0h EditID equ 1 IDR_MAINMENU equ 30 .code WinMain proc <20> local msg:MSG xor ebx,ebx mov edi,offset ClassName mov esi,IMAGE_BASE invoke LoadCursorFromFileA,"br_Rabbit3.cur" push rax ;hIconSm push rdi ;lpszClassName push IDR_MAINMENU;lpszMenuName push COLOR_WINDOW;hbrBackground push rax ;hCursor push rax ;hIcon push rsi ;hInstance push rbx ;cbClsExtra & cbWndExtra pushaddr WndProc;lpfnWndProc push sizeof WNDCLASSEX;cbSize & style invoke RegisterClassExA,esp ;addr WNDCLASSEX push rbx push rsi shl esi,9 ;rsi=512*400000h=CW_USEDEFAULT push rbx push rbx push rsi push rsi push rsi push rsi sub esp,20h invoke CreateWindowExA,WS_EX_CLIENTEDGE,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE invoke SetFocus,hwndEdit lea edi,msg @@: invoke GetMessageA,edi,0,0,0 invoke TranslateMessage,edi invoke DispatchMessageA,edi jmp @b WinMain endp WndProc proc <12> hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM local szReadWrite:QWORD ;number of bytes actually read or write mov hWnd,rcx mov wParam,r8 mov lParam,r9 cmp edx,WM_DESTROY je wmDESTROY cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_CREATE je wmCREATE cmp edx,WM_SIZE je wmSIZE leave jmp NtdllDefWindowProc_ wmDESTROY:invoke VirtualFree,pBuffer,0,MEM_RELEASE invoke CloseHandle,hFile invoke RtlExitUserProcess,0 wmSIZE: movzx eax,word ptr lParam+2 movzx r9,r9w;word ptr lParam mov qword ptr[rsp+28h],TRUE mov [rsp+20h],rax invoke MoveWindow,hwndEdit,0,0 jmp wmBYE OPEN: invoke GetOpenFileName,&dlgOpenOfn test eax,eax je wmBYE and qword ptr[rsp+30h],0 and qword ptr[rsp+28h],FILE_FLAG_OVERLAPPED or FILE_FLAG_NO_BUFFERING mov qword ptr[rsp+20h],OPEN_EXISTING invoke CreateFile,&Buffer,GENERIC_READ or GENERIC_WRITE,0,0 mov hFile,rax;handle to file invoke GetFileSize,eax,0 mov FileSize,rax lea eax,ovlp mov [rsp+20h],rax invoke ReadFile,hFile,pBuffer,FileSize,&szReadWrite ;забрать в окно редактора текст из буфера invoke SetWindowTextA,hwndEdit,pBuffer ;заголовок окна - имя открытого файла movzx edx,dlgOpenOfn.nFileOffset add edx,offset Buffer invoke SetWindowTextA,hWnd jmp wmBYE SAVE:;узнать длину текст в окне редактора invoke GetWindowTextLengthA,hwndEdit inc eax mov FileSize,rax ;вывести текст из окна редактора в буфер invoke GetWindowTextA,hwndEdit,pBuffer,FileSize ;прочитать текст из буфера в файл lea eax,ovlp mov [rsp+20h],rax invoke WriteFile,hFile,pBuffer,FileSize,&szReadWrite jmp wmBYE wmCREATE:mov dlgOpenOfn.hwndOwner,rcx push 0 push IMAGE_BASE push EditID push rcx push 0 push 0 push 0 push 0 sub esp,20h invoke CreateWindowExA,0,"edit",0,WS_VISIBLE or WS_CHILD or ES_LEFT \ or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or ES_AUTOHSCROLL or ES_AUTOVSCROLL mov hwndEdit,rax invoke VirtualAlloc,NULL,MEM_SIZE,MEM_COMMIT or MEM_RESERVE,PAGE_READWRITE mov pBuffer,rax jmp wmBYE wmCOMMAND:movzx r8d,word ptr wParam or r9,r9 ;cmp lParam,0 jnz wmBYE cmp r8d,ZZZ_EXIT ja wmBYE jmp [menu_handlers+r8*8] EXIT: invoke DestroyWindow,hWnd wmBYE: leave retn menu_handlers dq wmBYE,OPEN,SAVE,EXIT WndProc endp ;--------------------------------------- .data ClassName db 'Асинхронные файловые операции',0 dlgOpenTitle db 'Открытие файла',0 ovlp label OVERLAPPED dq 0;Internal ULONG_PTR ? dq 0;InternalHigh ULONG_PTR ? dd 0;_Offset DWORD ? dd 0;OffsetHigh DWORD ? dq 0;Pointer PVOID ? dq 0; hEvent HANDLE ? ;------------------------------------------ align 8 dlgOpenOfn label OPENFILENAME dd sizeof OPENFILENAME,?;lStructSize dq ? ;hwndOwner dq IMAGE_BASE ;hInstance dq FilterString ;lpstrFilter dq ? ;lpstrCustomFilter dd ? ;nMaxCustFilter dd ? ;nFilterIndex dq Buffer ;lpstrFile dd MAXSIZE,? ;nMaxFile dq ? ;lpstrFileTitle dd ?,? ;nMaxFileTitle dq ? ;lpstrInitialDir dq dlgOpenTitle ;lpstrTitle dd OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or \ OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY;Flags dw ? ;nFileOffset dw ? ;nFileExtension dq ? ;lpstrDefExt dd ?,? ;lCustData dq ? ;lpfnHook dq ? ;lpTemplateName ;------------------------------------------- FilterString db 'All Files (*.*)',0,'*.*',0 db 'Text Files (*.txt)',0,'*.txt',0,0 .data? hwndEdit dq ? ;handle for edit control pBuffer dq ? FileSize dq ? hFile dq ?;handle of file Buffer db MAXSIZE dup(?) end rc-файл Код (C): #define ZZZ_OPEN 1 #define ZZZ_SAVE 2 #define ZZZ_EXIT 3 #define IDR_MAINMENU 30 IDR_MAINMENU MENU { POPUP "&File" { MENUITEM "&Open",ZZZ_OPEN MENUITEM "&Save",ZZZ_SAVE MENUITEM SEPARATOR MENUITEM "&Exit",ZZZ_EXIT } MENUITEM "&Exit",ZZZ_EXIT }
Файлы проецируемые в памятьФайл нужно только открыть с помощью CreateFile, а ReadFile и WriteFile уже не требуются Создание отображения файлаПриложение открывает файл при помощи CreateFile. Для создания отображения файла взывают CreateFileMapping Код (C): HANDLE CreateFileMapping( HANDLE hFile, // идентификатор отображаемого файла LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // дескриптор защиты DWORD flProtect, // защита для отображаемого файла DWORD dwMaximumSizeHigh, // старшие 32 разряда размера файла DWORD dwMaximumSizeLow, // младшие 32 разряда размера файла LPCTSTR lpName); // имя отображенного файла Параметр hFile ― идентификатор файла, для которого выполняется отображение в память, или значение -1. В первом случае функция CreateFileMapping отобразит заданный файл в память, во втором - создаст отображение с использованием файла виртуальной памяти. Параметр lpFileMappingAttributes ― адрес дескриптора защиты. В большинстве случаев этот параметр = 0. Параметром flProtect, задает защиту для создаваемого отображения файла. Для этого параметра можно задать следующий набор значений, комбинируя их с дополнительными атрибутами, которые будут перечислены ниже: ЗначениеhexbinОписаниеPAGE_NOACCESS11Любая попытка чтения, записи или выполнения кода на этой странице вызывает нарушение доступаPAGE_READONLY210К выделенной области памяти предоставляется доступ только для чтения. При создании или открывании файла необходимо указать флаг GENERIC_READPAGE_READWRITE4100К выделенной области памяти предоставляется доступ для чтения и записи. При создании или открывании файла необходимо указать флаги GENERIC_READ и GENERIC_WRITEPAGE_WRITECOPY81000К выделенной области памяти предоставляется доступ для копирования при записи. При создании или открывании файла необходимо указать флаги GENERIC_READ и GENERIC_WRITE.PAGE_EXECUTE1010000Любая попытка чтения или записи на этой странице вызывает нарушение доступа, но выполнение кода разрешеноPAGE_EXECUTE_READ20100000Любая попытка записи на этой странице вызывает нарушение доступа, но чтение или выполнение кода разрешеноPAGE_EXECUTE_READWRITE401000000Страница доступна для чтения, записи и выполнение ― никакие действия не вызывают нарушение доступаPAGE_EXECUTE_WRITECOPY8010000000Любая попытка записи на этой странице заставляет систему выдать процессу закрытую копию данной страницыPAGE_GUARD100100000000Любая попытка чтения или записи сторожевой страницы вызывает исключение EXEPTION_GUARD_PAGE, и она перестает быть сторожевой; этот атрибут можно комбинировать с любым другим атрибутом, кроме PAGE_NOACCESPAGE_NOCACHE2001000000000Отмена кэширования для страницы памяти. Используется драйверами устройств. Это значение можно использовать вместе с любыми другими, кроме PAGE_NOACCESSЭти значения можно комбинировать при помощи логической операции ИЛИ со следующими атрибутами: АтрибутhexОписаниеSEC_FILE800000SEC_IMAGE1000000Используется при отображении программного файла, содержащего исполнимый код. Этот атрибут несовместим с остальными перечисленными в этом списке атрибутамиSEC_VLM2000000SEC_PROTECTED_IMAGE2000000SEC_RESERVE4000000Если указан этот атрибут, вместо выделения выполняется резервирование страниц виртуальной памяти. Зарезервированные таким образом страницы можно будет получить в пользование при помощи функции VirtualAlloc. Атрибут SEC_RESERVE можно указывать только в том случае, если в качестве параметра hFile функции CreateFileMapping передается значение -1SEC_COMMIT8000000Если указан этот атрибут, выполняется выделение физических страниц в памяти или в файле виртуальной памяти. Этот атрибут используется по умолчаниюSEC_NOCACHE10000000Отмена кэширования для всех страниц отображаемой области памяти. Должен использоваться вместе с атрибутами SEC_RESERVE или SEC_COMMITSEC_WRITECOMBINE40000000SEC_LARGE_PAGES80000000С помощью dwMaximumSizeHigh и dwMaximumSizeLow указывают 64-разрядный размер файла. Параметр dwMaximumSizeHigh содержит старшее 32-разряда, dwMaximumSizeLow ― младшие 32-разряда. Для файлов, длина которых укладывается в 32 разряда, dwMaximumSizeHigh=0. Если dwMaximumSizeHigh=dwMaximumSizeLow=0. В этом случае предполагается, что размер файла не будет изменяться. Через lpName указывают имя отображения, которое будет доступно всем работающим одновременно приложениям. lpName представляет собой текстовую строку, закрытую 0 и не содержащую символов "\". Если отображение используется только одним процессом, lpName=0. В случае успешного завершения функция CreateFileMapping возвращает идентификатор созданного отображения. При ошибке возвращается значение NULL. Так как имя отображения глобально, возможно возникновение ситуации, когда процесс пытается создать отображение с уже существующим именем. В этом случае функция CreateFileMapping возвращает идентификатор существующего отображения. Такую ситуацию определяют с помощью GetLastError, вызвав ее сразу после функции CreateFileMapping. Функция GetLastError вернет значение ERROR_ALREADY_EXISTS. Выполнение отображения файла в памятьМы выполнили первые два шага, необходимые для работы с файлом, отображаемым на память, ― открыть файл функцией CreateFile и создать отображения функцией CreateFileMapping. Получив от CreateFileMapping идентификатор объекта-отображения, выполняем отображение, вызвав MapViewOfFile или MapViewOfFileEx. В результате заданный фрагмент отображенного файла будет доступен в адресном пространстве процесса. Код (C): LPVOID MapViewOfFile( HANDLE hFileMappingObject, // идентификатор отображения DWORD dwDesiredAccess, // режим доступа DWORD dwFileOffsetHigh, // смещение в файле (старшее слово) DWORD dwFileOffsetLow, // смещение в файле (младшее слово) DWORD dwNumberOfBytesToMap);// количество отображаемых байт Функция MapViewOfFile создает окно размером dwNumberOfBytesToMap байт, которое смещено относительно начала файла на количество байт, заданное dwFileOffsetHigh и dwFileOffsetLow. Если dwNumberOfBytesToMap=0, будет выполнено отображение всего файла. Смещение нужно задавать таким образом, чтобы оно попадало на границу минимального пространства памяти, которое можно зарезервировать. Значение 64 Кбайта подходит в большинстве случаев. Более точно гранулярность памяти определяют при помощи GetSystemInfo. Этой функции в качестве единственного параметра передают указатель на структуру типа SYSTEM_INFO, определенную следующим образом: Код (ASM): SYSTEM_INFO struct wProcessorArchitecture dw ?; архитектура системы wReserved dw ?; зарезервировано dwPageSize dd ?; размер страницы lpMinimumApplicationAddress dq ?; минимальный адрес, доступный приложениям и библиотекам DLL lpMaximumApplicationAddress dq ?; максимальный адрес, доступный приложениям и библиотекам DLL dwActiveProcessorMask dd ?; маски процессоров dwNumberOfProcessors dd ?; количество процессоров dwProcessorType dd ?; тип процессора dwAllocationGranularity dd ?; гранулярность памяти wProcessorLevel dw ?; уровень процессора wProcessorRevision dw ?; модификация процессора SYSTEM_INFO ends Функция заполнит поля этой структуры различной информацией о системе. В поле dwAllocationGranularity будет записан минимальный размер резервируемой области памяти. Параметр dwDesiredAccess определяет требуемый режим доступа к отображению, то есть режимы доступа для страниц виртуальной памяти, используемых для отображения. Для этого параметра вы можете указать комбинацию из следующих значений: ЗначениеhexОписаниеFILE_MAP_COPY1Доступ для копирования при записи. При создании отображения необходимо указать атрибут PAGE_WRITECOPYFILE_MAP_WRITE2Доступ на запись и чтение. При создании отображения функции CreateFileMapping необходимо указать тип защитыFILE_MAP_READ4Доступ только на чтение. При создании отображения необходимо указать тип защиты PAGE_READWRITE или PAGE_READFILE_MAP_EXECUTE20Доступ на исполнение. При создании отображения необходимо указать тип защиты PAGE_EXECUTE или PAGE_EXECUTE_READ или PAGE_EXECUTE_READWRITEFILE_MAP_ALL_ACCESSF001FАналогично FILE_MAP_WRITEВ случае успешного выполнения отображения функция MapViewOfFile возвращает адрес отображенной области памяти. При ошибке возвращается значение NULL. При необходимости приложение может запросить отображение в заранее выделенную область адресного пространства. Для этого следует воспользоваться функцией MapViewOfFileEx: Код (C): LPVOID MapViewOfFileEx( HANDLE hFileMappingObject, // идентификатор отображения DWORD dwDesiredAccess, // режим доступа DWORD dwFileOffsetHigh, // смещение в файле (старшее слово) DWORD dwFileOffsetLow, // смещение в файле (младшее слово) DWORD dwNumberOfBytesToMap, // количество отображаемых байт LPVOID lpBaseAddress);// предполагаемый адрес для отображения файла Функция аналогична только что рассмотренной функции MapViewOfFile за исключением того, что она имеет еще один параметр lpBaseAddress ― предполагаемый адрес для выполнения отображения. Выполнение отображения с использованием функции MapViewOfFileEx используется в тех случаях, когда с помощью файла, отображаемого на память, организуется общая область памяти, доступная нескольким работающим параллельно процессам. При этом можно сделать так, что начальный адрес этой области был одним и тем же для любого процесса, работающего с данным отображением. Функция MapViewOfFileEx выполняет резервирование адресов, поэтому не нужно передавать ей адрес области памяти, полученный от функции VirtualAlloc. Адрес, указанный через параметр lpBaseAddress, должен находиться на границе гранулярности памяти. Приложение может создавать несколько отображений для разных или одинаковых фрагментов одного и того же файла.Открытие отображенияЕсли несколько процессов используют совместно одно и то же отображение, первый процесс создает это отображение с помощью функции CreateFileMapping, указав имя отображения, остальные процессы должны открыть отображение, вызвав функцию OpenFileMapping: Код (C): HANDLE OpenFileMapping( DWORD dwDesiredAccess, // режим доступа BOOL bInheritHandle, // флаг наследования LPCTSTR lpName); // адрес имени отображения файла Через параметр lpName этой функции следует передать имя открываемого отображения. Имя должно быть задано точно также, как при создании отображения функцией CreateFileMapping. Параметр dwDesiredAccess определяет требуемый режим доступа к отображению и указывается точно также, как и для описанной выше функции MapViewOfFile. Параметр bInheritHandle определяет возможность наследования идентификатора отображения. Если bInheritHandle=TRUE, порожденные процессы могут наследовать идентификатор, если bInheritHandle=FALSE ― нет.Отмена отображения файлаЕсли созданное отображение больше не нужно, его следует отменить с помощью функции UnmapViewOfFile: Код (C): BOOL UnmapViewOfFile( LPVOID lpBaseAddress// адрес области отображения Единственный параметр этой функции ― адрес области отображения, полученный от функций MapViewOfFile или MapViewOfFileEx. В случае успеха функция возвращает TRUE. Гарантируется, что все измененные страницы оперативной памяти, расположенные в отменяемой области отображения, будут записаны на диск в отображаемый файл. При ошибке функция возвращает FALSE. Если приложение создало несколько отображений для файла, перед завершением работы с файлом все они должны быть отменены с помощью функции UnmapViewOfFile. Далее с помощью функции CloseHandle следует закрыть идентификаторы отображения, полученный от функции CreateFileMapping и CreateFile.Принудительная запись измененных данныхПосле отмены отображения все измененные страницы памяти записываются в отображаемый файл. Если это потребуется, приложение может в любое время выполнить принудительную запись измененных страниц в файл при помощи функции FlushViewOfFile: Код (C): BOOL FlushViewOfFile( LPCVOID lpBaseAddr, // начальный адрес сохраняемой области DWORD dwNumberOfBytesToFlush); // размер области в байтах С помощью параметров lpBaseAddr и dwNumberOfBytesToFlush можно выбрать любой фрагмент внутри области отображения, для которого будет выполняться сохранение измененный страниц на диске. Если задать значение параметра dwNumberOfBytesToFlush равным нулю, будут сохранены все измененные страницы, принадлежащие области отображения asm-файл Код (ASM): ; GUI # include win64a.inc ZZZ_OPEN equ 1 ZZZ_SAVE equ 2 ZZZ_EXIT equ 3 MAXSIZE equ 256 MEM_SIZE equ 65535 EditID equ 1 IDR_MAINMENU equ 30 .code WinMain proc <20> local msg:MSG xor ebx,ebx mov edi,offset ClassName mov esi,IMAGE_BASE invoke LoadCursorFromFileA,"br_Rabbit3.cur" push rax ;hIconSm push rdi ;lpszClassName push IDR_MAINMENU;lpszMenuName push COLOR_WINDOW;hbrBackground push rax ;hCursor push rax ;hIcon push rsi ;hInstance push rbx ;cbClsExtra & cbWndExtra pushaddr WndProc;lpfnWndProc push sizeof WNDCLASSEX;cbSize & style invoke RegisterClassExA,esp ;addr WNDCLASSEX push rbx push rsi shl esi,9 ;rsi=512*400000h=CW_USEDEFAULT push rbx push rbx push rsi push rsi push rsi push rsi sub esp,20h invoke CreateWindowExA,WS_EX_CLIENTEDGE,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE invoke SetFocus,hwndEdit lea edi,msg @@: invoke GetMessageA,edi,0,0,0 invoke TranslateMessage,edi invoke DispatchMessageA,edi jmp @b WinMain endp WndProc proc <12> hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM local hFile:QWORD ;handle of file local szReadWrite:QWORD ;number of bytes actually read or write mov wParam,r8 mov lParam,r9 mov hWnd,rcx cmp edx,WM_DESTROY je wmDESTROY cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_CREATE je wmCREATE cmp edx,WM_SIZE je wmSIZE leave jmp NtdllDefWindowProc_ wmDESTROY:test OpenFlag,1 jnz @f invoke UnmapViewOfFile,pMapping invoke CloseHandle,hMapping invoke CloseHandle,hFile @@: invoke RtlExitUserProcess,NULL wmSIZE: movzx eax,word ptr lParam+2 movzx r9d,word ptr lParam invoke MoveWindow,hwndEdit,0,0,,eax,TRUE jmp wmBYE OPEN: test OpenFlag,1 jnz @f invoke UnmapViewOfFile,pMapping invoke CloseHandle,hMapping invoke CloseHandle,hFile and OpenFlag,0 @@: mov dlgOpenOfn.Flags,OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or\ OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY invoke GetOpenFileName,&dlgOpenOfn test eax,eax je wmBYE and qword ptr[rsp+30h],0 mov qword ptr[rsp+28h],FILE_ATTRIBUTE_ARCHIVE mov qword ptr[rsp+20h],OPEN_EXISTING invoke CreateFile,&dOpenBuffer,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE,0 mov hFile,rax;handle to file ;создание в памяти объекта "проекция файла" and qword ptr[rsp+28h],0 mov qword ptr[rsp+20h],MEM_SIZE invoke CreateFileMapping,eax,0,PAGE_READWRITE,0 mov hMapping,rax ;отображение проекции файла на адресное пространство процесса and qword ptr[rsp+20h],0 invoke MapViewOfFile,eax,FILE_MAP_WRITE,0,0 mov pMapping,rax invoke SendMessageA,hwndEdit,WM_SETTEXT,0,eax movzx edx,dlgOpenOfn.nFileOffset add edx,offset dOpenBuffer invoke SetWindowTextA,hWnd or OpenFlag,1 jmp wmBYE SAVE: invoke SendMessageA,hwndEdit,WM_GETTEXT,MEM_SIZE,pMapping jmp wmBYE wmCREATE:mov dlgOpenOfn.hwndOwner,rcx push 0 push IMAGE_BASE push EditID push rcx push 0 push 0 push 0 push 0 sub esp,20h invoke CreateWindowExA,0,"edit",0,WS_VISIBLE or WS_CHILD or ES_LEFT \ or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or ES_AUTOHSCROLL or ES_AUTOVSCROLL mov hwndEdit,rax jmp wmBYE wmCOMMAND:movzx r8d,word ptr wParam or r9,r9 ;cmp lParam,0 jnz wmBYE cmp r8d,ZZZ_EXIT ja wmBYE jmp [menu_handlers+r8*8] EXIT: invoke DestroyWindow,hWnd wmBYE: leave retn menu_handlers dq wmBYE,OPEN,SAVE,EXIT WndProc endp ;--------------------------------------- .data ClassName db 'Файлы проецируемые в память',0 dlgOpenTitle db 'Открытие файла',0 ;------------------------------------------ align 8 dlgOpenOfn label OPENFILENAME dd sizeof OPENFILENAME,?;lStructSize dq ? ;hwndOwner dq IMAGE_BASE ;hInstance dq FilterString ;lpstrFilter dq ? ;lpstrCustomFilter dd ? ;nMaxCustFilter dd ? ;nFilterIndex dq Buffer ;lpstrFile dd MAXSIZE,? ;nMaxFile dq ? ;lpstrFileTitle dd ?,? ;nMaxFileTitle dq ? ;lpstrInitialDir dq dlgOpenTitle ;lpstrTitle dd OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or \ OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY;Flags dw ? ;nFileOffset dw ? ;nFileExtension dq ? ;lpstrDefExt dd ?,? ;lCustData dq ? ;lpfnHook dq ? ;lpTemplateName ;------------------------------------------- FilterString db 'All Files (*.*)',0,'*.*',0 db 'Text Files (*.txt)',0,'*.txt',0,0 .data? dOpenBuffer db MAXSIZE dup(?) hwndEdit dq ? ;handle for edit control hMapping dq ? ;file mapped handle pMapping dq ? OpenFlag db 0 end rc-файл Код (C): #define ZZZ_OPEN 1 #define ZZZ_SAVE 2 #define ZZZ_EXIT 3 #define IDR_MAINMENU 30 IDR_MAINMENU MENU { POPUP "&File" { MENUITEM "&Open",ZZZ_OPEN MENUITEM "&Save",ZZZ_SAVE MENUITEM SEPARATOR MENUITEM "&Exit",ZZZ_EXIT } MENUITEM "&Exit",ZZZ_EXIT }
Выделение памяти в COMCOM определяет несколько функций для выделения и освобождения памяти в куче. Почему COM определяет собственные функции выделения памяти? Одна из причин ― предоставление уровня абстракции над распределителем кучи. COM не привязан к конкретному языку программирования.CoTaskMemAllocВыделяет блок памяти Код (C): CoTaskMemAlloc( cb // Размер выделенного блока памяти в байтах Если функция выполняется успешно, она возвращает выделенный блок памяти. В противном случае возвращается значение NULL CoTaskMemReallocИзменяет размер ранее выделенного блока памяти задач Код (C): LPVOID CoTaskMemRealloc( LPVOID pv, // Указатель на перераспределенный блок памяти cb //Размер перераспределенного блока памяти в байтах. CoTaskMemFreeОсвобождает блок памяти задачи, ранее выделенный с помощью вызова функции CoTaskMemAlloc или CoTaskMemRealloc Код (C): void CoTaskMemFree( LPVOID pv // Указатель на блок памяти, который требуется освободить
Спасибо за статью! А я это так, к общей информации. По факту с выпуском х64 и появлении виртуальной памяти shared memory механизм перестал работать / стал бесполезным? Я так это понимаю, ничего кроме хип алок не использую.
По просьбе Mikl___ выкладываю пример работы с буферами неопределённого размера (автоматически расширяемыми при необходимости) на основе COM-интерфейса IStream.