nouyana писал(а):Правильно ли я понял, что и в результате использования PutMem4, и в результате использования GetMem4 переменные меняются своими значениями. Только в случае с PutMem4 это происходит через замену ссылок (указателей), а в случае с GetMem4 меняются сами данные (4 байта)?
PutMem4 принимает два 32-битных значения: первое это адрес, куда нужно записать, а второе — это само число, которое нужно записать. И записывает.
GetMem4 принимает два 32 битных значения: первое это адрес, откуда нужно прочитать, а второе — это адрес, куда нужно записать прочитанное. Читает и записывает.
Эти функции сделанны именно такими, какими сделанны, чтобы соответствовать семантике вызовов, которые применяются в COM (вообще) и в VB (в частности) для get/put-свойств. Их только не обернули в Get/Put-свойства, но я это недоразумение исправил — пришёл и обернул их в свойство «At», о чём целый топик и написан. В нём они работают именно как обёрнутые свойства.
nouyana писал(а):Зачем нам указатель именно на ByRef?
Постановка вопроса показывает полное напонимание сути происходящих процессов.
Нет никакого указателя «на ByRef». Если конечнно предлогом «на» ты пытаешься сказать, кто на кого указывает, а не что на чём базируется.
Если первое, то
нет никакого указателя, указывающего на ByRef. Если второе, то да, указатель базируется на
ByRef-аргументе. Точно так же, как машина едет
на безине (то есть используя горючие свойства бензина), а не
на бензин (то есть не в направлении бензина, не туда, где находится бензин).
nouyana писал(а):Я так понимаю, что ptrAddress в принципе изначально может быть любым, лишь бы в памяти не нагадить, так?
ptrAddress — это обычная плоская переменная, которая нам нужна, чтобы в ней хранить адрес самого указателя. Адрес указателя нам нужен для того, чтобы записывая 32-битные значения (которые по факту являются адресами) по адресу указателя менять (то есть «перевешивать») указатель. Иными словами, ptrAddress хранит адрес ячейки памяти, куда записывается другой адрес, по которому уже потом можно читать и писать 16-битные (в данном примере) значения, используя аргумент Replacer.
nouyana писал(а): Тогда почему мы не можем сделать указатель на какую-нибудь обычную переменную?
Мы можем сделать указатель на обычную переменную, но в качестве указателя мы можем использовать только ByRef-аргумент, потому что его работа основывается на том, то по своей сути это указатель на какое-то значение, в отличие от ByVal-аргументов, которые хранят само значение, а не указатель на него.
- Код: Выделить всё
a& = 1: b& = 2: c& = 3: d& = 4: e& = 5: f& = 6
proc_xxx a&, b&, c&, d&, e&, f&
Sub proc_xxx(byval arg1 as long, byval arg2 as long, byval arg3 as long, byref arg4 as long, byval arg5 as long, byref arg6 as long)
...
End Sub
При таком вызове в процедуру будут переданы: единица, двойка, тройка,
какой-то адрес, пятёрка,
какой-то адрес.
Обращаясь изнутри процедуры (proc_xxx) к arg3 мы «видим» тройку, а обращаясь к arg4 видим не «какой-то адрес», а то, что по нему находится (а по нему находится 4-ка).
Иными словами, arg4 (и arg6) в отличие от остальных аргументов, содержат не значения, а адреса, то есть являются указателями. Но при попытке читать или изменять эти аргументы мы читаем/меняем не сам адрес, а то, что по нему находится. Причём это происходит для VB-программиста скрытно. Сами адреса (которые фактически хрантся в arg4 и arg6) VB программист напрямую не может ни прочитать (получить), ни изменить. Тем не менее, узнать, на что указывает ByRef-аргумент можно с помощью VarPtr, а вот заставить ByRef-аргумент указывать не на то, на что он должен указывать (на что указатель установлен в момент вызова процедуры), а на какое-то вообще левое произвольное место памяти мы не можем.
Но раз у нас есть PutMem4/GetMem4/CopyMemory, которые дают нам возможность произвольных модификаций памяти в произвольных местах, мы можем вычислить адрес самого указателя и поменять его.
VarPtr(arg4) даст нам не адрес arg4, а адрес Long-значения, на которое ссылается arg4. Чтобы получить реальный адрес arg4, мы получаем адрес arg3 (который мы можем получить с помощью
VarPtr(arg3)), а затем к адресу arg3 прибавляем 4, чтобы получить адрес arg4.
Т.е., зная тот факт, что аргументы 4-байтовые и в стеке размещаются компактно подряд (в прямо порядке), мы адрес arg4 можем вычислить как:
VarPtr(arg3) + 4 или
VarPtr(arg2) + 8 или
VarPtr(arg1) + 12 или
VarPtr(arg5) - 4.
Всё, с тех пор как мы знаем реальный адрес arg4, мы можем менять реальное содержимое arg4, а реальным содержимым является адрес. После того, как мы поменяем реальное содержимое arg4 на какое-то свой (какой-то свой адрес), обратившись к arg4 обычным образом, например так:
arg4 = arg4 + 1будет изменено значение, которое в памяти лежит по тому адресу, который мы предварительно в arg4 загнали.
И ещё раз, если мы напишем:
arg4 = 13, то на самом деле (не с точки зрения языка VB6, его философии и конституции), а с точки зрения фактических процессов, происходящих с памятью и процессором компьютера, мы этим присвоением меняем не реальное содержимое arg4, а содержимое ячейки памяти, адрес которой хранится в arg4.
А вот само содержимое arg4 (реальное, фактическое, «низкоуровневое») мы можем поменять только так:
PutMem4 real_address_of_byref_argument(arg4), новый_адресФункции
real_address_of_byref_argument в VB6 нет и быть не может, но заместо неё реальный адрес ByRef-аргумента, как я уже сказал, можно вычислить с помощью трюка, например как
VarPtr(arg3) + 4 или
VarPtr(arg5) - 4.
nouyana писал(а): PutMem4 "перевешивает указатель" или записывает новое значение по адресу?
Монета круглая или металлическая? Какого ответа ты ожидаешь и почему надо выбирать либо то, либо другое? Монета и круглая, и металлическая — одно другому не мешает. Точно так же и PutMem4 записывает новое значение по указанному адресу, чем осуществляет (в рамках того примера, о котором мы говорим) перевешивание указателя.
nouyana писал(а):Каким образом VB6 в строке 102 понял, что 1000000 - это не указатель, а значение?
VB6 ничего не понял, ему и не нужно понимать. VB6 просто сгенерировал код, который осуществляет вызов процедуры GetMem4 с передачей именно тех параметров, как ты укзал. Что делает GetMem4 и как интерпретирует переданные параметры — VB6 глубоко безралично, ему лишь бы код сгенерировать.
Дальше этот код выполняется процессором. Процессору тоже глубоко безразлично, что делает GetMem4 и как интерпретирует переданные параметры. Процессор слепо и бездумно выполняет череду инструкций: одну за другой.
В этом коде в PutMem4 был передан адрес переменной val (полученный вызовом VarPtr) и значение 1000000. PutMem4 слепо по переданному адресу записала число 1000000. Вот и всё.