На начальном этапе тестирования функция работала нормально. Работает и точка, - подумал я. Какого-то фига (вскоре выяснилось - что ой как не зря) мне пришла идея подкинуть функции длинную строку (эдак символов 27).
Среда пала. c000005 -- Access violation.
Каким образом размер входных данных мог привести к Access violation ? Ведь я сам писал библу и на 90% уверен, что это невозможно.
Единственное слабое место - запись в LFT (Length Filter Table - специальную таблицу, которая может при необходимости расширяться).
Изначально LFT имеет размер 16 ячеек. Если длина входной строки больше - LFT требуется увеличивать. Поэтому моя функция (KrmAddKwd), (вызов которой теперь почему-то приводит к GPF) в самом начале вызывает KrmEnlargeLFT и передаёт ей длину входной строки.
Если длина > размера LFT, LFT расширяется до нужного размера (HeapReAlloc-ом), и нарощенная часть заполняется валидными числами (LFT - таблица указателей на функции). Если длина строки меньше или равна (т.е. LFT достаточно велика), ничего не происходит, KrmEnlargeLFT завершается.
Поэтому возможно для недлинных входных строк LFT достаточно велик, а для 27-символьной происходит попытка расширить LFT, и, возможно, происходит сбой. Я так думал. (Многие сейчас подумают - нафиг гадать - возьми да прогони под отладчиком! Погодите, дойдёт дело и до отладчика.)
Проверить моё предположение было проще простого.
LFT создаётся при вызове KrmFinalize (которая проверяет, все ли параметры инициализации валидны, инициализирует и ставит флаг "инициализация произведена", разрешающий другим функциям выполняться). Перед KrmFinalize ставим KrmSetLFTInitialSize 40.
Запускаем. Среда падает
Значит дело не в LFT. А в чём же? В остальном коде нет никаких зависимостей на длину взодной строки.
Теперь перейдём к отладчику.
Компилирую. На всякий случай запускаю скомпилированный exe-шник (русское "авось"). Облом - GPF.
(Сразу после вызова KrmAddKwd находится такой код:
- Код: Выделить всё
MsgBox "Функция успешно отработала!"
End
Запускаю любимый OllyDbg. Ставим брекпоинт на входе в KrmAddKwd. Запускаем. Выполнение останавливается на брекпоинте. Дальше пошагово трассируем код.
Функция выполняется... выполняется... и что вы думаете? Выполнение успешно доходит до конца функции, где происходит retn в вызвавший её VB-шный Sub Main.
Трассируем дальше: выполнение доходит до rtcMsgBox и я наблюдаю сообщение "Функция успешно отработала!"
Далее идёт вызов _vbaEnd - т.е. тот самый End, который стоит после MsgBox-а.
Итог: каким-бы образом я не извращался, под отладчиком всё работает как часы. И следовательно причину возникновение GPF найти не удастся. Облом
Причём я совершенно не могу понять, почему так. (Может кто знает, почему?)
Ладно, моё решение следующее: вставлю в свою KrmAddKwd вызов MessageBox-а, и буду двигать его дальше и дальше по направлению выполнения процедуры, пока сообщение будет появляться до GPF. Как только сообщение перестанет появляться - вызов MessageBox-а ушёл за "больную" инструкцию.
Вставил - работает. Подвинул - работает. Ещё подвинул - работает. Надоело двигать, вставил в самый конец:
- Код: Выделить всё
push 0
push 0
push pp1
push 0
call [MessageBoxA]
ret 12d
О чудо! Сообщение вывелось. Значит в процедуре всё валидно, и никаких ошибок нет (я был в этом уверен на 90%).
Значит, ошибка происходит уже в коде VB-программы. Но почему? Вчера у меня уже было такое - но в отличие от этого раза, тогда была тупейшая ошибка - я в процедуре (совсем другой) поюзал EBX, но не восстановил его перед возвратом (ибо код копипастился) - но это обнаружить не составило труда.
В KrmAddKwd с EBX всё в порядке (перед этими четырьмя пушами стоит pop ebx), а других регистров, которые по соглашению stdcall функция должна восстанавливать, я не юзаю.
В чём же дело?