Ну, сегодня это уже одно и то же.
Давайте сперва разберёмся с теми, что попроще.
Для начала, CoTaskMemAlloc - это в точности то же самое, что и CoGetMalloc(MEMCTX_TASK) + IMalloc::Alloc, а CoTaskMemFree - это полный эквивалент той же самой CoGetMalloc(MEMCTX_TASK) + IMalloc::Free. CoTaskMemAlloc и CoTaskMemFree (а также реже используемая CoTaskMemRealloc) - это просто удобные оболочки, которые дают вам возможность избегать лишних вызовов CoGetMalloc. Соответственно, вы можете безопасно выделить память, скажем, через CoGetMalloc(MEMCTX_TASK) + IMalloc::Alloc, а затем освободить её через CoTaskMemFree, или наоборот. Это один и тот же менеджер памяти (аллокатор, allocator).
Похожим образом, SHAlloc и SHFree - это просто оболочки вокруг SHGetMalloc, которая выделяет/освобождает память, используя менеджер памяти оболочки. Память, которую вы выделили через SHGetMalloc + IMalloc::Alloc, может быть освобождена через SHFree.
Итак, у нас получается такая схема:
Менеджер памяти Shell | Менеджер памяти OLE | |||||
SHAlloc/ SHFree | = | SHGetMalloc | ?? | CoGetMalloc | = | CoTaskMemAlloc/ CoTaskMemFree |
Ну а теперь: что это за знаки вопроса?
Если вы прочитаете комментарий в shlobj.h, вы получите подсказку (прим. пер.: решил оставить без перевода):
//=========================================================================== // // Task allocator API // // All the shell extensions MUST use the task allocator (see OLE 2.0 // programming guild for its definition) when they allocate or free // memory objects (mostly ITEMIDLIST) that are returned across any // shell interfaces. There are two ways to access the task allocator // from a shell extension depending on whether or not it is linked with // OLE32.DLL or not (purely for efficiency). // // (1) A shell extension which calls any OLE API (i.e., linked with // OLE32.DLL) should call OLE's task allocator (by retrieving // the task allocator by calling CoGetMalloc API). // // (2) A shell extension which does not call any OLE API (i.e., not linked // with OLE32.DLL) should call the shell task allocator API (defined // below), so that the shell can quickly loads it when OLE32.DLL is not // loaded by any application at that point. // // Notes: // In next version of Windowso release, SHGetMalloc will be replaced by // the following macro. // // #define SHGetMalloc(ppmem) CoGetMalloc(MEMCTX_TASK, ppmem) // //===========================================================================
(Да, эти опечатки "guild" и "Windowso" сидят там с 1995)
Это обсуждение решительно намекает на то, что же происходит.
Когда разрабатывалась Windows 95, компьютеры обычно имели около 4 Мб памяти (крутые парни имели 8 Мб). Но Проводник также сильно использовал архитектуру COM для расширений оболочки (shell extension), а загрузка в память библиотеки OLE32.DLL была хорошим ударом в зубы. В таких стеснённых ограничениях на память, даже потеря 4-х Кб памяти была заметна.
Решение: сыграть в "Слабо загрузить OLE?" ("OLE Chicken").
Сама оболочка, как оказалось, не слишком-то использовала COM: единственными поддерживаемыми ею объектами были in-process apartment-threaded COM-объекты без маршалинга. Поэтому команда оболочки написала "мини-COM", который поддерживал только эти операции, и использовала его вместо полноценного COM (ну, им повезло, что один из главных разработчиков оболочки также был супер-экспертом в COM). Оболочка имела свой собственный мини-менеджер памяти, свой собственный биндер, свой собственный мини-цикл drag-drop - всё, что необходимо, если вы не использовали никаких других программ, которые использовали OLE32.
Как только запускалась какая-то программа, которая использовала OLE32, у вас появлялась проблема: теперь у вас в системе было две копии OLE: настоящая и фальшивая версия оболочки. Если ничего не сделать, то вы не сможете обмениваться данными между настоящим-COM и фальшивым-COM-оболочки. Например, вы не сможете перетаскивать (drag/drop) данные между Проводником (который использует фальшивый-COM-оболочки) и окном, которое использует настоящий-COM.
Решение: при поддержке других частей системы, оболочка определяла, что "COM теперь с нами", как только кто-то загружал OLE32.DLL, и тогда она переносила всю свою информацию, которой до этого она управляла сама, в мир настоящего COM. Как только она заканчивала это, все псевдо-COM функции переключались на функции настоящего-COM. Например, после загрузки OLE32.DLL, вызовы фальшивого мини-менеджера памяти Shell перенаправлялись настоящему менеджеру памяти.
Но что такое "Слабо загрузить OLE?"? Это ещё один вариант различных "слабо/не слабо" игр, самой известной из которых, вероятно, является Schedule Chicken (вот здесь есть перевод профессионала на эту тему). В игре в "Слабо загрузить OLE?", каждая программа пыталась избежать загрузки OLE32.DLL так долго, как могла, так чтобы она не стала бы программой, повинной в долгой задержке, пока OLE32.DLL загружается и раскручивается (не забывайте, мы говорим о машинах эры 1995-го, когда выделение 32-х килобайт памяти вызывало на вашу голову гнев команды оптимизации производительности).
Окей, давайте ещё раз посмотрим на этот комментарий.
Открывающий параграф упоминает возможность, когда расширение оболочки не прилинковывает к себе OLE32.DLL. Вариант (1) обсуждает расширение оболочки, которое использует OLE32, в этом случае оно должно использовать официальные OLE функции типа CoGetMalloc. Но вариант (2) обсуждает расширение оболочки, которое не использует OLE32. Таким расширениям оболочки следует использовать функции фальшивого-COM-оболочки типа SHGetMalloc, вместо функций настоящего COM, так, чтобы не создавалось зависимости от OLE32. Поэтому, если OLE32 ещё не была загружена, то и загрузка таких расширений оболочки не приводила к загрузке OLE32, экономя, таким образом, время на загрузку и инициализацию OLE32.DLL.
Итак, законченный вариант нашей схемы для 1995-го года теперь выглядит вот так:
До загрузки OLE32.DLL:
После загрузки OLE32.DLL:
Менеджер памяти Shell | Менеджер памяти OLE | |||||
SHAlloc/ SHFree | = | SHGetMalloc | ≠ | CoGetMalloc | = | CoTaskMemAlloc/ CoTaskMemFree |
После загрузки OLE32.DLL:
Менеджер памяти Shell | Менеджер памяти OLE | |||||
SHAlloc/ SHFree | = | SHGetMalloc | = | CoGetMalloc | = | CoTaskMemAlloc/ CoTaskMemFree |
Последний блок "Note" указывает на путь развития, которым пошла оболочка. Потом оказалось, что загрузка OLE32.DLL стала не такой болезненной как это было в Windows 95, поэтому оболочка могла выкинуть фальшивый-COM и использовать настоящий. В этот момент, обращение к менеджеру памяти Shell - это будет то же самое, что обращение к менеджеру памяти COM.
Вообще-то это время наступило давным-давно. Дни машин с 4 Мб памяти - это уже легенды сегодня. Оболочка выкинула свою мини-реализацию COM и сегодня везде использует полноценный COM.
Поэтому, современная схема - это та, которая со знаком равно. Все четыре функции взаимозаменяемы в Windows XP и выше.
Что, если вы хотите работать на старых системах? Ну, использовать CoTaskMemAlloc/CoTaskMemFree допустимо всегда. Почему? Ну, вы и сами можете догадаться. Поскольку эти функции экспортируются из OLE32.DLL, то факт их использования вами означает, что OLE32.DLL уже загружена, после чего в силу вступает последний вариант схемы, со знаком равно, и все находятся в одной большой счастливой семье.
Случай, когда вам нужно быть осторожным - это если ваша DLL не использует OLE32.DLL. В этом случае вы не знаете, находитесь ли вы в ситуации "До" или "После", поэтому вам лучше бы играть безопасно и использовать менеджер памяти Shell для работы с теми вещами, которые просят его по документации.
Я надеюсь, что это обсуждение также объясняет историческую подоплёку функции SHLoadOLE, которая сегодня просто ничего не делает, посольку OLE теперь загружается всегда. Но в старые дни, это был сигнал оболочке: "окей, настало время brain-dump твой фальшивый-COM в настоящий-COM".
Читать далее: В чём разница между LocalAlloc и GlobalAlloc?
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.