TN058: MFC модуль стан реалізації

Цей технічних записці описується виконання конструкції MFC "модуль держави". Розуміння здійснення державою модуль має вирішальне значення для використання MFC спільні бібліотеки DLL з DLL (або в процесі OLE-сервер).

Перш ніж читати цю примітку, зверніться до "Управління на державних даних з MFC модулі" у , створення нових документів, Windows і думки в Visual C++ програміста керівництво. Ця стаття містить відомості про важливі використання і огляд інформації з цього питання.

Огляд

Є три типи відомостей про стан MFC: модуль держави, стану процесів та режим обговорення. Іноді можна поєднувати ці типи стан. Наприклад, MFC s Дескриптор карти є модуль місцевих і потік місцевих. Це дозволяє два різні модулі мають різні карти в кожному з їх теми.

Процес держави та режим обговорення схожі. Ці елементи даних є те, що традиційно були глобальних змінних, але мають повинні бути специфічними для даного процесу або потоку для підтримки належного Win32s або для належної підтримки багатопоточності. Якої категорії елемента даного даних вписується в залежить від цього елемента та його бажаного семантику щодо процесу і потік межі.

Модуль держави є унікальною, оскільки вона може містити глобальний стан або державного, місцевих процесу або потоку місцевих. Також це може бути включений швидко.

Модуль стану перемикання

Кожна тема містить вказівник на стан "поточні" або "активні" модуль (не дивно, вказівник є частиною MFC в місцевий режим обговорення). Вказівник змінюється, коли потоку виконання проходить модуль межі, наприклад, заяву, що ставлячи на керування OLE або DLL або на керування OLE кличе назад до програми.

Поточний стан модуля увімкнено за номером AfxSetModuleState. Здебільшого, ви ніколи не буде займатися безпосередньо з API. MFC, у багатьох випадках буде назвати це для вас (в WinMain, OLE запис очок, AfxWndProc, тощо.). Це робиться в будь-який компонент ви пишете на статично зразки в спеціальний, WndProcі спеціальні WinMain (або DllMain) що не знає, який модуль держава повинна бути поточного. Ви можете побачити цей код поглянути на DLLMODUL.CPP або APPMODUL.CPP в каталозі MFC\SRC.

Це рідкісне, що ви хочете, щоб установити стан модуля не набір знаків і назад. Більшу частину часу, ви хочете, щоб "натиснути" власні модуль держава як поточний і потім, після того, як ви зробили, "поп" початковому контексті назад. Це робиться шляхом макрос AFX_MANAGE_STATE і самостійно AFX_MAINTAIN_STATE.

CCmdTarget має спеціальні можливості для підтримки модуль стану перемикання. Зокрема, CCmdTarget це кореневий клас для автоматизації OLE і OLE COM точки входу. Як точку входу піддаватися системи, ці точки входу необхідно встановити правильний модуль держави. Як даного 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, конкретно "MFC розширення" DLL не перейти модуль держави у своїх RawDllMain (насправді, вони зазвичай навіть не RawDllMain). Це тому, що вони мають на меті вести себе "як якби" вони були фактично присутні у додаток, що використовує їх. Вони є дуже великою частиною застосунок, який працює, і це свій намір змінити цей застосунок глобальний стан.

OLE елементи керування та інші DLL є дуже різними. Вони не хочуть, щоб змінити IP-телефонії додатків держави; Програма, яка закликає їх може навіть не бути MFC програми і так може бути жодна держава, щоб змінити. Саме з цієї причини, що модуль стану перемикання була винайдена.

Експортовані функції з DLL, такі, як той, який запускає діалогове вікно у ваш DLL вам потрібно додати наступний код на початок функції:

AFX_MANAGE_STATE (AfxGetStaticModuleState ())

Це свопи поточний стан модуля з державою, що повернувся з AfxGetStaticModuleState до кінця поточного сфера.

AFX_MODULE_STATE макрос використовується не можуть виникнути проблеми з ресурсами в DLL. За промовчанням 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)
недійсним GetGlobalString (LPCTSTR lpsz, int cb)
{
   lstrcpyn (lpsz, strGlobal, комерційний банк);
}

Вважають, що станеться, якщо наведений вище код в розташований в DLL і що DLL завантажується на двох процесів, A та B (це по суті, може бути два екземпляри цього додатка). Дзвінки на SetGlobalString("Hello from A") . Як результат, пам'ять виділяється для CString даних у контексті процесу а. майте на увазі, що CString сама є глобальною і відображається як a та b. Тепер називає b GetGlobalString(sz, sizeof(sz)) . B буде мати можливість бачити дані, які Сет. Це тому, що Win32s надає ніякого захисту між процесами, як це робить Win32. Так що це перша проблема; у багатьох випадках це не бажано мати однієї програми впливають на глобальні дані, яка має бути власником іншої програми.

Але чекати-більше проблем. Давайте припустимо, що зараз виходить за межі. Коли виходів A, пам'ять, що використовується на ' strGlobal ' рядок стає доступним для системи — тобто всі пам'яті процесу a звільняється автоматично операційною системою. Це не звільнені, оскільки під назвою CString деструктор; Він не був закликав ще. Він звільнив просто тому, що застосування, які виділяються його залишив сцену. Тепер, якщо b називається GetGlobalString(sz, sizeof(sz)) , він не може отримати припустимі дані. Деякі цього застосунку можливо, використовували цю пам'ять за щось інше.

Очевидно, існує проблема тут. 3. X MFC використовувати технологію яка називається локального сховища ниті (TLS). 3. X MFC б виділити TLS індексу, що під Win32s дійсно діє як процес локального сховища індекс, навіть якщо він не називається, що і потім буде посилатися на всіх даних на основі TLS індекс. Це схоже на TLS індексу, яка була використана, щоб зберігати дані потік локальна в Win32 (див. нижче для отримання додаткової інформації з цього питання). Це викликало кожен MFC DLL використовувати принаймні два TLS покажчики на процес. Коли вам рахунок для завантаження багато OLE керування DLL (OCXs), ви швидко біжіть з TLS покажчики (доступні лише 64). Крім того, MFC мали місце всіх цих даних в одному місці, в єдину структуру. Це було не дуже на розширюваним і не ідеальний зв'язку з його використання показників TLS.

MFC 4. x адрес, це набір шаблонів класу, ви можете "обтікати" дані, які повинні бути процес місцевих. Наприклад, цю проблему говорилося вище можуть бути встановлені написання:

структура CMyGlobalData: Громадська CNoTrackObject
{
   CString strGlobal;
};
CProcessLocallt;CMyGlobalData > globalData;

__declspec(dllexport) до втрати SetGlobalString(LPCTSTR lpsz)
{
   globalData - > strGlobal = lpsz;
}

__declspec(dllexport)
недійсним GetGlobalString (LPCTSTR lpsz, int cb)
{
   lstrcpyn (lpsz, globalData - > strGlobal, комерційний банк);
}

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;

недійсним MakeRandomString()
{
   / / роду shuffle картки (не більше одного)
   CString & вул = threadData - > strThread;
   вул.Порожній ();
   під час (вул.GetLength()! = 52)
   {
      TCHAR ch = rand() % 52 + 1;
      Якщо (вул.FIND(CH) < 0)
         ch + = вул; / / не знайдено, додати його
   }
}

Якщо MakeRandomString називається з двома різні теми, кожен буде "shuffle" рядок різними способами без втручання іншого. Це тому, що є насправді є strThread інстанції на потік, замість того, щоб просто примірника глобальної.

Зверніть увагу, як посилання використовується для захоплення CStrin&g адресу раз, замість того, щоб один раз на циклу. Цикл код можна було б написати з threadData-gt;strThread скрізь ' str ' використовується, але код буде набагато більш повільними у виконання. Найкраще в кеші посилання на дані, коли такі посилання відбуватися петля.

В CThreadLocal клас шаблон використовує той же механізми, CProcessLocal робить і ті ж методи реалізації.

Технічні примітки за номером |nbsp; Технічні примітки за категоріями

Index