На самом деле у меня есть два вопроса, которые в некоторой степени связаны.
Функции Windows, которые я использую:
Я использую библиотеку Irvine32 и Visual Studio 2022 в качестве среды IDE. Вот код, который я буду использовать, чтобы говорить о вопросах:
; main.asm
INCLUDE Irvine32.inc
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode: DWORD
.data
testString BYTE "HELLO", 0
.code
main PROC
MOV esi, OFFSET testString
CALL StringHeapAlloc
INVOKE ExitProcess, 0
main ENDP
; --------------------------------------------------------------------
; StringHeapAlloc
;
; This function will allocate memory in the heap and copy, a given
; string, into the allocated memory location.
; RECEIVES: ESI
; RETURNS: EAX contains the heap handle;
; EBX contains the array address
; REQUIRES: ESI contains the address to the string being copied
; --------------------------------------------------------------------
StringHeapAlloc PROC USES esi
.data
MAX_STRING_SIZE = 20
; Dynamic Memory Information
hHandle DWORD ? ; contains the heap handle
pHeap DWORD ? ; contains the pointer to the memory allocated
dwFlag DWORD HEAP_ZERO_MEMORY ; contains the flags for allocating memory
dwByte DWORD MAX_STRING_SIZE + 1 ; contains the number of bytes to allocate
.code
; gets heap handle for heap allocation
INVOKE GetProcessHeap
MOV hHandle, eax ; storing heap handle
; allocates memory in the heap for the string
INVOKE HeapAlloc, hHandle, dwFlag, dwByte
MOV pHeap, eax ; storing pointer to array
; copies string into the allocated memory
INVOKE Str_copy, esi, pHeap
; returning heap handle and pointer to handle
MOV eax, hHandle
MOV ebx, pHeap
RET
StringHeapAlloc ENDP
END main
В сборке я использую метод динамического выделения памяти:
Я так понимаю, что указатель на кучу — это просто адрес определенного места в памяти, которое было выделено. Я не совсем понимаю, что такое дескриптор кучи. Я провел небольшое исследование с помощью отладчика VS и обнаружил, что:
Когда GetProcessHeap выполняется, он возвращает дескриптор кучи в регистр EAX. После выполнения GetProcessHeap.
Когда HeapAlloc выполняется, он возвращает указатель на кучу в регистр EAX. После выполнения HeapAlloc.
Дескриптор кучи — 006E0000. Указатель на кучу — 006E6568. Я заметил, что дескриптор кучи, кажется, находится внутри указателя на кучу каждый раз, когда я запускаю код. Если бы мы думали об этом аналогично, является ли дескриптор кучи улицей с домами с адресами 19000, а указатель на кучи — адресами жителей этой улицы (19000, 19001 и т. д.)?
Всегда ли можно получить дескриптор кучи из указателя на кучу? В примере, где я запускал код, дескриптор кучи был 006E0000, а указатель на кучу был 006E6568. Является ли дескриптор кучи всегда первыми 4 байтами в указателе на кучу?
Чтобы освободить память, мне нужно использовать HeapFree. Однако эта функция требует, чтобы у меня был дескриптор кучи и указатель на нее. Прямо сейчас, каждый раз, когда я динамически выделяю память, я сохраняю дескрипторы кучи в массиве, содержащем дескрипторы кучи, и указатели на кучу в массиве, содержащем указатели. Если дескриптор кучи может быть получен, я мог бы избавиться от этого дополнительного массива для хранения дескрипторов кучи.
Редактировать: изменен EDX на ESI в основной функции. Смещение testString должно быть сохранено в ESI для работы StringHeapAlloc.
Обновлено: завершена документация по функции StringHeapAlloc и добавлены «MOV eax, hHandle» и «MOV ebx, pHeap» в конце функции StringHeapAlloc. Это не должно влиять на исходные вопросы.
Спасибо! Эти ответы очень помогли мне и указали мне правильное направление для исследований. Так это правильный способ думать об этом? Функция GetProcessHeap будет возвращать один и тот же дескриптор кучи на протяжении одного запуска программы, в которой она вызывается. Дескриптор кучи, возвращаемый функцией GetProcessHeap, представляет собой дескриптор кучи по умолчанию, который создается для программы при ее запуске.
Функция GetProcessHeap будет возвращать один и тот же дескриптор кучи на протяжении одного запуска программы, в которой она вызывается. — Да.
Дескрипторы всех видов — это просто значения. Они могут быть указателями, но в некоторых случаях просто целыми числами. (Указатели обычно [предпочтительнее, потому что они позволяют разным типам дескрипторов различать типы в языках, которые имеют концепцию типа, например C).
Суть вышеизложенного в том, что вам не нужно беспокоиться о том, какая структура поддерживает указатель. Это просто значение, сохраненное вызывающим кодом и понятное вызываемому коду.
Обратите внимание, что большинство дескрипторов окон имеют вид
struct HNAME__ { int unused };
typedef HNAME__ * HNAME;
но вы никогда не используете структуру «HNAME__», вы просто используете значение HNAME, которое вам передает API.
2 - нет. как вы думаете, для чего передать дескриптор кучи в вызове
HeapFree
, если его можно вывести из указателя на кучу? Является ли дескриптор кучи всегда первыми 4 байтами в указателе на кучу? - нет. и этот вопрос не связан со сборкой/массм. GetProcessHeap не создает дескриптор кучи. он возвращает дескриптор кучи вызывающего процесса. вам не нужно хранить кучу ручек..