TN058: Implementace MFC stav modulu
[!POZNÁMKA]
Následující technické poznámce nebyly aktualizovány od byla poprvé zahrnuta v dokumentaci online.Proto některé postupy a témata mohou být nesprávné nebo zastaralé.Nejnovější informace je vhodné vyhledat téma zájmu v dokumentaci online index.
Tato technická Poznámka popisuje provedení konstrukce MFC "modul stát".Znalost stavu implementace modulu je rozhodující pro použití MFC sdílených knihoven DLL z knihovny DLL (nebo v procesu serveru OLE).
Před přečtením této poznámky odkazují "Správa stav dat z MFC moduly" v zobrazení, systému Windows a vytváření nových dokumentů.Tento článek obsahuje důležité informace a přehled informací o této problematice.
Přehled
Existují tři typy informací o stavu MFC: stav modulu, stav procesu a stav podprocesu.Někdy tyto typy státu lze kombinovat.Například jeho MFC popisovač mapy jsou místní modul a místní podprocesu.To umožňuje dva různé moduly mají různé mapy v každé jejich podprocesy.
Stav procesu a stav podprocesu jsou podobné.Tyto datové položky jsou věci, které tradičně globální proměnné, ale je třeba mít specifické pro daný proces nebo podproces pro podporu řádné Win32s nebo pro podporu řádné multithreading.Kategorie, které odpovídá dané datové položky v závisí na danou položku a její Požadovaná sémantika s hranicemi procesů a podprocesů.
Stav modulu je jedinečný, může obsahovat skutečně globální státu nebo státu, který je místní proces nebo podproces místní.Kromě toho je možné vypnout rychle.
Stav modulu přepínání
Každý podproces obsahuje ukazatel na stav modulu "aktuální" nebo "aktivní" (logicky ukazatel je součástí místní stav podprocesu v MFC).Tento ukazatel se změní modul hranice, jako je například volání do prvku OLE nebo knihovnu DLL nebo ovládací prvek OLE volání zpět do aplikace aplikace předává podproces provádění.
Aktuální stav modulu přepnutí voláním AfxSetModuleState.Z větší části nebudou nikdy řešit přímo pomocí rozhraní API.MFC, v mnoha případech bude volat jej můžete (WinMain, OLE, míst na AfxWndProcatd.).Provádí se v jakékoli součásti zápis staticky propojením speciální WndProca zvláštní WinMain (nebo DllMain), ví, které modul stát by měl být aktuální.Tento kód můžete zobrazit pohledem na DLLMODUL.CPP nebo APPMODUL.CPP v adresáři MFC\SRC.
Je zřídka chcete nastavit stav modulu a není nastavte ji zpět.Většinu času chcete vlastní modul "push" stát jako aktuální a potom po dokončení "pop" zpět původní kontextu.Je to makro AFX_MANAGE_STATE a speciální třídy AFX_MAINTAIN_STATE.
CCmdTargetmá speciální funkce pro podporu přepínání stav modulu.Zejména CCmdTarget je kořenová třída používá pro automatizaci OLE a OLE COM vstupních bodů.Jako další vstupní bod vystaveny systému, musí tyto vstupní body nastavit stav modulu.Jak se dané CCmdTarget vědět, co by měl být stav modulu "správný"?Odpovědí je, že ji "pamatuje" co "aktuální" stav modulu je, když je vyrobeno, tak, že jej lze nastavit aktuální stav modulu, "pamatovat" hodnota je vyšší tzv.Výsledkem modul uvedeno, že dané CCmdTarget je objekt propojen s je stav modulu, který byl aktuální při byl objekt vytvořen.Přijmout jednoduchý příklad serveru INPROC načtení, vytvoření objektu a voláním své metody.
Načte knihovnu DLL OLE pomocí LoadLibrary.
RawDllMain se nazývá první.Nastaví stav modulu státu známé statického modulu pro knihovnu DLL.Z tohoto důvodu RawDllMain staticky propojeny ke knihovně DLL.
Konstruktor pro tříd přidružené našeho objektu se nazývá.COleObjectFactoryje odvozen z CCmdTarget a následkem toho pamatuje stát, ve kterém modulu byla vytvořena.To je důležité – zdroj tříd je dotaz, vytváření objektů, zná nyní určen stav modulu tak, aby aktuální.
DllGetClassObjectse nazývá získat třídu factory.MFC prohledá seznam factory třídy přidružené k tomuto modulu a vrátí jej.
COleObjectFactory::XClassFactory2::CreateInstance se nazývá.Před vytvořením objektu a jeho vrácení, tato funkce nastaví stav modulu stav modulu, který byl aktuální v kroku 3 (který byl aktuální při COleObjectFactory byla vytvořena).To se provádí uvnitř METHOD_PROLOGUE.
Při vytvoření objektu příliš je CCmdTarget derivátů a stejným způsobem COleObjectFactory zapamatována byl aktivní, které stav modulu tak, aby se tento nový objekt.Nyní ví, kterému státu modul přepnout do objektu vždy, když se nazývá.
Klient obdržel od objektu OLE COM volání funkce jeho CoCreateInstance volání.Při volání objektu používá METHOD_PROLOGUE přepnout stav modulu stejně jako COleObjectFactory nemá.
Jak můžete vidět stav modulu se šíří z objektu na objekt jsou vytvořeny.Je důležité, aby byly odpovídajícím způsobem nastavit stav modulu.Pokud není nastaveno, DLL nebo COM objekt může pracovat špatně MFC aplikace, která volá, nelze najít vlastní zdroje nebo může selhat jinými způsoby miserable.
Poznámka: některé typy knihoven DLL, konkrétně "MFC rozšíření" dll není přepnout stav modulu v jejich RawDllMain (ve skutečnosti, obvykle dokonce nemají RawDllMain).Je to proto, že jsou určeny k chovat ", jako" byly skutečně přítomen v aplikaci, která je používá.Jsou velmi část aplikace, která běží a je úmysl měnit globální stav této aplikace.
Ovládací prvky OLE a další knihovny DLL se velmi liší.Není má měnit stav volající aplikace; aplikace, která je volání i nemusí být aplikace MFC a tak mohou existovat žádný stát změnit.To je důvod, aby byla vyvinuta přepínání stav modulu.
Exportované funkce z knihovny DLL jako jeden, který se otevře dialogové okno v knihovny DLL potřebujete následující kód přidejte na začátek funkce:
AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
To Zamění aktuální stav modulu stát vrátil z AfxGetStaticModuleState do konce aktuálního oboru.
Pokud dojde k potížím s prostředky v knihovnách DLL AFX_MODULE_STATE makro není používán.Ve výchozím nastavení používá MFC načtení prostředku šablony popisovače prostředku hlavní aplikace.Tato šablona skutečně uloženy v knihovně DLL.Hlavní příčina je že MFC a informace o modulu není bylo změněno tak, AFX_MODULE_STATE makro.Popisovače prostředku je obnovit z modulu státu a MFC.Chybný prostředek úchytu použít není přepnutí stav modulu způsobí.
AFX_MODULE_STATEnení nutné umístit všechny funkce v knihovně DLL.Například InitInstance lze volat pomocí MFC kódu v aplikaci bez AFX_MODULE_STATE protože MFC automaticky přesune modul stavu před InitInstance a potom přepínače je zpět po InitInstance vrátí.Totéž platí pro všechny zprávy mapa obslužné rutiny.Pravidelné knihovny DLL mají skutečně zvláštní hlavní okno postup, který před směrováním zprávy automaticky přepne stav modulu.
Místní Data procesu
Místní data proces by takové velké znepokojení ji nebyla pro obtížnost Win32s DLL modelu.V Win32s všech knihoven DLL sdílet globální data, i když je načteno více aplikacemi.To je velmi odlišné od "reálný" Win32 DLL datového modelu, kde každý soubor DLL získá kopii jeho data prostoru do každého procesu, který se připojí ke knihovně DLL.Chcete-li přidat složitost přiděleno haldy v knihovně DLL Win32s dat je ve skutečnosti určitý proces (alespoň pokud přechází vlastnictví).Zvažte následující kód a data:
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);
}
Zvažte, co se stane, pokud výše uvedený kód v nachází v knihovně DLL a že DLL je zaveden dva procesy a a B (by ve skutečnosti být dvě instance téže aplikace).A calls SetGlobalString("Hello from A").Výsledkem je přidělena paměť pro CString v kontextu procesu A.Pamatujte CString sám je globální a je viditelný i a B.Nyní volá b GetGlobalString(sz, sizeof(sz)).B budou moci zobrazit data a nastavení.Je to proto, že nabízí Win32s bez ochrany mezi procesy jako Win32.Je první problém; v mnoha případech není žádoucí, aby jedna aplikace, které ovlivňují globální data, která je považována za vlastněn jinou aplikací.
Existují také další problémy.Řekněme, že nyní ukončí.Když a opustí, paměť použitá "strGlobal' řetězec je k dispozici pro systém –, je všechny paměti přidělené procesu a uvolní automaticky operačním systémem.Není uvolněno, protože CString je volána destruktoru; to nebyl byla volána ještě.Jednoduše uvolněno protože aplikace, která je přidělena opustil scény.Nyní, pokud se nazývá b GetGlobalString(sz, sizeof(sz)), může jej získat platná data.Jiná aplikace může použít tuto paměť něco jiného.
Jasně problém existuje.MFC 3.x používá techniku zvanou podproces místní úložiště (TLS).MFC 3.x by přidělit TLS index, který pod Win32s skutečně funguje jako proces místní úložiště indexu, i když ji není zavolána a pak by referenční všechna data na základě tohoto indexu TLS.Podobný TLS index, který byl použit při ukládání dat místní podproces Win32 (viz další informace o tomto předmětu níže).Příčinou každé knihovny DLL MFC využívají nejméně dva indexy TLS jeden proces.Pokud je účet pro načítání mnoho OLE ovládacího prvku dll (soubory OCX), rychle spustit z indexů TLS (existují pouze 64 k dispozici).Kromě toho bylo MFC umístit tato data na jednom místě, v jedné struktuře.Nebyl velmi extensible a nebyl ideální s ohledem na jeho použití indexů TLS.
MFC 4.x adresy sadu tříd šablon si můžete "wrap" kolem data, která by měla být místní proces.Například nelze pomocí zápisu stanoví výše uvedené potíže:
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 implementuje ve dvou krocích.První je vrstva nad Win32 Tls * rozhraní API (TlsAlloc, TlsSetValue, TlsGetValue, atd) využívající pouze dva rejstříky TLS jeden proces bez ohledu na to, kolik knihovny DLL, které máte.Druhý, CProcessLocal je poskytnut přístup k datům této šablony.Přepíše operátor - > což je co umožňuje intuitivní syntaxi, kterou naleznete výše.Všechny objekty, které jsou baleny ve CProcessLocal musí být odvozen od CNoTrackObject.CNoTrackObjectposkytuje přidělování nižší úrovně (LocalAlloc/LocalFree) a virtuálního destruktoru, MFC může automaticky zničit procesu místní objekty, když je proces ukončen.Tyto objekty mohou mít vlastní destructor, pokud je požadováno další čištění.Výše uvedený příklad nevyžaduje, jedna, protože kompilátor vygeneruje výchozí objekt vložený zničit CString objektu.
Existují jiné zajímavé výhody tohoto přístupu.Nejen, jsou všechny CProcessLocal objekty zničeny automaticky, není vyrobeno dokud jsou potřeba.CProcessLocal::operator->bude konkretizovat přidruženého objektu se nazývá prvním a dříve.V příkladu výše, to znamená, že "strGlobal' řetězec bude až do první sestavit SetGlobalString nebo GetGlobalString se nazývá.V některých případech to může pomoci snížit dobu spuštění knihovny DLL.
Místní Data podprocesu
Podobné zpracování dat místní místní data podprocesu je používán při data musí být místní daný podproces.Samostatnou instanci data je nutné pro každý podproces, který přistupuje k datům.Mnohokrát slouží namísto synchronizace rozsáhlé mechanismy.Data nemusí být sdílen více podprocesů, takových mechanismů může být nákladné a zbytečné.Předpokládejme, že jsme měli CString objektu (podobně jako ukázka výše).Můžeme vytvořit podproces místní podle balení s CThreadLocal šablony:
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
}
}
}
Pokud MakeRandomString byla volána ze dvou různých podprocesů každé by "náhodně" řetězec různými způsoby bez zasahování do druhé.Důvodem je, že je skutečně na strThread instanci na podproces namísto pouze jednu instanci globální.
Poznámka: odkaz slouží k zachycení CString adresa jednou namísto jednou za opakování smyčky.Kód smyčky byla nelze zapsat s threadData->strThread všude "str" se používá, ale by zpomalení spuštění kódu.Je nejlepší odkaz na data v mezipaměti dojde tyto odkazy smyčky.
CThreadLocal Šablona třídy používá stejné mechanismy, CProcessLocal nepodporuje a stejné postupy provádění.