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

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

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

Ч А С Т Ь I I

ПРОИЗВОДИТЕЛЬНАЯ

ОТЛАДКА

Г Л А В А

4

Поддержка отладки ОС и как работают отладчики Win32

Изучение работы инструментария — ключевая часть нашей работы. Зная воз можности инструментов, вы можете максимизировать их отдачу и меньше вре мени тратить на отладку. В основном отладчики очень помогают, но иногда они способны быть источником коварных проблем. Особенно интересна отладка не управляемого кода, поскольку здесь вмешивается ОС и меняет поведение процес сов, так как они работают под отладчиком. Кроме того, имеется весьма интерес ная поддержка внутри самой ОС, помогающая в некоторых сложных ситуациях при отладке. В этой главе я объясню, что такое отладчик, покажу, как работают отладчики в ОС Microsoft Win32, а также мы обсудим хитрые приемы, необходи мые для эффективного использования средств отладки Win32.

После краткого обзора Win32 отладчиков я перейду к особенностям специаль ных функций, доступных при запуске процесса под отладчиком. Чтобы показать, как работают отладчики, я представлю пару, исходные коды которых находятся в прилагаемых к этой книге файлах примеров: MinDBG выполняет тот минимум функций, который позволяет ему называться отладчиком, а WDBG является при мером настоящего отладчика Win32 и делает все, что положено, включая мани пуляции с таблицами символов для просмотра локальных переменных и струк тур, управление точками прерывания, генерацию дизассемблированного кода, а также координацию с графическим интерфейсом пользователя (GUI). При обсуж дении WDBG я также освещу такие темы, как работа точек прерывания, и расска жу о типах файлов символов. В завершение я расскажу о написанной мной очень крутой оболочке для сервера символов, которая упрощает работу с локальными

ГЛАВА 4 Поддержка отладки ОС и как работают отладчики Win32

143

 

 

переменными и аргументами. Этот сервер был самым трудным кодом, написан ным мной для этой книги, и я уверен, что вы найдете его весьма полезным!

Почему нет главы, посвященной отладчикам .NET?

Вы, возможно, удивляетесь, почему в этой книге нет главы, посвященной работе отладчиков Microsoft .NET. Сначала я предполагал написать такую главу, но в результате исследования отладочного API .NET (.NET Debugging API) я понял, что в отличие от практически недокументированых отладчи ков Win32 команда разработчиков исполняющей среды .NET проделала огромную работу по описанию отладочного интерфейса .NET. Кроме того, приведенный здесь пример отладчика показывает, как сделать все, что тре буется от отладчика .NET. Этот пример почти на 98% — консольный отлад чик CORDBG. В нем нет только команд дизассемблирования неуправляемого кода. Работа над отладчиком .NET заняла у меня пару недель, и я быстро понял, что здесь делать нечего (разве что изложить своими словами пре красную документацию по .NET) и мне не удастся показать что либо новое, кроме того, что видно из примера CORDBG. Файлы Debug.doc и DebugRef.doc, описывающие отладочный API .NET, уже установлены на ваш компьютер в процессе установки Visual Studio .NET и находятся в каталоге <Каталог ус тановки Visual Studio .NET>\SDK\v1.1\Tool Developers Guide\Docs.

И последнее. Прежде чем погрузиться в эту главу, я хочу определить два тер мина, которые буду использовать на протяжении всей книги: отладчик (debugger) и отлаживаемая программа (debuggee). Отладчик — это просто процесс, способ ный управлять другим процессом для его отладки, а отлаживаемая программа — это процесс, запускаемый под отладчиком. В некоторых ОС отладчик называют родительским процессом, а отлаживаемую программу — дочерним.

Типы отладчиков Windows

Если вы программировали для Win32, то, возможно, слышали о нескольких ти пах отладчиков. В мире Microsoft Windows доступны два типа: отладчики пользо вательского режима и отладчики режима ядра.

Большинство программистов в основном знакомо с отладчиками пользователь ского режима. Не будет сюрпризом узнать, что отладчики первого типа предназ начены для отладки приложений пользовательского режима. Отладчики второго типа, как следует из их названия, позволяют отлаживать ядро ОС. Такие отладчи ки применяют главным образом разработчики драйверов.

Отладчики пользовательского режима

Отладчики пользовательского режима служит для отладки любых приложений, ра ботающих в пользовательском режиме. Сюда входят любые программы с GUI, а также, что для вас будет неожиданностью, такие приложения, как службы Windows. Обычно отладчики пользовательского режима используют графические интерфей сы. Главный признак отладчиков пользовательского режима — это то, что они при меняют отладочный API Win32. Так как ОС помечает отлаживаемую программу как

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

работающую в специальном режиме, вы можете вызвать функцию API IsDebugger Present для определения, работает ли ваш процесс под отладчиком. Проверка, работаете ли вы под отладчиком, может пригодиться, если вам требуется боль ше диагностической информации, только когда к вашему процессу подключен отладчик.

В Microsoft Windows 2000 и более ранних ОС проблема отладочного API Win32 заключается в том, что если процесс был однажды запущен под отладчиком и отладчик завершается, то отлаживаемая программа тоже завершается. Иначе го воря, отлаживаемая программа была постоянно отлаживаемой. Это ограничение было прекрасно, когда все работали над клиентскими приложениями, но оно было бедствием при отладке серверных приложений, особенно когда программисты пытались отлаживать рабочие серверы. В Microsoft Windows XP/Server 2003 и более поздних версиях вы можете подсоединять к работающим процессам и отсоеди нять от них все, что вам понадобится, без каких либо условий. В Visual Studio .NET вы можете отсоединиться от процесса, выбрав Detach (отсоединить) в диалого вом окне Processes (процессы).

Интересно, что Visual Studio .NET теперь предлагает службу Visual Studio Debugger Proxy (DbgProxy) под Windows 2000, позволяющую отлаживать процесс, а затем от него отсоединиться. DbgProxy работает как отладчик, т. е. ваше приложение работает под отладчиком. Теперь вы и под Windows 2000 можете отсоединить, а затем повторно присоединить к процессу все, что надо. Но я все еще наблюдаю одну проблему программистов: независимо от используемой ими ОС (Windows XP/Server 2003 или DbgProxy под Windows 2000), они продолжают «вечную от ладку», забывая задействовать преимущества новой возможности отсоединения.

Для интерпретирующих языков и исполняющих сред, применяющих принцип виртуальной машины, сами виртуальные машины предлагают полный комплект отладки и не используют отладочный API Win32. Вот некоторые примеры сред такого типа: виртуальные машины Java от Microsoft или Sun, механизм сценариев Microsoft для Web приложений и, конечно, общеязыковая исполняющая среда Microsoft .NET (common language runtime, CLR).

Как я уже говорил, отладка приложений .NET освещена в документах (каталог Tool Developers Guide). Я также не буду касаться отладочных интерфейсов Java и языков сценариев, которые выходят за рамки данной книги. О том, как писать отладчик сценариев, см. в MSDN тему «Microsoft Windows Script Interfaces Intro duction». Как и при отладке в CLR .NET, объекты отладчика сценариев предостав ляют богатые интерфейсы для доступа к сценариям, в том числе встроенным в документы.

Отладочным API Win32 пользуется неожиданно большое количество программ. Сюда входят отладчик Visual Studio .NET при отладке неуправляемого кода, кото рый я освещаю в деталях в главах 5 и 7, отладчик Windows (Windows Debugger, WinDBG), обсуждаемый в главе 8, BoundsChecker от Compuware NuMega, программа Platform SDK Depends (которая может быть установлена в составе Visual Studio .NET), отладчики Borland Delphi и C++ Builder, а также символьный отладчик NT (NT Symbolic Debugger, NTSD). Я уверен, имеется и много других.

ГЛАВА 4 Поддержка отладки ОС и как работают отладчики Win32

145

 

 

Стандартный вопрос отладки

Как мне защитить Win32-программу от вмешательства отладчика?

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

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

Следующая функция, которую вы можете добавить к своему коду, делает то же, что и IsDebuggerPresent. Конечно же, добавление только этой функ ции не исключит возможности вмешиваться в ваш процесс с помощью от ладчика. Чтобы затруднить отладку, между основными командами разбро саны другие безобидные команды, так что хакеры не смогут искать IsDebug gerPresent по последовательности байтов. Об антихакерских технологиях можно написать целую книгу. Однако, если вы можете провести «двухчасо вой тест», означающий, что если среднему программисту требуется более двух часов на взлом вашего приложения, то ваше приложение, вероятно, защищено от всех хакеров, кроме самых настырных и талантливых.

BOOL AntiHackIsDebuggerPresent ( void )

{

BOOL bRet = TRUE ;

__asm

{

// Получить блок информации потока (Thread Information block, TIB).

MOV EAX , FS:[00000018H]

//Байты со смещением 0x30 в TIB — это поле указателя, который

//указывает на структуру, имеющую отношение к отладчику.

MOV

EAX , DWORD PTR [EAX+030H]

//Второй DWORD в этой отладочной структуре указывает,

//что процесс отлаживается.

MOVZX

EAX , BYTE

PTR [EAX+002H]

// Возвращаем результат.

MOV

bRet , EAX

 

}

return ( bRet ) ;

}

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

Отладчики режима ядра

Отладчики режима ядра располагаются между центральным процессором и ОС. Это значит, что, когда вы останавливаетесь в отладчике режима ядра, ОС тоже останавливается. Как можно себе представить, внезапная остановка ОС полезна, если вы работаете над проблемами согласования по времени и синхронизации.

Существуют три отладчика режима ядра: отладчик ядра KD, WinDBG и SoftICE.

Отладчик ядра KD

Windows 2000/XP/Server 2003 интересны тем, что на самом деле часть отладчика режима ядра является частью NTOSKRNL.EXE — главного файла ядра ОС. Этот от ладчик доступен как в рабочей, так и в отладочной версии ОС. Для переключения в режим отладки ядра для систем на базе процессоров x86 установите параметр загрузки /DEBUG в файле BOOT.INI и дополнительно /DEBUGPORT при необходимос ти установить порт связи для отладчика режима ядра на порт, отличный от порта по умолчанию (COM1). KD работает на отдельной машине, называемой хостом, и взаимодействует с целевой машиной через нуль модемный кабель или, скажем, через кабель интерфейса 1394 (FireWire) при работе с Windows XP/Server 2003.

Отладчик режима ядра NTOSKRNL.EXE делает достаточно для управления цен тральным процессором, позволяя отлаживать ОС. Основная работа по отладке — управление символами, обработка расширенных точек прерывания и дизассемб лирование — происходит на стороне KD. Когда то в Microsoft Windows NT 4 Device Driver Kit (DDK) был описан протокол связи через нуль модемный кабель. Одна ко Microsoft больше не приводит описание этого протокола.

KD входит в Debugging Tools for Windows (отладочные средства Windows), которые можно загрузить с http://www.microsoft.com/ddk/debugging (текущая вер сия на момент написания этой книги также доступна на прилагаемом к книге компакт диске). Вся сила KD становится очевидной при ознакомлении с коман дами, предлагаемыми им для доступа к внутренним состояниям ОС. Если вам ког да либо хотелось увидеть, что происходит в ОС, эти команды покажут вам это. Знание работы драйверов устройств Windows поможет разобраться с выводом этих команд. Интересно, что при всей своей мощи KD почти никогда не применялся за пределами Microsoft, так как это консольное приложение и им весьма утоми тельно пользоваться при отладке на уровне исходного кода. Однако для команд разработчиков ОС Microsoft этот отладчик ядра — единственный выбор.

WinDBG

WinDBG входит в состав Debugging Tools for Windows. Этот гибридный отладчик можно задействовать и как отладчик режима ядра, и как отладчик пользовательс кого режима, а при небольшой доработке WinDBG позволяет одновременно от лаживать программы режима ядра и пользовательского режима. При отладке в режиме ядра WinDBG предлагает все возможности KD, так как он обращается к тому же отладочному ядру, что и KD. Однако WinDBG предоставляет графический интерфейс, который вовсе не так легко задействовать, как отладчик Visual Studio

.NET, хоть и проще, чем KD. WinDBG позволяет отлаживать драйверы устройств почти так же просто, как будто вы работаете с приложениями пользовательского режима.

ГЛАВА 4 Поддержка отладки ОС и как работают отладчики Win32

147

 

 

Как отладчик пользовательского режима WinDBG весьма хорош, и я настоя тельно рекомендую, чтобы вы установили его. WinDBG предлагает гораздо боль ше возможностей, чем отладчик Visual Studio .NET, так как предоставляет вам куда больше сведений о вашем процессе. Однако за это надо платить: WinDBG слож нее в использовании, чем отладчик Visual Studio .NET. И все же я бы посоветовал вам потратить некоторое время и силы на изучение WinDBG, а я вам покажу клю чевые возможности и приемы работы с ним в главе 8. Эти затраты окупятся за счет того, что он поможет вам найти ошибку значительно быстрее, чем исполь зуя отладчик Visual Studio .NET. Я провожу около 95% времени в отладчике Visual Studio .NET, а остальное время — в WinDBG.

SoftICE

Этот отладчик режима ядра компании Compuware NuMega, как мне известно, — единственный коммерческий отладчик режима ядра на рынке. Это также един ственный отладчик режима ядра, работающий на одной машине. В отличие от других отладчиков режима ядра SoftICE прекрасно отлаживает программы пользо вательского режима. Как я уже говорил, отладчики режима ядра располагаются между центральным процессором и ОС. SofICE также располагается между цент ральным процессором и ОС при отладке программ пользовательского режима, останавливая всю ОС.

Вас может не вдохновить то, что SoftICE может остановить ОС. Но давайте рассмотрим такой случай. Что, если вам нужно отлаживать чувствительный к вре менным задержкам код? При использовании такой функции API, как SendMessage Timeout, вы легко выйдете за пределы этого времени, пока вы проходите по шагам в другом потоке с помощью обычного отладчика с графическим интерфейсом. Используя SoftICE, вы можете ходить от оператора к оператору сколь угодно дол го, так как таймер, от которого зависит исполнение SendMessageTimeout, не будет работать, пока вы работаете под SoftICE. SoftICE — единственный отладчик, по зволяющий эффективно отлаживать многопоточные приложения. То, что SoftICE останавливает всю ОС, когда он активен, означает, что разрешение проблем со гласования времени производится гораздо проще.

То, что SoftICE располагается между центральным процессором и ОС, упрощает

иотладку межпроцессного взаимодействия. Если вы занимаетесь COM програм мированием с множеством внешних серверов, вы можете просто устанавливать точки прерывания во всех процессах и ходить по шагам между ними. Наконец, в SoftICE вы запросто пройдете по шагам из пользовательского режима в режим ядра

иобратно.

Другое важное преимущество SoftICE над другими отладчиками в том, что в нем собрана феноменальная коллекция информационных команд, которые позволя ют увидеть практически все, что происходит в ОС. Хотя KD и WinDBG тоже име ют солидный набор таких команд, в SoftICE их гораздо больше. В SoftICE вы мо жете просмотреть практически все: от состояния всех событий синхронизации до полной информации о HWND и расширенной информации о любом потоке си стемы. SoftICE может рассказать вам все, что происходит в вашей системе.

Как можно ожидать, вся эта замечательная грубая сила имеет свою цену. SoftICE, как и любой отладчик режима ядра, имеет весьма крутую кривую обучения, так

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

как по существу он сам является ОС. Однако ваши затраты на обучение окупятся с лихвой от предоставляемых им преимуществ.

Поддержка отлаживаемых программ операционными системами Windows

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

Отладка Just-In-Time (JIT)

Из некоторых маркетинговых материалов по Visual Studio .NET может показать ся, что в Visual Studio за JIT отладкой скрывается чудо, однако чудеса происходят в самой ОС. При отказе приложения Windows анализирует состояние раздела ре естра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug, что бы определить, какой отладчик ей вызвать для отладки приложения. Если этот раз дел пуст, Windows XP выводит стандартное диалоговое окно аварийного завер шения, а Windows 2000 — информационное окно с адресом аварийного завер шения. Если в этом разделе реестра задано значение и заполнены остальные зна чения, под Windows XP в левом нижнем углу становится активной кнопка Debug (отладка), и вы получаете возможность отлаживать приложение. Под Windows 2000 доступна кнопка Cancel, позволяющая запустить отладчик.

JIT отладка использует три следующих важных значения в разделе AeDebug реестра:

Auto;

UserDebuggerHotKey;

Debugger.

Если Auto содержит значение 0 (нуль), ОС генерирует стандартное диалоговое окно аварийного завершения и делает доступной кнопку Cancel, позволяя присо единить отладчик. При значении 1 (единица) отладчик запускается автоматичес ки. Если вы хотите свести с ума кого нибудь из своих коллег, установите незамет но на их системах значение Auto, равное 1, — они не будут понимать, почему при каждом аварийном завершении приложения у них запускается отладчик. Значе ние UserDebuggerHotKey идентифицирует «горячую» клавишу перехода к отладке (мы очень скоро обсудим ее использование). Последнее и самое важное значение Debugger указывает отладчик, который должна запускать ОС при аварийном завер шении приложения. Есть только одно требование к отладчику: он должен поддер живать присоединение к процессу. После обсуждения значения UserDebuggerHotKey я объясню подробнее значение Debugger и его формат.

«Быстрые» клавиши прерывания и значение UserDebuggerHotKey

Иногда нужно переключиться на отладчик побыстрее. Если вы отлаживаете кон сольное приложение, нажатие Ctrl+C или Ctrl+Break вызовет специальное исклю чение DBG_CONTROL_C, которое переключит вас прямо в отладчик и позволит начать отладку.

ГЛАВА 4 Поддержка отладки ОС и как работают отладчики Win32

149

 

 

У ОС Windows есть милая возможность: в приложениях с графическим интер фейсом вы можете переключиться на отладчик в любой момент времени. При работе под отладчиком по умолчанию нажатие клавиши F12 заставляет вызвать DebugBreak почти в тот момент, когда была нажата кнопка. Кстати, даже если вы используете клавишу F12 как акселератор или иначе обрабатываете ввод с клавиа туры сообщений для клавиши F12, вы все равно попадете в отладчик.

Клавиша прерывания по умолчанию — F12, но, если надо, можно указать и другую. Значение UserDebuggerHotKey есть цифровое значение VK_*, соответствую щее клавише, которую вы желаете применять как «горячую» клавишу отладчика. Так, если вы хотите для переключения в отладчик задействовать Scroll Lock, уста новите значение UserDebuggerHotKey в 0x91 и для вступления нового значения в силу перезагрузите компьютер. Замечательной шуткой для ваших коллег может оказаться замена значения UserDebuggerHotKey на 0x45 (латинская буква E) — каждый раз, когда они нажмут клавишу E, программа переключится на отладчик. Однако я не несу никакой ответственности, если ваши коллеги ополчатся на вас и сделают вашу жизнь несчастной.

Значение Debugger

В разделе реестра AeDebug есть значение Debugger, которое и определяет основные действия. Сразу после установки ОС значение Debugger выглядит похожим на строку, передаваемую функции API wsprintf: drwtsn32 p %ld e %ld g. Так оно и есть: p является идентификатором аварийно завершающегося процесса, а e — описатель собы тия, нужный отладчику, чтобы сигнализировать, что в его цикле произошел вы ход из первого потока. Сигнал об этом событии сообщает ОС, что отладчик ус пешно присоединился к процессу. –g говорит программе Dr. Watson, что надо продолжить выполнение программы после присоединения.

Вы всегда можете изменить значение Debugger, чтобы вызывать другой отлад чик. Чтобы сделать отладчик Visual Studio .NET «родным» отладчиком, откройте Visual Studio .NET и выберите Options из меню Tool. В диалоговом окне Options выберите папку Debugging, затем — страницу свойств Just In Time и убедитесь, что установлен флажок рядом с пунктом Native. Вы можете настроить WinDBG или Dr. Watson своим предпочтительным отладчиком путем запуска из командной строки WinDBG –I (заметьте: ключ чувствителен к регистру ввода) или DRWTSN32 –I. Изменив значение Debugger, обязательно завершите Task Manager (Диспетчер за дач), если он выполнялся. Диспетчер задач кэширует раздел реестра AeDebug во время своей работы, поэтому, если вы попытаетесь отладить процесс из списка на стра ничке Processes (Процессы) Диспетчера задач, отладчик может не заработать, если предыдущим отладчиком был Visual Studio .NET.

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

Хорошо иметь возможность оперативной отладки, когда отладчик вызывается при аварийном завершении приложения, но здесь есть существенное ограничение: вы можете иметь одновременно только один такой отладчик, задаваемый значени ем Debugger. Как мы увидим в следующих главах, отладчики имеют сильные и сла бые стороны в зависимости от конкретной ситуации. Ничего не может быть хуже, если «выскочит» не тот отладчик, о котором вы знаете, что он позволит запросто найти ошибку, которую вы несколько недель пытались воспроизвести.

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

Это серьезная проблема, и я решил приложить руку к ее решению. Однако, поскольку все, похожее на ошибку, запускает JIT отладку под Visual Studio .NET, я сделал много проб и ошибок, чтобы воплотить свою идею. Прежде всего расска жу, как работает программа Debugger Chooser или, для краткости, DBGCHOOSER.

Идея, заложенная в DBGCHOOSER, состоит в том, что она работает как про грамма прокладка, вызываемая при аварийном завершении отлаживаемой програм мы и передающая настоящему отладчику информацию, нужную для отладки при ложения. Для настройки DBGCHOOSER сначала скопируйте ее в каталог своего компьютера, где она не может быть случайно удалена. ОС пытается запустить отладчик, заданный значением Debugger раздела реестра AeDebug, и, если отладчик недоступен, у вас не будет шансов отладить приложение в случае его аварийного завершения. Для инициализации DBGCHOOSER просто запустите его (рис. 4 1). Первый запуск DBGCHOOSER устанавливает умолчания, характерные для большин ства машин программистов. Если какие то ваши отладчики не указаны здесь, ука жите их пути. Уделите особое внимание отладчику Visual Studio .NET, так как обо лочка оперативного отладчика, используемая Visual Studio .NET, отсутствует в пути по умолчанию. По щелчку кнопки OK в диалоговом окне настройки DBGCHOOSER записывает параметры отладчика в INI файл, хранящийся в каталоге Windows, и настраивает себя отладчиком по умолчанию в разделе реестра AeDebug.

Рис. 4 1. Диалоговое окно настройки DBGCHOOSER

Как только случится одно из редких (я надеюсь) аварийных завершений, пос ле щелчка кнопки Debug диалогового окна аварийного завершения вы увидите диалоговое окно выбора отладчика (рис. 4 2). Просто выберите нужный отлад чик и начните отладку.

В реализации DBGCHOOSER нет ничего особенного. Первое, что может заин тересовать, это то, что, когда вызывается CreateProcess для выбранного пользова телем отладчика, нужно обеспечить установку флага наследования описателей в TRUE. Чтобы с описателями все было классно, я заставил DBGCHOOSER ждать за вершения порожденного отладчика. Таким образом, я знаю, что все наследуемые описатели сохранены для отладчика. Хотя прийти к этой идее было труднее, чем ее реализовать, чтобы заставить Visual Studio .NET правильно работать, пришлось немного потрудиться. Все классно работало с WinDBG, Microsoft Visual C++ 6 и

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