Это техническое примечание описывает реализацию конструкций состояния MFC «модуль». Понимание состояния реализации модуля является критическим для совместно с использованием MFC DLL из библиотеки DLL (или внутрипроцессный OLE-сервер).
Перед чтением этой записки, обратитесь к «Управление данными о состоянии модулей MFC» в создание новых документов, окна и представления в руководство Visual C++ программиста. В этой статье содержатся сведения об использовании важные и обзорная информация по этому вопросу.
Обзор
Существует три вида MFC сведений о состоянии: состояние модуля, состояние процесса и состояние потока. Иногда эти государства типы могут быть объединены. Например модуль местной и потока местные сопоставлений дескрипторов MFC. Это позволяет два различных модулей для различных карт в каждой из их потоков.
Состояние процесса и состояние потока похожи. Эти элементы данных являются вещи, которые традиционно являются глобальные переменные, но нужно быть специфическими для данного процесса или потока для поддержки надлежащего Win32s или для надлежащей поддержки многопоточности. К какой категории элемент данных помещается в зависит от этого элемента и его необходимые семантики в границы процесса и потока.
Состояние модуля является уникальным в том, что он может содержать подлинно глобального государство или государства, которое местные процесс или поток местные. Также он может быстро переключаться.
Переключение состояния модуля
Каждый поток содержит указатель на «текущие» или «активные» модуль состояния (не удивительно, указатель является частью MFC местных состояние потока). Этот указатель изменяется, когда поток исполнения проходит модуль границы, такие, как приложение в элемент OLE или DLL или управления OLE призывая вернуться в приложение.
Текущее состояние модуля переключается путем вызова AfxSetModuleState. В основном вам никогда не будет дело непосредственно с интерфейсом API. MFC, во многих случаях, называют это для вас (на WinMain, Оле точки входа, AfxWndProcи т.д.). Это делается в любой компонент, вы пишете, статически увязав в специальном WndProcи специальные функции WinMain (или DllMain), знает, какой модуль государство должно быть текущей. Вы можете увидеть этот код, принимая взгляд на DLLMODUL.CPP или APPMODUL.НПК в каталоге MFC\SRC.
Это редкий, что вы хотите задать состояние модуля и затем не задавать его обратно. Большую часть времени, вы хотите «толчка» ваш собственный модуль государственные как текущий и затем, после того как вы сделаны, «поп» исходный контекст обратно. Это делается путем макрос AFX_MANAGE_STATE и специальный класс AFX_MAINTAIN_STATE.
CCmdTarget имеет специальные функции для поддержки переключения состояния модуля. В частности, CCmdTarget это корневой класс, используемый для автоматизации OLE и OLE COM точки входа. Как и любой другой точки входа подвержены системы, эти точки входа необходимо установить состояние правильный модуля. Как данный CCmdTarget узнает каким должно быть состояние "правильной" модуль? Ответ заключается в том, что он «помнит», какие «текущее» состояние модуля, когда он состоит, таким образом, чтобы можно задать текущее состояние модуля к этому «вспомнил», называется значение, когда она является более поздней. В результате состояние модуля, с которым связан данный объект CCmdTarget является состояние модуля, которая была текущей, когда объект был построен. Возьмем простой пример загрузки сервера INPROC, создания объекта и вызова его методов.
Как вы можете видеть, состояние модуля распространяется от объекта к объекту при их создании. Важно иметь соответствующим образом установить состояние модуля. Если не задано, ваш DLL или COM объект может плохо взаимодействуют с приложения MFC, которое зовет его, или могут быть не в состоянии найти свои собственные ресурсы или могут не другими способами несчастной.
К сведению, что некоторые виды библиотек DLL, специально «Расширения MFC» библиотеки DLL не переключить состояние модуля в их 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 на самом деле имеют специальное окно Мастер процедуру, которая автоматически переключает состояние модуля перед маршрутизацией любое сообщение.
Местные данные процесса
Местные данные процесса не будет такой большую озабоченность не сложность модели Win32s DLL. В Win32s все библиотеки DLL поделиться их глобальных данных, даже при загрузке несколькими приложениями. Это полностью отличается от модели данных «Реал» Win32 DLL, где каждая DLL получает отдельную копию данных пространства в каждом процессе, который придает DLL. Чтобы добавить в сложности, данных в куче в библиотеке Win32s DLL фактически является специфики процесса (по крайней мере, выходит собственности). Рассмотрим следующие данные и код:
статические CString strGlobal; / / в области видимости файла
ключевое слово __declspec(dllexport) аннулировать SetGlobalString(LPCTSTR lpsz)
{
strGlobal = lpsz;
}
ключевое слово __declspec(dllexport)
void GetGlobalString (LPCTSTR lpsz, int cb)
{
lstrcpyn (lpsz, strGlobal, cb);
}
Рассмотрим, что произойдет, если приведенный выше код в находится в библиотеке DLL, и что DLL загружается на два процесса, A и B (может, в самом деле, быть два экземпляра одного приложения). Звонков SetGlobalString("Hello from A") . В результате выделяется память для данных CString в контексте процесса а. Держите в разуме что CString сам глобальный и видна для обеих a и B. B вызывает GetGlobalString(sz, sizeof(sz)) . B будет иметь возможность видеть данные, задать A. Это потому что Win32s предлагает не защиты между процессами как Win32 делает. Так вот первая проблема; во многих случаях желательно не иметь одно приложение затрагивают глобальные данные, которые считаются собственностью в другое приложение.
Но подождите, есть больше проблем. Предположим, что сейчас завершает работу. Когда a выходы, используемую память ' strGlobal ' строка становится доступным для системы — то есть, все память, выделенную a процесс освобождается автоматически операционной системой. Это не высвобождается потому, что деструктор CString называется; Он не был вызван еще. Он освобождается просто потому, что приложение, которым он покинул место происшествия. Теперь, если Б под названием GetGlobalString(sz, sizeof(sz)) , он не может получить правильные данные. Другое приложение может использовать эту память для что-то другое.
Явно существует проблема здесь. MFC 3.x используется метод называется локальной памяти потока (TLS). MFC 3.x выделит индекс TLS, который под Win32s действительно действует как процесс локального хранения индекса, даже если он не называется и затем будет ссылаться все данные, основанные на этом индекс TLS. Это похоже на индекс TLS, которая использовалась для хранения данных локального потока на Win32 (см. ниже для получения дополнительной информации по этому вопросу). Это вызвало все библиотеки DLL MFC использовать по крайней мере двух индексов TLS для каждого процесса. Когда вам приходится загрузки многие OLE управления DLL (ocx), вы быстро хватить TLS индексов (доступны только 64). Кроме того MFC пришлось поставить все эти данные в одном месте, в рамках единой структуры. Он был не очень расширяемый и не идеален в его использования TLS индексов.
MFC 4.x решает эту проблему с набором шаблонов классов можно «обернуть» вокруг данных, которые должна быть процессом местные. К примеру можно исправить проблемы, упомянутые выше, написание:
структура CMyGlobalData: государственные CNoTrackObject
{
CString strGlobal;
};
CProcessLocallt;CMyGlobalData > globalData;
ключевое слово __declspec(dllexport) аннулировать SetGlobalString(LPCTSTR lpsz)
{
globalData - > strGlobal = lpsz;
}
ключевое слово __declspec(dllexport)
void GetGlobalString (LPCTSTR lpsz, int cb)
{
lstrcpyn (lpsz, globalData - > strGlobal, cb);
}
MFC реализует это в два этапа. Во-первых есть слой над Win32 Tls * API-интерфейсы (TlsAlloc, TlsSetValue, Tls&GetValueи т.д.) которые используют только два TLS индексы каждого процесса, независимо от того, сколько у вас DLL. Во-вторых, CProcessLocal шаблон предоставляется доступ к этим данным. Он переопределяет оператор gt; Вот то, что позволяет интуитивно понятный синтаксис, который вы видите выше. Все объекты, которые упаковываются в CProcessLocal должен быть производным от CNoTrackObject . CNoTrackObject обеспечивает более низкого уровня распределителя (реализации LocalAlloc/LocalFree) и виртуальный деструктор таким образом, что MFC автоматически может уничтожить все процесс местные объекты, когда процесс завершается. Такие объекты могут иметь пользовательские деструктор, если дополнительной очистки не требуется. Приведенный выше пример не требует, так как компилятор создаст по умолчанию деструктор для уничтожения внедренный объект CString.
Есть другие интересные преимущества такого подхода. Являются не только все CProcessLocal объектов, уничтожены автоматически, они не создаются до тех пор, пока они требуются. CProcessLocal::operator-gt; будет создавать связанный объект впервые его называют и не раньше. В приведенном выше примере это означает, что ' str&Global ' строка не создается до впервые называют SetGlobalString или GetGlobalString . В некоторых случаях это может помочь уменьшить время запуска DLL.
Местные данных потока
Подобно обработки местных данных, локальные данные потока используется когда данные должны быть локальными для данного потока. То есть вам понадобится отдельный экземпляр данных для каждого потока, который получает доступ к этим данным. Это может многократно использоваться в вместо механизмов обширные синхронизации. Если данные не нужно совместно использоваться несколькими потоками, такие механизмы может быть дорогостоящим и ненужным. Предположим, что у нас объект CString (так же, как пример выше). Мы можем сделать его потока местные, обернув его с CThreadLocal шаблон:
структура CMyThreadData: государственные CNoTrackObject
{
CString strThread;
};
CThreadLocallt;CMyThreadData > threadData;
void MakeRandomString()
{
/ / a вид карт shuffle (не один большой)
CString & str = threadData - > strThread;
ул.Empty();
во время (ул.GetLength()! = 52)
{
TCHAR ch = rand() % 52 + 1;
Если (ул.Find(CH) < 0)
str += ch; / / не найден, добавьте его
}
}
Если MakeRandomString был вызван из двух разных потоков, каждый будет «волочить ноги» строка различными способами, не вмешиваясь с другой. Это потому, что есть на самом деле strThread экземпляр каждого потока вместо того, чтобы только один глобальный экземпляр.
Обратите внимание, как ссылка используется для захвата CStrin&g адрес один раз вместо того, один раз в итерации цикла. Код цикла можно было написано с threadData-gt;strThread везде ' str ' используется, но код будет гораздо медленнее в исполнении. Лучше всего для кэширования ссылку на эти данные, когда происходят такие ссылки в петли.
CThreadLocalШаблон класса использует те же механизмы, CProcessLocal делает и те же методы осуществления.
Технические примечания по номеру |nbsp; Технические примечания по категориям