Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004

.pdf
Скачиваний:
362
Добавлен:
13.08.2013
Размер:
3.3 Mб
Скачать

ГЛАВА 6

Улучшенная отладка приложений .NET в среде Visual Studio .NET

241

 

 

 

 

 

conv.i4

//

Преобразование длины

массива в

 

 

//

4 байтовое значение.

 

 

newarr Создание одномерного массива, начинающегося с нулевого индекса

Создает новый массив указанного типа с числом элементов, указанным в верши не стека. Число элементов удаляется из стека, и в вершину стека помещается но вый массив.

ldc.i4.5

// Число элементов

создаваемого

 

//

массива будет равно 5.

 

//

Создание нового

массива.

newarr System.ComponentModel.MemberAttribute

newobj Создание нового объекта

Создает новый объект и вызывает его конструктор. Перед этим все аргументы конструктора заносятся в стек. Если создание объекта происходит успешно, ар гументы удаляются из стека, и в стек заносится ссылка на объект.

.method public hidebysig specialname rtspecialname

instance void .ctor(class [mscorlib]System.IO.Stream 'stream',

 

class System.String name) il managed

{

 

 

 

ldarg.1

// Загрузка аргумента с индексом 1 (поток).

 

// Создание нового класса.

newobj instance void [mscorlib]

 

System.IO.StreamWriter::.ctor(class

 

[mscorlib]System.IO.Stream)

box

Преобразование размерного типа в объектный

Преобразует значение в объект, оставляя после этого объект в стеке. Именно она выполняет упаковку (boxing). При передаче параметров вы будете часто видеть следующий код:

Заметьте: этому методу передается размерный тип INT32.

.method public hidebysig specialname

instance void set_Indent(int32 'value') il managed

{

ldstr

"Indent"

// Занесение имени метода.

ldarga.s

'value'

// Загрузка адреса аргумента

 

 

// первого параметра.

box

[mscorlib]System.Int32

// Преобразование адреса в объект.

 

 

// Загрузка сообщения.

ldstr

"The Indent property must be non negative."

 

// Создание нового объекта класса ArgumentOutOfRangeException

newobj

instance void [mscorlib]System.ArgumentOutOfRangeException::

.ctor(class System.String, class System.Object, class System.String)

242 ЧАСТЬ II Производительная отладка

unbox Преобразование упакованного размерного типа в обычную форму

Возвращает управляемую ссылку на размерный тип в упакованной форме. Возвра щаемая ссылка — не копия, а действительный объект. В откомпилированном коде C# и Visual Basic .NET за командой unbox следует команда ldind (косвенная загруз ка значения в стек) или ldobj (копирование размерного типа в стек).

//Преобразование значения в System.Reflection.Emit.LocalToken unbox System.Reflection.Emit.LocalToken

//Заталкивание значения в стек

ldobj System.Reflection.Emit.LocalToken

unbox [mscorlib]System.Int16

//

Преобразование значения в объект Int16.

ldind.i2

//

Занесение значения объекта в стек.

call Вызов метода

callvirt Вызов метода, ассоциированного с объектом в период выполнения

Команда call вызывает статические и невиртуальные нормальные методы. Для вызова виртуальных методов и методов интерфейсов служит команда callvirt. Ар гументы размещаются в порядке слева направо. Этот порядок противоположен большинству соглашений вызова в мире IA32. Вот пример использования callvirt:

//Загрузка параметра. ldfld class System.String

System.CodeDOM.Compiler.CompilerResults::pathToAssembly

//Вызов виртуального метода set_CodeBase.

callvirt instance void [mscorlib]

System.Reflection.AssemblyName::set_CodeBase

(class System.String)

ldarg.0

// Загрузка указателя this, который

 

// всегда является первым параметром.

ldarg.1

// Загрузка первого аргумента.

ldnull

// Загрузка значения null.

 

// Вызов виртуальной функции.

callvirt instance void

 

System.Diagnostics.TraceListener::Fail(class System.String

 

class System.String)

ret

// Возвращение в исходную программу.

Другие инструменты восстановления алгоритма

ILDASM — великолепный инструмент, но я хочу упомянуть еще два средства, ко торые считаю просто бесценными. Обе программы имеют «правильную» цену: они бесплатны! Первый инструмент — .NET Reflector (http://www.aisto.com/roeder/ dotnet/) Лутца Родера (Lutz Roeder), поддерживающий все функции ILDASM и многие другие. Одна из важнейших функций .NET Reflector заключается в том, что он позволяет легко искать в сборке типы. Конечно, хотелось бы надеяться, что все разработчики будут правильно документировать генерируемые исключения, но

ГЛАВА 6 Улучшенная отладка приложений .NET в среде Visual Studio .NET

243

 

 

это не всегда так. Выберите в .NET Reflector меню Type Search (поиск типов)1 и введите в поле Type Search слово «except». В результате этого будут показаны все типы, в имя которых входит слово «exception» (исключение).

Иногда очень важно быстро узнать, какие методы вызывает конкретный ме тод. Выделите в дереве, изображаемом .NET Reflector, интересующий вас метод и выберите в меню View подменю Call Tree (дерево вызовов). Развернув в окне Call Tree дочерние вызовы, вы увидите иерархию вызовов для конкретного метода. Это прекрасный способ изучения совместной работы отдельных элементов программы.

Наконец, .NET Reflector поддерживает более развитые по сравнению с ILDASM возможности вывода дизассемблированного кода. Выберите метод, про который хотите узнать, нажмите Enter и увидите окно Disassembler. Если вас интересует, что делает команда, наведите на нее курсор, и появится подсказка с ее объясне нием. Типы параметров и локальных значений, а также методы, вызываемые коман дами call, подчеркиваются. Просто щелкните интересующий вас элемент, и в ос новном окне .NET Reflector будет выведен тип или метод, чтобы вы могли полу чить о нем более подробную информацию.

Вторая программа, которую я хочу упомянуть, называется Anakrino, что на греческом означает «иследовать» или «судить». Anakrino — это декомпилятор для

.NET, который показывает для сборки код C# или Managed Extensions for C++. Эту программу, которую написал Джей Фримен (Jay Freeman), можно загрузить по адресу http://www.saurik.com/net/exemplar/. В отличие от .NET Reflector исходный код Anakrino доступен. Нельзя сказать, что Anakrino не имеет недостатков, одна ко это тоже великолепный инструмент для изучения кода .NET Framework. Рабо тать с Anakrino очень просто, поэтому я не буду про это рассказывать. Однако хочу предупредить: исходный код Anakrino довольно «оригинален» и включает массу шаблонов, поэтому если вы захотите улучшить его, вам придется приложить не мало усилий. На момент написания книги уже доступны более полезные коммер ческие декомпиляторы, но они чрезмерно дороги, поэтому недостатки Anakrino вполне простительны.

Резюме

Управляемый код прекрасен, потому что нам больше не нужно беспокоиться об искажениях и утечках памяти, однако нам все равно еще нужно уметь пользоваться различными инструментами отладки. В этой главе я сосредоточился на особен ностях, связанных с Visual Studio .NET и отладкой управляемых приложений.

В начале главы я рассмотрел некоторые вопросы, касающиеся улучшенных точек прерывания. Поддерживаемая Visual Studio .NET возможность вызова методов и свойств из условных точек прерывания замечательна, но это подразумевает, что вы должны быть более внимательными для избежания побочных эффектов. Не забывайте, что, если вы укажете некорректное условие прерывания и отладчик не сможет его вычислить, он не остановится.

1Работа более поздних версий этой программы отличается от описанной автором. —

Прим перев.

244 ЧАСТЬ II Производительная отладка

Крайне полезное окно Watch предоставляет удивительные возможности для отладки управляемых приложений. Его механизм вычисления выражений позво ляет легко вызывать методы и свойства, благодаря чему вы можете изменять ра боту отлаживаемой программы, облегчая ее тестирование. Кроме того, разраба тывая приложения C# и Managed Extensions for C++, вы можете добавлять к пра вилам авторазвертывания собственные типы, что позволяет сделать отладку еще более быстрой.

Наконец, хотя вы, возможно, никогда не будете программировать на MSIL, его легко освоить, и он поможет вам по настоящему разобраться во всех тонкостях работы библиотеки классов .NET Framework. Подробнее о MSIL см. файл Partition III CIL.DOC, расположенный в подкаталоге <установочный каталог Visual Studio

.NET>\SDK\v1.1\Tools Developers Guide\docs. В этом документе вы найдете сведе ния обо всех командах и о том, что они делают.

Г Л А В А

7

Усложненные технологии неуправляемого кода

в Visual Studio .NET

Хотя разработка управляемого кода прекрасно защищает вас от всякого рода проблем, разработка неуправляемого кода (native code) дает вам все шансы не только прострелить себе ногу, но и поранить человека в соседнем отсеке. Разра ботка неуправляемого кода более трудоемка, но вы получаете при этом максималь ные контроль и скорость. Кроме того, хоть, по словам некоторых деятелей мар кетинга, вам нужно бросить все и перейти на .NET, такие резкие перемены вряд ли случатся в ближайшем будущем.

В этой главе я расскажу о «продвинутых» технологиях Microsoft Visual Studio

.NET, позволяющих отлаживать неуправляемые приложения (native applications). Я начну с обсуждения точек прерывания, поскольку в неуправляемом коде в ва шем распоряжении еще больше вариантов остановки процесса. Мы продолжим дополнительными подробностями об окне Watch и удаленной отладкой. В заклю чение я расскажу о языке ассемблера Intel IA32 (Pentium), чтобы вы всегда могли выяснить что происходит, даже не имея под рукой исходного кода и затерявшись во дебрях чужого кода или ОС.

Усложненные точки прерывания

для неуправляемого кода

В главе 5 говорилось об общих точках прерывания для неуправляемого и управ ляемого кода. В этой главе я обращусь к уникальным особенностям неуправляе мых приложений и некоторым проблемам, с которыми вы столкнетесь. Я также

246 ЧАСТЬ II Производительная отладка

расскажу о чудесных точках прерывания по данным (data breakpoints), доступных в неуправляемых приложениях.

Усложненный синтаксис точек прерывания

В отличие от отладки управляемого кода в отладке неуправляемого кода есть до полнительные возможности управлять местом и временем инициации точек пре рывания. В силу природы генерирования неуправляемых символов вам много раз придется предоставлять отладчику дополнительную помощь для правильного размещения точек прерывания в нужных местах. Когда речь идет об отладочных символах, правила гораздо мягче строгих правил C++. Так, вполне обосновано наличие нескольких символов верхнего уровня (top level symbols) для LoadLibrary. Каждый модуль, импортирующий LoadLibrary, содержит для нее символ (указыва ющий на импорт), и экспортирующий модуль также содержит символ (указыва ющий на экспорт). Усложненный синтаксис точек прерывания помогает устано вить диапазон действия точно на нужный символ.

Усложненный синтаксис точек прерывания интересен тем, что вы привыкли постоянно видеть его в Microsoft Visual C++ 6 и предыдущих версиях, поскольку именно так в старом диалоговом окне Breakpoint отображались установленные вами точки прерывания. В Visual Studio .NET вы больше не увидите отображения усложненного синтаксиса точек прерывания, но вам все таки следует знать, что это за синтаксис для полноценного управления отладчиком.

Усложненный синтаксис точек прерывания состоит из двух частей. Первая — это контекстная часть (context portion), а вторая представляет местоположение, выражение или переменную. Контекстную часть можно воспринимать так же, как область видимости переменной при программировании. Контекст просто предо ставляет отладчику ясное расположение вашей точки прерывания.

В терминах отладчика контекст определяют функция, исходный файл и дво ичный модуль, и в усложненном синтаксисе точек прерывания контекст описы вается как «{[функция],[исходный файл],[двоичный модуль]}». Надо указать лишь достаточное количество информации о контексте для установки точки прерыва ния, так что контекстная часть может содержать единственное поле или включать все три. Для заурядной точки прерывания по месту (location breakpoint) отладчи ку понадобятся только сведения об имени исходного файла. Так, в Visual C++ 6 стандартная точка прерывания по месту из строки 20 файла TEST.CPP отобража лась в диалоговом окне Breakpoint как {,TEST.CPP,}.20. Вообще то, если вы хотите установить такую же точку прерывания в Visual Studio .NET по настоящему слож ным способом, можете ввести {,TEST.CPP,}@20 в поле ввода Function на вкладке Function диалогового окна New Breakpoint. Когда вы щелкнете OK, появится ин формационное окно «IntelliSense could not find the specified location. Do you still want to set the breakpoint?» («IntelliSense не может обнаружить указанное место. Вы по прежнему хотите установить точку прерывания?»), потому что IntelliSense не знает усложненного синтаксиса точек прерывания. Щелкните в информаци онном окне Yes и, запустив программу, вы увидите, что точка прерывания устано вилась. Если вы уже в отладке, то увидите появившуюся в строке красную точку.

Возможность указывать контекст для точки прерывания по месту позволяет решить весьма гадкую отладочную проблему. Рассмотрим случай, когда у вас есть

ГЛАВА 7 Усложненные технологии неуправляемого кода в Visual Studio .NET

247

 

 

исходный файл с диагностической функцией CheckMyMem, используемой двумя DLL — A.DLL и B.DLL, — и в каждую DLL функция вносится статическим подключением (static linking). Поскольку вы широко применяете профилактическое программи рование, вы часто вызываете эту функцию из обеих DLL. Однако случайные сбои происходят только в B.DLL. Если вы установите стандартную точку прерывания по месту в строке 27 исходного кода CheckMyMem, она будет инициироваться в обе их DLL, хотя вы хотите просмотреть вызовы, сделанные только в B.DLL. Чтобы указать, что точка прерывания по месту должна инициироваться только в B.DLL, надо вручную ввести контекст точки прерывания {,CHECKMYMEM.CPP,B.DLL}@27. Хотя вам, возможно, кажется, что это надуманный пример, и вы никогда не разделяете исходный код между модулями подобным способом, вы, вероятно, никогда не задумывались о том, что происходит при использовании подставляемых функций (inline functions) в классах C++!

Во второй части усложненного синтаксиса точек прерывания указывается место, выражение или переменная. Однако, как вы скоро увидите, в Visual Studio .NET, кроме номера строки и имени функции, другие значения устанавливать нельзя. Это не проблема, так как устанавливать усложненные точки прерывания в Visual Studio .NET гораздо проще, чем было в Visual C++ 6.

Точки прерывания в системных и экспортируемых функциях

В главе 5 я рассказывал о замечательных способах, которыми можно просто вве сти имя функции или метода и автоматически получить установленную точку прерывания. Однако я не рассказал об установке точки прерывания в функции, импортируемой вашей программой из DLL. Устанавливая точки прерывания в этих экспортируемых из DLL функциях, можно решать чрезвычайно трудные пробле мы. Так, можно получать управление обработкой в известной точке для отслежи вания вытекающих нарушений целостности памяти. Еще хороший пример: вы хотите взглянуть, какая информация передается в разных параметрах. Интерес но, что, попытавшись установить точку прерывания для экспортируемой функ ции, вы будете разочарованы. Она не работает. С отладчиком все в порядке — просто вам надо дать ему контекстную информацию о том, где найти эту функ цию. Кроме того, важна еще одна небольшая деталь: имя функции зависит от того, загружены ли символы для DLL. Прежде чем я стану это обсуждать, установите отладчик Visual Studio .NET на загрузку экспортов (exports) как символов. В диа логовом окне Options на странице свойств Native из папки Debugging установите флажок Load DLL Exports. Причина установки этого параметра в том, что, даже если у вас нет символов, по крайней мере экспортируемые символы для модуля будут сопровождаться таблицей «псевдосимволов», построенной вне экспортиру емых функций из DLL. Тогда вместо шестнадцатеричных чисел вы увидите имена этих экспортируемых функций.

Чтобы проиллюстрировать установку точки прерывания в системной DLL, я установлю ее в функции LoadLibrary из KERNEL32.DLL. Возможно, вы захотите про делать это со мной, чтобы увидеть все этапы в действии. Так как LoadLibrary вызы вается из реальных программ, для отладки можно выбрать любое приложение. Начнем с пошагового выполнения, чтобы запустить отладчик и инициализиро вать все таблицы символов. Если вы попытаетесь просто указать LoadLibrary в

248 ЧАСТЬ II Производительная отладка

диалоговом окне New Breakpoint, то увидите, что при щелчке кнопки OK кажет ся, что точка прерывания установилась. Однако вам уже должно быть известно, что всегда надо проверять окно Breakpoints и следить, не отображается ли рядом с точкой прерывания значок вопросительного или восклицательного знака, ука зывающий на то, что точка прерывания не установилась. В приложении, которое я использую для установки точек прерывания (WDBG из главы 4), в окне Breakpoint отображается вопросительный знак рядом с текстом, показывающим тип LoadLib rary(const unsigned short *).

Первый шаг установки точки прерывания в экспортируемой функции — опре деление наличия загруженных символов для модуля, содержащего экспорт. Посколь ку все вы должны были прекратить чтение в конце второй главы и создать сервер символов, чтобы иметь возможность всегда получать все символы ОС, то симво лы должны быть загружены. Проверить загрузку символов можно двумя способа ми. Во первых, если в окне Debug Output вы видите текст «‘<Program>‘ : Loaded ‘<DLL>‘, Symbols loaded.», символы загружены. Второй способ задействует окна Modules, доступные из подменю Windows меню Debug или нажатием Ctrl+Alt+U при использовании сочетаний клавиш, установленных по умолчанию. Самая правая колонка в окне Module — Information — сообщает, загружены ли символы. Выде лите интересующий вас модуль и промотайте до упора вправо. Если столбец Information для вашего модуля сообщает Symbols Loaded, символы в вашем рас поряжении. Если в ней сказано что то другое и вы уверены в наличии правиль ного PDB файла для DLL, щелкните правой кнопкой элемент в окне Modules и выберите из контекстного меню команду Reload Symbols. Появившееся диалого вое окно Reload Symbols: filename.pdb позволит найти нужный PDB файл. Поскольку сервер символов превратит установку символов в тривиальную операцию, я на стоятельно рекомендую вам избрать этот способ. Если в окне Debug Output или Modules сказано что то другое, символы не загружены.

Если символы не загружены, строкой местоположения вам послужит имя, эк спортируемое из DLL. Имя можно проверить, запустив утилиту DUMPBIN из ком плекта поставки Visual Studio .NET для DLL: DUMPBIN /EXPORTS Имя DLL. Запустив DUMPBIN для KERNEL32.DLL, вы не увидите функции LoadLibrary, но увидите две функции с похожими именами: LoadLibraryA и LoadLibraryW. (LoadLibraryExA и LoadLibraryExW — это различные API.) Суффиксы указывают набор символов, ис пользуемый функцией: суффикс A соответствует ANSI, а W — Wide или Unicode. Все ОС Microsoft Windows, кроме Windows 98/Me, для многоязыковой поддержки применяют встроенный набор Unicode. Если вы откомпилировали вашу программу с определением UNICODE, лучше использовать версию LoadLibraryW. Если нет — го дится LoadLibraryA. Однако LoadLibraryA — это просто оболочка, выделяющая па мять для преобразования строки ANSI в Unicode и вызывающая LoadLibraryW, так что формально вы могли бы также применить LoadLibraryW. Если вы уверены, что ваша программа будет вызывать только одну из этих функций, можете установить точку прерывания только в ней. Если нет, установите точки прерывания в обеих функциях.

Если ваше приложение предназначено только для Microsoft Windows 2000/XP/

.NET Server 2003, всегда используйте Unicode. Вы сможете получить большой при

ГЛАВА 7 Усложненные технологии неуправляемого кода в Visual Studio .NET

249

 

 

рост производительности. В своей колонке «Under the Hood» в «Microsoft Systems Journal» за декабрь 1997 года Мэтт Петрек (Matt Pietrek) сообщил, что ANSI оболочки приводят к серьезным потерям производительности. Кроме ускорения работы программы, с использованием Unicode вы на несколько шагов приблизитесь к полной интернационализации.

Если символы не загружены, синтаксис точки прерывания для остановки в

LoadLibrary будет таким: {,,KERNEL32.DLL}LoadLibraryA или {,,KERNEL32.DLL}LoadLib raryW. Если символы загружены, придется выполнить некоторые вычисления, так как потребуется соответствие расширенному имени символа (decorated symbol name). Вам понадобится знание соглашений вызова экспортируемых функций и прототипов функций. Ниже я разберу соглашения вызова подробнее. Для функ ции LoadLibrary прототип из WINBASE.H (с раскрытыми для ясности нескольки ми макросами) выглядит так:

__declspec (dllimport)

HMODULE

__stdcall

LoadLibraryA(

LPCSTR lpLibFileName

);

Макрос WINBASEAPI раскрывается в стандартное соглашение вызова — __stdcall — которое, кстати, является соглашением вызова для всех функций системного API. Функции стандартного вызова дополняются префиксом из символа нижнего под черкивания и суффиксом из символа «@», за которым следует число помещаемых

встек байтов. К счастью, его просто вычислить: это сумма количества байтов всех параметров. В семействе процессоров Intel Pentium можно просто сосчитать число параметров и умножить его на 4. В случае с LoadLibrary, принимающей один па раметр, итоговым именем будет _LoadLibraryW@4. Вот несколько примеров, кото рые дадут вам представление о том, как выглядят итоговые имена: для CreateProcess, имеющей 10 параметров, это _CreateProcessW@40; а для TlsAlloc, не имеющей пара метров, — _TlsAlloc@0. Даже если функция не имеет параметров, следует сохранять формат «@#». Как в случае, когда символы не загружены, правила ANSI и Unicode сохраняются. Если символы загружены, синтаксис точки прерывания для остановки

вLoadLibrary будет {,,KERNEL32.DLL}_LoadLibraryA@4 или {,,KERNEL32.DLL}_LoadLibraryW@4. Определив усложненный синтаксис для установки точки прерывания, вызовите

диалоговое окно New Breakpoint (Ctrl+B). На вкладке Function в поле ввода Function введите соответствующий усложненный синтаксис точки прерывания. После щел чка OK вы получите обычное предупреждение о том, что IntelliSense не находит точку прерывания. Щелкните OK, и отладчик установит точку прерывания. В окне Breakpoints вы увидите рядом с точкой прерывания полную красную точку, а в колонке Name точка прерывания будет представлена во всем блеске своего син таксиса. Щелкнув точку прерывания правой кнопкой и выбрав из контекстного меню команду Go To Disassembly, вы увидите, где в памяти располагается экспор тируемая функция.

250 ЧАСТЬ II Производительная отладка

Условные выражения

Хотя управляемый код позволяет вызывать методы и свойства через модифика торы условных выражений для точек прерывания, неуправляемый код — нет. Кроме того, условные выражения не могут вычислять значения макросов C++, поэтому, если вы хотите сравнить значение с TRUE, придется использовать значение 1 (хотя true и false вычисляются корректно). В коде C++, как и в некоторых языках управ ляемого кода, любые условные выражения должны использовать значения C++. Даже с этими ограничениями модификаторы условных выражений для точек преры вания по месту чрезвычайно эффективны, так как, кроме возможности вычислять значения переменных, вы получаете доступ к специальному набору значений — псевдорегистрам (pseudoregisters).

По большей части псевдорегистры представляют значения регистров ЦП. В Visual Studio .NET усовершенствованы используемые и отображаемые типы ре гистров. Кроме обычных регистров ЦП, Visual Studio теперь поддерживает допол нительные, такие как MMX, SSE, SSE2 и 3Dnow (табл. 7 1). Реальные регистры ЦП сопровождаются разграничителем @, а два специальных значения начинаются с символа $. Полный список значений регистров см. в документации по процессо рам Intel и AMD (Advanced Micro Devices). Помните, в Visual C++ 6 вы также могли указывать @ перед псевдорегистрами? Для обратной совместимости такая возмож ность сохранена и в Visual Studio .NET 2003, но будущие версии будут поддержи вать для псевдорегистров только знак $, поэтому привыкайте к нему уже сейчас. Кроме того, чтобы просмотреть значение, некоторые из вас вводили значения регистров без знака @, предшествующего имени. Однако я всегда буду показывать регистры с префиксом @.

Табл. 7-1. Примеры псевдорегистров

Псевдорегистр

Описание

@EAX

Регистр возвращаемого значения (32 битное значение)

@BL

Младшее слово регистра EBX (16 битное значение)

@MM0

MMX регистр 0

@XMM1

SSE регистр 1

$ERR

Значение последней ошибки (специальное значение)

$TIB

Блок информации потока (специальное значение)

 

 

Два последних значения в табл. 7 1 предоставляют условным точкам прерыва ния дополнительные возможности. $ERR позволяет просмотреть значение после дней ошибки в потоке (значение, возвращаемое вызовом API GetLastError) и ос тановиться, только если выполняется условие последней ошибки. Так, если вы хотите остановиться, только если значение последней ошибки, возвращаемое функцией API, равно ERROR_INSUFFICIENT_BUFFER, что указывает на недостаточный размер буфера данных, то вначале следует найти ERROR_INSUFFICIENT_BUFFER в WINER ROR.H и узнать, что значение равно 122. Условное выражение для точки преры вания будет таким: $ERR==122.

Специальный псевдорегистр $TIB открывает решение неприятной проблемы Visual Studio .NET. К сожалению, здесь нет встроенных способов явной установки точки прерывания по месту, которая инициировалась бы только в определенном

Соседние файлы в предмете Программирование на C++