TN058: MFC moduł stanu wykonania

Ta Uwaga techniczna opisuje realizację konstrukcji MFC "moduł Państwa". Zrozumienie realizacji Państwo moduł jest krytyczny przy użyciu MFC współużytkowane biblioteki DLL z biblioteki DLL (lub OLE w procesie serwera).

Przed odczytaniem tę notatkę, przeczytaj "Zarządzanie Państwa danych z MFC moduły" w tworzenia nowych dokumentów, Windows i widoki w Visual C++ Programmer's Guide. Ten artykuł zawiera informacje o użytkowaniu ważne i przegląd informacji na ten temat.

Przegląd

Istnieją trzy rodzaje informacji o stanie usługi MFC: moduł Państwa, stanu procesu i stanu wątku. Czasami te typy Państwa mogą być połączone. Na przykład MFC firmy uchwyt mapy są moduł lokalnych i lokalnej wątku. Pozwala to dwa różne moduły mają różne mapy w każdym z ich wątki.

Stan procesu i stan wątku są podobne. Te elementy danych są rzeczy, które tradycyjnie zmienne globalne, ale muszą wymagać specyficznych dla danego procesu lub wątku dla wsparcia prawidłowego Win32s lub właściwego wsparcia wielowątkowości. Element danych danej mieści się w kategorii zależy od tego elementu i jego pożądanych semantyki w odniesieniu do granic procesów i wątków.

Moduł Państwo jest unikatowy, że może zawierać prawdziwie globalną Państwo lub Państwa, które jest procesem lokalnym lub wątek lokalnych. Również jej mogą być szybko przełączane.

Państwo Moduł przełączający

Każdy wątek zawiera wskaźnik stanu modułu "bieżące" lub "aktywny" (nie jest zaskakujące, wskaźnik jest częścią Państwa lokalnej wątku MFC firmy). Ten wskaźnik jest zmieniany podczas wątku wykonywania przechodzi granica moduł, takich jak aplikacja niestawienie się do formantu OLE lub biblioteka DLL lub formantu OLE oddzwanianie do aplikacji.

Bieżący stan modułu jest włączony przez wywołanie AfxSetModuleState. W większości przypadków będzie można nigdy nie dotyczą bezpośrednio z interfejsem API. MFC, w wielu przypadkach będzie wywoływać je dla Ciebie (w WinMain, punkty wejścia OLE AfxWndProc, itp.). To odbywa się w dowolny składnik piszesz statycznie łącząc w specjalnym, WndProcoraz specjalne WinMain (lub DllMain) który wie, które Państwo moduł powinny być obecne. Ten kod można zobaczyć, przyjmując przyjrzeć się DLLMODUL.CPP lub APPMODUL.CPP w katalogu MFC\SRC.

Jest rzadko, że chcesz ustawić stanu modułu i nie ustawić ją ponownie. Większość czasu chcesz "push" własne moduł Państwa jako bieżący i następnie, po zakończeniu "pop" pierwotnym kontekście wstecz. Robi się to przez makro AFX_MANAGE_STATE i specjalnej klasy AFX_MAINTAIN_STATE.

CCmdTarget posiada specjalne funkcje do obsługi przełączania stanu modułu. W szczególności CCmdTarget jest, że klasa głównego używana dla automatyzacji OLE i modelu COM OLE punkty wejścia. Jak inne punkty wejścia narażonych na system, te punkty wejścia musi ustawić stan poprawnego modułu. Jak danego CCmdTarget wiedzieć, jakie powinny być Państwo "poprawny" moduł? Odpowiedź na pytanie brzmi, że to "pamięta" co "bieżący" Stan modułu jest, gdy jest skonstruowany, takie, że można ustawić bieżący stan modułu do tego "zapamiętanych" wartość, gdy jest później nazywany. W rezultacie Państwa moduł, który dany obiekt CCmdTarget jest skojarzony z jest Państwo moduł, który był bieżącego, kiedy obiekt został zbudowany. Przeanalizujmy prosty przykład ładowanie serwera INPROC, tworzenia obiektu i wywołanie metody jego.

  1. Biblioteka DLL jest ładowany przez OLE przy użyciu LoadLibrary.

  2. RawDllMain nazywa się po raz pierwszy. To ustawia stan modułu Państwu znane statycznych modułu dla tej biblioteki DLL. Z tego powodu RawDllMain statycznie jest połączony z biblioteki DLL.

  3. Konstruktor dla fabryki klas skojarzonych z naszych obiektu nazywa się. COleObjectFactory pochodzi z CCmdTarget i w efekcie go pamięta, w których Państwo moduł został skonkretyzowany. Jest to ważne — zapytany Fabryczna klasa do tworzenia obiektów teraz wie jakie Państwo moduł ma być bieżący.

  4. DllGetClassObject nazywa się uzyskanie Fabryczna klasa. MFC przeszukuje listę fabryki klasy skojarzone ze niniejszego modułu i zwraca go.

  5. Nazywa się COleObjectFactory::XClassFactory2::CreateInstance . Przed utworzeniem obiektu i zwracania, ta funkcja ustawia stan modułu Państwu moduł, który był w kroku 3 (jeden, że bieżące, gdy COleObjectFactory został skonkretyzowany). Robi się to wewnątrz z METHOD_PROLOGUE.

  6. Gdy tworzony jest obiekt, zbyt jest pochodną CCmdTarget i w ten sam sposób COleObjectFactory pamiętane, które Państwo moduł był aktywny, co powoduje to nowy obiekt. Teraz obiekt wie, które Państwo moduł, aby przełączyć się do ilekroć jest nazywany.

  7. Klient wywołuje funkcję na obiekcie OLE COM, otrzymanej z jego funkcji CoCreateInstance . Po wywołaniu obiektu zastosowano METHOD_PROLOGUE , aby przełączyć stan modułu, podobnie jak COleObjectFactory jest.

Jak widać, stan modułu są propagowane z obiektu do obiektu, są one tworzone. Należy odpowiednio ustawić stanu modułu. Jeśli nie jest ustawiona, biblioteka DLL lub COM obiekt może oddziaływać słabo z aplikacji MFC, który dzwoni, może być niemożliwe do znalezienia środków własnych lub mogą nie działać w inny sposób miserable.

Uwaga niektórych rodzajów biblioteki dll, szczególnie "MFC rozszerzenie" dll nie przełączyć stan modułu w ich RawDllMain (w rzeczywistości są zazwyczaj nawet nie RawDllMain). Jest to, ponieważ mają one zachowywać się "tak" były faktycznie obecna w aplikacji, która korzysta z nich. Są one bardzo częścią aplikacji, która działa i jest ich zamiarem do modyfikowania stanu globalnej tej aplikacji.

OLE formanty i inne biblioteki DLL są bardzo różne. Czy chcesz zmodyfikować stan aplikacji wywołujących; Aplikacja, która je wywołuje nie może nawet Aplikacja MFC i tak więc może być żadne Państwo do modyfikowania. Właśnie dlatego, że moduł Państwo przełączania został wynaleziony.

Dla eksportowanych funkcji z biblioteki DLL, który uruchamia okno dialogowe w bibliotece DLL należy dodać poniższy kod do początku funkcji:

AFX_MANAGE_STATE (AfxGetStaticModuleState ())

Zamienia bieżący stan modułu z państwem, w powrócił z AfxGetStaticModuleState do końca bieżącego zakresu.

Problemy z zasobami w bibliotekach DLL wystąpi, jeśli makro AFX_MODULE_STATE nie jest używany. Domyślnie MFC używa uchwyt zasobów aplikacji głównej załadować szablon zasobów. Ten szablon jest faktycznie przechowywane w bibliotece DLL. Główną przyczynę jest, że informacje o stanie modułu MFC firmy nie został przełączane przez makro AFX_MODULE_STATE . Uchwyt zasobów jest odbierana od stanu modułu MFC firmy. Nie przełączania stanu modułu powoduje uchwyt Zły zasób, aby służyć.

AFX_MODULE_STATE nie trzeba umieszczać w każdej funkcji w bibliotece DLL. Na przykład InitInstance może być wywołana przez kod MFC w aplikacji bez AFX_MODULE_STATE , ponieważ MFC jest automatycznie przenoszony stan modułu przed InitInstance i następnie przełączniki go ponownie po InitInstance zwraca. To samo dotyczy wszystkich wiadomości mapę obsługi. Regularne biblioteki DLL faktycznie mieć procedury specjalne okno wzorca, który automatycznie przełącza stan modułu przed przesłaniem wiadomości.

Proces dane lokalne

Dane lokalne proces mogłoby nie być takie wielkim problemem miał on nie zostały na trudności w modelu Win32s DLL. W Win32s wszystkie biblioteki DLL udostępnianie ich danych globalnych, nawet, jeśli ładowany przez wiele aplikacji. To zupełnie różne od "real" modelu danych biblioteki DLL systemu Win32, jeżeli każdej biblioteki DLL otrzyma oddzielną kopię swojej przestrzeni danych w każdym procesie, który dołącza do biblioteki DLL. Aby dodać do złożoności, dane przydzielone na sterty w bibliotece DLL Win32s jest w rzeczywistości proces (co najmniej tak dalece jak własności przechodzi). Należy wziąć pod uwagę następujące dane i kod:

statyczne CString strGlobal; / / at zakresu pliku

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

__declspec(dllexport)
void GetGlobalString (LPCTSTR lpsz, int cb)
{
   lstrcpyn (lpsz, strGlobal, cb);
}

Należy rozważyć, co się dzieje, jeśli powyższy kod znajduje w się w bibliotece DLL i że biblioteka DLL jest ładowany przez dwóch procesów a i B (mogłyby w rzeczywistości będą dwa wystąpienia tej samej aplikacji). Wywołania SetGlobalString("Hello from A") . W rezultacie, pamięci zarezerwowanej dla danych CString w kontekście procesu A. Keep pamiętać, że CString sobie jest globalne i widoczne zarówno a i B. Teraz b wywołuje GetGlobalString(sz, sizeof(sz)) . B będą mogli wyświetlać dane, A ustawiona. Jest to spowodowane Win32s oferuje żadnej ochrony między procesami tak jak robi to Win32. Tak to pierwszy problem; w wielu przypadkach nie jest pożądane posiadanie jednej aplikacji wpływają na danych globalnych, która uważana jest własnością innej aplikacji.

Ale Chwileczkę — istnieje więcej problemów. Załóżmy, że obecnie kończy pracę. Kiedy a opuszcza, pamięci używanej przez ' strGlobal ' ciąg jest dostępne dla systemu — oznacza to, że wszystkie pamięć przydzielona przez proces a jest zwolniona z automatycznie przez system operacyjny. Nie jest zwalniane, ponieważ jest wywoływana destruktor CString ; nie został on wywołany jeszcze. Jest zwalniane po prostu ponieważ aplikacji, która przydzielona go opuścił sceny. Teraz jeśli b o nazwie GetGlobalString(sz, sizeof(sz)) , mogą nie być poprawne dane. Inna aplikacja może używać tej pamięci do czegoś innego.

Oczywiście jest tu problem. MFC 3.x stosowane techniki zwanej pamięci lokalnej wątku (TLS). MFC 3.x czy przydzielić indeks TLS, że w ramach Win32s naprawdę działa jako indeks proces local storage, nawet jeśli go nie nazywa i następnie będzie odwołać wszystkie dane oparte na indeks TLS. To jest podobne do indeksu TLS, który był używany do przechowywania danych lokalne dla wątku dla platformy Win32 (patrz poniżej aby uzyskać więcej informacji na ten temat). Przyczyną co MFC DLL korzystanie z co najmniej dwóch wskaźników TLS na proces. Gdy konto do ładowania bibliotek wielu formantu DLL OLE (formanty ocx), szybko uruchomić można się wskaźników TLS (brak dostępnych tylko 64). Ponadto MFC musiał umieścić wszystkie te dane w jednym miejscu, w jednej strukturze. Nie był bardzo rozszerzonego i nie były idealne w odniesieniu do jego stosowania wskaźników TLS.

MFC 4.x adresy to zestaw szablonów klasy Użytkownik może "oblewać" dane, które powinny być procesem lokalnym. Na przykład problem wymienione powyżej można by ustalić pisząc:

struct CMyGlobalData: CNoTrackObject publicznych
{
   CString strGlobal;
};
CProcessLocallt;CMyGlobalData > globalData;

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

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

MFC implementuje ten w dwóch etapach. Pierwszy z nich to warstwę nad Win32 Tls * API (TlsAlloc, TlsSetValue, Funkcje Tls&GetValueitp.), który służy tylko dwa indeksy TLS na proces, niezależnie od tego, jak wiele bibliotek DLL masz. Po drugie, CProcessLocal szablonu jest pod warunkiem, aby uzyskać dostęp do tych danych. Zastępuje ona operatora BT; co jest, co umożliwia intuicyjny składni, którą widzisz powyżej. Wszystkie obiekty, które są pakowane przez CProcessLocal musi pochodzić od CNoTrackObject . CNoTrackObject zapewnia alokatora niższego poziomu (Funkcja LocalAlloc/funkcji LocalFree) i wirtualnych destruktor takie, że MFC automatycznie mogą niszczyć proces obiektów lokalnych, gdy proces zostaje zakończony. Takie obiekty mogą mieć niestandardowe destruktor, jeśli wymagane jest dodatkowe oczyszczanie. Powyższy przykład nie wymaga jednego, ponieważ kompilator generuje destruktor domyślny do zniszczenia osadzonego obiektu CString.

Istnieją inne interesujące korzyści tego podejścia. Są nie tylko wszystkie CProcessLocal obiektów zniszczone automatycznie, nie są skonstruowane dopóki są potrzebne. CProcessLocal::operator-gt; będzie utworzyć wystąpienie obiektu skojarzonego nazywa się po raz pierwszy, a nie wcześniej. W powyższym przykładzie, który oznacza że ' str&Global ' ciąg nie można skonstruować aż do SetGlobalString lub GetGlobalString jest wywoływana po raz pierwszy. W niektórych przypadkach to pomoże, zmniejszyć czas uruchamiania biblioteki DLL.

Dane lokalne wątku

Podobne do przetwarzania danych lokalnych, dane lokalne wątku jest używana podczas danych musi być lokalnie do danego wątku. Oznacza to, że trzeba osobne wystąpienie dane dla każdego wątku, który uzyskuje dostęp do danych. To wielokrotnie można w miejsce mechanizmów rozległe synchronizacji. Jeśli dane nie muszą być udostępnione przez wiele wątków, takie mechanizmy może być kosztowne i niepotrzebne. Załóżmy, że mieliśmy obiektu CString (podobnie jak próbki powyżej). Abyśmy mogli go wątku lokalnych przez zawijania z CThreadLocal szablonu:

struct CMyThreadData: CNoTrackObject publicznych
{
   CString strThread;
};
CThreadLocallt;CMyThreadData > threadData;

void MakeRandomString()
{
   / / a rodzaj karty losowo (nie jeden Wielki)
   CString & str = threadData - > strThread;
   str.Tego powinna być stosowana;
   podczas (str.GetLength()! = 52)
   {
      TCHAR ch = rand() % 52 + 1;
      Jeżeli (str.Find(ch) < 0)
         str += ch; / / nie znaleziono, dodaj go
   }
}

Jeśli MakeRandomString została wywołana z dwóch różnych wątków, każdy będzie "losowo" ciąg w różny sposób bez zakłócania drugiej. Jest to spowodowane jest rzeczywiście strThread instancji na wątku zamiast tylko jedno wystąpienie globalnego.

Należy zwrócić uwa&gę, jak odwołanie jest używany do przechwytywania adres CString , raz zamiast raz iteracji pętli. Kod pętla może został napisany z threadData-gt;strThread wszędzie ' str ' jest używany, ale kod byłby znacznie wolniej w realizacji. Najlepiej jest pamięci podręcznej odniesienie do danych wystąpieniu takiego odniesienia w pętli.

CThreadLocalSzablonu klasy używa tych samych mechanizmów, CProcessLocal czy i te same techniki realizacji.

Uwagi techniczne przez liczbę |nbsp; Uwagi techniczne według kategorii

Index