Это перевод Pushing the Limits of Windows: Physical Memory. Автор: Марк Руссинович.
Это первый пост в блоге из серии "Pushing the Limits of Windows", которую я буду писать ближайшие месяцы и в которой буду описывать как Windows и приложения используют конкретный ресурс, лицензионные и реализационные ограничения ресурса, как измерить использование ресурса и как диагностировать его утечки. Чтобы эффективно управлять своими Windows системами вам нужно понимать как Windows управляет физическими ресурсами, такими как процессоры (CPUs) и память (memory), а также логическими ресурсами, такими как виртуальная память (virtual memory), дескрипторы (handles) и объекты оконного менеджера (window manager objects). Знание пределов и ограничений этих ресурсов и методы слежения за ними позволит вам соотносить использование ресурсов с приложениями, которые их используют, эффективно изменять систему для определённой нагрузки и идентифицировать приложения с утечкой ресурсов.
...when altering one's mind becomes as easy as programming a computer, what does it mean to be human?..
пятница, 30 января 2009 г.
понедельник, 19 января 2009 г.
Почему возвращаемые значения-дескрипторы так непоследовательны?
Это перевод Why are HANDLE return values so inconsistent? Автор: Реймонд Чен.
Если вы внимательно посмотрите на различные функции, которые возвращают дескрипторы объектов ядра (THandle), то вы увидите, что какие-то их них возвращают 0 (например, CreateThread), а какие-то -1 - INVALID_HANDLE_VALUE (например, CreateFile). И вам каждый раз приходится заглядывать в документацию, чтобы проверить, что же возвращает вот эта конкретная функция при неудаче.
Почему же возвращаемые значения так отличаются?
Причины, как вы уже подозреваете, - исторические.
Эти значения выбирались, исходя из соображений совместимости с 16-ти битной Windows. 16-ти битные функции OpenFile, _lopen и _lcreat возвращали -1 при ошибке, поэтому 32-х разрядная функция CreateFile также возвращает -1 (INVALID_HANDLE_VALUE), чтобы упростить перенос приложений с Win16.
(Вооружённые этим знанием, вы уже можете ответить на следующий простой вопрос: почему, когда мне нужно вызывать CreateFile, хотя в действительности я не открываю файл? Разве не должна функция называться OpenFile? Ответ: да, OpenFile было бы более подходящим именем, но это имя уже занято.)
С другой стороны, в Win16 не было эквивалентов для CreateThread или CreateMutex, поэтому они возвращают 0.
Поскольку прецедент уже установил непоследовательность возвращаемых значений, то при добавлении новой функции всегда получается выбор между возвратом 0 или INVALID_HANDLE_VALUE.
Эта непоследовательность имеет несколько последствий.
По-первых, конечно же, вам нужно аккуратно проверять возвращаемые значения.
Во-вторых , это означает, что если вы хотите написать какую-то общую обёртку вокруг значения дескриптора - то вам надо различать два возможных значения "не дескриптор".
В-третьих, если вы хотите инициализировать значение дескриптора, то вам придётся делать это по-разному, в зависимости от функции, с которой вы собрались его использовать. Например, следующий код неверен:
Если вы внимательно посмотрите на различные функции, которые возвращают дескрипторы объектов ядра (THandle), то вы увидите, что какие-то их них возвращают 0 (например, CreateThread), а какие-то -1 - INVALID_HANDLE_VALUE (например, CreateFile). И вам каждый раз приходится заглядывать в документацию, чтобы проверить, что же возвращает вот эта конкретная функция при неудаче.
Почему же возвращаемые значения так отличаются?
Причины, как вы уже подозреваете, - исторические.
Эти значения выбирались, исходя из соображений совместимости с 16-ти битной Windows. 16-ти битные функции OpenFile, _lopen и _lcreat возвращали -1 при ошибке, поэтому 32-х разрядная функция CreateFile также возвращает -1 (INVALID_HANDLE_VALUE), чтобы упростить перенос приложений с Win16.
(Вооружённые этим знанием, вы уже можете ответить на следующий простой вопрос: почему, когда мне нужно вызывать CreateFile, хотя в действительности я не открываю файл? Разве не должна функция называться OpenFile? Ответ: да, OpenFile было бы более подходящим именем, но это имя уже занято.)
С другой стороны, в Win16 не было эквивалентов для CreateThread или CreateMutex, поэтому они возвращают 0.
Поскольку прецедент уже установил непоследовательность возвращаемых значений, то при добавлении новой функции всегда получается выбор между возвратом 0 или INVALID_HANDLE_VALUE.
Эта непоследовательность имеет несколько последствий.
По-первых, конечно же, вам нужно аккуратно проверять возвращаемые значения.
Во-вторых , это означает, что если вы хотите написать какую-то общую обёртку вокруг значения дескриптора - то вам надо различать два возможных значения "не дескриптор".
В-третьих, если вы хотите инициализировать значение дескриптора, то вам придётся делать это по-разному, в зависимости от функции, с которой вы собрались его использовать. Например, следующий код неверен:
varВ этом коде два бага. Во-первых, возвращаемое из CreateFile значение проверяется неверно. Код выше проверяет на 0 вместо INVALID_HANDLE_VALUE. Во-вторых, код неверно инициализирует переменную H. Вот исправленный вариант:
H: THandle;
begin
H := 0;
if UseLogFile then
H := CreateFile(...);
DoOtherStuff;
if H <> 0 then
Log(H);
DoOtherStuff;
if H <> 0 then
CloseHandle(H);
end;
varВ-четвёртых, вам нужно быть особенно внимательными со значением INVALID_HANDLE_VALUE: чисто случайно оно совпадает со значением псевдо-дескриптора, возвращаемым GetCurrentProcess. Многие функции ядра принимают такие псевдо-дескрипторы, поэтому, если вы, например, облажаетесь и случайно вызовите, скажем, WaitForSingleObject со значением INVALID_HANDLE_VALUE, в итоге вы обнаружите, что вы ждёте текущий процесс. Такое ожидание, конечно же, никогда не завершится, потому что процесс переходит в сигнальное состояние только при выходе, поэтому вы в итоге ждёте самого себя.
H: THandle;
begin
H := INVALID_HANDLE_VALUE;
if UseLogFile then
H := CreateFile(...);
DoOtherStuff;
if H <> INVALID_HANDLE_VALUE then
Log(H);
DoOtherStuff;
if H <> INVALID_HANDLE_VALUE then
CloseHandle(H);
end;
Почему 16-ти битные DOS и Windows всё ещё с нами
Это перевод Why 16-bit DOS and Windows are still with us. Автор: Реймонд Чен.
Многие люди хотят, чтобы мы выкинули из Windows системы обеспечения совместимости с 16-ти битным DOS и 16-ти разрядными Windows программами. Поверьте мне, когда придёт время выкинуть этот старый хлам - я буду первым в очереди, чтобы это сделать.
Но это время ещё не пришло (*).
Видите ли, ребята из группы установки и развёртывания (Setup and Deployment group) посетили много различных компаний по всему миру, чтобы узнать, как они используют Windows в своём бизнесе. И при этом вылезла одна вещь, которая имеет отношения к этим системам поддержки старых программ:
Компании всё ещё используют 16-ти разрядные приложения. И очень часто.
Каждая компания имеет свой набор приложений, важных для бизнеса (Line of Business - LOB). Это программы, которые компания использует в своём бизнесе ежедневно, программы, без которых они просто не смогут жить. Например, у нас в Microsoft есть две критичные LOB-программы: система отслеживания ошибок (defect tracking system) и система контроля версий (source control system).
И, подобно Microsoft-ским системам, многие из LOB-приложений в больших корпорациях не являются коммерчески-распространяемым софтом; они разрабатываются только внутри корпорации, учитывают все её особенности и требования, а также часто являются секретом фирмы. Например, в компании по оказанию финансовых услуг её пакет анализа и предсказаний - это то, что отличает эту компанию от её конкурентов.
LOB-приложение - это ключевой момент при апгрейде. Если обновление Windows поломает работу LOB-приложения - всё, игра окончена. Никакого обновления. Ни одна компания не будет выкидывать на свалку приложение, которое критично для их бизнеса.
И так уж получается, что целая куча таких LOB-приложений - 16-ти битные программы. Некоторые - даже под DOS. Некоторые - 16-ти битные Windows программы, написанные в какой-то древней версии Visual Basic.
"Ну так попросите их портировать программы в Win32".
Проще сказать, чем сделать.
Но это должна быть действительно большая приманка.
Вот пример из реальной жизни: только на последней неделе я посетил друга, который живет в очень неплохом, профессионально управляемом жилом комплексе. Один раз нам нужно было зайти в офис, и я мельком глянул на экран их компьютера. На нём стояла Windows XP. А как же программа, которую они использовали для управления комплексом? Она была запущена в DOS-окне.
Примечания переводчика:
(*) В 32-х разрядной Windows Vista всё ещё поддерживаются старые 16-ти разрядные приложения (конечно, сильно ограниченно, но всё же). А вот 64-х разрядная Windows Vista - уже нет.
Многие люди хотят, чтобы мы выкинули из Windows системы обеспечения совместимости с 16-ти битным DOS и 16-ти разрядными Windows программами. Поверьте мне, когда придёт время выкинуть этот старый хлам - я буду первым в очереди, чтобы это сделать.
Но это время ещё не пришло (*).
Видите ли, ребята из группы установки и развёртывания (Setup and Deployment group) посетили много различных компаний по всему миру, чтобы узнать, как они используют Windows в своём бизнесе. И при этом вылезла одна вещь, которая имеет отношения к этим системам поддержки старых программ:
Компании всё ещё используют 16-ти разрядные приложения. И очень часто.
Каждая компания имеет свой набор приложений, важных для бизнеса (Line of Business - LOB). Это программы, которые компания использует в своём бизнесе ежедневно, программы, без которых они просто не смогут жить. Например, у нас в Microsoft есть две критичные LOB-программы: система отслеживания ошибок (defect tracking system) и система контроля версий (source control system).
И, подобно Microsoft-ским системам, многие из LOB-приложений в больших корпорациях не являются коммерчески-распространяемым софтом; они разрабатываются только внутри корпорации, учитывают все её особенности и требования, а также часто являются секретом фирмы. Например, в компании по оказанию финансовых услуг её пакет анализа и предсказаний - это то, что отличает эту компанию от её конкурентов.
LOB-приложение - это ключевой момент при апгрейде. Если обновление Windows поломает работу LOB-приложения - всё, игра окончена. Никакого обновления. Ни одна компания не будет выкидывать на свалку приложение, которое критично для их бизнеса.
И так уж получается, что целая куча таких LOB-приложений - 16-ти битные программы. Некоторые - даже под DOS. Некоторые - 16-ти битные Windows программы, написанные в какой-то древней версии Visual Basic.
"Ну так попросите их портировать программы в Win32".
Проще сказать, чем сделать.
- Зачем компании вообще тратить какие-то усилия на портирование программы, когда текущая версия работает и так?. Если она не поломалась - то и чинить её нечего.
- Портированную программу нужно отладить и протестировать параллельно с основной версией. Существующей версии около 10 лет. Все её выкрутасы хорошо знакомы. Она выжила в тот раз, когда в 1998 остановилась линия доставки для производства, а когда производство возобновилось, им пришлось утроить объём данных, чтобы нагнать график. Новая версия же не проходила нагрузочных тестов. Кто знает, сможет ли она так же хорошо послужить в критических ситуациях?
- Конвертирование из DOS-программы в Windows приведёт к большим затратам на переучивание персонала ("Я всегда нажимала F4 для отправки заказа. А теперь у меня тут панелька с кучей кнопок с непонятными картинками, и мне нужно запомнить, что они все означают". Представьте себе, что кто-то отнял у вас ваш любимый текстовый редактор, а взамен дал другой с совершенно другими горячими главишами. "Но новый же лучше".)
- Часто компании больше не имеют исходных кодов своих внутренних программ, поэтому они не могли бы портировать программу, даже если бы захотели. Программа может использовать сторонний VB-контрол от компании, которая уже давно мертва. Она может использовать нестандартное оборудование, драйвер к которому имеется только в 16-ти битном варианте. А даже если у них остались исходники, то их автор уже не работает у них. В случае отсутствующего драйвера, у компании вообще может не быть человека, который смог бы написать 32-х битный драйвер (я знаю одну компанию, которая использовала ножные педали для управления одной из своих программ).
Но это должна быть действительно большая приманка.
Вот пример из реальной жизни: только на последней неделе я посетил друга, который живет в очень неплохом, профессионально управляемом жилом комплексе. Один раз нам нужно было зайти в офис, и я мельком глянул на экран их компьютера. На нём стояла Windows XP. А как же программа, которую они использовали для управления комплексом? Она была запущена в DOS-окне.
Примечания переводчика:
(*) В 32-х разрядной Windows Vista всё ещё поддерживаются старые 16-ти разрядные приложения (конечно, сильно ограниченно, но всё же). А вот 64-х разрядная Windows Vista - уже нет.
среда, 7 января 2009 г.
Правильный порядок отключения и включения окон
Это перевод The correct order for disabling and enabling windows. Автор: Реймонд Чен.
Если вы хотите показать UI модально, вам нужно отключить (disable) владельца (owner window в терминах WinAPI) и включить (enable) модальное дочернее окно, плюс сделать всё наоборот, когда модальное окно закрывается (*).
И если вы что-то сделаете неправильно, то клавиатурный фокус будет вытворять странные вещи.
Если вы хотите показать UI модально, вам нужно отключить (disable) владельца (owner window в терминах WinAPI) и включить (enable) модальное дочернее окно, плюс сделать всё наоборот, когда модальное окно закрывается (*).
И если вы что-то сделаете неправильно, то клавиатурный фокус будет вытворять странные вещи.
Почему меняется метка времени файла, когда я копирую его на дискету?
Это перевод Why do timestamps change when I copy files to a floppy? Автор: Реймонд Чен.
Дискеты используют файловую систему FAT - так же, как и DOS или Windows 95. С другой стороны, системы на базе Windows NT (Windows 2000, XP, 2003, ...) обычно используют файловую систему NTFS (хотя вы можете отформатировать свой диск в FAT и на Windows NT системе, но это не поведение по-умолчанию).
Файловые системы NTFS и FAT хранят время и дату разными способами. Заметим, например, что FAT отмечает время последней записи (last-write time) только с двухсекундной точностью. Поэтому, если вы копируете файл с NTFS-диска на FAT-диск, то время файла может измениться, но не более, чем на две секунды.
Дискеты используют файловую систему FAT - так же, как и DOS или Windows 95. С другой стороны, системы на базе Windows NT (Windows 2000, XP, 2003, ...) обычно используют файловую систему NTFS (хотя вы можете отформатировать свой диск в FAT и на Windows NT системе, но это не поведение по-умолчанию).
Файловые системы NTFS и FAT хранят время и дату разными способами. Заметим, например, что FAT отмечает время последней записи (last-write time) только с двухсекундной точностью. Поэтому, если вы копируете файл с NTFS-диска на FAT-диск, то время файла может измениться, но не более, чем на две секунды.
Почему я не могу добавлять гиперссылки в балун-подсказки области уведомлений?
Это перевод Why can't I put hotlinks in notification icon balloon tips? Автор: Реймонд Чен.
Короткий ответ: "Потому что там нет флага NIF_PARSELINKS".
Длинный ответ:
Когда такие всплывающие подсказки впервые были разработаны, у них не было возможности вставлять в текст ссылки. Соответственно, программы могли свободно показывать там небезопасный текст, поскольку этот текст не мог стать "живым". Так, к примеру, антивирус мог показывать сообщения "Документ 'XYZ' был проверен и вирусов в нём не найдено".
Теперь предположим, что в балунах разрешили гиперссылки. Посмотрите, как эта возможность могла быть использована: я могу написать web-страничку, которая содержит
Вы скачиваете сообщение и, поскольку вы очень осторожный человек, вы скармливаете его своему антивирусу для проверки. Появляется балун:
"О, как удобно" - говорите вы себе, - "антивирус даже включил ссылку на документ, чтобы мне было проще его открыть".
И тогда вы щёлкаете по ссылке и ваш жёсткий диск переформатируется.
"Тогда почему бы вам не добавить флаг NIF_PARSELINKS, так что люди могли бы включать ссылки только в некоторых своих подсказках, а те, кто писал на старом API, остались бы не задетыми?".
(Я слышал, как один человек пытался передать флаг TTF_PARSELINKS в поле NOTIFYICONDATA.uFlags и удивлялся, почему это не работает. Я надеюсь, что это очевидно).
Потому что это просто перекладывание ответственности на другого. Любой использующий этот флаг был бы обязан быть очень осторожным, чтобы не допустить непроверенных данных в своих балунах. Большинство людей скажут: "Вау! Новый флаг! Это круто!" и начнут использовать его где попало, вообще без рассмотрения требований к безопасности. Тогда любой сможет убедить какую-нибудь программу показать "вредный" текст в своём балуне и, таким образом, использовать брешь в безопасности.
"Ааа, да перестань, кто ж будет таким глупым, чтобы писать код, без учёта требований безопасности?".
Я надеюсь, это был вопрос-шутка.
Лучшим способом убедиться в безопасности вещей - сделать их такими, чтобы небезопасное их использование стало невозможным.
Короткий ответ: "Потому что там нет флага NIF_PARSELINKS".
Длинный ответ:
Когда такие всплывающие подсказки впервые были разработаны, у них не было возможности вставлять в текст ссылки. Соответственно, программы могли свободно показывать там небезопасный текст, поскольку этот текст не мог стать "живым". Так, к примеру, антивирус мог показывать сообщения "Документ 'XYZ' был проверен и вирусов в нём не найдено".
Теперь предположим, что в балунах разрешили гиперссылки. Посмотрите, как эта возможность могла быть использована: я могу написать web-страничку, которая содержит
<TITLE><A HREF="file:C:\Windows\system32\format.com?C:">Потом я называю файл "План вечеринки.html", прикрепляю его к e-mail, и отправляю его вам.
План вечеринки</A></TITLE>
Вы скачиваете сообщение и, поскольку вы очень осторожный человек, вы скармливаете его своему антивирусу для проверки. Появляется балун:
| Сканирование завершено | × |
| Документ 'План вечеринки' был проверен и вирусов в нём не найдено. | |
И тогда вы щёлкаете по ссылке и ваш жёсткий диск переформатируется.
"Тогда почему бы вам не добавить флаг NIF_PARSELINKS, так что люди могли бы включать ссылки только в некоторых своих подсказках, а те, кто писал на старом API, остались бы не задетыми?".
(Я слышал, как один человек пытался передать флаг TTF_PARSELINKS в поле NOTIFYICONDATA.uFlags и удивлялся, почему это не работает. Я надеюсь, что это очевидно).
Потому что это просто перекладывание ответственности на другого. Любой использующий этот флаг был бы обязан быть очень осторожным, чтобы не допустить непроверенных данных в своих балунах. Большинство людей скажут: "Вау! Новый флаг! Это круто!" и начнут использовать его где попало, вообще без рассмотрения требований к безопасности. Тогда любой сможет убедить какую-нибудь программу показать "вредный" текст в своём балуне и, таким образом, использовать брешь в безопасности.
"Ааа, да перестань, кто ж будет таким глупым, чтобы писать код, без учёта требований безопасности?".
Я надеюсь, это был вопрос-шутка.
Лучшим способом убедиться в безопасности вещей - сделать их такими, чтобы небезопасное их использование стало невозможным.
Что такого особенного в окне рабочего стола?
Это перевод What's so special about the desktop window? Автор: Реймонд Чен.
Окно, возвращаемое функцией GetDesktopWindow является очень особым. И я видел, как им часто злоупотребляют, как только можно.
Окно, возвращаемое функцией GetDesktopWindow является очень особым. И я видел, как им часто злоупотребляют, как только можно.
Недопустимые ID потоков и процессов
Это перевод Invalid thread and process IDs. Автор: Реймонд Чен.
Возможно вам нужно значение, которое вы будете использовать в качестве признака/часового. При этом вам нужно, чтобы оно гарантировано не совпало бы с каким-нибудь допустимым значением ID потока или процесса. Какие значения вы могли бы взять?
В явном виде на эту тему в MSDN ничего не написано. Но вы можете использовать свою голову и сообразить сами.
Если вам нужен недопустимый ID потока (TID) - вы можете использовать ноль. Почему ноль? Это следует из функции SetWindowsHookEx: заметьте, что если вы в параметре TID укажете 0, то это будет означать установку ловушки на все потоки текущего десктопа. Отсюда следует, что ноль не является допустимым идентификатором потока.
Это значение подтверждается также и функцией GetThreadID, которая использует ноль, как признак ошибки.
Аналогично, если вам нужно недопустимое значение для идентификаторапроцесса (PID), вы можете использовать DWORD - 1. Это следует из функции AllowSetForegroundWindow: значение ASFW_ANY имеет особый смысл, что значит, что это значение не является допустимым ID процесса.
Или, вы снова можете использовать ноль, поскольку это значение возвращается как признак ошибки функциями GetProcessId и GetProcessIdOfThread.
Возможно вам нужно значение, которое вы будете использовать в качестве признака/часового. При этом вам нужно, чтобы оно гарантировано не совпало бы с каким-нибудь допустимым значением ID потока или процесса. Какие значения вы могли бы взять?
В явном виде на эту тему в MSDN ничего не написано. Но вы можете использовать свою голову и сообразить сами.
Если вам нужен недопустимый ID потока (TID) - вы можете использовать ноль. Почему ноль? Это следует из функции SetWindowsHookEx: заметьте, что если вы в параметре TID укажете 0, то это будет означать установку ловушки на все потоки текущего десктопа. Отсюда следует, что ноль не является допустимым идентификатором потока.
Это значение подтверждается также и функцией GetThreadID, которая использует ноль, как признак ошибки.
Аналогично, если вам нужно недопустимое значение для идентификаторапроцесса (PID), вы можете использовать DWORD - 1. Это следует из функции AllowSetForegroundWindow: значение ASFW_ANY имеет особый смысл, что значит, что это значение не является допустимым ID процесса.
Или, вы снова можете использовать ноль, поскольку это значение возвращается как признак ошибки функциями GetProcessId и GetProcessIdOfThread.
Почему прямоугольники не включают в себя конечную точку?
Это перевод Why are RECTs endpoint-exclusive? Автор: Реймон Чен.
Потому что с такими прямоугольниками (и линиями) значительно проще работать.
Потому что с такими прямоугольниками (и линиями) значительно проще работать.
Ни один код не является необитаемым островом
Это перевод No code is an island. Автор: Реймон Чен. Входит в книгу The Old New Thing.
Norman Diamond в комментарии заметил, что на Windows 2003 Server, ползунок Аппаратного ускорения видеоадаптера всё ещё указывает значение "Полное" как рекомендуемую установку, хотя по-умолчанию на Server ставится "Полное минус один".
Norman Diamond в комментарии заметил, что на Windows 2003 Server, ползунок Аппаратного ускорения видеоадаптера всё ещё указывает значение "Полное" как рекомендуемую установку, хотя по-умолчанию на Server ставится "Полное минус один".
"Гонка вооружений" между пользователями и программами
Это перевод The arms race between programs and users. Автор: Реймонд Чен.
Между теми, кто пишет программы, и теми, кто их использует, идёт постоянная борьба. Например, часто можно услышать вопрос типа такого: "что нужно сделать в своей программе, чтобы пользователь не мог закрыть её?".
Между теми, кто пишет программы, и теми, кто их использует, идёт постоянная борьба. Например, часто можно услышать вопрос типа такого: "что нужно сделать в своей программе, чтобы пользователь не мог закрыть её?".
Некорректные проверки номеров версий
Это перевод Bad version number checks. Автор: Реймонд Чен.
Номера версий. Они очень важны. И так много людей проверяет их неправильно.
Номера версий. Они очень важны. И так много людей проверяет их неправильно.
Ну конечно же мы умеем это делать
Это перевод Sure, we do that. Автор: Реймонд Чен.
Интерфейс видео драйвера DirectX для Windows 95 имел метод, который реализовывался каждым драйвером, с прототипом типа "function DoesDriverSupport(const guidCapability: TGUID): Bool", куда мы передавали GUID возможности, а драйвер говорил в ответ, поддерживает ли он эту возможность или нет.
Были определены GUID возможностей - такие как GUID_CanStretchAlpha, например, чтобы узнать, поддерживает ли драйвер масштабирование растра с альфа каналом.
Потом нам попался готовый драйвер, который возвращал TRUE, когда вы вызывали DoesDriverSupport(GUID_XYZ), но когда DirectDraw пытался использовать эту возможность, то вызов был неудачным (и с весьма впечатляющими эффектами).
Тогда один из разработчиков DirectDraw позвонил производителю и спросил его: "а ваша карточка поддерживает XYZ?".
Их ответ: "А что ещё такое XYZ?".
Оказалось, что их реализация DoesDriverSupport в драйвере была примерно такой:
(Наверное драйвер писал отдел продаж).
Поэтому ребята из DirectDraw изменили логику вопросов драйверу. Один из разработчиков зашёл в офис своего босса, нашёл там сетевую карту, записал с неё MAC-адрес, а затем разбил карту молотком.
Видите ли, этот последний шаг очень важен: алгоритм генерации GUID основывается на комбинации данных времени и пространства. Когда вы просите CoCreateGuid сгенерировать новый GUID, она кодирует время вашего запроса в первой части GUID, а информацию, которая уникально идентифицирует вашу машину (в том числе MAC-адрес сетевой карты, который глобально уникален благодаря стандартам, которые применяются к сетевым картам) - во вторую.
Разбив карту молотком, разработчик воспрепятствовал повторному использованию этой карты для генерации GUID.
Затем он изменил логику вопросов следующим образом: когда DirectDraw запускался, он генерировал случайный GUID на основе этой сетевой карты (который более не может существовать, т.к. его сетевую карту уничтожили) и передавал её в DoesDriverSupport. Если при этом драйвер отвечал: "да, это мы поддерживаем", то DirectDraw говорил: "Ага! Попался! Я больше не верю ни единому твоему слову.".
Интерфейс видео драйвера DirectX для Windows 95 имел метод, который реализовывался каждым драйвером, с прототипом типа "function DoesDriverSupport(const guidCapability: TGUID): Bool", куда мы передавали GUID возможности, а драйвер говорил в ответ, поддерживает ли он эту возможность или нет.
Были определены GUID возможностей - такие как GUID_CanStretchAlpha, например, чтобы узнать, поддерживает ли драйвер масштабирование растра с альфа каналом.
Потом нам попался готовый драйвер, который возвращал TRUE, когда вы вызывали DoesDriverSupport(GUID_XYZ), но когда DirectDraw пытался использовать эту возможность, то вызов был неудачным (и с весьма впечатляющими эффектами).
Тогда один из разработчиков DirectDraw позвонил производителю и спросил его: "а ваша карточка поддерживает XYZ?".
Их ответ: "А что ещё такое XYZ?".
Оказалось, что их реализация DoesDriverSupport в драйвере была примерно такой:
function DoesDriverSupport(const guidCapability: TGUID): Bool; stdcall;Иными словами, когда DirectX спрашивал "а вот это вы умеете делать?", они отвечали "ну конечно же мы умеем это делать" вообще даже без проверки, в чём же заключался вопрос.
begin
Result := True;
end;
(Наверное драйвер писал отдел продаж).
Поэтому ребята из DirectDraw изменили логику вопросов драйверу. Один из разработчиков зашёл в офис своего босса, нашёл там сетевую карту, записал с неё MAC-адрес, а затем разбил карту молотком.
Видите ли, этот последний шаг очень важен: алгоритм генерации GUID основывается на комбинации данных времени и пространства. Когда вы просите CoCreateGuid сгенерировать новый GUID, она кодирует время вашего запроса в первой части GUID, а информацию, которая уникально идентифицирует вашу машину (в том числе MAC-адрес сетевой карты, который глобально уникален благодаря стандартам, которые применяются к сетевым картам) - во вторую.
Разбив карту молотком, разработчик воспрепятствовал повторному использованию этой карты для генерации GUID.
Затем он изменил логику вопросов следующим образом: когда DirectDraw запускался, он генерировал случайный GUID на основе этой сетевой карты (который более не может существовать, т.к. его сетевую карту уничтожили) и передавал её в DoesDriverSupport. Если при этом драйвер отвечал: "да, это мы поддерживаем", то DirectDraw говорил: "Ага! Попался! Я больше не верю ни единому твоему слову.".
Управление памятью в 16-ти разрядных Windows
Это перевод The management of memory for resources in 16-bit Windows. Автор: Реймонд Чен.
В прошлый раз я грозился обсудить управление ресурсами в 16-ти битных Windows.
В прошлый раз я грозился обсудить управление ресурсами в 16-ти битных Windows.
Что идёт не так, если вы добавляете "Copy To" в контекстное меню
Это перевод What goes wrong when you add "Copy To" to the context menu. Автор: Реймонд Чен.
Lockergnome ошибочно подсказали людям вот эту страничку, на которой говориться (среди прочих вещей) о добавлении команды "Copy To" ("Копировать в") в контекстное меню. Я подумывал, не добавить ли этот трюк (tweak) в Tweak UI, но теперь решительно против него. И вот почему:
Команды "Copy to Folder" и "Move to Folder" ("Копировать в папку" и "Переместить в папку") не были предназначены для работы в контекстном меню. Их планировалось использовать только в тулбаре Проводника (щёлкните правой по тулбару своего Проводника и выберите "Настроить...", затем выберите "Переместить в" и "Копировать в" из списка доступных кнопок слева). Если вы добавите их в контекстное меню, то вы можете заметить, что диалоги "Копировать в" и "Переместить в" начинают показываться в моменты, когда вы совершенно их не ждёте - например, при двойном щелчке на вложении в Outlook.
Причина заключается в том, что эти два пункта являются слишком жадными. Когда вы спрашиваете их "А ты обрабатываешь команду <X>?", они говорят "О, да! Это моя!". Это нормально работает в тулбаре, потому что вопрос "Ты обрабатываешь команду <X>?" задаётся им только когда пользователь щёлкает по их же кнопке. Но в контекстном меню их спрашивают горазо чаще и с различными значениями X.
Поэтому, когда Outlook запускает вложение, оболочка (shell) загружает обработчики контекстного меню и спрашивает каждого: "это ты обрабатываешь команду Open?". Пункт "Удалить" говорит: "Неа, извини". Так же говорят и "Вырезать" и "Отправить в" и "Общий доступ и безопастность". Но команда "Копировать в" счастливо кричит: "Да-да! Это моя команда!".
И тогда появляется диалог "Копировать в", когда вы его совершенно не ждёте.
Это пример того, что может произойти, когда вы берёте какой-то объект и используете его в ситуации, для которой он не разрабатывался.
Lockergnome ошибочно подсказали людям вот эту страничку, на которой говориться (среди прочих вещей) о добавлении команды "Copy To" ("Копировать в") в контекстное меню. Я подумывал, не добавить ли этот трюк (tweak) в Tweak UI, но теперь решительно против него. И вот почему:
Команды "Copy to Folder" и "Move to Folder" ("Копировать в папку" и "Переместить в папку") не были предназначены для работы в контекстном меню. Их планировалось использовать только в тулбаре Проводника (щёлкните правой по тулбару своего Проводника и выберите "Настроить...", затем выберите "Переместить в" и "Копировать в" из списка доступных кнопок слева). Если вы добавите их в контекстное меню, то вы можете заметить, что диалоги "Копировать в" и "Переместить в" начинают показываться в моменты, когда вы совершенно их не ждёте - например, при двойном щелчке на вложении в Outlook.
Причина заключается в том, что эти два пункта являются слишком жадными. Когда вы спрашиваете их "А ты обрабатываешь команду <X>?", они говорят "О, да! Это моя!". Это нормально работает в тулбаре, потому что вопрос "Ты обрабатываешь команду <X>?" задаётся им только когда пользователь щёлкает по их же кнопке. Но в контекстном меню их спрашивают горазо чаще и с различными значениями X.
Поэтому, когда Outlook запускает вложение, оболочка (shell) загружает обработчики контекстного меню и спрашивает каждого: "это ты обрабатываешь команду Open?". Пункт "Удалить" говорит: "Неа, извини". Так же говорят и "Вырезать" и "Отправить в" и "Общий доступ и безопастность". Но команда "Копировать в" счастливо кричит: "Да-да! Это моя команда!".
И тогда появляется диалог "Копировать в", когда вы его совершенно не ждёте.
Это пример того, что может произойти, когда вы берёте какой-то объект и используете его в ситуации, для которой он не разрабатывался.
понедельник, 5 января 2009 г.
Ещё причины, почему не надо делать ничего страшного в DllMain: случайная блокировка
Это перевод Another reason not to do anything scary in your DllMain: Inadvertent deadlock. Автор: Реймонд Чен.
Ваша функция DllMain работает внутри блокировки загрузчика - это один из немногих моментов, когда ОС позволяет вашему коду выполняться в момент удерживания внутренней блокировки. Это означает, что ваш код должен быть очень внимателен, чтобы не нарушить иерархию блокировок в своей DllMain; иначе вам грозит мёртвая блокировка.
(У вас ведь есть такая иерархия, да?).
Ваша функция DllMain работает внутри блокировки загрузчика - это один из немногих моментов, когда ОС позволяет вашему коду выполняться в момент удерживания внутренней блокировки. Это означает, что ваш код должен быть очень внимателен, чтобы не нарушить иерархию блокировок в своей DllMain; иначе вам грозит мёртвая блокировка.
(У вас ведь есть такая иерархия, да?).
Несколько причин, чтобы не делать ничего страшного в своей DllMain
Это перевод Some reasons not to do anything scary in your DllMain. Автор: Реймонд Чен.
Как сегодня уже все знают, вам не следует делать ничего даже отдалённо интересного в своей функции DllMain (*). Олег Львович написал об этом две очень хорошие статьи: одна о том, как всё это работает, а вторая - что может пойти не так (**).
Как сегодня уже все знают, вам не следует делать ничего даже отдалённо интересного в своей функции DllMain (*). Олег Львович написал об этом две очень хорошие статьи: одна о том, как всё это работает, а вторая - что может пойти не так (**).
воскресенье, 4 января 2009 г.
DllMain - страшилка на ночь
Это перевод DllMain : a horror story. Автор: Олег Львович. Примечание: в отличие от других постов, этот пост сильно отличается от оригинала. Произведено множество замен от C к Delphi.
В последний раз я говорил о DllMain и всяческих нехороших вещах, которые могут произойти, если вы используете её не по назначению. Я также упомянул, что это не тот случай, когда вы можете сказать "я всегда осторожен и со мной такого никогда не случится" - всё может выйти из под контроля очень быстро.
Имейте ввиду, что загрузчик ОС со временем эволюционирует. Создатели ОС в курсе, что далеко не все DLL ведут себя хорошо в этом смысле, и стараются минимизировать ущерб от плохих DllMain, насколько это возможно. Однако сейчас мы покажем, что выстрелить себе в ногу более чем возможно (и очень просто).
В последний раз я говорил о DllMain и всяческих нехороших вещах, которые могут произойти, если вы используете её не по назначению. Я также упомянул, что это не тот случай, когда вы можете сказать "я всегда осторожен и со мной такого никогда не случится" - всё может выйти из под контроля очень быстро.
Имейте ввиду, что загрузчик ОС со временем эволюционирует. Создатели ОС в курсе, что далеко не все DLL ведут себя хорошо в этом смысле, и стараются минимизировать ущерб от плохих DllMain, насколько это возможно. Однако сейчас мы покажем, что выстрелить себе в ногу более чем возможно (и очень просто).
DllMain и жизнь до родов
Это перевод DllMain and life before birth. Автор: Олег Львович.
Предисловие
Загрузчик ОС (OS loader) всегда интересовал меня - вероятно потому, что он работает за сценой. И обычно никто и не чешется понять, что же он делает в точности, до тех пор, пока не начинают происходить странные и забавные вещи. И они происходят. И тогда нам приходится читать документацию, и нас заставляют запоминать, что загрузка модуля - это нечто большее, чем просто маппинг его в адресное пространство процесса. Фактически, у нас есть замечательная статья Matt Pietrek, которая обсуждает все эти вещи. Я настоятельно рекомендую всем, кто имеет дело с нативным кодом, прочитать эту статью - она может сильно просветить вас - как это было для меня. Когда вы знаете, как работают эти вещи, то маловероятно, что вы забудете сменить базу своим модулям (re-base), учесть ранее связывание и т.п.
Предисловие
Загрузчик ОС (OS loader) всегда интересовал меня - вероятно потому, что он работает за сценой. И обычно никто и не чешется понять, что же он делает в точности, до тех пор, пока не начинают происходить странные и забавные вещи. И они происходят. И тогда нам приходится читать документацию, и нас заставляют запоминать, что загрузка модуля - это нечто большее, чем просто маппинг его в адресное пространство процесса. Фактически, у нас есть замечательная статья Matt Pietrek, которая обсуждает все эти вещи. Я настоятельно рекомендую всем, кто имеет дело с нативным кодом, прочитать эту статью - она может сильно просветить вас - как это было для меня. Когда вы знаете, как работают эти вещи, то маловероятно, что вы забудете сменить базу своим модулям (re-base), учесть ранее связывание и т.п.
Подписаться на:
Сообщения (Atom)