Поделиться через


TN058. Реализация состояния модуля MFC

Примечание

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

Это техническое примечание описывает реализацию конструкций «состояние» модуля MFC. О реализации состояния модуля критическое для использования общих библиотек DLL MFC из библиотеки DLL (или OLE внутрипроцессного сервера).

Прежде чем чтения эту заметку см. в разделе «управление данными состояния модулей MFC» раздела Создание новых документов, Windows и представления. Этот раздел содержит важные данные потребления и сведения о вопросу.

Обзор

3 Типа сведений о состоянии MFC. Состояние модуля, состояние процесса и состояния потока. Иногда эти типы состояния можно комбинировать. Например, сопоставления дескрипторов MFC и локального модуля и локальный потока. Это позволяет 2 различных модулей, чтобы иметь различные сопоставления в каждой из них потоков.

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

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

Переключение состояния модуля

Каждый поток содержит указатель на текущий «» или «» активное состояние модуля (не удивительно, указатель часть локального состояния потока MFC). Этот указатель изменяется, когда поток выполнения передает границу модуля, например приложения при вызове в элемент управления OLE или DLL или элемент управления OLE при вызове обратно в приложение.

Текущее состояние модуля переключено с помощью метода AfxSetModuleState. В большинстве случаев, никогда не будет работать непосредственно с помощью API. MFC, во многих случаях вызове автоматически (на WinMain, OLE точках входа, AfxWndProc и т д). Это можно сделать в любом компоненте написания статического связывания особым WndProc и отдельное WinMain (или DllMain), который знает, состояние модуля должно быть является текущим. Можно просмотреть этот код, выполняя поиск DLLMODUL.CPP или APPMODUL.CPP в каталоге MFC\SRC.

Его редко, необходимо задать состояние модуля и затем не установить ее. В большинстве случаев требуется «между» собственное состояние модуля в качестве текущего значения и затем, после этого «извлекает» исходную back контекста. Это делается макросом AFX_MANAGE_STATE и специальным классом AFX_MAINTAIN_STATE.

CCmdTarget содержит функции для поддержки переключения состояния модуля. В частности, CCmdTarget корневой класс, используемый для точки входа модели COM и OLE ole-автоматизации. Как и любая другая представленной точка входа в систему, эти точки входа необходимо задать верное состояние модуля. Как заданного CCmdTarget знает, какое состояние модуля «» должно быть? Ответить на его «запоминает», «» текущее состояние модуля при построении, таким образом, чтобы он может задать текущее состояние модуля к этому «вспомненное» значение при его позже данной функции. В результате состояние модуля, что данный объект CCmdTarget связан с состоянием модуля, которая была текущей во время, когда был создан объект. Создание простой пример загрузки сервер INPROC, создание объекта и вызывать соответствующие методы.

  1. Библиотека DLL загружается OLE с помощью LoadLibrary.

  2. сначала вызывается метод RawDllMain. Он устанавливает состояние модуля в известное состояние статического модуля DLL. По этой причине RawDllMain статически связана с библиотекой DLL.

  3. Конструктор для класса фабрики обработать, связанной с объектом. COleObjectFactory является производным от CCmdTarget и поэтому он запоминает в него модуля, состояние экземпляра. Это важно, если запрос, чтобы создать фабрику класса объектов, известно, что теперь состояние модуля, чтобы сделать текущий.

  4. DllGetClassObject вызывается, чтобы получить фабрику класса. MFC поиск в списке фабрики класса, связанный с данным модулем и возвращает его.

  5. Вызывается метод COleObjectFactory::XClassFactory2::CreateInstance. Перед созданием объекта и возвращения ее, эта функция задает состояние модуля в состояние модуля, которая была текущей в шаге 3 (один, которая была текущей во время, когда было, после чего создан ее экземпляр COleObjectFactory ). Это делается в METHOD_PROLOGUE.

  6. При создании объекта, это слишком производный CCmdTarget и таким же образом вспомненное COleObjectFactory, состояние модуля были активны, делает этот новый объект. Теперь объект знает, состояние модуля, который необходимо переключиться, когда он вызывается.

  7. Клиент вызывает функцию OLE на COM-объект полученным из его вызова CoCreateInstance. Вызывается, когда объект используется METHOD_PROLOGUE для переключения состояния модуля, как COleObjectFactory действий.

Как видно, состояние модуля распространяется из объекта в объект при их создании. Важно, чтобы иметь состояние модуля, но соответствующим образом. Если он не задан, то в DLL или COM-объект — могут взаимодействовать с приложением MFC, которая вызывает его, или может не найти собственные ресурсы или сбой в других горемычных способами.

Обратите внимание, что некоторые виды библиотек DLL, в частности «библиотеки DLL расширения MFC» не переключают состояние модуля в их ( RawDllMain, который они обычно даже не имеет RawDllMain). Это происходит потому, что они должны быть «, если они фактически» присутствовали в приложении, которое использует их. Они много часть приложения, выполняется, и их изменение глобального состояния этого приложения.

Элементы управления OLE и другие библиотеки DLL очень различаются. Они не хотят изменение состояния вызывающего приложения; приложения, вызывающего их может не быть приложением MFC и поэтому не может быть ни одному состоянием, которое необходимо изменить. Причина в том, что переключение состояния модуля. изобретен.

Экспортированных функций из библиотеки DLL, подобный приведенному запускает диалоговое окно в библиотеке DLL, необходимо добавить следующий код в начало функции:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

Это не будет обменивать состояние модуля с состояние, возвращенное из AfxGetStaticModuleState до конца текущей области.

Проблемы с ресурсами в библиотеке DLL возникают, если макрос AFX_MODULE_STATE не используется. По умолчанию MFC использует дескриптор ресурса основного приложения загрузить шаблон ресурса. Этот шаблон, хранится в библиотеке DLL. Первопричина, что данные состояния модуля MFC не были переключены макросом AFX_MODULE_STATE. Дескриптор ресурса взят из состояния модуля MFC. Нельзя переключать состояние модуля, неверный дескриптор ресурса для использования.

AFX_MODULE_STATE не для формирования в каждой функции из библиотеки DLL. Например, InitInstance может быть вызван кодом MFC в приложении без AFX_MODULE_STATE, поскольку MFC автоматически сдвигает состояние модуля перед InitInstance и затем переключателями его снова после InitInstance возвращает. То же самое верно для всех обработчиков сообщений схемы. Обычная библиотека DLL фактически находятся в отдельной master оконную процедуру, автоматически меняет состояние модуля, прежде чем направлять все сообщения.

Процесс локальных данных

Процесс локальные данные не были бы такого огромного беспокойства нет их, не будут несоответствия модели для библиотеки DLL Win32s. Во всех DLL Win32s совместно использовать их глобальных данных, даже если загружается несколькими приложениями. Это отличается от очень «имеет» модель данных DLL Win32, где каждое DLL получает отдельные копии его пространства данных в каждом процесс, вложение в библиотеке DLL. Чтобы добавить к сложности данные, размещенный в куче в библиотеке DLL Win32s фактически отростчатая с (по крайней мере, сколько владение переходит). Рассмотрим следующие данные и код:

static CString strGlobal; // at file scope

__declspec(dllexport) 
void SetGlobalString(LPCTSTR lpsz)
{
   strGlobal = lpsz;
}

__declspec(dllexport)
void GetGlobalString(LPCTSTR lpsz, size_t cb)
{
   StringCbCopy(lpsz, cb, strGlobal);
}

Рассмотрим, что произойдет, если приведенный выше код в находится в библиотеке DLL и будет загружена библиотека DLL 2 процессами a и B, (может, на самом деле, быть 2 экземпляр одного приложения). Вызовы SetGlobalString("Hello from A"). В результате память выделяется для данных CString в контексте A. процесса. Имейте в виду, что CString само глобально и отображается как к a и B к. Теперь вызовы GetGlobalString(sz, sizeof(sz)) B. Б. просмотр данных, — a. Это происходит потому, что Win32s не обеспечивают защиту между процессами Win32, как требуется. В первую очередь; в большинстве случаев не желательно иметь одну глобальных данных на приложения, считается, что имены другим приложением.

Также дополнительные проблемы. Рассмотрим следующий фраза, а теперь не влияет. Когда отобразится a, память, используемая строкой 'strGlobal' сделана доступной для системы, т е всю память, выделенная процессом a освобождается автоматически операционной системой. Она не освобождается, поскольку деструктор CString вызова; она не была вызвана во время выполнения. Он освобождается просто, поскольку приложение, выбранной его выходило сцены. Теперь, если B вызывает GetGlobalString(sz, sizeof(sz)), не может получить доступ к допустимые данные. Другое приложение может использовать эту память для подобное.

Четко проблема существует. MFC 3.x использовал метод локальной памяти потока (TLS). MFC 3.x выделитьTm бы индекс TLS, в действительности Win32s действует как индекс процесс- локального хранилища, даже если не вызывается, и затем ссылатьсяTfо, все данные, основанные на этом индексе TLS. Это аналогично индексу TLS, используемого для хранения данных для локального на Win32 (см. ниже дополнительные сведения в этой теме). Это вызовет каждое библиотеки DLL MFC — использование по крайней мере 2 индекса TLS на процесс. При указании нескольких загрузки библиотеки DLL элемента управления (OLE OCXs), можно быстро выполнять из индексов TLS (доступное только 64). Кроме того, MFC должен установить все эти данные в одном месте, в одной структуре. Он не имеет очень расширяемый и не был идеален по его использования индексов TLS.

Адреса MFC 4.x это с набором шаблонов класса «можно использовать программы-оболочки» для данных, которые должны быть отростчатым локальным. Например, ошибка упомянутая выше может быть устранена путем записи:

struct CMyGlobalData : public CNoTrackObject
{
   CString strGlobal;
};
CProcessLocal<CMyGlobalData> globalData;

__declspec(dllexport) 
void SetGlobalString(LPCTSTR lpsz)
{
   globalData->strGlobal = lpsz;
}

__declspec(dllexport)
void GetGlobalString(LPCTSTR lpsz, size_t cb)
{
   StringCbCopy(lpsz, cb, globalData->strGlobal);
}

MFC реализует это в шаге 2. Во-первых, уровень поверх API Win32 Tls* (TlsAlloc, TlsSetValue, TlsGetValue и т д), которые используют только 2 индекса TLS на процесс, независимо от количества библиотеки DLL, имеют. Во-вторых, предоставляемых, что доступ шаблон CProcessLocal эти данные. Переопределяет operator->, что позволяет пользователям понятный синтаксис отображается выше. Все объекты, которые будут создаваться программу-оболочку CProcessLocal должен наследоваться от CNoTrackObject. CNoTrackObject предоставляет распределитель низкого уровня (LocalAlloc и LocalFree) и виртуальный деструктор так, что MFC автоматически может удалить процесс локальных объектов, когда процесс завершен. Такие объекты могут иметь пользовательский деструктор, если необходима дополнительная очистка. В приведенном выше примере не требуется один, поскольку компилятор создает деструктор по умолчанию для удаления встроенный объект CString.

Другие интересные преимущества к этому подходу. Не только не создаются все объекты CProcessLocal удаляется автоматически, они до тех пор, пока они не потребуются CProcessLocal::operator-> создается связанный объект в первый раз, он будет вызван, и не раньше. В приведенном выше примере, это означает, что строка 'strGlobal' не будет построена до появления первой вызывается метод GetGlobalString или SetGlobalString. В некоторых случаях это помогает сократить время запуска библиотеки DLL.

Локальные данные потока

Аналогично отростчатым локальным данным, локальным данным потока используется, когда данные должны быть локальным для данного потока. То есть, необходим отдельный экземпляр данных для каждого потока, доступа к этим данным. Это можно использовать несколько раз вместо основные механизмов синхронизации. Если данные не должны совместно использоваться несколькими потоками, такие механизмы могут оказаться весьма ресурсоемкими и ненужными. Предположим, что мы содержит объект CString (как в примере выше). Это можно сделать его локального потока, создайте его с шаблоном CThreadLocal:

struct CMyThreadData : public CNoTrackObject
{
   CString strThread;
};
CThreadLocal<CMyThreadData> threadData;

void MakeRandomString()
{
   // a kind of card shuffle (not a great one)
   CString& str = threadData->strThread;
   str.Empty();
   while (str.GetLength() != 52)
   {
      unsigned int randomNumber;
      errno_t randErr;
      randErr = rand_s( &randomNumber );
      if ( randErr == 0 )
      {
         TCHAR ch = randomNumber % 52 + 1;
         if (str.Find(ch) < 0)
            str += ch; // not found, add it
      }
   }
}

Если MakeRandomString вызывался 2 из разных потоков, каждое «в случайном порядкеTfо:» строка различными способами, не препятствует с другим. Это происходит потому, что является экземпляром strThread в каждом потоке, а не только один глобального экземпляра.

Следует заметить, что ссылка используется, чтобы получить адрес CString один раз, а не только на итерацию цикла. Используется код цикла может быть записан с threadData->strThread везде 'str', однако код будет гораздо медленнее при выполнении. Рекомендуется кэшировать ссылки на данные, если эти ссылки возникают в циклах.

Шаблон класса CThreadLocal используются те же механизмы, что и CProcessLocal выполняет те же методы реализации.

См. также

Другие ресурсы

Технические примечания по номеру

Технические примечания по категории